En este artículo vamos a hacer un breve repaso de la programación orientada a objetos con C++, pero que es aplicable a cualquier lenguaje orientado a objetos. Muchos desarrollamos software orientado a objetos, pero muchas veces olvidamos los principios básicos de este metodología de programación y viene bien recordarlos para hacer un buen diseño de software.
Clases y Objetos
Una clase es un conjunto de atributos (datos) y comportamientos (métodos) que juntas forman un todo útil y significativo. Una clase es una especificación que describe cómo las instancias individuales de la clase, conocidos como objetos, se debe construir. Por ejemplo, su mascota Rover es una instancia de la clase "perro". Así, hay una relación de uno a varios entre una clase y sus instancias.
Encapsulación
Encapsulación significa que un objeto presenta sólo una interfaz limitada con el mundo exterior, los detalles del estado interno del objeto y la implementación quedan ocultos. Esto es bueno para el usuario de la clase que solo debe conocer esta interfaz para usarla y no como está implementada dicha clase. También permite al programador que escribió la clase asegurar que las instancias de la misma tengan un estado consistencia lógica
Herencia
La herencia permite que una nueva clase se defina como una extensión de una clase preexistente. La nueva clase modifica o extiende los datos, la interfaz y/o el comportamiento de una clase existente. Si la clase Hija extiende de la clase Padre se dice que Hija hereda o deriva de la clase Padre. En esta relación a la clase Padre se le conoce como clase base o superclase, y la clase Hija como clase derivada o subclase. Evidentemente la herencia conduce a la jerarquía (forma de árbol) con varias relaciones entre clases.
Herencia crea una "es-un" entre clases. Por ejemplo, un círculo es un tipo de Figura. Así que si estuviera escribiendo una aplicación de dibujo 2D, probablemente tendría sentido para derivar la clase Circulo de una clase base llamada Figura.
Podemos dibujar diagramas de jerarquías de clases utilizando las convenciones definidas en el Lenguaje de Modelado Unificado (UML). En esta notación, un rectángulo representa una clase, y una flecha con una cabeza triangular hueco representa la herencia. La flecha va desde las clases hija a la clase Padre. Véase la figura para ver un ejemplo de una jerarquía de clases sencilla representado como un diagrama de clases UML estático.
Herencia múltiple
Algunos lenguajes de programación sportan herencia múltiple (HM), lo que significa que una clase puede tener más de una clase padre. En teoría la HM puede ser muy elegante, pero en la práctica este tipo de diseño por lo general da lugar a una gran confusión y dificultades técnicas (véase Herencia múltiple). Esto es debido a que la herencia múltiple transforma un simple árbos de clases en un grafo potencialmente complejo. Un grafo de clases será de todo menos un simple árbol, por ejemplo, un diamante mortal (http://es.wikipedia.org/wiki/Problema_del_diamante), en el que la clase derivada termina con dos copias de la clase base abuelo (ver figura). En C++, la herencia virtual permite evitar la duplicación de los datos de los abuelos.
La mayoría de desarrolladores de C++ evita la herencia múltiple por completo o solo la permiten de una forma muy limitada. Una regla común es solo permitir clases simple, sin padres, que se multipliquen en la clase heredera, dicho de otra manera solo una jerarquía de herencia con añadido de clases simples. Estas clases simples se denominan mix-in ya que pueden ser utilizadas para añadir una funcionalidad en un punto arbitrário del árbol de clase. Ver la figura para ver como la clase Animator solo es un añadido a la jerarquía establecida.
Polimorfismo
El poliformismo es una característica que permite que una colección de objetos diferentes puedan ser manipulados a través de una interfaz común única. La interfaz común hace que una colección hetereogénea de objetos pueda parecer homogénea, desde el punto de vista del código mediante el uso de una interfaz.
Por ejemplo, un programa de dibujo 2D se podría dar una lista de varias formas para dibujar en la pantalla. Una forma de sacar esta colección heterogénea de formas es utilizar una sentencia switch para realizar comandos diferentes de dibujo para cada tipo distinto de la forma.
El problema de este método es que drawShape() necesita saber como dibujar cada tipo de figura. Si añadiéramos un tipo de figura nueva tendríamos que modificar este método para que aceptara el nuevo tipo de figura.
La solución es el polimorfismo, consiste en declarar, dentro de la clase Shape una función virtual pura, así cada clase derivada tiene que encargarse de implementar la función Draw() con este método al llamar al método Draw() de Shape lo que hace es llamar a la implementación de la clase hija, y esta no necesita conocer como está implementada dicha función.
Estas son las características más destacadas de la programación orientada a objetos y siempre viene bien recordarlas porque muchas veces las olvidamos a la hora de diseñar software complicandonos mucho la vida.