martes, 5 de marzo de 2013

Clases y Objetos - ¿Cómo?, ¿Por qué?, ¿Para qué?

Este post tiene como idea mostrar un poco del funcionamiento de las clases y los objetos en JAVA desde una perspectiva nada formal y más bien práctica. Cuando yo aprendí a trabajar con JAVA era inmigrante de C++ por lo cual aprendí un poco "a las patadas" y aunque después de varios años de trabajar con JAVA ya  me considero un programador muy decente, quiero aprovechar este post para contarles algunas cosas que me ayudaron en ese proceso de aprender solo.

Clases y Objetos ¿Qué es eso?


Bueno, primero lo primero... Básicamente los lenguajes orientados por objetos tienen la idea de que los que escriben aplicaciones trabajen con conceptos más parecidos al mundo real. La forma más acertada que encontraron en su época fue el uso de variables de "tipos personalizados" para dejar de hablar de números , bytes, caracteres y cadenas; de hecho en lenguajes como C++ el concepto de cadena de caracteres estuvo relegado a librerías que ayudaban a usarlos, pero quienes tuvimos que sufrir con C (a secas) vimos como tocaba manejar las cadenas de caracteres como arreglos dinámicos desde una variable tipo char*. Bueno, en java al menos se tomaron la molestia de implementar una clase String muy decente...

Volviendo al asunto de los tipos, se quería que los programadores pensaran en conceptos más abstractos, como una Persona, un Directorio, un Automóvil  etc. La forma de implementarlos estos conceptos se pensó en términos de variables compuestas personalizables (muy al estilo de un struct en C). En principio una clase es una definición de un tipo de dato compuesto que además de las variables internas que lo definen (atributos), tiene unos métodos (procedimientos, funciones) que también lo caracterizan. En teoría un objeto tiene unas propiedades y comportamientos que lo hacen diferente de los demás (atributos y métodos).

Si volvemos al concepto de variable, una variable debería tener al menos un tipo y un nombre y además se le puede asignar un valor. La idea con un objeto realmente no cambia mucho....

        
 int i = 0;
 Persona p = new Persona("Daniel Santamaría", 4243430);

En la primera línea vemos la declaración de una variable entera que se llama i y que le estamos asignando el valor cero. En la segunda línea vemos una estructura similar en su sintaxis, una variable de tipo persona que se llama p (la variable) y le estamos asignando una persona. En este caso hablamos de un hipotético objeto persona que estamos usando y que posiblemente tiene "por dentro" una variable String para representar el nombre y un long que representará el número de teléfono. La palabra reservada "new" se utiliza en varios lenguajes de programación (C#, JAVA, C++, PHP, entre otros) para crear un nuevo objeto (separar memoria y ponerlo allí).

Definición de una clase


Para que esto sea posible, en algún lado de nuestro programa es necesario definir ¿Qué es una clase persona?. Para ello se usa un archivo aparte y la palabra reservada "class".


public class Persona
{
 private String nombre;
 private long telefono;
 
 public Persona(String n, long t)
 {
  this.nombre = n;
  this.telefono = t;
 }
}

La anterior porción de código (probablemente ubicada en un archivo Persona.java) define una clase Persona con dos atributos (nombre y teléfono). El uso de la palabra "private" para declarar dichas variable permite que esta variable solamente pueda ser accedida al interior de la clase persona (no accesible desde otras clases). El uso de esta palabra reservada y otras (public, protected, etc) no será tratado en este texto. Sin embargo es importante decir que : "Es muy importante que los atributos de una clase sean privados", ya que de no serlo, otras clases podrían interferir en la correcta operación del objeto. La idea de fondo es que cada clase "se encargue de lo suyo"...

También hay allí un método, específicamente un método constructor. El constructor se encarga normalmente de inicializar el estado de un objeto, sea dando valores por defecto o recibiéndolos como parámetro. Nótese también el uso de la palabra "this" con la que se hace explícito el hecho de que quiero trabajar con la variable que es atributo de este mismo objeto. Esto resuelve inconsistencias, por ejemplo si dentro del método se declara una variable con el mismo nombre.

El molde y la galleta - Diferencia entre clase y objeto


La definición de una clase permite decirle al lenguaje ¿Qué es una persona? (en el ejemplo anterior). Bueno, eso es una clase y se refiere al concepto literal de clase :


  •  "Orden en que, con arreglo a determinadas condiciones o calidades, se consideran comprendidas diferentes personas o cosas." (Tomada del diccionario de la real academia de la lengua española)

El objeto vendría siendo una persona en particular, o sea una instancia particular de la clase persona. Esa instancia es básicamente una variable ya creada con valores específicos, única e irrepetible. En resumen la clase se comporta como el "molde" y el objeto como "la galleta" que se puede hacer con el molde. Uno mira con que la rellena, pero sigue siendo una galleta.

Métodos, especialmente los "getters" y "setters"


Como había dicho anteriormente es de suprema importancia que los atributos estén bajo el control de la clase que los contiene. La clase Persona del ejemplo es la única autorizada para cambiar el nombre de una persona de manera directa, sin embargo se puede hacer que un método permita modificar o consultar esos valores (de lo contrario la clase sería casi que inútil). Esto es lo que hace un método get(pedir valor) o set (cambiar valor). Lo interesante de estos métodos es que en ellos se puede controlar lo que sucede al hacer el cambio, notificar al objeto si es necesario, realizar validaciones, etc. Por ejemplo:


public class Persona
{
 private String nombre;
 private long telefono;
 
 public Persona(String n, long t)
 {
  this.nombre = n;
  this.telefono = t;
 }
 public String darNombre()
 {
  return this.nombre;
 }
 public long darTelefono()
 {
  return this.telefono;
 }
 public void cambiarNombre(String n)
 {
  if(!n.equals(""))
  {
   this.nombre = n;
  }
 }
 public void cambiarTelefono(long t)
 {
  if(t!=0)
  {
   this.telefono = t;
  }
 }
}

En este ejemplo he puesto 4 métodos más, los dos primeros retornan el valor del atributo, los dos siguientes permiten cambiar el valor del atributo, pero bajo algunas restricciones, por ejemplo el nombre no se puede cambiar si el valor enviado como parámetro es un String vacío, así mismo no se podrá cambiar el teléfono por un cero. Estas restricciones son tontas, pero podrían ser tan complejas como se requiera, por ello la importancia de mantener ese control dentro de la misma clase.

Desde afuera de la clase, estos métodos podrían ser llamados usando el operador punto (.):


 Persona p = new Persona("Daniel Santamaría", 4243430);
 p.cambiarNombre("Pepito Perez");
 p.cambiarTelefono ( 5554455 );
 System.out.printLn(p.darNombre());

Listo! con ello podrán crear clases simples.... Ahora faltan las relaciones entre clases...

Relaciones entre clases - ¿Quién tiene Qué?

Cuando uno modela clases, espera tener varias (obvio) y algunas de ellas tendrán que relacionarse para modelar contextos más complejos. La relación más común entre clases es la contenencia (agregación o composición), básicamente modela el hecho de que un concepto esta compuesto por otro. Consideremos el caso de una agenda de teléfonos, la cual contiene un conjunto de personas. En un diagrama de clases UML se representaría de la siguiente forma:

El diagrama representa una relación de agregación entre Agenda y Persona, la flecha en el diagrama significa que literalmente una Agenda puede tener de 0 a N Personas (significado del simbolo *). En ese caso, si miramos la implementación de la clase Agenda, necesitaremos tener una variable que pueda guardar N personas, por ejemplo un arreglo.


public class Agenda 
{
 private int numeroPersonas =0;
 public static final int MAX_PERSONAS = 100;
 private Persona[] personas = new Persona[MAX_PERSONAS];
 
 public void agregarPersona(Persona p)
 {
  this.personas[numeroPersonas]=p;
  this.numeroPersonas++;
 }
 public Persona[] listarPersonas()
 {
  return this.personas;
 }
 //.... otros métodos de la clase
}

Nótese que se incluyeron algunos atributos extra para la administración de la colección, los cuales no están en el diagrama. Si somos rígidos, deberían estar en él, pero desde el punto de vista conceptual, solamente requeríamos modelar el conjunto de personas. Por otro lado aparece un atributo "personas" que es un arreglo de personas, este sería quien representa la relación existente entre las dos clases, pero no necesariamente puede tratarse de un arreglo existiendo diversas estructuras de datos que podrían cumplir la misma función (por ejemplo listas, arboles, pilas, etc.)

No hay comentarios:

Publicar un comentario