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
3
Mar 14

Pildoritas de POO y Java: Conceptos básicos de programación. Operaciones

pildoras poo java
En la anterior pildorita de POO y Java vimos qué era un algoritmo y enumeramos los tipos de instrucciones que los componen y que nos permiten describir un problema: Datos, Operaciones y Sentencias. Además, vimos la primera parte, los Datos.

En esta pildorita vamos a ver el siguiente componente que forma un algoritmo: Las operaciones. Veremos los tipos de operadoresy su orden de precedencia.

Operaciones

Las operaciones son sentencias que realizan un cálculo sobre varios datos. Para realizar dichos cálculos tenemos una serie de operadores aritméticos (sumar, restar, multiplicar...), lógicos (mayor que, menor que, igual que...), y de asignación.

Los operadores sólo pueden ser utilizados con determinados tipos más o menos restrictivos (hay operadores que sirven para numéricos, otras para numéricos y de texto..). Además, los operadores pueden ser utilizados con dos datos – binarios - con un dato – unarios - e incluso terciarios.

Operadores aritméticos

Los operadores aritméticos actúan sobre datos numéricos, es decir, enteros y de coma flotante. Los básicos son + (sumar), - (restar), * (multiplicar), / (dividir) y % (módulo, que es el resto de una división de enteros).

Binarios

Operador Símbolo Descripción
Suma + Suma dos operandos
Resta - Resta dos operandos
Multiplicación * Multiplica dos operandos
División / Divide dos operandos
Módulo % Devuelve el resto de la división de dos operandos
//Suma
int resultadoSuma = 5 + 2; // resultadoSuma vale 7

//Resta
float resultadoResta = 5.3f - 2.07f; // resultadoResta vale 3.2300003

//Multiplicación
short resultadoMultip = 10 * 2; //resultadoMultip vale 20

// División
long resultadoDiv = 1238907 / 92384; //resultadoDiv vale 13

// Módulo		
int resultadoMod = 10 % 3; //resultadoMod vale 1

El operador suma, +, también puede operar con los String, concatenándolos entre ellos. También podemos concatenar a las cadenas valores de otros tipos, como int o long. Veamos un ejemplo:

System.out.println("resultadoSuma: " + resultadoSuma);

El comando System.out.println sirve para mostrar por consola un input introducido como parámetro (entre los paréntesis). Lo que veríamos por pantalla en este caso sería lo siguiente:

"resultadoSuma: 7"

Unarios

Operador Símbolo Descripción
Conversión de entero + Convierte el operando a entero si es un byte, short o char
Negación - Negación aritmética del operador (cambio de signo)
int a = -5;
int b = -a; //b vale 5

Operadores Lógicos

Los operadores lógicos comparan dos valores y devuelven la relación existente entre ambos. Por tanto, el resultado de estos operadores es de tipo booleano, es decir, en términos de verdadero o falso.

Los operadores lógicos a su vez pueden dividirse en dos subtipos: comparación y condicionales. Ambos tipos de operadores además se pueden combinar para realizar expresiones más complejas.

Operadores de comparación

Operador Símbolo Descripción
Mayor que > Devuelve true si el primer operando es mayor que el segundo
Mayor o igual que >= Devuelve true si el primer operando es mayor o igual que el segundo
Menor que < Devuelve true si el primer operando es menor que el segundo
Menor o igual que <= Devuelve true si el primer operando es menor o igual que el segundo
Igual == Devuelve true si los dos operandos son iguales
Distinto != Devuelve true si los dos operadores no son iguales
//Mayor que
boolean mayorQue = 3 > 1; //mayorQue vale true

// Menor que
boolean menorQue = 3 < 1; //menorQue vale false

//Mayor o igual que
boolean mayorIgualQue = 3 >= 3; //mayorIgualQue vale true

//Menor o igual que
boolean menorIgualQue = 3 <= 1; //menorIgualQue vale false

//Igual
boolean igual = 5 == 5; //igual vale true

// Distinto		
boolean distinto = 5 != 5; //distinto vale false

Operadores condicionales

Los operadores condicionales actúan sobre dos datos de tipo booleano, y devuelven un resultado también booleano. También pueden evaluar dos expresiones de comparación, ya que estas expresiones devolverán valores boolean.

Para mayor comprensión de los operadores condicionales os recomiendo ver el apartado de Operadores Condicionales de la Pildorita de Bases de Datos sobre Cláusulas. Las tablas de la verdad os pueden ayudar a comprenderlos mucho mejor

Operador Símbolo Descripción
AND && Devuelve true si ambos operadores son verdaderos
OR || Devuelve true si uno de los operadores es verdadero
Falso ! Opera sobre un elemento y devuelve su valor contrario
//AND
boolean and = true && true; // Vale true
boolean and2 = true && false; //Vale false
boolean andComplejo = (2 > 3) && (1 < 5); //false AND true: Vale false

//OR
boolean or = true || false; //Vale true. El intérprete ni siquiera mirará el sgundo elemento
boolean or2 = false || false; //Vale false
boolean orComplejo = (2 < 3) || (1 > 2); //Vale true

//Negacion
boolean not = !(3 > 1); //not vale false

Operadores de asignación

Los operadores de asignación son operadores especiales que asignan un valor a una variable. El operador básico ya lo hemos visto en anterior ejemplos, el =, pero en conjunción con otros símbolos como el de suma, se pueden realizar atajos.

Binarios

Operador Símbolo Ejemplo Expresión equivalente
Asignación básica = A = B; No Aplica
Suma += A += B; A = A + B
Resta -= A -= B; A = A – B
Multiplicación *= A *= B A = A * B
División /= A /= B A = A / B
Modulo %= A %= B A = A % B
//Asignación básica
int c = 5; // c vale 5

//Suma
c += 15; // c vale 20

//Resta
c-=5; //c vale 15

//Multiplicación
c *= 2; //c vale 30

//División
c /= 2; //c vale 15

//Módulo
c %= 10; //c vale 5

Unarios

Operador Símbolo Descripción
Post-incremento op++ Incrementa en 1 el operador. Se evalúa el valor anterior al incremento
Pre-incremento ++op Incrementa en 1 el operador. Se evalua el valor posterior al incremento
Post-decremento op-- Decrementa en 1 el operador. Se evalúa el valor anterior al incremento
Pre-decremento --op Decrementa en 1 el operador. Se evalúa el valor posterior al incremento
int unarios = 8;

unarios++; //unarios vale 9
++unarios; //unarios vale 10
unarios--; //unarios vale 9
--unarios; //unarios vale 8

boolean postfijo = (9 == unarios++); //postfijo vale false
boolean prefijo = (9 == unarios++); //prefijo vale true

Orden de precedencia

Cuando construimos expresiones complejas donde utilizamos más de un tipo de operador, hay que tener en cuenta el Orden de precedencia de los Operadores.

Operador Símbolo
1 operadores unarios postfijos op++, op--
2 Operadores unarios prefijos ++op, --op, +op, -op, !
3 Multiplicación y División *, /, %
4 Suma y Resta +, -
5 Operadores relacionales <, >, <=, >=
6 Operadores de equivalencia ==, ¡=
7 AND &&
8 OR ||
9 Condicional ?:
10 Operadores de asignación =, +=, -=, *=, /=, %=

 

Para romper el orden de precedencia, al igual que en las matemáticas, utilizaremos paréntesis o corchetes.
  
Y con esto terminamos esta pildorita de operaciones. En próximas pildoritas hablaremos de las sentencias de flujo de ejecución, las funciones y finalmente ciertas consideraciones especiales de Java.

Índice de Pildoritas

Share
24
Feb 14

Pildoritas de POO y Java: Conceptos básicos de programación. Datos

pildoras poo java

Antes de adentrarnos a la Programación Orientada a Objetos tenemos que ver algunos conceptos básicos indispensables para poder entender cualquier programa. Para ello explicaremos mejor qué es un algoritmo y veremos que elementos contiene. Cada una de sus partes se dividirá en una Pildorita.

Por primera vez habrán algunos ejemplos en Java, pero serán muy básicos y completamente alejados de la POO. La idea de estas tres pildoritas será aprender la sintáxisis de Java más básica antes de adentrarnos en POO.

 

¿Qué es un algoritmo?

Un algoritmo es un conjunto de instrucciones o reglas ordenadas con principio y final, que describen como realizar un problema o actividad hasta llegar a su solución.

Las instrucciones pueden servir para almacenar información, realizar operaciones, o dirigir el orden de la ejecución de las sentencias del algoritmo.

La programación no es más que describir algoritmos comprensibles para un ordenador, por lo que los conceptos de la algorítmica son en sí mismos los de la programación.

 

Elementos de un algoritmo

Datos

Es la información con la cual trabaja el programa para resolver el problema. Los datos son fundamentalmente numéricos y caracteres, aunque hay otros tipos como los booleanos, que indican Verdadero o Falso, pero que pueden resumirse en 1 ó 0. También existen tipos más complejos utilizados para organizar los tipos simples, como Arrays, Listas, tablas hash, etc.

Variables

Son contenedores de datos, por lo que también son del tipo de dato que contienen. Este contenedor puede almacenar cualquier valor y que varíe según el tiempo de ejecución en el que se encuentre el programa.

En algunos lenguajes el tipo de las variables se decide en tiempo de ejecución, dependiendo del dato que contengan en ese momento, lo que se conoce como lenguajes poco tipados. Por el contrario, otros lenguajes, en cambio, si que necesitan que se indique el tipo de la variable. Este es el caso de Java, que es fuertemente tipado.

Para definir una variable en Java, bastaría con la siguiente sentencia:

int total;

También se puede declarar una variable y al mismo tiempo asignarle un valor.

int total = 5;

Constantes

Las constantes contienen también datos, pero el valor de este dato jamás cambiará. Un ejemplo de constante en java sería:

static final int PI = 3,1416;

Realmente la palabra reservada static no es necesaria, pero con ella indicamos que esa variable pertenece a la clase y es compartida por todas las instancias de la misma (quizás ahora os suene a chino pero en unas pildoritas entenderéis por qué lo pongo así :P)

Tipos de datos

Los datos pueden ser numéricos (de carácter matemático) o alfanuméricos (para representar caracteres), pero los lenguajes definen una serie de tipos dentro de estos dos grandes grupos. Por otra parte, los tipos pueden ser primitivos, es decir, que estén incrustados dentro del lenguaje y complejos, que son formas más sofisticadas de almacenar los datos.

 

Tipos primitivos

Numéricos

Enteros
Tipo Longitud Rango
byte Tamaño 8-bits {-128, 127}
short Tamaño 16-bits {-32768, 32767}
int Tamaño 32-bits {-2147483648, 2147483647}
long Tamaño 64-bits {-9223372036854775808, 9223372036854775807}

 

Ejemplos en Java:

byte b = 10;
short  s = 5;
int i = 127;
long l  = -2839403;

 

Decimales
Tipo Longitud Rango
float Tamaño 32-bits.
double Tamaño 64-bits

 

Ejemplos en Java:

float f = -23412.234f; 
double d = 12349.29857;

Pese a que Java es fuertemente tipado, permite poder sumar números de diferentes tipos. El intérprete convertirá (casting) el dato con un tipo menos preciso en el que más. El resultado, eso sí, se deberá almacenar en una variable del tipo más preciso.

Veamos un ejemplo:

d = d + i; //d valdrá 12476.29857

* Con la doble barra indicamos comentarios en Java.

 

Si intentáramos hacer la operación i = i + d; tendríamos un error, a no ser que forzáramos a Java por medio de un Casteo.

i = (int) (i + d) //En i se almacenaría el número 12603

Lógicos

Lógicos
Tipo Descripción
boolean Puede contener los valores true o false.

 

Ejemplo en Java:

boolean si = true;

 

Alfanuméricos

Caracteres
Tipo Longitud Rango
char Tamaño 16-bits Unicode. Desde 'u0000' a 'uffff'. {0, 65535}

 

Ejemplo en Java:

char letraA = 'A';
char letraM = \u004d; 
char letraZ = 90;

Tipos complejos

En Java los tipos complejos no son más que Clases. Aún no hemos ahondado en este concepto, pero basta decir que las clases son tipos que no van incrustados en el propio lenguaje. Hay multitud de estos tipos complejos, todos ellos encuadrados en la librería java.util. Ya veremos que son las librerías, pero por ahora basta decir que son definiciones de tipos almacenadas en ficheros externos.

String

Los String son conjuntos (arrays) de caracteres. Aunque los Arrays no se explican hasta el siguiente punto, tampoco se necesita saber por ahora que son, ya que para nosotros su funcionamiento es trasparente. Para crear un string sólo hay que entrecomillar varios caracteres.

String titulo = “Pildoritas de POO y Java”;

Array

Un array es un conjunto de datos del mismo tipo y ordenados. Para entender este concepto hay que pensar en “cajitas” consecutivas que contienen datos del tipo del array.

Lo mejor para entenderlo es verlo visualmente. Veamos un array de int:

0 1 2 3 4
1 3 -2 8 5

 

Para crear un array en Java lo haríamos de la siguiente forma:

int[] arrayEnteros = {5, 3, -2, 8, 5};

Los arrays en Java tienen una dimensión finita que se define a la hora de su creación. En nuestro ejemplo al inicializarlo con los valores anteriores, el intérprete ya sabe que el array es de longitud 5. No obstante, también pueden definirse sin dimensión:

int[5] arrayEnteros;

Además, el número de “cajita” de un array se enumera de 0 a N-1, siendo N el tamaño del array.

Para crear un “array” del cual desconocemos su número de elementos de antemano, utilizaremos la clase Vector, que además de añadir esta mejora, ofrece métodos de inserción o borrado entre dos elementos, etc.

Por ejemplo, si quisiéramos cambiar el valor del tercer elemento de -2 a 0, haríamos lo siguiente:

arrayEnteros[2] = 0;

Los arrays pueden ser de más de una dimensión, pero para eso es mejor usar otros tipos, como las matrices.

Matriz

La matriz no es más que un array de dos dimensiones. Es, en definitiva cuenta, una tabla. Veamos mejor con un ejemplo su estructura y funcionamiento en Java.

int [][] matriz = {{2,4,5,6},{8,10,12,14},{16,18,20, 22}};

Lista

Las Listas (ArrayList) son también arrays dinámicos, como en el caso de los vectores, pero hay una serie de diferencias que hacen que ambos tipos de colecciones convivan en armonía.

Los vectores tienen la característica thread-safety. No entraremos mucho en el tema, pero hay una disciplina de la programación, la programación concurrente, por la que varios procesos pueden ejecutar al mismo tiempo un mismo código. En ese caso se comparten variables, cuestión que crea muchos problemas de inconsistencia de datos, interbloqueos, etc. Los vectores controlan internamente que nunca dos procesos toquen al mismo tiempo un elemento de su colección de datos.

Esta característica tan deseable en concurrencia tiene una desventaja, y es que las variables de tipo vector son más pesadas y ocupan más memoria. Por tanto, si sabemos que el conjunto de elementos no se va a ver afectado por la concurrencia, la opción de la Lista es más deseable.
 
 
Y ya llegamos al final de esta primera Pildorita de conceptos básicos. En la próxima pildorita veremos los Operadores.

Í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