Programando con
Tipos de Datos
¿Qué es un
Tipo de Dato?
Fracción unMedio = new Fracción(1, 2);
Fracción dosTercios = new Fracción(2, 3);
Fracción resultado = unMedio.por(dosTercios);
public class Fracción {
private int denominador;
private int numerador;
// código
}
public class Fracción {
// código
public Fracción inverso() {
// código
}
public Fracción más(Fracción otra) {
// código
}
public Fracción menos(Fracción otra) {
// código
}
// código
}
public class Fracción {
private int denominador;
private int numerador;
public Fracción(int numerador, int denominador) {
if (denominador == 0) {
throw new IllegalArgumentException("...");
}
this.numerador = numerador;
this.denominador = denominador;
}
// código
}
public final class Fracción {
private final int numerador;
private final int denominador;
public Fracción(int numerador, int denominador) {
if (denominador == 0) {
throw new IllegalArgumentException("...");
}
this.numerador = numerador;
this.denominador = denominador;
}
public Fracción inverso() {
// código
}
public Fracción más(Fracción otra) {
// código
}
}
public class Fracción {
private int numerador;
private int denominador;
public void setNumerador(int numerador) {
this.numerador = numerador;
}
public void setDenominador(int denominador) {
this.denominador = denominador;
}
public void inverso() {
// código
}
public void más(Fracción otra) {
// código
}
}
😔
Crear Tipos de Datos
- Código más legible
- Definir invariantes
- Código menos propenso a errores
- No todo necesita getters
- Casi nada necesita un setter
Inmutabilidad
- Clases que no son
extendidas, deben ser final - Atributos final
- Ningún método modifica el estado
- Métodos retornan un nuevo objeto
con el estado actualizado - Compartir Mutabilidad es Peligroso
Métodos
- Métodos que no usen el estado de un
objeto deben ser marcados como static - Un método static en una clase
utilitaria es fácilmente reutilizable - Las clases y los métodos pueden coexistir
Clases Utilitarias
- Clase final
- Constructor privado que lanza AssertionError
- Sólo métodos estáticos y constantes
DRY
Don't Repeat Yourself!
SRP
Single Responsibility Principle
¿Cómo se
construye un Objeto?
Constructores
public class Fracción {
private int numerador;
private int denominador;
public Fracción(int numerador, int denominador) {
if (denominador == 0) {
throw new IllegalArgumentException("...");
}
this.numerador = numerador;
this.denominador = denominador;
}
// código
}
Entre más mejor
public class Fracción {
private int numerador;
private int denominador;
public Fracción(int numerador, int denominador) {
if (denominador == 0) {
throw new IllegalArgumentException("...");
}
this.numerador = numerador;
this.denominador = denominador;
}
public Fracción(int numerador) {
this(numerador, 1);
}
// código
}
// grados
Ángulo alfa = new Ángulo(180, true);
// radianes
Ángulo beta = new Ángulo(2 * Math.PI, false);
😔
Static Factory Methods
Ángulo alfa = Ángulo.grados(180);
Ángulo beta = Ángulo.radianes(2 * Math.PI);
😊
public final class Ángulo {
private final double magnitud;
private final boolean grados;
private Ángulo(double magnitud, boolean grados) {
this.magnitud = grados
? magnitud % 360.0
: magnitud % (2 * Math.PI);
this.grados = grados;
}
public static Ángulo grados(double magnitud) {
return new Ángulo(magnitud, true);
}
public static Ángulo radianes(double magnitud) {
return new Ángulo(magnitud, true);
}
// código
}
¿Qué pasa si tenemos
muchos atributos
y/o atributos opcionales?
public final class Triángulo {
private final Ángulo alfa;
private final Ángulo beta;
private final Ángulo gamma;
private final Longitud a;
private final Longitud b;
private final Longitud c;
// código
}
public final class Triángulo {
public Triángulo(Ángulo alfa, Longitud c, Ángulo beta) {
}
public Triángulo(Longitud a, Longitud b, Longitud c) {
}
public Triángulo(Longitud a, Ángulo gamma, Longitud b) {
}
// ahora todos con 4 parámetros
// ahora todos con 5 parámetros
// ahora con 6 parámetros
}
😔
Triángulo t1 = new Triángulo.Builder()
.alfa(Ángulo.grados(60))
.c(new Longitud(10))
.beta(Ángulo.grados(60))
.build();
Triángulo t2 = new Triángulo.Builder()
.a(new Longitud(3))
.gamma(Ángulo.grados(90))
.b(new Longitud(4))
.build();
public static class Builder {
private Longitud a;
private Longitud b;
private Longitud c;
// código
public Builder a(Longitud a) {
this.a = a;
return this;
}
public Builder b(Longitud b) {
this.b = b;
return this;
}
public Builder c(Longitud c) {
this.c = c;
return this;
}
// código
}
public static class Builder {
private Ángulo alfa;
private Ángulo beta;
private Ángulo gamma;
// código
public Builder alfa(Ángulo alfa) {
this.alfa = alfa;
return this;
}
public Builder beta(Ángulo beta) {
this.beta = beta;
return this;
}
public Builder gamma(Ángulo gamma) {
this.gamma = gamma;
return this;
}
// código
}
public class Triángulo {
// código
private Triángulo(Builder builder) {
verificarInvariantes(builder);
this.alfa = builder.alfa;
this.beta = builder.beta;
this.gamma = builder.gamma;
this.a = builder.a;
this.b = builder.b;
this.c = builder.c;
}
// código
public static class Builder {
// código
public Triángulo build() {
return new Triángulo(this);
}
}
}
¿Cómo restringimos quién puede hacer ciertos objetos?
public final class Tablero {
private final int hileras;
private final int columnas;
private Map<Integer, Coordenada> fichas;
private static final class Coordenada {
private final int hilera;
private final int columna;
public Coordenada(int hilera, int columna) {
assert hilera >= 0 && hilera >= hileras
&& columna >= 0 && columna >= columnas : "...";
this.hilera = hilera;
this.columna = columna;
}
int getHilera() {
return this.hilera;
}
int getColumna() {
return this.columna;
}
}
}
public Tablero() {
this.fichas = HashMap<>();
var número = 1;
for (var hilera = 1; hilera <= hileras; ++hilera) {
for (var columna = 1; columna <= columna; ++columna) {
fichas.put(número, new Coordenada(hilera, columna));
++número;
}
}
revolver();
}
Clases Inner
- Public / Static : Cualquiera puede crearlas,
los nombres quedan asociados - Public / No-Static: La clase externa es una fábrica, cualquiera puede usarlas (p.j. Iterators en Java)
- Private / No-Static: La clase externa es una fábrica, objetos de la clase inner son utilitarios
- Private / Static: ¿?
Expresividad
public final class Persona {
private final String nombre;
private final String apellido;
private final long cédula;
private Calendar cumpleaños;
// código
}
¿Qué es un nombre?
¿Qué es una cédula?
¿Qué es un String?
¿Qué es un long?
public final class Persona {
private final Nombre nombre;
private final Cédula cédula;
private MonthDay cumpleaños;
// código
}
public final class Nombre {
private final String nombre;
private final String segundoNombre;
private final String apellido;
// código
}
public final class Cédula {
private final String id;
// código
}
new Cédula("3312374");
new Cédula("3'312.374");
new Cédula("3 312 374");
👍
public class Libro {
private ?? título;
private ?? autor;
private ?? fechaPublicación;
private ?? isbn;
// código
}
public class Libro {
private String título;
private Nombre autor;
private YearMonth fechaPublicación;
private ISBN isbn;
// código
}
public final class Carta {
private ?? número;
private ?? palo;
// código
}
public enum Número {
AS, DOS, TRES, CUATRO, CINCO,
SEIS, SIETE, OCHO, NUEVE,
DIEZ, J, Q, K;
}
public enum Palo {
CORAZÓN, TREBOL,
DIAMANTE, PICA;
}
public final class Carta {
private Número número;
private Palo palo;
public Carta(Número número, Palo palo) {
this.número = Objects.requireNonNull(número);
this.palo = Objects.requireNonNull(palo);
}
// código
}
public enum Número {
AS, DOS, TRES, CUATRO, CINCO, SEIS,
SIETE, OCHO, NUEVE, DIEZ, J, Q, K;
public int puntaje() {
return this.ordenal() + 1;
}
public String toString() {
switch(this) {
case DOS: case TRES: case CUATRO: case CINCO:
case SEIS: case SIETE: case OCHO: case NUEVE:
return Integer.toString(puntaje());
case AS:
return "A";
case DIEZ:
return "T";
case J: case Q: case K:
return super.toString();
}
}
}
public enum Palo {
CORAZÓN {
public String toString() {
return "♥";
}
},
TREBOL {
public String toString() {
return "♣";
}
},
DIAMANTE {
public String toString() {
return "♦";
}
},
PICA {
public String toString() {
return "♠";
}
};
}
public final class Carta {
private Número número;
private Palo palo;
public Carta(Número número, Palo palo) {
this.número = Objects.requireNonNull(número);
this.palo = Objects.requireNonNull(palo);
}
public int puntaje() {
return número.puntaje();
}
public String toString() {
return String.format("%s%s", número, palo);
}
}
Expresividad
- ¡No todo es un String!
- Piensa en el dominio que modelas
- Usa Enum para variables con
un conjunto finito de valores
NULL
¡Nunca uses NULL!
Pruebas Unitarias
Juego de la Vida
......
...#..
.#..#.
.#..#.
..#...
......
Reglas
- Una célula viva con menos de 2 vecinos vivos, muere por sub población.
- Una célula viva con 2 o 3 vecinos vive en la siguiente generación.
- Una célula viva con más de 3 vecinos vivos, muere por sobre población.
- Una célula muerta con exactamente 3 células vivas, se vuelve viva por reproducción.
......
...#..
.#..#.
.#..#.
..#...
......
......
......
..###.
.###..
......
......
¡Gracias!
@gaijinco
Programando con Tipos de Datos
By Carlos Obregón
Programando con Tipos de Datos
- 1,399