¡Acceso ilimitado 24/7 a todos nuestros libros y vídeos! Descubra la Biblioteca Online ENI. Pulse aquí
¡Acceso ilimitado 24/7 a todos nuestros libros y vídeos! Descubra la Biblioteca Online ENI. Pulse aquí
  1. Libros
  2. Java y Eclipse
  3. Conceptos básicos de la POO
Extrait - Java y Eclipse Desarrolle una aplicación con Java y Eclipse (2a edición)
Extractos del libro
Java y Eclipse Desarrolle una aplicación con Java y Eclipse (2a edición)
1 opinión
Volver a la página de compra del libro

Conceptos básicos de la POO

Introducción

La Programación Orientada a Objetos, o POO, corresponde a un paradigma - la representación de un sistema o un dominio, para simplificar - particular de la programación informática donde el objeto es el concepto central. En este capítulo se presentan los conceptos fundamentales de la POO enfocándose en lo esencial y apoyándose en el lenguaje Java. La mayoría de ellos están realizados y profundizados en un proyecto Luna, facilitando así su comprensión.

El interés de la programación orientada a objetos es proporcionar medios para modelizar diferentes dominios necesarios para su aplicación.

Este paradigma permite también minimizar y racionalizar los impactos debidos a modificaciones más o menos grandes de las aplicaciones durante su ciclo de vida, extender más fácilmente sus funcionalidades, aislar las responsabilidades del código, manipular abstracciones sin conocer necesariamente sus tipos concretos, y por lo tanto más generalmente proponer programas más robustos y abiertos al cambio.

Cuando creamos un programa con programación orientada a objetos, tratamos de modelizar informáticamente distintas nociones para poder reutilizarlas en otros programas. No se trata de modelizar todo lo que ocurre en el universo físico, a menos que se disponga de un plazo de desarrollo MUY MUY extenso y querer crear...

Objeto

En el paradigma de programación orientada a objetos, todo es un objeto. Una ventana, un botón pero también una factura, un cliente, etc. Más concretamente, un objeto puede considerarse como una entidad que posee características y posibles acciones. Por ejemplo, un coche tiene una forma y un color y puede arrancar y parar.

Un gato también tiene varias características: el número de patas, un color determinado de pelaje, un nombre y varios comportamientos: comer, ronronear, correr sobre las paredes, esconderse, tirar entre las piernas...

En la programación orientada a objetos, se representa Minino, vuestro gato, por un objeto, una instancia particular de la clase Gato. Este objeto tiene por lo tanto propiedades específicas a su gato: su nombre, su color, su edad, para empezar. También tiene propiedades comunes a todos los gatos, como las cuatro patas, los dos ojos, la sangre caliente...

Un gato - y todos los gatos - es también un mamífero y más generalmente un animal, y más aun genéricamente un ser vivo. La programación orientada a objetos intenta formalizar estas distintas categorizaciones y características para obtener una abstracción, una modelización informática del objeto que nos interesa.

Cuando escuche la palabra Objeto, piense en un ejemplar determinado de este objeto: el gato Minino, el coche que posee desde hace cuatro...

Clase

La creación espontánea no existe. El coche que conduce, la silla en la que está sentado, han sido fabricadas. Todos estos objetos vienen de una «fábrica» o de una planta. Para una primera introducción en la noción de clase, podemos retener este último término. También existen plantas de aviones, barcos, juguetes, etc.

Otra particularidad de las clases es el principio de agrupamiento. Evitamos fabricar en una misma planta coches, barcos o juguetes y ordenadores al mismo tiempo. Los objetos fabricados están por lo tanto ligados a categorías que son las clases en POO. Una clase es una especie de planta que fabrica categorías objetos que tienen características comunes (los barcos tienen un casco, los coches una carrocería, los gatos un nombre).

Para definir una clase Gato en Java, basta con utilizar la palabra clase class.

class Gato { 
   // ... continuará 
} 

Observe las llaves abiertas y cerradas: todo lo que se encuentra entre estas llaves pertenece a la misma clase, y todo lo que pertenece a la clase Gato debe encontrarse entre estas llaves. Se aplica el mismo principio para los métodos.

Para crear (también se dice instanciar) un nuevo objeto de tipo Gato que llamaremos miGato en la aplicación, use el siguiente código con la palabra clave new:

Gato miGato = new Gato(); 

Si la palabra fábrica...

Atributo

Volvamos a los que nos ocupa, es decir a nuestros gatos.

La primera versión de la clase Gato que se propuso no es muy interesante. Solo se ha formalizado el concepto de un Gato. Para ir más allá, se completa la clase en función de las necesidades de cada uno. Suponiendo que se trata de caracterizar los gatos por su nombre, color y edad, debe entonces añadir estas características (también llamadas propiedades o atributos) a la clase.

class Gato { 
   nombre; 
   color; 
   fechaDeNacimiento; 

En el estado actual de la clase, esta genera errores ya que las propiedades no están todavía tipadas.

Los atributos definen el aspecto estructural de una clase, sus datos.

Un gato tiene (posee) por lo tanto un nombre, un color y una edad, ya que es el objetivo de la modelización.

En realidad no. No tiene un atributo edad, solo una fecha de nacimiento.

¿Por qué?

Imagine que este programa se ejecuta sin problemas durante varios años. Si para cada vez que cumpla años debemos actualizar la edad de todos los gatos afectados, se complica la cosa. Sin embargo una fecha de nacimiento es fija y no será nunca modificada, salvo en casos excepcionales.

La edad es de hecho lo que llamamos un atributo derivado, que se puede obtener conociendo la fecha de nacimiento y la fecha actual.

Abordaremos la formalización de este atributo derivado...

Tipo de datos

Todo atributo debe imperativamente estar tipado: long para la fecha de nacimiento y cadena de caracteres para los demás en nuestro ejemplo. El primer tipo es llamado simple. Hablamos de tipos elementales o primitivos. Tendrá ocasión al abordar el desarrollo de utilizar tipos más enriquecidos, especialmente tipos de clases, como la clase String.

class Gato {  
   String nombre;   
   String color;  
   long fechaDeNacimiento;   
} 

En las aplicaciones muchas veces aparece el problema de conversión (casting) de datos. Por ejemplo, las solicitudes de introducción de datos mediante las ventanas de diálogo devuelven por defecto valores de tipo String. Para el tratamiento de fechas o de números debe entonces convertir estos valores con el riesgo de provocar el detenimiento repentino de la aplicación.

Quien puede con más, puede con menos. Considere el siguiente código:

     int numeroEntero = 5;  
     double numeroDecimal = 0;    
     numeroDecimal = numeroEntero;    
    
     numeroDecimal = 5.5;    
     numeroEntero = (int) numeroDecimal; 

A un double (que puede ser un número con signo muy grande con decimales) se le puede asignar un entero. Hablamos...

Método

Los métodos representan las acciones y los comportamientos que pueden llevar a cabo los objetos provenientes de una clase. Definen el aspecto dinámico o de comportamiento de la clase. Los gatos pueden dormir, comer, maullar, etc.

Presentamos algunos de estos métodos.

class Gato {    
     // propriedades   
     String nombre;   
     String color;     
     long fechaDeNacimiento;   
 
     // métodos    
      void dormir(){   
            return ;  
     }  
  
     void comer(Object comida){   
     }    
  
     String maullar(){   
           String elMaullido = "miau";  
           return elMaullido;   
     }   
} 

Los métodos que no devuelven ningún valor corresponden a procedimientos. Siempre van precedidos de la palabra clave void.

Los métodos que devuelven un valor corresponden a funciones. Siempre van precedidas del tipo de datos devuelto.

Los métodos...

Accesibilidad

La POO añade el principio de accesibilidad (visibilidad) que consiste en controlar el acceso a las clases y a sus miembros (propiedades y métodos).

El término de accesibilidad se utiliza también para designar las técnicas de uso de una aplicación para usuarios minusválidos, por lo que en esta sección usaremos el término de visibilidad.

Los niveles de visibilidad en Java son los que siguen:

  • Clases :

  • public: las clases con visibilidad pública son accesibles por todo el mundo (todas las clases de todos los packages).

  • <nada>: el hecho de no indicar la visibilidad de la clase hace a esta inaccesible fuera de las clases de su propio package.

  • private: este nivel solo concierne las clases internas. Son clases definidas dentro de una clase y que solo son utilizadas por esta.

  • Miembros :

  • public: los miembros (propiedades y métodos) son accesibles desde el exterior de la clase. Para proteger las propiedades de modificaciones no autorizadas o equivocadas, se aconseja que nunca se declaren públicas.

  • protected: los miembros son accesibles por las clases hijas (ver la noción de herencia más adelante) pero también con Java a las demás clases del mismo package.

  • <nada>: si no se específica la visibilidad, los atributos y métodos son accesibles únicamente para las clases pertenecientes al mismo package.

  • private: es el nivel más elevado de restricción. Las propiedades y métodos solo son accesibles desde la propia clase. En cuanto a las propiedades, pueden ser accedidas fuera de la clase pero únicamente mediante...

Encapsulación

El hecho de agrupar en el seno de una misma clase propiedades y métodos y definir el nivel de accesibilidad constituye la encapsulación, uno de los conceptos esenciales de la POO.

Una clase va a actuar sobre los datos privados que le son propios, con la garantía que nada interferirá con el tratamiento. Un desarrollador sabrá muy rápidamente qué parte del código es responsable (o infractora), ya que una sola clase está habilitada para modificar el dato en cuestión. Solo son visibles los miembros públicos, lo que refuerza la seguridad de los datos y el mantenimiento de los programas. Además, la implementación de los métodos es enmascarada. Finalmente, con el concepto de encapsulación, la clase solo presenta un acceso compuesto por los miembros públicos. Se esconde lo que es privado a los ojos del resto del mundo.

Retomando el ejemplo anterior, para modificar el nombre del gato, debe obligatoriamente usar el método setNombre(), lo que permite efectuar eventuales controles y tratamientos adicionales. Tomando esto como principio, puede aplicarlo a otros casos como el débito de una cuenta que no se puede realizar sin el método apropiado y habilitado. Los riesgos de error se ven así reducidos y la seguridad aumenta. Sobre todo, la modificación del código que se ocupa del maullido de un gato o del débito...

Constructor

Para poder instanciar objetos de una clase, hace falta un constructor. Es un método especial, con el mismo nombre que la clase y escrito sin valor de retorno, que permite crear objetos. Si se omite un constructor, Java los insertará al realizar la compilación del programa creando un constructor básico del tipo NombreDeLaClase(), sin parámetros (se trata del constructor por defecto). Es posible declarar varios constructores en una clase, como en el siguiente ejemplo.

public class Gato {    
 // propriedades    
 ...   
 // constructores   
  
 private Gato() 
   super();   
 }  
  
 private Gato(long laFechaDeNacimiento) {   
   this();   
   this.fechaDeNacimiento = laFechaDeNacimiento;   
 }  
  
 public Gato(String elNombre, String elColor,  
               long laFechaDeNacimiento){   
   this(laFechaDeNacimiento) ;   
   this.nombre = elNombre;   
   this.color = elColor;   
 }   
 // métodos   
 ...   
} 

El primer constructor Gato() es privado....

Herencia

Las costumbres del pensamiento humano utilizan habitualmente las nociones de jerarquía y de clasificación, como por ejemplo para la zoología o para un árbol genealógico.

La POO intenta restituir esta manera de pensar gracias a la noción de herencia, con la que las clases «hijas» recuperan las propiedades y comportamientos de su clase «madre», reservándose la posibilidad de poder completarlas y/o modificarlas.

Retomando el ejemplo de Minino, un Mamífero es un concepto más general al de Felino que es un concepto más general que Gato, del cual Minino es una instancia específica. En términos de programación orientada a objetos, es posible crear una jerarquía de clases transponiendo esta manera de pensar. Existe por lo tanto un objeto Minino construido desde una clase Gato que hereda de la clase Felino que hereda de la clase Mamífero.

images/03eit01.png

De una manera más general, todos los felinos, cánidos y cetáceos heredan las características comunes de la clase Mamífero: animal de sangre caliente, la hembra amamanta a sus crías, etc. Minino pertenece a una gran familia. Va a heredar de todas las propiedades y métodos de sus clases ancestrales, dejando de lado los elementos específicos de los cánidos y cetáceos. Para tener en cuenta esto, hay que revisar la definición de la clase Gato y añadir las de las clases madres. Para hacerlo sencillo, aquí tiene un ejemplo limitado a las clases Felino y Gato.

public class Gato extends Felino { 
 private String nombre; 
 private long fechaDeNacimiento; 
 public Gato(String laEspecie, String elColor, 
          String elNombre, long laFechaDeNacimiento) {    super(laEspecie, elColor); 
   this.nombre = elNombre; 
   this.fechaDeNacimiento = laFechaDeNacimiento; 
 } 
 // los mutadores y accesores 
 ...  
 //...

Interfaces

Una interfaz es un medio para crear un contrato para las clases y por lo tanto los objetos, contrato que permitirá definir los comportamientos obligatorios para todas las clases implementando esta interfaz.

El principal interés de una interfaz es factorizar comportamientos comunes para una utilización estándar. Si, durante el ciclo de desarrollo de una aplicación se percata de que alguno de sus objetos puede tener una mejor implementación (como un coche con mejor motor), y si usa una interfaz para comunicarse con este objeto, puede modificar su código con más facilidad substituyendo su antigua clase con la nueva que integrará exactamente la misma interfaz.

Volvamos al ejemplo del coche: cada modelo de coche es único. Se construye cada modelo siguiendo un esquema diferente, con tecnologías diferentes. Sin embargo, la interfaz de cada modelo de coche es siempre más o menos la misma: un volante, pedales para el freno y el acelerador, las palancas para los intermitentes... El volante siempre está delante suyo, los pedales se encuentran en el mismo sitio sea cual sea el coche que conduzca, como las palancas de intermitentes... ¡Felicidades! Conoce bien la interfaz para conducir un coche y no tiene que volver a examinarse del permiso de conducir cada vez que se sienta en un coche diferente del suyo, como un coche inglés.

Por lo tanto ya ha aprendido a dominar una interfaz...

Polimorfismo

El polimorfismo es una noción importante en POO, y significa etimológicamente «que puede presentarse en diferentes formas».

El polimorfismo es la capacidad del código para elegir dinámicamente en la ejecución el método a ejecutar, e implica que una misma llamada a un método pueda tener comportamientos diferentes, ya sea en función de los parámetros de esta llamada, o del objeto con el que se efectúa la llamada.

Java conlleva dos tipos de polimorfismo principales: la sobrecarga y la redefinición.

El mecanismo de polimorfismo puede también obtenerse con la ayuda de interfaces.

Desde Java 1.5, también existe un polimorfismo llamado paramétrico, usando los genéricos, que se estudiarán en el siguiente capítulo.

1. Por sobrecarga

El polimorfismo de sobrecarga corresponde a la capacidad de un objeto de elegir en tiempo de ejecución (dinámicamente) el método que corresponde mejor a sus necesidades entre sus propios métodos o los de la clase madre.

Otra particularidad de este polimorfismo es que todos los métodos afectados llevan el mismo nombre. En el seno de una clase, se diferencian obligatoriamente y únicamente por el nombre de los parámetros o el tipo de estos últimos.

Recordatorio: una firma de un método solo se puede implementar una única vez en una misma clase.

Se modifica la clase Gato para evidenciar este mecanismo de sobrecarga.

public class Gato {   
 ...   
 public void comer(String comida){   
   System.out.println("Como " + comida);   
 }  
  
public void comer() {  
   System.out.println("Voy a cazar mi comida");    
}  
   
 public void comer(List<Object> comida){   
   System.out.println("Me...

Principios SOLID

Para obtener una modelización y un desarrollo más fiables y robustos en programación orientada a objetos, es posible utilizar cinco principios básicos agrupados bajo el acrónimo SOLID (en inglés). Estos principios vinculados entre sí permiten arrancar un desarrollo sobre bases sanas que permitirán simplificar modificaciones posteriores.

1. Single Responsibility

El principio de responsabilidad única define que todas las clases de una aplicación deben responsabilizarse de una de las funciones de la modelización, y que esta funcionalidad debe definirse en la clase. De esta forma, cada clase solo tiene una única razón para modificarse, lo que permite guardar el control de su complejidad, mejorar la legibilidad y posibilitar un mejor trabajo en equipo como ventajas inmediatas.

Un contra ejemplo de este principio es una clase que agrupa varias funcionalidades, como una clase Comando que modeliza los datos, permite el acceso directo a la base de datos y gestiona ella misma la impresión. Esto crea una clase compleja, cuyas distintas funcionalidades se encuentran enlazadas entre ellas, y que será sensible a cualquier cambio, ya que no se puede garantizar que una modificación de la parte de impresión no impactará al acceso a las bases de datos.

En vez de utilizar una herramienta multifunción cuyos elementos resultan mediocres en cuanto a su uso, es preferible disponer de una caja con diferentes herramientas, cada...

Algunos principios útiles

La literatura es abundante en cuanto a principios aplicables a la POO. Detallamos algunos de ellos en esta sección.

1. DRY (Don’t Repeat Yourself)

El principio DRY («No se repita» en español) tiene como finalidad limitar las repeticiones de información y procesamientos en una aplicación y postula que cada elemento de información en un sistema debe tener una representación única, no ambigua y con autoridad.

Considera el copia-pega excesivo de código como un indicador de inestabilidad en la aplicación.

El credo personal del autor en relación a este principio es: cree una vez, cópielo la segunda, factorice la tercera.

2. KISS (Keep It Simple, Stupid)

El principio KISS («Sea simple y estúpido») privilegia la simplicidad en el diseño para conseguir lo más rápidamente posible el objetivo a alcanzar.

Aquí debe evitar la complejidad innecesaria del código, sobre todo en cuanto a optimizaciones que solo deberían llevarse a cabo cuando una versión sencilla de la aplicación es operacional.

Una aplicación simple resulta más sencilla de comprender y mantener, pero no necesariamente más sencilla de desarrollar. O citando a Leonardo da Vinci: «La simplicidad es la sofisticación suprema».

3. YAGNI (You Aren’t Gonna Need It)

El principio YAGNI...