Clases y Objetos

Programación Orientada a Objetos

La programación orientada a objetos (POO, u OOP según sus siglas en inglés) es un paradigma de programación que viene a innovar la forma de obtener resultados. Los objetos manipulan los datos de entrada para la obtención de datos de salida específicos, donde cada objeto ofrece una funcionalidad especial.

La modularidad es la capacidad de dividir el problema en pequeñas partes independientes entre sí, esto va más allá de la encapsulación de un simple procedimiento de la programación estructurada que hemos visto hasta ahora. Aquí se encapsula un conjunto de operaciones y datos que tienen mucha relación entre sí formando un módulo.

Un lenguaje basado en objetos nos da la posibilidad de definir objetos y realizar operaciones sobre ellos y es similar al paradigma de abstracción de datos. La adición de de los conceptos de de clases y herencia nos hace hablar de lenguajes orientados a objetos. Un objeto es la instancia de una clase.

Mediante la herencia podemos reutilizar el comportamiento de una clase en la definición de nuevas clases.

Muchos de los objetos pre-diseñados de los lenguajes de programación actuales permiten la agrupación en bibliotecas o librerías, sin embargo, muchos de estos lenguajes permiten al usuario la creación de sus propias bibliotecas.

Está basada en varias técnicas, incluyendo herencia, cohesión, abstracción, polimorfismo, acoplamiento y encapsulamiento.

Los objetos son entidades que tienen un determinado «estado», «comportamiento (método)» e «identidad»:

  • La identidad es una propiedad de un objeto que lo diferencia del resto; dicho con otras palabras, es su identificador (concepto análogo al de identificador de una variable o una constante).
  • Los métodos (comportamiento) y atributos (estado) están estrechamente relacionados por la propiedad de conjunto. Esta propiedad destaca que una clase requiere de métodos para poder tratar los atributos con los que cuenta.

Una clase es una plantilla para la creación de objetos de datos según un modelo predefinido. Las clases se utilizan para representar entidades o conceptos. Cada clase es un modelo que define un conjunto de variables (el estado), y métodos apropiados para operar con dichos datos (el comportamiento). Cada objeto creado a partir de la clase se denomina instancia de la clase.

Las clases son un pilar fundamental de la programación orientada a objetos. Permiten abstraer los datos y sus operaciones asociadas al modo de una caja negra. Los lenguajes de programación que soportan clases difieren sutilmente en su soporte para diversas características relacionadas con clases. La mayoría soportan diversas formas de herencia.

C++ está diseñado para la programación orientada a objetos (POO), y en ese paradigma, todas las entidades que podemos manejar son objetos. Los punteros en C++ sirven para señalar objetos, y también para manipularlos.

La OOP tiene tres características básicas que se suelen presentar como Encapsulación, Polimorfismo y Herencia.

  • La encapsulación es un procedimiento por el cual, los datos y las funciones se encierran en un contenedor llamado objeto, que usamos para aislar ambos elementos de la manipulación exterior y forzar a que esta se haga de un modo controlado y validado por nosotros mismos. El mecanismo que C++ utiliza para encapsular estos objetos se llaman clases.
  • Se suele definir el polimorfismo como: Un interface único, múltiples métodos. Y es algo que reduce la complejidad de los programas de una forma notable. A usar las funciones u operadores de diferentes maneras dependiendo del tipo de los datos, le llamamos polimorfismo y cuando redefinimos un operador o función para comportarse de forma diferente con esos datos, decimos que el operador esta sobrecargado (Overloaded)- Por ejemplo en C++ podemos sumar números enteros o números float con el mismo símbolo “+”, y nos parece tan normal, pero en realidad los procedimientos que se aplican son completamente distintos.
  • La herencia es un proceso por el cual un objeto hereda las propiedades y métodos de otro, sin necesidad de volverlas a definir desde el principio.

Ejemplo de uso de un objeto: Imaginemos que hemos hecho un coche coche controlado por bluetooth y tenemos 5 botones para las 4 direcciones y parar. Creamos una clase llamada ‘Coche’ que se inicializa con los pines donde conecto los dos motores del coche.

 
#include “Coche.h”

Coche MiCocheRC(6,7)

void setup() {
MiCoche.Arranca()
}

void loop() {
char valor = leeBluetooth();

switch (valor) {
    case ‘F’:
      MiCoche.Adelante();
      break;
    case ‘R’:
      MiCoche.Derecha();
      break;
    case ‘L’:
       MiCoche.Izquierda();
      break;
    case ‘B’:
       MiCoche.Atras();
      break;
    case ‘S’:
       MiCoche.Para();
      break;
  }
}

Dentro del fichero ‘Coche.h’ se define la clase y las variables y métodos. Dentro del fichero ‘Coche.cpp’ está el código para mover los motores según cada una de las 5 funciones que he definido.

Más información:

Clases y Objetos en Arduino

Antes de que empecemos a hablar sobre Clases y Objetos, es importante insistir en que, la OOP no es tanto un lenguaje de programación diferente, sino más bien, una manera diferente de organizar tus programas y tus ideas, de acuerdo con unos principios guía que nos permiten modelar nuestro código de un modo distinto a como lo hemos hecho hasta ahora.

Para definir las Clases, existen una serie de reglas y de nuevas instrucciones, pero por lo demás el lenguaje sigue siendo el de siempre.

Si queremos hacer un contador en Arduino, creamos una variable global llamada “contador”. Pero esto ilustra bastante bien el problema de que si quiero 6 contadores voy a necesitar 6 variables globales. La idea en OOP es crear una Clase que nos permita definir Objetos tipo Contador que se pueda reutilizar y que nos permita mezclar diferentes contadores en un mismo programa.

Una de las ideas básicas tras la OOP es encapsular los datos y las funciones (o propiedades y métodos) de nuestro programa en un contenedor común, y más importante aún, aplicamos el principio de: “Esconder los datos y mostrar los métodos o funciones”.

Cuando definimos Clases, veremos que hay partes que son públicas y otras que son privadas. La sintaxis para definir la Clase contador que nos ocupa:

 
class Contador{
  private:
    int N;

  public:
    void SetContador( int n){
      N = n;
    }

    void Incrementar(){
      N++;
    }

    int GetCont(){
      return (N);
    }
};

Debajo de la cláusula “private:” viene las variables y funciones ocultas al exterior. Solo pueden ser invocadas desde el interior de la clase, es decir no se pueden ejecutar por una llamada exterior. Y lo contrario ocurre con lo que definamos tras la cláusula “public:”

Vamos a definir una variable privada llamada N, que llevará la situación del contador, y después necesitaremos los métodos necesarios para trabajar con ella. En principio vamos a definir tres funciones públicas: Una que ponga el contador a un valor dado antes de nada, Otra que sirva para incrementar el contador, y otra tercera para que nos entregue el valor del contador en un momento dado.

La variable N se comporta como si fuera una variable global pero sólo dentro del ámbito de la Clase (Encapsulación), y como está definida como private es inaccesible desde el exterior (Cualquier intento de llegar a ella causará un error del compilador)

Para usar esta clase, primero se instancian tantas ocurrencias de la clase como queramos.

 
#include "Contador.h"

Contador C1,C2;

void setup()
   {
      C1.SetContador(10);
      C2.SetContador(100);
   }
void loop()
   { 
      C1.Incrementar() ;
      Serial.print("C1 = ") ; Serial.println(C1.GetCont());

      C2.Incrementar() ; C2.Incrementar() ; C2.Incrementar();
      Serial.print("C2 = ") ; Serial.println(C2.GetCont());
   }

Usamos el “.” para referir la función a la que queremos llamar, con el Objeto al que se le aplica, como hemos visto antes en otros programas aunque sin entrar en muchos detalles.

Podemos definir una función que se ejecuta siempre que se crea un objeto, y es tan habitual que tiene nombre. Se le llama Constructor de la Clase, y para ello basta con llamarla igual que la Clase (Sin tipo):

 
Class Contador{
  private:
    int N;   

  public:
    Contador( ){        // Constructor
      N = 0;
    }
 
    void SetContador(int n){
      N = n;
    }

    void Incrementar(){
      N++;
    }

    int GetCont(){
      return(N);
    }
};

Una peculiaridad de los constructores es que no tienen un tipo definido, otra de las razones por las que el compilador sabe que es un constructor.

En este caso cuando hago Contador C1,C2 ; ya está inicializado el contador a 0 que lo hace el propio constructor.

Ejercicio: Hacer un programa que cuente las pulsaciones de los botones A y B usando la clase contador guardada en un fichero “Contador.h”

Soluciónhttps://github.com/jecrespo/aprendiendoarduino-Curso_Programacion_Arduino/tree/master/Ejercicio11-Clase_Contador

Ver que necesito dos funciones detecta flanco y no puedo usar solo una porque si llamo a una y a otra simultáneamente el valor static se mantiene entre la llamada de una y otra lo que hace que falle. Para resolver este problema, hacer una clase DetectaFlanco y entonces puedo reutilizar el código ya que cada vez que instancio una nueva clase es como una función nueva.

Ejercicio: Hacer el programa anterior pero creando  una clase llamada DetectaFlanco en un fichero “DetectaFlanco.h”

Soluciónhttps://github.com/jecrespo/aprendiendoarduino-Curso_Programacion_Arduino/tree/master/Ejercicio12-Clase_Detecta_Flanco

Cuando las clases y las funciones miembro son tan pequeñas y sencillas como en este caso, la forma que hemos visto de definirlas puede valer, pero en seguida se quedará corta. Por eso podemos declarar las funciones y variables miembros en la declaración de Clase, y definirlas fuera para mayor comodidad y evitar errores de sintaxis complicados de detectar.

Podemos reescribir la clase Contador así:

Fichero “Contador.h”:

 
class Contador
   {  private:
         int N ;

      public:
         Contador( ) ;               	// Constructor
         void SetContador( int n) ;  	// Declaracion de funcion externa
         void Incrementar() ;        	// Declaracion de funcion externa
         int GetCont() ;             	// Declaracion de funcion externa
   } ;

Fichero “Contador.cpp”:

 
#include <Contador.h>
 
  void Contador::SetContador( int n)
        {  N = n ;	}

   void Contador::Incrementar()
        {  N++ ; }
 
   int Contador::GetCont()
        { return (N) ;}

Declaramos las funciones miembros dentro de la Clase (Para informar al compilador), pero no incluimos su código aquí, porque sería muy confuso en cuanto crezcan de tamaño (Pero fijaros que ahora hay un punto y coma al final de las declaraciones que antes no había).

En cualquier otro lugar podemos definir esas funciones sin más que hacer referencia a la Clase a la que pertenecen usando el operador ‘::’ (Scope Operator u Operador Ámbito) y el compilador entiende que son miembros de la clase que precede al operador. Este operador le indica al compilador, que estas funciones o variables son miembros de la clase, y sólo pueden invocarse de acuerdo a las condiciones que se especifican en la declaración de la Clase (Que debe coincidir con esta claro está).

Si editais cualquiera de las librerías de Arduino, encontrareis que ésta es la forma habitual de programar las clases y librerías.

Soluciónhttps://github.com/jecrespo/aprendiendoarduino-Curso_Programacion_Arduino/tree/master/Ejercicio12-Clase_Detecta_Flanco_cpp

Clase Serial Arduino

Un ejemplo de clase es una que usamos habitualmente, la clase Serial: https://www.arduino.cc/en/Reference/Serial que está definida en los ficheros:

Por lo tanto cuando estamos Serial.begin(9600) estamos llamando al método begin del objeto Serial. En este caso no hemos hecho un include del fichero donde está incluida esta clase porque el IDE lo incluye automáticamente en el proceso de compilación ni tampoco hacemos una declaración del objeto con el constructor, puesto que también lo hace el IDE de Arduino.

9 comentarios en “Clases y Objetos

  1. Pingback: Crear Librerías Arduino | Aprendiendo Arduino

  2. Pingback: Crear Librerias Arduino | Aprendiendo Arduino

  3. Pingback: Saber más Arduino Avanzado 2018 | Aprendiendo Arduino

  4. Pingback: Saber Más Gestión de Proyectos con Arduino | Aprendiendo Arduino

  5. Pingback: 1.Clases y objetos – Ejercicios Para iniciar en la programación

  6. Pingback: Actividad de recuperación Unidad 2 – jpGordillo

Deja un comentario

Este sitio utiliza Akismet para reducir el spam. Conoce cómo se procesan los datos de tus comentarios.