27
Dec 16

Pildoritas de POO y Java: Polimorfismo

pildoras poo java
Llevo casi desde que comencé con las pildoritas de POO anunciando que "el polimorfismo va a llegaaaaar" y nada, que no llegaba...¡hasta hoy!

El polimorfismo va muy ligado a la herencia, y se apoya en ella para poder generalizar comportamientos de diferentes clases, como si de una se tratara, siendo para nosotros transparente su tipo, siempre y cuando éste herede de una clase Padre común que define la funcionalidad generalizada.

Polimorfismo

El Polimorfismo es el envío de mensajes a objetos de tipos diferentes utilizando la misma sintaxis. En lenguajes con un sistema de tipos dinámicos, es decir, que una variable puede contener todo tipo de valores y su tipo se setea en tiempo de ejecución. No es necesario el uso de herencia para aplicar el polimorfismo pero en lenguajes fuertemente tipados como Java es necesario utilizar una clase común padre de la cual heredarán todos los objetos polimórficos.

Por ejemplo, basándonos en el ejemplo de nuestra anterior pildorita de Herencia, las clases Profesor y Alumno heredan de Persona varios métodos comunes, como el método toString, que además reimplementan de forma específica cada clase hijo a través de la anotación @Override.

Ahora imaginemos que tenemos un método auxiliar en nuestra aplicación que se dedica a mostrar los datos por pantalla de cualquier persona vinculada a la Escuela, Universidad...¿Cómo lo haríamos de forma general utilizando polimorfismo?

public void mostrarDatos() {
		Profesor profe1 = new Profesor(1, "Antonio", "Brena", "1111111K", 52, "M");
		Profesor profe2 = new Profesor(2, "Alicia", "Hernandez", "2222222K", 45, "F");
		Alumno alumno1 = new Alumno(1, "Mar", "Millan", "44444444K", 29, "F");
		Persona trabajador1 = new Persona("Francisco", "García", "33333333K", 39, "M");
		
		List<Persona> lsPersonas = new ArrayList<Persona>();
		lsPersonas.add(profe1);
		lsPersonas.add(profe2);
		lsPersonas.add(alumno1);
		lsPersonas.add(trabajador1);

		for (Persona per : lsPersonas) {
			System.out.println(per.toString());
		}		
	}

El resultado de ejecutar este método es el siguiente:

DATOS DEL PROFESOR:
Nombre: Antonio
Apellidos: Brena
DNI: 1111111K
Edad: 52
Sexo: M

DATOS DEL PROFESOR:
Nombre: Alicia
Apellidos: Hernandez
DNI: 2222222K
Edad: 45
Sexo: F

DATOS DEL ALUMNO:
Nombre: Mar
Apellidos: Millan
DNI: 44444444K
Edad: 29
Sexo: F

DATOS DE LA PERSONA:
Nombre: Francisco
Apellidos: García
DNI: 33333333K
Edad: 39
Sexo: M

Como se puede observar, para nosotros es indiferente el tipo del objeto que estemos invocando, ya que sabemos que todos esos objetos al heredar de la clase padre Persona tendrán obligatoriamente el método toString. Así mismo, si uno de esos objetos tiene una implementación propia de dicho método, ejecutará la implementación especifica de forma transparente para nosotros.

Aquellos lenguajes que tengan interfaces (como es el caso de Java), pueden también implementar el polimorfismo utilizando este recurso, lo que se conoce como composición de objetos. Esta alternativa se considera mejor práctica a la hora de aplicar polimorfismo que el uso de herencia, ya que este solución sólo obliga a que las diferentes clases compartan el tipo y obligatoriedad de implementar los métodos del interfaz, pero no se comparten comportamientos ni atributos. Pero, ¿qué es una interfaz?

Interfaces y Clases Abstractas

Cuando queremos unificar atributos y métodos en una clase padre, como hemos hecho en la clase Persona, pero no queremos que ésta sea instanciable, crearíamos nuestra clase Persona como clase abstracta:

public abstract class Persona {
	 
    private String nombre;
    private String apellidos;
    private String dni;
    private Integer edad;
    private String sexo;
    
    Persona() {
    	super();
    }
    
    public Persona(String nombre, String apellidos, String dni, Integer edad, String sexo) {
    	super();
    	this.nombre = nombre;
    	this.apellidos = apellidos;
    	this.dni = dni;
    	this.edad = edad;
    	this.sexo = sexo;
    }
    
    public String getNombre() {
        return nombre;
    }
 
    public void setNombre(String nombre) {
        this.nombre = nombre;
    }
 
    public String getApellidos() {
        return apellidos;
    }
 
    public void setApellidos(String apellidos) {
        this.apellidos = apellidos;
    }
 
    public String getDni() {
        return dni;
    }
 
    public void setDni(String dni) {
        this.dni = dni;
    }
 
    public Integer getEdad() {
        return edad;
    }
 
    public void setEdad(Integer edad) {
        this.edad = edad;
    }
 
    public String getSexo() {
        return sexo;
    }
 
    public void setSexo(String sexo) {
        this.sexo = sexo;
    }
    
    @Override 
    public String toString() {
        String nuevalinea = System.getProperty("line.separator");
        String resultado = "DATOS DE LA PERSONA: " + nuevalinea + 
                "Nombre: " + this.getNombre() + nuevalinea + 
                "Apellidos: " + this.getApellidos() + nuevalinea + 
                "DNI: " + this.getDni() + nuevalinea +
                "Edad: " + this.getEdad() + nuevalinea +
                "Sexo: " + this.getSexo() + nuevalinea;
 
        return resultado;
    }
    
	@Override
	public boolean equals(Object object) {
		final Persona aux = (Persona) object;
		if (this == object)
	           return true;
		else if (object == null)
	           return false;
		else if (getClass() != object.getClass())
	           return false;
		else if (this.dni.equals(aux.getDni())) {
			return true;
		} else {
			return false;
		}
	}

     public abstract void metodoAbstracto();
}

Por tanto, en nuestro anterior ejemplo tendríamos un error al crear el objeto Persona trabajador1, ya que, como hemos dicho, las clases abstractas no pueden ser instanciadas:

También podemos añadir métodos abstractos. Un ejemplo de ello es el último método que hemos creado en el ejemplo de la clase abstracta Persona, el método metodoAbstracto(), que como se puede observar siquiera tiene cuerpo. Si creamos un método abstracto estaremos obligados a definir la implementación de dicho método en las clases hijas.

¿Y qué es una interfaz? Pues una clase completamente abstracta, es decir, una clase en la que no se implementan métodos, ni hay atributos, excepto aquellos que sean estáticos y/o constantes.

Los interfaces tienen como finalidad definir la forma y comportamiento de una clase.

Por ejemplo, podríamos tener una interfaz IMamifero, que implemente Persona:

public interface IMamifero {
	public void crecer();
	public void reproducirse();
	public void morir();
}

Para que nuestra clase implemente este interfaz, lo indicaremos a través de la palabra reservada implements.

Al hacer esto, como se puede ver en la anterior imagen, tendremos un error en la mayoría de entornos que nos indicará que tenemos que implementar forzosamente los métodos definidos en el interfaz IMamifero:

public class Persona implements IMamifero {
	 
    private String nombre;
    private String apellidos;
    private String dni;
    private Integer edad;
    private String sexo;
    
    Persona() {
    	super();
    }
    
    public Persona(String nombre, String apellidos, String dni, Integer edad, String sexo) {
    	super();
    	this.nombre = nombre;
    	this.apellidos = apellidos;
    	this.dni = dni;
    	this.edad = edad;
    	this.sexo = sexo;
    }
    
    public String getNombre() {
        return nombre;
    }
 
    public void setNombre(String nombre) {
        this.nombre = nombre;
    }
 
    public String getApellidos() {
        return apellidos;
    }
 
    public void setApellidos(String apellidos) {
        this.apellidos = apellidos;
    }
 
    public String getDni() {
        return dni;
    }
 
    public void setDni(String dni) {
        this.dni = dni;
    }
 
    public Integer getEdad() {
        return edad;
    }
 
    public void setEdad(Integer edad) {
        this.edad = edad;
    }
 
    public String getSexo() {
        return sexo;
    }
 
    public void setSexo(String sexo) {
        this.sexo = sexo;
    }
    
    @Override 
    public String toString() {
        String nuevalinea = System.getProperty("line.separator");
        String resultado = "DATOS DE LA PERSONA: " + nuevalinea + 
                "Nombre: " + this.getNombre() + nuevalinea + 
                "Apellidos: " + this.getApellidos() + nuevalinea + 
                "DNI: " + this.getDni() + nuevalinea +
                "Edad: " + this.getEdad() + nuevalinea +
                "Sexo: " + this.getSexo() + nuevalinea;
 
        return resultado;
    }
    
	@Override
	public boolean equals(Object object) {
		final Persona aux = (Persona) object;
		if (this == object)
	           return true;
		else if (object == null)
	           return false;
		else if (getClass() != object.getClass())
	           return false;
		else if (this.dni.equals(aux.getDni())) {
			return true;
		} else {
			return false;
		}
	}

	@Override
	public void crecer() {
		System.out.println("Crece");
		
	}

	@Override
	public void reproducirse() {
		System.out.println("Fornicia");
		
	}

	@Override
	public void morir() {
		System.out.println("R.I.P.");	
	}
}

Al hacer esto, ya sí que podremos ejecutar nuestra aplicación sin errores y, si queremos, hacer polimorfismo con los métodos definidos por la interfaz:

Profesor profe1 = new Profesor(1, "Antonio", "Brena", "1111111K", 52, "M");
		Profesor profe2 = new Profesor(2, "Alicia", "Hernandez", "2222222K", 45, "F");
		Alumno alumno1 = new Alumno(1, "Mar", "Millan", "44444444K", 29, "F");
		Persona trabajador1 = new Persona("Francisco", "García", "33333333K", 39, "M");
		
		List<Persona> lsPersonas = new ArrayList<Persona>();
		lsPersonas.add(profe1);
		lsPersonas.add(profe2);
		lsPersonas.add(alumno1);
		lsPersonas.add(trabajador1);

		for (Persona per : lsPersonas) {
			per.reproducirse();
		}

Consola:

Fornicia
Fornicia
Fornicia
Fornicia

Como veis, el Polimorfismo no es más que el uso más potente de la herencia, pero no es, ni mucho menos, complejo, al igual que tampoco lo son las ideas de clase abstracta e interfaz una vez se entiende el concepto general de Herencia.

Así que nada, si habéis seguido estas pildoritas al pie de la letra podemos ya afirmar (con extremada timidez, eso sí) que comprendéis y entendéis las bases de lo que es la Programación Orientada a Objetos. A partir de ahora, sólo necesitaréis tiempo y práctica para poder desarrollar cualquier aplicación que deseéis con este paradigma y, eso sí, teniendo claros los conceptos fundamentales que os harán desarollar como las personitas, y no como muchos: con los pies.

Y aunque ésta sí será nuestra última pildorita de POO, ni mucho menos será la última de Java. En las próximas semanas os hablaré de las excepciones (un concepto de Java que escapa a la POO pero fundamental a la hora de programar y realizar un correcto tratamiento de errores) y a partir de ahí, ya se verá. Tenía pensado hacer un programa básico como ejemplo resumen de todo lo que hemos visto, y tras esto, seguir entrando en chicha con conceptos de programación más avanzados.

Índice de Pildoritas

Share
20
Dec 16

Pildoritas de POO y Java: Herencia

pildoras poo java
Como ya dejé entrever en anteriores posts de esta serie de artículos, la pildorita de hoy era la madre de todos los corderos. En ella veríamos un concepto fundamental en POO, la Herencia, y el Polimorfismo, un potente recurso que nos servirá para que la herencia adquiera su máximo potencial a partir de la sobrecarga.

De nuevo del dicho al hecho hay un trecho, y dejo patente una vez más lo noob que soy aún en el terreno de los manuales y tutoriales: La segunda parte de la pildorita, el Polimorfismo, al final va a tener su propio post dedicado, ya que se me iba a ir de las malos su extensión y ya se sabe que mejor separar en trocitos cortos, abarcables y fácilmente comprensibles, que en tochos insufribles y con mil ideas de golpe.

Así que nada, tendréis que esperar a la próxima semana para saber que es eso del Polimorfismo.

En fin, al lío.

Herencia

La herencia tiene como meta la extensión y reutilización de código por lo que se utiliza cuando varias clases tienen funcionalidades comunes, en cuyo caso éstas pueden heredar de una clase padre la funcionalidad común.

Por ejemplo, si volvemos a la imagen de la pildorita de introducción a POO, todas las entidades de la imagen son mamíferos, es decir, todos ellos caminan y maman, no obstante, una Persona podrá, por ejemplo, plantearse el sentido de la vida, mientras un Delfín (que sepamos) no. Así mismo, aunque tanto el Delfín como el Humano pueden desplazarse, uno lo hace nadando con su aleta, mientras otro caminando sobre sus dos piernas, por lo que, aunque sus funcionalidad es igual, se implementa de distinta forma.

mamiferos

Para implementar la herencia entre clases en Java se utiliza la palabra reservada extends. Así mismo, veremos por primera vez una anotación, una muy importante a la hora de utilizar herencia, @Override, y la palabra reservada super.

Para entender mejor la herencia partiremos de nuestro socorrido ejemplo de la clase Alumno.

public class Alumno {
	private String nombre;
    private String apellidos;
    private String dni;
    private Integer edad;
    private String sexo;
	
	private int codAlumno;
	private Lis<Asignatura> asignaturas;
	
	public Alumno (int codAlumno, String nombre, String apellidos, String dni, Integer edad, String sexo, List<Asignatura> lsAsignaturas) {
		this.nombre = nombre;
    	this.apellidos = apellidos;
    	this.dni = dni;
    	this.edad = edad;
    	this.sexo = sexo;
		this.codAlumno = codAlumno;
		this.asignaturas = lsAsignaturas;
	}

        public void setCodAlumno(int codAlumno) {
		this.codAlumno = codAlumno;
	}
	public int getCodAlumno() {
		return codAlumno;
	}
	public void setAsignaturas(List<Asignatura> asignaturas) {
		this.asignaturas = asignaturas;
	}
	public List<Asignatura> getAsignaturas() {
		return asignaturas;
	}
	
	public void asignarAsignatura(Asignatura asignatura) {
		asignaturas.add(asignatura);
	}

	public double getNotaMedia() {
		double resultado = 0;
		for (int i = 0; i < asignaturas.size(); i++) {
			Asignatura asig = asignaturas.get(i);
			resultado += asig.getNota();
		}
		return Math.round((resultado != 0 ? resultado / asignaturas.size(): 0));
	}
}

Imaginemos por otra parte, que tenemos una clase Profesor, definida tal que:

public class Profesor {
	private String nombre;
    private String apellidos;
    private String dni;
    private Integer edad;
    private String sexo;
	
	private int codAlumno;
	
	public Alumno (int codAlumno, String nombre, String apellidos, String dni, Integer edad, String sexo) {
		this.nombre = nombre;
    	this.apellidos = apellidos;
    	this.dni = dni;
    	this.edad = edad;
    	this.sexo = sexo;
	this.codAlumno = codAlumno;
	}

        public void setCodAlumno(int codAlumno) {
		this.codAlumno = codAlumno;
	}
	public int getCodAlumno() {
		return codAlumno;
	}

 public String getNombre() {
        return nombre;
    }
 
    public void setNombre(String nombre) {
        this.nombre = nombre;
    }
 
    public String getApellidos() {
        return apellidos;
    }
 
    public void setApellidos(String apellidos) {
        this.apellidos = apellidos;
    }
 
    public String getDni() {
        return dni;
    }
 
    public void setDni(String dni) {
        this.dni = dni;
    }
 
    public Integer getEdad() {
        return edad;
    }
 
    public void setEdad(Integer edad) {
        this.edad = edad;
    }
 
    public String getSexo() {
        return sexo;
    }
 
    public void setSexo(String sexo) {
        this.sexo = sexo;
    }
 
}

Como se puede ver, las clases Alumno y Profesor comparten mucho código replicado. Los campos nombre, apellidos, DNI, sexo y edad, por tanto, se pueden encapsular en una clase padre, que llamaremos Persona y haremos que ambas clases hereden de ella toda esa funcionalidad común.

public class Persona {
	 
    private String nombre;
    private String apellidos;
    private String dni;
    private Integer edad;
    private String sexo;
    
    Persona() {
    	super();
    }
    
    public Persona(String nombre, String apellidos, String dni, Integer edad, String sexo) {
    	super();
    	this.nombre = nombre;
    	this.apellidos = apellidos;
    	this.dni = dni;
    	this.edad = edad;
    	this.sexo = sexo;
    }
    
    public String getNombre() {
        return nombre;
    }
 
    public void setNombre(String nombre) {
        this.nombre = nombre;
    }
 
    public String getApellidos() {
        return apellidos;
    }
 
    public void setApellidos(String apellidos) {
        this.apellidos = apellidos;
    }
 
    public String getDni() {
        return dni;
    }
 
    public void setDni(String dni) {
        this.dni = dni;
    }
 
    public Integer getEdad() {
        return edad;
    }
 
    public void setEdad(Integer edad) {
        this.edad = edad;
    }
 
    public String getSexo() {
        return sexo;
    }
 
    public void setSexo(String sexo) {
        this.sexo = sexo;
    }

@Override     
public String toString() {
        String nuevalinea = System.getProperty("line.separator");
        String resultado = "DATOS DE LA PERSONA: " + nuevalinea + 
                "Nombre: " + this.getNombre() + nuevalinea + 
                "Apellidos: " + this.getApellidos() + nuevalinea + 
                "DNI: " + this.getDni() + nuevalinea +
                "Edad: " + this.getEdad() + nuevalinea +
                "Sexo: " + this.getSexo() + nuevalinea;
 
        return resultado;
    }
    
	@Override
	public boolean equals(Object object) {
		final Persona aux = (Persona) object;
		if (this == object)
	           return true;
		else if (object == null)
	           return false;
		else if (getClass() != object.getClass())
	           return false;
		else if (this.dni.equals(aux.getDni())) {
			return true;
		} else {
			return false;
		}
	}
}


public class Alumno extends Persona {
	
	private int codAlumno;
	private List<Asignatura> asignaturas;
	
	public Alumno (int codAlumno, String nombre, String apellidos, String dni, Integer edad, String sexo) {
		super(nombre, apellidos, dni, edad, sexo);
		this.codAlumno = codAlumno;
		this.asignaturas = new ArrayList<Asignatura>();
	}
	public void setCodAlumno(int codAlumno) {
		this.codAlumno = codAlumno;
	}
	public int getCodAlumno() {
		return codAlumno;
	}
	public void setAsignaturas(List<Asignatura> asignaturas) {
		this.asignaturas = asignaturas;
	}
	public List<Asignatura> getAsignaturas() {
		return asignaturas;
	}
	
	public void asignarAsignatura(Asignatura asignatura) {
		asignaturas.add(asignatura);
	}

	public double getNotaMedia() {
		double resultado = 0;
		for (int i = 0; i < asignaturas.size(); i++) {
			Asignatura asig = asignaturas.get(i);
			resultado += asig.getNota();
		}
		return (resultado != 0 ? resultado / asignaturas.size(): 0);
	}

@Override 
    public String toString() {
        String nuevalinea = System.getProperty("line.separator");
        String resultado = "DATOS DEL ALUMNO: " + nuevalinea + 
                "Nombre: " + this.getNombre() + nuevalinea + 
                "Apellidos: " + this.getApellidos() + nuevalinea + 
                "DNI: " + this.getDni() + nuevalinea +
                "Edad: " + this.getEdad() + nuevalinea +
                "Sexo: " + this.getSexo() + nuevalinea;
 
        return resultado;
    }
}


public class Profesor extends Persona {
	private int codProfesor;
	
	public Profesor (int codProfesor, String nombre, String apellidos, String dni, Integer edad, String sexo) {
		super(nombre, apellidos, dni, edad, sexo);
		this.codProfesor = codProfesor;
	}
	public void setCodProfesor(int codProfesor) {
		this.codProfesor = codProfesor;
	}

	public int getCodProfesor() {
		return codProfesor;
	}

@Override 
    public String toString() {
        String nuevalinea = System.getProperty("line.separator");
        String resultado = "DATOS DEL PROFESOR: " + nuevalinea + 
                "Nombre: " + this.getNombre() + nuevalinea + 
                "Apellidos: " + this.getApellidos() + nuevalinea + 
                "DNI: " + this.getDni() + nuevalinea +
                "Edad: " + this.getEdad() + nuevalinea +
                "Sexo: " + this.getSexo() + nuevalinea;
 
        return resultado;
}

Por tanto, la clase Persona encapsula toda la funcionalidad común, la cual heredan las clases Profesor y Alumno. Cualquier hijo de la clase Persona, ya sea Profesor o Alumno, podrá invocar a los métodos heredados por su clase padre.

Es importante aclarar, eso sí, que sólo se heredarán aquellos elementos con visibilidad Protected o Public, como vimos en la pildora de visibilidad muy por encima. Por ejemplo, desde la clase Alumno no podemos acceder directamente al DNI con un super.dni, pero sí que podemos hacerlo a través de los métodos setDNI y getDNI, los cuales son públicos.

Hemos creado dos nuevos métodos comunes, equals y toString, que como se puede ver llevan una anotación, @Override. Esto significa que es un método heredado que se va a reescribir para tener una conducta específica y adaptada a la clase hijo. Por ejemplo, los métodos toString de Alumno y Profesor, como se puede ver, devuelven un texto personalizado para esas clase, es decir, sobrescribe el método heredado genérico para cualquier Persona.

Y os preguntareis, ¿y los métodos equals y toString de la clase padre Persona? ¿Por qué tienen también la anotación @Override? Pues porque estos métodos también son heredados. En Java todos los objetos heredan de la clase Object y la clase Object tiene definidos métodos básicos, como toString o equals.

Por último, otra palabra reservada en herencia que utilizaremos mucho es super, la cual tiene una funcionalidad similar a this. Mientras que this, la que ya conocemos, hace mención a la clase en sí, super hacer mención a su clase padre. Por eso, para llamar al constructor de la clase Persona utilizamos la palabra super. De la misma forma, si por ejemplo quisiéramos utilizar el método toString de la clase padre dentro de un método de, por ejemplo, Alumno, utilizaríamos la sentencia super.toString().

Y ya está, no hay más misterio. Como veis, la herencia, aunque potente, es muy sencilla de implementar y entender. En la próxima pildorita veremos la herencia en todo su esplendor con el Polimorfismo y ya con eso, podremos decir que ya sí que sí entendéis la Programación Orientada a Objetos y podéis animaros sin miedo a empezar a programar vuestras propias aplicaciones en Java, C#, o cualquier otro lenguaje POO. No obstante, hay algunas cosas ajenas a la POO pero muy importantes en Java, como las Excepciones, que daremos en otra pildorita extra y con los que no sólo podremos programar en POO, sino tener un buen control de los errores.

Índice de Pildoritas

Share
14
Dec 16

Pildoritas de POO y Java: Relaciones entre Clases

pildoras poo java
En la anterior pildorita os dije que hablaríamos de la Herencia y el Polimorfismo, y aunque esa pildorita está prácticamente terminada en borradores, dándole vueltas a las siguientes pildoritas, me daba cuenta que la que tenía planteada como continuación debía de ir antes, es decir, ésta, sobre relaciones entre clases. Así que nada, esto significa más espera entre la anterior pildorita y ésta, pero el lado bueno es que la siguiente la sacaré en breves.

Llevo unos 7 años trabajando en el desarrollo software y he pasado por multitud de proyectos de gran envergadura. Si algo tengo claro en todos estos años, es que se hace muy mal código - por diversos motivos que no vienen al caso - y que dónde más se falla es, precisamente, en las relaciones de clases: códigos espagueti, pésima modularización y encapsulación... Todos esos problemas vienen, en la mayoría de casos, porque no se tienen del todo claro las relaciones entre clases en la parte más importante del desarrollo, el diseño. Es por eso que antes de entrar en temas de Herencia (algo que muchos desarrollores sí que comprenden bien), creo que es mejor para el aprendizaje de los anteriores conceptos que veamos este tema, que permite asentarlos. Ya habrá tiempo de entrar en la siguiente relación, la generalización, ya que la Herencia no deja de ser también una relación de dos clases.

En la anterior pildorita vimos lo que era un mensaje, y que cuando un objeto lanza un mensaje a otro, estos se comunican y colaboran entre sí. Se dice que existe una Relación entre dos clases si dos objetos de las respectivas clases colaboran entre sí.

Hay tres tipos de relaciones entre clases que a continuación veremos de forma más detallada: Composición, asociación y uso.

Para mayor comprensión de las mismas, utilizaremos el siguiente ejemplo, donde se pueden ver los tres tipos de relaciones:

public class Alumno {
	private String nombre;
    private String apellidos;
    private String dni;
    private Integer edad;
    private String sexo;
	
	private int codAlumno;
	private Lis<Asignatura> asignaturas;
	
	public Alumno (int codAlumno, String nombre, String apellidos, String dni, Integer edad, String sexo, List<Asignatura> lsAsignaturas) {
		this.nombre = nombre;
    	this.apellidos = apellidos;
    	this.dni = dni;
    	this.edad = edad;
    	this.sexo = sexo;
		this.codAlumno = codAlumno;
		this.asignaturas = lsAsignaturas;
	}

        public void setCodAlumno(int codAlumno) {
		this.codAlumno = codAlumno;
	}
	public int getCodAlumno() {
		return codAlumno;
	}
	public void setAsignaturas(List<Asignatura> asignaturas) {
		this.asignaturas = asignaturas;
	}
	public List<Asignatura> getAsignaturas() {
		return asignaturas;
	}
	
	public void asignarAsignatura(Asignatura asignatura) {
		asignaturas.add(asignatura);
	}

	public double getNotaMedia() {
		double resultado = 0;
		for (int i = 0; i < asignaturas.size(); i++) {
			Asignatura asig = asignaturas.get(i);
			resultado += asig.getNota();
		}
		return Math.round((resultado != 0 ? resultado / asignaturas.size(): 0));
	}
}

 

Composición

Es la relación que se constituye entre el todo y la parte. En otras palabras, existe una relación de composición entre la clase A y la clase B, cuando la clase A "tiene un" objeto de la clase B.

Esta colaboración se traduce en la delegación de la clase todo a las partes de ciertas acciones determinadas para implementar la funcionalidad global.

Con esta colaboración se respetará el principio de encapsulación y modularidad por parte de las clases que componen a la clase A.

A la hora de plasmar en código esta relación con los conceptos que hemos visto en las anteriores pildoritas, esto se traduce en un atributo de la clase B en la clase A. Por tanto, tendrá una visibilidad privada y no es versátil, es decir, no puede ser intercambiable por otros objetos. Así mismo, tendrá una duración en la colaboración no momentánea en el tiempo.

Es el caso del atributo codAlumno de nuestra Clase de ejemplo Alumno. Si hiciéramos una clase Profesor, no tendría sentido introducir un código de alumno, por tanto, no es una entidad versátil. Así mismo, el código de alumno se utilizará en muchas funcionalidades de la clase Alumno, ya que es un identificador único similar al DNI. Así mismo ocurre con el resto de atributos que identifican inequívocamente a un Alumno, como nombre, apellidos, DNI...todos aquellos datos que se inicializan a la hora de construir el objeto y que existirán durante todo el ciclo de vida del objeto Alumno.

 

Uso

Es la relación que se establece de forma momentánea entre un objeto cliente (pide algo) y un objeto servidor (suministra ese algo). Por tanto, el objeto de la clase A enviará un mensaje a un objeto de la clase B en un momento dado sin dependencias futuras. Así mismo, el uso de la clase B no dependerá únicamente de la clase A, sino que podrá ser utilizada por otras muchas clases.

Esto significa que la clase A, la cliente, tendrá como parámetro o valor devuelto de un método un objeto de la clase servidora B si esa colaboración es pública, y como un objeto local dentro de un método si es una colaboración privada. La versatilidad de ser intercambiada la clase B por otra, es, por tanto, elevada.

Por ejemplo, el método getNotaMedia() de la clase Alumno, redondea el resultado final utilizando la clase Math e invocando a su método round. Esta clase no es necesaria obviamente para ese cálculo. Podríamos decidir no redondear, o redondear nosotros creando un método propio que realice esta funcionalidad.
 

Aociación

La asociación es una relación de uso donde la relación entre la clase A y la clase B sí que perdura en el tiempo, es decir, se realiza en diversos momentos del ciclo de vida del objeto A, por lo que se crea una relación de dependencia entre ambos objetos. Al igual que la relación de uso, la responsabilidad de manejar ese objeto B no dependerá únicamente del objeto A, sino que podrá ser utilizado por más objetos de diferentes clases.

La asociación, al ser una relación pública generalmente y de una temporalidad no momentánea, pero teniendo una versatilidad reducida, se traducirá en un atributo en la clase A cliente que apuntará a la clase B servidora.

Un ejemplo de este tipo de relación sería la existente entre el Alumno y las Asignaturas. La clase Asignatura es independiente del Alumno, ya que puede existir sin necesidad de que haya Alumnos matriculados a ella. Así mismo, el Alumno puede estar dado de alta en la universidad sin estar matriculado en ninguna asignatura.
 
Espero que esta pildorita haya sido clara y de verdad afiance conceptos fundamentales ya no en el desarrollo de buenos programas en POO, sino de códigos de calidad bien estructurados y fácilmente mantenibles. Cuando vi este capítulo de la asignatura de Programación II me resultó lioso y no le presté toda la atención que merecía. Me centraba en saber sacar las clases de un problema, definir que atributos tenía, y los métodos y sus funcionalidades. Vaya, estaba anclada en lo que conocía, la imperativa. No fue hasta ya después en Ingeniería del Software que llegué a entender del todo para que servía esto y que, la Programación Orientada a Objetos sin estas relaciones no deja de ser modulada, fallo que luego he visto años más tardo muy extendido en la profesión. Sin entender las relaciones, no se sabe de verdar trabajar con POO y poder de verdad explotar la modularidad y ecapsulación que ofrece.

La siguiente pildorita, sí que sí, será la de Herencia y Polimorfismo, la cual, como comenté antes, está ya acabada, así que en una semana tendréis una nueva dosis javera con la que ya prácticamente habremos acabado esta serie 🙂

Índice de Pildoritas

Share
8
Sep 16

Pildoritas de POO y Java: Atributos y Métodos

pildoras poo java
En la anterior pildorita de POO y Java introducimos el concepto de Visibilidad de una clase, la cual nos da la tan ansiada encapsulación.

No obstante, aunque ya llevamos dos pildoritas viendo atributos y métodos, todavía no hemos ahondado suficiente en ciertos conceptos a tener en cuenta a la hora de utilizarlos, pero que ahora que tenemos claro el concepto de Ámbito, podemos afrontar.

Atributos

Los atributos son las variables y constantes que definen los estados o datos de un objeto. Vimos en la anterior pildorita que, al igual que con el resto de elementos existentes en una clase, los atributos también se ven afectados por los modificadores de visibilidad. No obstante, estos modificadores de visibilidad (public, private, static o final), pueden venir seguidos de los modificadores de las características del atributo (su vida útil, sus posibles cualidades y valores, etc.).

  • static: El modificador static, que vimos en la anterior pildorita, se trata como un modificador de característica, y como vimos ya, especifica que el atributo pertenece a la clase y no a los objetos creados a partir de ella.
  • final: El atributo es una constante, por lo que deberá de tener obligatoriamente un valor inicial. Se suele especificar su nombre en mayúsculas por convención.
  • trascient: El atributo no es serializable, es decir, no puede ser convertido en una cadena de bytes que resulte fácilmente manipulable por red, almacenamiento en ficheros, bases de datos, etc. Utilizando este modificador evitamos que este atributo y su valor se tengan en cuenta a la hora de serializar el objeto que los contiene.
  • volatile: Este modificador indica a Java que este atributo puede ser accedido de forma concurrente por varios hilos de forma simultánea.

Métodos

Los métodos son un conjunto de instrucciones definidas dentro de una clase, que realizan tareas y, por norma general, operan sobre los estados de los atributos de dicha clase.

Así mismo, los métodos son el mecanismo que implementa la comunicación entre objetos, lo que comúnmente se llama Paso de Mensajes.

Un objeto, el emisor, solicita ejecutar uno de sus métodos a otro objeto, con el objetivo de que este realice alguna acción, o devuelva cierta información.

Los métodos así mismo, siguen la misma estructura que las funciones y procedimientos de la programación estructurada y pueden recibir, o no, parámetros, así como devolver, o no, valores.

[modificadores] tipo nombreMetodo ([lista parametros]) {
     // instrucciones
     [return valor;]
}

Los modificadores, que no son obligatorios (los corchetes significan que puede estar, o no), indicarán el ámbito de uso del método, y son los que ya hemos visto en la pildorita de Visibilidad y Ámbito.

Constructores

El constructor es un método especial de la clase que se ejecuta siempre que se crea un objeto de dicha clase. Sirve para inicializar los estados del objeto y que sus atributos siempre contengan valores válidos.

Una clase puede tener uno o más constructores. Así mismo, si no se define un constructor, el compilador crea automáticamente el constructor por defecto, un constructor sin parámetros que no hace nada y en el que los atributos del objeto se inicializan a los predeterminados por el sistema.

Para que un método sea interpretado como constructor de una clase debe tener las siguientes características:

  • Tiene el mismo nombre que la clase que contiene
  • No puede devolver ningún valor, ni siquiera el valor void
  • Es público, para que pueda invocarse desde cualquier parte donde se quiera crear un objeto de dicha clase
  • No se hereda

Por ejemplo, volviendo al ejemplo de Persona, cuando creábamos un objeto de este tipo en la clase PruebaPersona, lo hacíamos a través de la sentencia, Persona mar = new Persona();, dicha sentencia llama al constructor por defecto, public Persona();

Si quisiéramos, podríamos crear un nuevo constructor que inicialice los atributos a los valores pasados por parámetro, y así nos ahorraríamos las sentencias de set como mar.setNombre("Mar"), en la que dábamos valor a dicho atributo.

public class Persona {
	 
    private String nombre;
    private String apellidos;
    private String dni;
    private Integer edad;
    private String sexo;
    
    public Persona(String nombre, String apellidos, String dni, Integer edad, String sexo) {
    	super(); // Se llama al constructor por defecto de la clase superior, Object, ya que no se hereda. No es necesario en este caso, pero por convención se pone siempre
    	this.nombre = nombre;
    	this.apellidos = apellidos;
    	this.dni = dni;
    	this.edad = edad;
    	this.sexo = sexo;
    }

Ahora bien, al definir nosotros un constructor, el compilador dejará de crear el constructor por defecto, por lo que, en caso de querer un constructor sin parámetros, deberemos definirlo nosotros también. ¿Y como hacemos esto? Gracias a la Sobrecarga de métodos.

Sobrecarga de métodos

La sobrecarga de métodos es la creación de varios métodos con el nombre, pero con firmas diferentes.

La firma de un método se compone del nombre, la lista de argumentos y el dato devuelto. No obstante en Java, esto último no se tiene en cuenta, por lo que la firma es la combinación del nombre, y el número y tipo de argumentos.

Por ejemplo, los dos constructores de nuestro anterior ejemplo, tienen firmas diferentes por lo que el intérprete es capaz de diferenciarlos aunque tengan el mismo nombre.

public Persona(String nombre, String apellidos, String dni, Integer edad, String sexo) {
	this.nombre = nombre;
    	this.apellidos = apellidos;
    	this.dni = dni;
    	this.edad = edad;
    	this.sexo = sexo;
}

public Persona() {
	super();
}
 

La sobrecarga de métodos da pie a un concepto mucho más amplio conocido como Polimorfismo, pero esto ya es temática para la próxima pildorita, en la cual veremos también otro concepto fundamental de la Programación Orientada a Objetos: la Herencia.

Índice de Pildoritas

Share
24
May 16

Pildoritas de POO y Java: Visibilidad y ámbito

pildoras poo java

En la anterior píldora de POO y Java vimos que en la Programación Orientada a Objetos definimos clases, las cuales una vez se instancian son objetos concretos de esa entidad, con su propio estado.

Así mismo, sabemos que las clases y objetos se componen de atributos y métodos, es decir, estados y acciones sobre esos estados. Vimos la manera de definir variables (atributos) y funciones (métodos) en los primeros capítulos de Imperativa, no obstante, en POO hay que tener en cuenta nuevos conceptos propios del paradigma. El fundamental es la Visibilidad, la cual nos da la tan ansiada encapsulación. También debemos conocer cierto concepto previo utilizado en Java y que afecta a la definición de lo qué es visibilidad: Los paquetes.

Para mayor claridad de los conceptos, utilizaremos variaciones en el ejemplo inicial de la anterior pildorita, por lo que es recomendable tenerla abierta en otra pestaña.
 

Paquetes

Los paquetes no son más, siendo muy burdos, que carpetas, y sirven para organizar nuestras clases y darles un nombre único. Los paquetes además, como ocurre con las carpetas, se organizan en una estructura de árbol. Cuando creamos un paquete, se crea una estructura de directorios asociados.

En la anterior pildorita se definió una clase y aunque no se llegó a explicar, la primera línea con la palabra package, indica el paquete donde se encuentra alojada.

// Directorio donde se encuentra la clase Persona y PruebaPersona: 
// ejemplo1/persona
package ejemplo1.persona

carpetas
Así mismo, cuando utilicemos una clase del mismo paquete en otra clase, no tendremos que especificar su ruta. Si no es el caso, utilizaremos la palabra import seguida del paquete y clase.

import ejemplo1.persona.Persona

 

Visibilidad y acceso

La visibilidad, o vista, nos indica por quién es accesible un determinado elemento y es aplicable tanto a clases, como a atributos y métodos. Para diferenciar la visibilidad de un elemento, se utilizan los modificadores de acceso, palabras reservadas que especifican el tipo de visibilidad de dicho atributo, método, etc.

Hay cuatro tipos de modificadores: public, private, protected, default y static.

public

La vista public permite acceder al elemento desde cualquier clase del espacio de trabajo. Un ejemplo de esta vista es la de todos los métodos de la clase Persona o la de su atributo codigoUnico. Como vimos, desde PruebaPersona se accede a él sin necesidad de métodos intermedios.

package ejemplo1.persona;

public class Persona {
 
    public String codigoUnico; 
    
    public String toString() {
        String nuevalinea = System.getProperty("line.separator");
        String resultado = "DATOS DE LA PERSONA: " + nuevalinea + 
                "Codigo Unico: " + this.codigoUnico + nuevalinea +
                "Nombre: " + this.getNombre() + nuevalinea + 
                "Apellidos: " + this.getApellidos() + nuevalinea + 
                "DNI: " + this.getDni() + nuevalinea +
                "Edad: " + this.getEdad() + nuevalinea +
                "Sexo: " + this.getSexo() + nuevalinea;
 
        return resultado;
    }
}

package ejemplo1.persona

public class PruebaPersona {
 
    public static void main(String[] args) {
         
        Persona mar = new Persona();        
        mar.codigoUnico = "112";
        System.out.println(mar.toString());
         
    }
}

private

La vista private, por el contrario, sólo permite que el elemento sea utilizado por otros objetos de la misma clase, pero no por elementos de clases diferentes. Como se ve en el ejemplo anterior, el método toString() utiliza los atributos privados sin necesidad de utilizar métodos públicos que devuelvan sus valores. Sin embargo, para poder utilizarse desde la clase PruebaPersona es necesario hacerlo a través de sus métodos set y get.

package ejemplo1.persona;

public class Persona {
	
	private String nombre;
	
	 public String getNombre() {
        return nombre;
    }
 
    public void setNombre(String nombre) {
        this.nombre = nombre;
    }
}

package ejemplo1.persona

public class PruebaPersona {
 
    public static void main(String[] args) {
         
        Persona mar = new Persona();        
        mar.setNombre("Mar");
        System.out.println("El nombre de la persona es: " + mar.getNombre());
         
    }
}

¿Y que pasaría si intentaramos acceder a un atributo privado? Por ejemplo, el DNI:
private-example
Como se puede ver, se muestra un error pre-compilación, y se nos sugiere o bien cambiar la visibilidad del atributo - a public, por ejemplo - o utilizar un método set, como se hace en la línea anterior.

protected

La vista protected indica que un elemento es accesible desde una clase del mismo paquete, o por una clase que herede de la clase en la que este elemento se encuentra, sin importar el paquete. Una vez más, dejaremos aparcada la herencia, la cual requiere una pildorita por si sola. Cuando toque explicaremos más en profundidad este aspecto.

default

La vista default, que se puede también indicar sin poner ningún modificador de acceso delante del elemento, permite al elemento ser utilizado desde cualquier clase del mismo paquete.

public class Persona {
 
    String codigoUnico; 

}

package ejemplo1.persona

public class PruebaPersona {
 
    public static void main(String[] args) {
         
        Persona mar = new Persona();        
        mar.codigoUnico = "112";
        System.out.println("El Codigo de Persona es: " + mar.codigoUnico);
         
    }
}

static

Por último, existe el modificador static, el cual indica que el elemento pertenece a la clase y no a las instancias de la misma (objetos). Por ejemplo, como vimos en el ejemplo de la anterior pildorita, el main se declara como static. Esto permite acceder al método main sin necesidad de instanciar la clase que lo contiene.

package ejemplo1.persona;
/**
 * Clase Persona
 * @version 1.0
 * @since 2014
 */
public class Persona {
 
    static int numPersonas = 0;
    private String nombre;
   
   // Esto es un constructor 
   Persona() {
	   numPersonas++;
   }

	public void setNombre(String nombre) {
		this.nombre = nombre;
	}
	
	
	public String getNombre() {
		return nombre;
	}
}

package ejemplo1.persona;
/**
 * Clase Test para probar la clase Persona
 * @version 1.0
 * @since 2014
 */
public class PruebaPersona {
 
    public static void main(String[] args) {
         
        Persona objeto1 = new Persona();
        objeto1.setNombre("Mar");
        
        Persona objeto2 = new Persona();
        objeto2.setNombre("Pepe");
        
        System.out.println("El nombre del objeto 1 es: " + objeto1.getNombre());
        System.out.println("El nombre del objeto 2 es: " + objeto2.getNombre());
        System.out.println("Se han creado " + Persona.numPersonas + " personas");
    }
}

El resultado que tendremos es el siguiente:

El nombre del objeto 1 es: Mar
El nombre del objeto 2 es: Pepe
Se han creado 2 personas

Existen otros modificadores de acceso como native, utilizado para indicar código diferente a Java, trascient, que se utiliza para indicar que este elemento no es persistente, final, para especificar que una variable determinada tendrá un valor fijo, volatile y synchronized, utilizados para la programación concurrente y abstract, para herencia. Al no ser modificadores de acceso alejados del concepto de visibilidad, no entran en esta pildorita, pero ya habrá tiempo de verlos.

Índice de Pildoritas

Share
5
Apr 16

Pildoritas POO y Java: El que mucho abarca... u_U!

pildoras poo java

Ayer entré en este blog y miré la parte de estadísticas de la web, cosa que no miraba desde hacía decenios.

Pese a que el rumbo de este sitio ha virado enormemente a la política, por lo que pude ver la mayoría de mis visitas – cosa que por otra parte me dio una muy buena alegría – llegan a este blog gracias a mis Pildoritas de Bases de Datos y Programación orientada a objetos en Java.

Estuve revisando las pildoritas y bueno, con las de POO se me cayó la cara de vergüenza, literalmente. Subí el último post, la introducción de POO, en noviembre de 2014, que se dice pronto. Desde entonces todo aquel que entra a esta serie de pildoras se encuentra con la “grata” sorpresa de que falta justamente la parte de POO, ¡en unas pildoritas que supuestamente son de POO!

Así que nada, ayer volví a releer las pildoritas, consulté apuntes y recoloqué ideas en mi cabeza, con la clara resolución de terminar los, como mucho, 3 o 4 posts que pueden faltar para concluírlas.

Para este finde espero tener el siguiente post, centrado en los ámbitos de las clases y objetos.

Como ya va siendo tradición en mis posts, una vez más pido disculpas a todas aquellas personas que puedan haberlas leído y se hayan quedado con la frustración de no verlas acabadas.

Share
21
Nov 14

Pildoritas de POO y Java: Introducción a la Orientación a Objetos

pildoras poo java

Como veíamos en la pildorita introductoria, la evolución de los lenguajes de la programación siempre ha seguido una meta clara: encontrar la metodología más intuitiva para los seres humanos. Para ello, la programación siempre intenta alcanzar el mayor grado de abstracción, modularidad y encapsulación. La Programación Orientada a Objetos no es más que el enésimo y último (o penúltimo si contemplamos la programación orientada a aspectos) intento de alcanzar esas metas, utilizando para ello dos conceptos muy claros: Clases y Objetos.

El mundo real está repleto de Objetos. Algunos son tangibles, como una persona, una mesa o un coche y otros intangibles, como un crédito, una raíz cuadrada o la ventana de una aplicación. Los seres humanos somos criaturas muy observadoras que tendemos además a clasificar esos objetos en diferentes tipos (o Clases) por medio del proceso de abstracción, por el cual se identifican las características esenciales comunes de un conjunto de objetos, prescindiendo de los detalles superfluos.

Por ejemplo, por medio de la observación de otros animales y nosotros mismos, hemos identificado que un conjunto de los mismos somos vertebrados, de sangre caliente, tenemos glándulas mamarias y somos vivíparos (con la excepción del ornitorrinco xD) y los hemos clasificado como Mamíferos.

Un capítulo de biología sobre los Mamíferos nos explica las partes que lo componen, las funciones que realizan, etc., eso es una Clase. Por otra parte tú, o mis gatetes, son objetos de esta clase. Los objetos son entidades concretas que siguen la descripción de dicha Clase.

mamiferosObjetos de la clase Mamífero

En el desarrollo POO se definirá antes de nada la clase y una vez hecho esto, crearemos objetos de esa clase y trabajaremos con ellos. Poniéndonos filosóficos y egocéntricos, los programadores de POO son algo así como Demiurgos, encargados de describir ideas que sirven de molde para los objetos que luego compondrán el mundo real.

Por otra parte, como la POO es imperativa al fin y al cabo, todo se entiende a partir de variables que cambian sus estados durante el transcurso de la ejecución. Para ello, los objetos se compone de atributos, variables que almacenan diferentes estados, y métodos, funciones que realizan diferentes acciones sobre los estados de los atributos.

Hay que tener en cuenta que los atributos también serán objetos (los tipos primitivos no dejan de ser clases también) que componen ese supra-clase. La interacción de estos diferentes objetos entre sí, a partir de lo que se llama mensajes, y las relaciones que existen entre ellos, son precisamente las causantes de que esta programación imperativa marque la diferencia.

Por otra parte, las clases también tienen un ámbito privado y otro público, para conseguir las tan preciadas metas de la encapsulación y abstracción. Tanto las clases, como variables, constantes o métodos, pueden tener un ámbito privado o público, dependiendo si interactuarán con otras clases o son sólo de carácter interno. En el caso de Java también existe otro ámbito, el protected, pero por ahora no vamos a entrar en él.

Veamos ahora un ejemplo muy sencillo de definición de una clase y uso de la misma, para que podáis asimilar las ideas expuestas hasta ahora.

Aunque es un ejemplo muy básico pondré un Diagrama de Clases para que se vea mejor. El UML es un lenguaje de diseño que en sí mismo daría para una serie de Pildoritas nuevas (que espero algún día hacer xD). Cada símbolo tiene significados muy concretos y diferencias con otros que, aunque visualmente no sean muy notables, suelen ser muy importantes a la hora de plasmar nuestro código. También es cierto que se puede llegar a entender en líneas generales el problema sin tener ni idea de UML, que es lo que pretendo en este caso 🙂

f3aec65a

Vamos a definir una clase Persona. Los atributos están al comienzo de la "tabla" Persona y separados por una línea, los métodos. Los símbolos + y - indican su ámbito (público o privado). Por último, tendremos una clase PruebaPersona que simplemente tiene un método main y utiliza una clase Persona.

Para el desarrollo del diagrama, por cierto, he utilizado una herramienta online que no conocía pero me ha enamorado, yuml.me. Genera vía online diagramas de clases, casos de uso y diagramas de actividad. Simplemente hay que definir el diagrama con una sintáxis que se explica muy bien en los ejemplos y te genera directamente una URL de edición, imágenes png, jpg, archivos pdf y json...Vamos, una pasada. Os lo recomiendo.

 package ejemplo1.persona;
/**
 * Clase Persona
 * @version 1.0
 * @since 2014
 */
public class Persona {

	private String nombre;
	private String apellidos;
	private String dni;
	private Integer edad;
	private String sexo;
	
	public String codigoUnico;

	public String getNombre() {
		return nombre;
	}

	public void setNombre(String nombre) {
		this.nombre = nombre;
	}

	public String getApellidos() {
		return apellidos;
	}

	public void setApellidos(String apellidos) {
		this.apellidos = apellidos;
	}

	public String getDni() {
		return dni;
	}

	public void setDni(String dni) {
		this.dni = dni;
	}

	public Integer getEdad() {
		return edad;
	}

	public void setEdad(Integer edad) {
		this.edad = edad;
	}

	public String getSexo() {
		return sexo;
	}

	public void setSexo(String sexo) {
		this.sexo = sexo;
	}
	
	public String toString() {
		String nuevalinea = System.getProperty("line.separator");
		String resultado = "DATOS DE LA PERSONA: " + nuevalinea + 
				"Codigo Unico: " + this.codigoUnico + nuevalinea +
				"Nombre: " + this.getNombre() + nuevalinea + 
				"Apellidos: " + this.getApellidos() + nuevalinea + 
				"DNI: " + this.getDni() + nuevalinea +
				"Edad: " + this.getEdad() + nuevalinea +
				"Sexo: " + this.getSexo() + nuevalinea;

		return resultado;
	}
}

Como veis, la clase persona está compuesta por los atributos que le describen, en este caso nombre, apellidos, dni, edad, sexo y codigoUnico. Además, se han creado una serie de métodos para poder obtener los valores de los atributos privados (métodos get) y cambiarlos (métodos set), así como un método toString que devuelve es una representación en formato String de la clase.

Todos los atributos menos codigoUnico son privados, por lo que no se puede acceder directamente desde otra clase a los mismos si no es a través de los métodos públicos get y set creados para este fin. Por su parte el atributo codigoUnico se puede modificar y obtener directamente desde otras clases, como se puede ver en la clase de Prueba de a continuación, ya que está en la vista pública de la clase.

La palabra reservada this, por su parte, lo que hace es indicar que se hace referencia a atributos o métodos del objeto. Existen otras palabras reservas como super, que ya veremos más adelante para que sirven.

package ejemplo1.persona
/**
 * Clase Test para probar la clase Persona
 * @version 1.0
 * @since 2014
 */
public class PruebaPersona {

	public static void main(String[] args) {
		
		Persona mar = new Persona();
		
		mar.codigoUnico = "112";
		
		mar.setNombre("Mar");
		mar.setApellidos("Millan");
		mar.setEdad(27);
		mar.setSexo("F");
		mar.setDni("01168899Q");
		
		
		System.out.println(mar.toString());
		
	}
}

SALIDA DE LA CLASE PruebaPersona:

DATOS DE LA PERSONA:
 Codigo Unico: 112
 Nombre: Mar
 Apellidos: Millan
 DNI: 01168899Q
 Edad: 27
 Sexo: F

Como podéis ver para crear un objeto se utiliza la siguiente sentencia: Persona mar = new Persona(). Esto se llama constructor y lo que hace es crear en memoria una instancia, es decir, un objeto, del tipo Persona. No entraremos en que es un constructor y lo veremos en la siguiente pildorita, pero por ahora basta saber que todas las clases tienen uno sin parámetros, como el utilizado en este ejemplo, encargados de crear objetos concretos en memoría de la clase a la que pertenecen.

El atributo codigoUnico le hemos dado un valor refiriendonos directamente a él, lo cual se hace indicando el nombre del objeto de la clase que hemos creado seguido del atributo o método separado por un punto.

El resto de parametros al ser privados, sólo se pueden setear a través de los métodos creados con tal fin. Como se ve para este fin utilizamos sus métodos set, pasándoles por parámetro los valores que queremos que tomen estas variables.

Por último, podemos ver una sentencia System.out.println al que le pasamos el string que genera el método toString(), que muestra por pantalla dicha cadena.

Espero que esta Pildorita os haya dado una visión general de la Programación Orientada a Objetos. En las próximas pildoritas empezaremos a meternos con mayor profundidad en los diferentes conceptos que hoy hemos tocado de pasada.

Índice de Pildoritas

Share
17
Feb 14

Pildoritas de POO y Java: Introducción

pildoras poo java

Comenzamos ya sí que sí, estas Pildoritas de POO y Java. Este primer post es puramente introductorio, pero no por ello menos importante.

Vamos a ver muy por encima qué es la programación, un poco de fundamentos de informática relacionados a la misma y el progreso histórico que ha sufrido esta rama hasta llegar a nuestros días. También veremos tres de los pilares fundamentales de la programación como son la Abstración, la Encapsulación y la Modularidad.

Como no me puedo extender demasiado (podría surgir otra serie de Pildoritas si lo hiciera xD), esta parte será muy resumida (aunque no os lo parecerá), así que recomiendo abrir los enlaces a la Wikipedia que hay en el post.

¿Qué es la programación?

Según la wikipedia es "el proceso de diseñar, codificar, depurar y mantener el código fuente de programas computacionales".

Quizás de primeras esta definición os suene a chino, así que mejor explicarlo un poco mejor.

Un poco de historia

Los ordenadores están compuestos de una CPU, de la Memoria y de los dispositivos de Entrada/Salida. Dentro de la CPU tenemos registros que almacenan estados pequeños, la ALU (Unidad Aritmético-Lógica), encargada de realizar operaciones aritméticas y lógicas, y la Unidad de Control, que coordina el flujo del procesador y comunica a través de los buses de datos la información entre sus diferentes componentes. Esta Arquitectura se denomina Aquitectura Von Neumann, y es la utilizada tanto en el famoso ENIAC, como en el PC o el Smartphone con el que estás leyendo esta pildorita.

A partir de sentencias almacenadas en memoria e interpretadas por la CPU, se pueden realizar funciones que atajen determinados problemas. Estas sentencias, que primero comenzaron como puros 0 y 1, y más adelante con ensamblador hasta llegar a Java, no son más que algoritmos que explican al procesador que operaciones realizar. Eso es lo que comúnmente se conoce como programas informáticos.

Los programas no son más que el código fuente que describe las acciones a realizar por parte de un proceso informático, y un proceso informático es un programa en ejecución. En la definición de la wikipedia se habla del mantenimiento del código fuente de un programa, lo cual es redundante. El código fuente es en sí el programa.

Cuando nosotros “piquemos” en Java estaremos haciendo programas. Cuando ejecutemos ese programa, este será un proceso más en ejecución de nuestro sistema operativo (que no es más que otro proceso).

Desde que el ser humano convive con los ordenadores, ha intentado siempre que la comunicación con el ordenador se realizara en un lenguaje cada vez más amigable para las personas. Es por ello, que la evolución de los lenguajes, hasta llegar a la POO (y otras más novedosas como la Programación Orientada a Aspectos), ha seguido siempre estas tres metas.

Abstracción

Es el proceso mental por el cual el ser humano se centra en lo esencial, ignorando los detalles superfluos.

La abstracción es subjetiva y depende del observador, por lo cual los detalles en cada metodología son diferentes.

Encapsulación

Proceso que oculta las características esenciales del proceso de abstracción utilizado. Por tanto, la encapsulación y la abstracción son complementarios.

Resumiendo, si no es necesario el conocimiento de cierta información, no debe de conocerse.

Modularidad

Descomposición de un programa en trozos independientes (no acoplados) y con significado por si mismos (cohesivos).

Cuando menos acoplado e independientes sea una aplicación, menor será su nivel cohesivo, ya que cuanto más se separe el software resultará más incomprensible. Por otra parte, un software muy acoplado tampoco es bueno, ya que aumenta ingentemente el esfuerzo en el desarrollo y mantenimiento de ese código, por no hablar de que habrá código repetido y redundante.

 

En la persecución de estas tres metas, han surgido muchos paradigmas de programación, pero todos están englobados en dos grandes bloques: programación declarativa y programación imperativa.

Paradigmas de programación

La programación imperativa se describe en términos de estados y de sentencias que cambian dichos estados. En ella, se indica al ordenador en todo momento que tiene que hacer, se le da órdenes. La programación imperativa se divide en muchos otros sub-paradigmas, como la Programación Modular, la Estructura, o la Programación Orientada a Objetos.

Por contraposición, la programación Declarativa no indica al procesador como llegar exactamente a la solución de un problema, sino que establece preposiciones, condiciones, ecuaciones, restricciones, etc. que describen el problema. La programación declarativa está también dividida en otros paradigmas: Lenguajes lógicos, algebraicos y funcionales.

De los dos paradigmas el más extendido de lejos es el imperativo, no obstante, esto no significa que supla en sí el declarativo. Muchos problemas se abordan de mejor forma con uno de los paradigmas y no el otro. Por ejemplo, para programas de carácter científico o matemático, por norma general el paradigma más utilizado es el declarativo.

No obstante, como estas pildoritas van sobre Programación Orientada a Objetos, vamos a centrarnos en la Programación Imperativa para ver rápidamente el desarrollo de este paradigma hasta desembocar en su “opera prima”, la Programación Orientada a Objetos.

Desarrollo de la programación imperativa

Como decía antes, en un principio se microprogramaba, es decir, se indicaba con 0 y 1 las acciones a seguir por la unidad de proceso. Este es lo que se llama Código Máquina, y es el nivel más rudimentario de comunicarse con un ordenador.

Debido a lo complejo y tedioso que resulta implementar problemas medianamente extensos con esta mecánica, se creó el Lenguaje Ensamblador, que utiliza, ya sí, un lenguaje un poco más comprensible para un ser humano.

A partir de identificadores, variables (que sustituían las direcciones de memoria donde se encontraban alojados los datos), constantes en hexadecimal (y no en binario como hasta entonces) y controles de flujo (GO TO), se simplificó enormemente la tarea de programar.

No obstante, este primer lenguaje seguía sin ser una buena solución para programas complejos y extensos, resultando en muchos casos un esfuerzo titánico entender el flujo del programa.

Es por ello que surgió la Programación de Alto Nivel, algo ya más parecido a lo que podríamos ver hoy en día, y que acercaba enormemente los lenguajes de programación a los de los seres humanos. Con ella surgen las Estructuras de Control de flujo de ejecución, y los Subprogramas, aunque no se utilizan de forma permanente.

La imposición de esas dos novedades generaron dos nuevos paradigmas dentro de la Programación Imperativa: La Programación Estructurada y la Modular.

La Programación Estructurada nos da un conjunto de estructuras de control de flujo de comportamiento predecible y los algoritmos quedan expresados mediante jerarquías de composición de dichas estructuras de control. De esta forma resultaba mucho más sencillo codificar y comprender un programa.

El otro paradigma, el Modular, centra la construcción de programas en la realización de módulos, es decir, trozos o porciones de subprogramas y variables independientes. Además, surgieron los conceptos de parte Pública, la que puede ser utilizada fuera de los módulos, y parte Privada, aquella interna del módulo y que el resto de procesos no conoce. Por tanto, este paradigma aumentó considerablemente la abstracción, encapsulación y modularidad en los programas informáticos.

Al igual que estos paradigmas surgieron por el uso sistemático de los conceptos de abstracción, encapsulación y modularidad en la programación de alto nivel, la Programación Orientada a Objetos surgió por el uso sistemático de los Tipos Abstractos de Datos (los módulos) y la Herencia, concepto adquirido de la Inteligencia Artificial. No entraremos en la Herencia hasta la parte teórica de POO, pero basta decir que aumentaba enormemente la cercanía de los lenguajes de programación a nuestra concepción del mundo.

 

Bueno, con esto ya ponemos punto final a la pildorita más coñazo de toda la serie. Espero que la chapa no os haya repelido a leer las próximas entregas, pero este tostón es necesario para aquellos que se acerquen por primera vez al mundo de la programación. ¿Cómo aprender a programar si no tienes ciertos conceptos claros de informática?

Además, yo me he podido dar el gusto de meter una pequeña introducción de la informática y hasta mencionar el ENIAC. Por un momento me he recordado a mi misma cuando estudiaba diciendo "¿Otra vez el coñazo del ENIAC?"

Índice de Pildoritas

Share
10
Feb 14

Pildoritas de POO y Java

pildoras poo java

Los pocos que me seguisteis en las Pildoritas de Bases de Datos, recordareis que dije al final que tenía en mente hacer una nueva serie de artículos sobre programación. El tema en cuestión que tenía en mente era POO y Java.

Seguramente muchos acabéis aquí atraídos por los cantos de sirena de Java. A día de hoy es el segundo lenguaje más utilizado en el mundo, sólo superado por C, y el más utilizado en Programación Orientada a Objetos. No obstante, y aprovechando este reclamo, lo que quiero de verdad en estas pildoritas es ahondar en lo que a mi parecer es lo más importante, tener claros los conceptos de la POO.

Si tienes formación y/o experiencia en POO y quieres aprender Java está sera una primera aproximación en para aprender lo básico. Si en cambio vienes aquí sin conocimientos en programación o en otros paradigmas, podrás adquirir (o esa es mi intención) una base sólida en este nuevo paradigma. Cuando uno trabaja día a día desarrollando, lo verdaderamente importante no es la tecnología, con la cual uno se hace después de un tiempo de adaptación, sino tener claro como hacer un programa en POO.

Las Pildoritas comenzarán con una parte introductoria, donde se entrará un poco en la historia de la programación y como se ha llegado hasta la POO. También aprovecharé esta misma parte para ver unos conceptos básicos de programación, como controles de flujo, funciones, etc., en cuya parte también se verá la sintaxis básica de Java. Tras esto continuaremos con la teoría específica de POO, todo esto con ejemplos en Java. Por último, se verán conceptos particulares de Java (véase Excepciones) que no se pueden incluir dentro de la teoría de objetos.

Espero poder ser capaz de ordenar todo lo que tengo en mi cabeza y que me queden unas pildoritas decentes, donde se adquieran buenos conocimientos de POO y Java. Si lo consigo, podré seguir con Pildoritas de Java más avanzadas.

Para finalizar esta parte de presentación, quisiera también indicar que, aunque no voy a copiar y pegar de mis libros de facultad, sí que me guiaré por el contenido de mis apuntes de carrera. No veo una manera mejor de hacerlo. Es por ello que me gustaría referenciar el libro de Programación II de la Escuela Universitaria de Informática de la Universidad Politécnica de Madrid, ya que en más de una ocasión lo consultaré en la realización de estas pildoritas.

Índice de Pildoritas

Share