Skip to content

Fabian-Martinez-Rincon/Orientacion-a-Objetos-2

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

49 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

contributions welcome GitHub stars GitHub repo size in bytes

Antes de empezar vamos a ver que tipos de patrones tenemos, durante la materia solo vemos 3 tipos:

  • Patrones Creacionales Estos patrones están relacionados con los mecanismos de creación de objetos, buscando adaptar el proceso de creación a diferentes situaciones. Los patrones creacionales ocultan los detalles de creación de objetos y ayudan a hacer el sistema independiente de cómo sus objetos son creados, compuestos y representados.
  • Patrones Estructurales Estos patrones se ocupan de cómo las clases y objetos son compuestos para formar estructuras más grandes. Los patrones estructurales ayudan a asegurar que si cambia una parte del sistema, el sistema completo no necesita cambiar. También son útiles para compartir funcionalidades de una manera que ofrece ventajas significativas.
  • Patrones de Comportamiento Estos patrones están relacionados con algoritmos y la asignación de responsabilidades entre objetos. Lo que describen es cómo interactúan y distribuyen la responsabilidad entre las clases y objetos.

Adapter (Estructural)

Permite a interfaces incompatibles trabajar juntas. Su principal uso es hacer que el código existente funcione con otra interfaz, sin alterar el código original. Actúa como un puente entre estas interfaces.

Adapter

  • Cliente Es la parte que requiere un servicio.
    • Ejemplo Un programa de análisis de datos que requiere cargar datos. Supongamos que está diseñado para trabajar con datos en formato JSON, pero los datos disponibles están en formato XML.
  • Target Es la interfaz en la que el cliente realiza las solicitudes.
    • Ejemplo En el caso de nuestro programa de análisis de datos, Target podría ser una interfaz con un método como loadData() que está diseñado para aceptar datos en formato JSON.
  • Adapter Traduce las solicitudes del Target en una forma que el Adaptee puede entender, haciendo cualquier adaptación necesaria.
    • Ejemplo Un adaptador de datos que implementa loadData() donde, en lugar de requerir datos en formato JSON, convierte datos en formato XML a JSON y luego invoca el método specificRequest() del Adaptee para procesar los datos.
  • Adaptee es la clase que tiene las funcionalidades que necesitamos, pero su interfaz no es compatible con lo que el Client espera. Esta clase no sabe nada sobre Target y trabaja de manera independiente.

Ejemplo Practico

Client
public class MediaPlayer {
	private Media media;
	public MediaPlayer(Media media) {
		this.media = media;
	}	
	public String playMedia() {
		return media.play();
	}
	public Media getMedia() {
		return media;
	}
	public void setMedia(Media media) {
		this.media = media;
	}
}
Target
public interface Media {
	public String play();
}
Adapter
public class VideoStreamAdapter implements Media {
	private VideoStream adaptee;
	
	public VideoStreamAdapter(VideoStream adaptee) {
		this.adaptee = adaptee;
	}
	public String play() {
		return adaptee.reproduce();
	}
}
Adaptee
public class VideoStream {
	public String reproduce() {
		return "Directo.stream";
	}
}
Hijos de Media
public class Audio implements Media {
	public String play() {
	    return "musica.mp3";
	}
}
public class VideoFile implements Media {
	public String play() {
		return "Video.mp4";
	}
}
MediaPlayerTest
public class MediaPlayerTest {
	Audio audio;
	VideoFile video;
	VideoStream stream;
	VideoStreamAdapter adapter;
	MediaPlayer client;
	
	@BeforeEach
	void setUp() throws Exception{
		audio = new Audio();
		video = new VideoFile();
		stream = new VideoStream();
		adapter = new VideoStreamAdapter(stream);
	}
	@Test
	public void testClientDirecto() {
		client = new MediaPlayer(adapter);
		assertEquals("Directo.stream",client.playMedia());
	}
	@Test
	public void testClientAudio() {
		client = new MediaPlayer(audio);
		assertEquals("musica.mp3",client.playMedia());
	}
	@Test
	public void testClientVideo() {
		client = new MediaPlayer(video);
		assertEquals("Video.mp4",client.playMedia());
	}
}

Template (Comportamiento)

Define la estructura de un algoritmo en una operación, delegando algunos pasos a las subclases. Permite que las subclases redefinan ciertos pasos de un algoritmo sin cambiar su estructura general.

Este patron no tiene mucho secreto, en el primer objeto se define el esqueleto del algoritmo y en los objetos hijos se implementan los pasos.

Ejemplo Practico

AbstractClass
public abstract class Empleado {
	private int cantidadHijos;
	private boolean casado;
	
	public Empleado(int cantidadHijos, boolean casado) {
		this.cantidadHijos = cantidadHijos;
		this.casado = casado;
	}
	public double sueldo() {
		return this.sueldoBasico() + this.sueldoAdicional() - this.descuento();
	}
	public double descuento(){
		return this.sueldoBasico() * 0.13 + this.sueldoAdicional() * 0.5;
	}
	public int getCantidadHijos() {
		return this.cantidadHijos;
	}
	public boolean isCasado() {
		return this.casado;
	}

	public abstract double sueldoBasico();
	public abstract double sueldoAdicional();
}
ConcreteClass1
public class Pasante extends Empleado {
	private int cantidadExamen;

	public Pasante(int cantidadHijos, boolean casado, int cantidadExamen) {
		super(cantidadHijos, casado);
		this.cantidadExamen = cantidadExamen;
	}

	public double sueldoBasico() {
		return 20000;
	}

	public double sueldoAdicional() {
		return this.cantidadExamen * 2000;
	}
}
ConcreteClass2
public class Planta extends Empleado{
	private int aniosAntiguedad;

	public Planta(int cantidadHijos, boolean casado, int aniosAntiguedad) {
		super(cantidadHijos, casado);
		this.aniosAntiguedad = aniosAntiguedad;
	}

	public double sueldoBasico() {
		return 50000;
	}

	public double sueldoAdicional() {
		double sum = this.getCantidadHijos() * 2000 + this.aniosAntiguedad * 2000;
		return this.isCasado()? sum + 5000 : sum;
	}
}
ConcreteClass3
public class Temporario extends Empleado{
	private int cantidadHoras;

	public Temporario(int cantidadHijos, boolean casado, int cantidadHoras) {
		super(cantidadHijos, casado);
		this.cantidadHoras = cantidadHoras;
	}

	public double sueldoBasico() {
		return 20000 + this.cantidadHoras * 300 ;
	}

	public double sueldoAdicional() {
		double sum = this.getCantidadHijos() * 2000;
		return this.isCasado()? sum + 5000 : sum;
	}
}
Test
public class MediaPlayerTest {
	Empleado pasante;
	Empleado planta;
	Empleado temporario;
	
	@BeforeEach
	void setUp() throws Exception{
		pasante = new Pasante(10, false, 10);
		planta = new Planta(10, true, 10);
		temporario = new Temporario(10, false, 10);
	}
	@Test
	public void testSueldos() {
		assertEquals(27400.0,pasante.sueldo());
		assertEquals(66000.0,planta.sueldo());
		assertEquals(30010.0,temporario.sueldo());
	}
}

Composite (Estructural)

Este patrón es utilizado principalmente para organizar objetos en estructuras de árbol que representan jerarquías parte-todo. Permite a los clientes tratar objetos individuales y composiciones de objetos de manera uniforme.

  • Component Es la interfaz o clase abstracta que define las operaciones comunes para tanto los objetos simples (Leaf) como los compuestos (Composite). Actúa como la clase base para todos los objetos dentro de la estructura.
    • Operation(): Debe ser implementado por todos los objetos concretos, tanto hojas como compuestos.
    • Add(Component): Agrega subcomponentes, utilizado principalmente en los Composite.
    • Remove(Component): Remueve subcomponentes, utilizado principalmente en los Composite.
    • GetChild(int): Obtiene un subcomponente específico, utilizado principalmente en los Composite.
  • Leaf Son los bloques de construcción básicos de la estructura, donde se implementan las operaciones más concretas sin delegar a otros objetos.
  • Composite Implementa métodos para manejar sus hijos y también implementa la operación que se aplica a cada uno de sus hijos.
    • Operation(): Implementa el método realizando una operación que generalmente implica iterar sobre sus hijos y llamando a su método Operation().
    • Add(Component), Remove(Component), GetChild(int): Estos métodos están implementados para manipular y acceder a los subcomponentes.

Ejemplo Practico

Component
public abstract class FileSystem{
	private String nombre;
	private LocalDate fecha;

	public FileSystem(String nombre, LocalDate fecha) {
		this.nombre = nombre;
		this.fecha = fecha;
	}

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

	public LocalDate getFecha() {
		return this.fecha;
	}

	public abstract int tamanoTotalOcupado();
	public abstract Archivo archivoMasGrande();
	public abstract Archivo archivoMasNuevo();
}
Leaf
public class Archivo extends FileSystem{
	private int tamano;

	public Archivo(String nombre, LocalDate fecha, int tamano) {
		super(nombre, fecha);
		this.tamano = tamano;
	}
	public Archivo archivoMasGrande() {
		return this;
	}
	public Archivo archivoMasNuevo() {
		return this;
	}
	public int tamanoTotalOcupado() {
		return this.tamano;
	}
}
Composite
public class Directorio extends FileSystem {
	private List<FileSystem> files;

	public Directorio(String nombre, LocalDate fecha) {
		super(nombre, fecha);
		this.files = new ArrayList<>();
	}

	public void agregar(FileSystem archivo) {
		this.files.add(archivo);	
	}

	public int tamanoTotalOcupado() {
		return (
			this.files.stream()
			.mapToInt(file -> file.tamanoTotalOcupado())
			.sum()
			) + 32;
	}

	public Archivo archivoMasGrande() {
		return this.files.stream()
			.map(file -> file.archivoMasGrande())
			.max((a1,a2) -> Integer.compare(a1.tamanoTotalOcupado(),a2.tamanoTotalOcupado()))
			.orElse(null);
	}

	public Archivo archivoMasNuevo() {
		return this.files.stream()
			.map(file -> file.archivoMasNuevo())
			.max((a1,a2) -> a1.getFecha().compareTo(a2.getFecha()))
			.orElse(null);
	}
}
ClientTest
public class MediaPlayerTest {
	Archivo archivoChico, archivoGrande;
	Directorio directorio, directorioCompuesto;
	
	@BeforeEach
	void setUp() throws Exception{
		archivoChico = new Archivo("notas.txt", LocalDate.of(2000, 2, 20), 10);
		archivoGrande = new Archivo("apuntes.txt", LocalDate.of(2010, 2, 20), 50);
		
		directorio = new Directorio("Carpeta1", LocalDate.now());
		directorio.agregar(archivoChico);
		directorio.agregar(archivoGrande);
		
		directorioCompuesto = new Directorio("CarpetaCompuesta", LocalDate.now());
		directorioCompuesto.agregar(directorio);
	}
	
	@Test
	public void testEspacio() {
		assertEquals((10 + 50 + 32), directorio.tamanoTotalOcupado());
		assertEquals((10 + 50 + 32 + 32), directorioCompuesto.tamanoTotalOcupado());
		assertEquals(archivoGrande, directorioCompuesto.archivoMasGrande());
		assertEquals(archivoGrande, directorioCompuesto.archivoMasNuevo());
	}
}

Strategy (Comportamiento)

Define una familia de algoritmos, encapsula cada uno de ellos y los hace intercambiables. Este patrón permite que el algoritmo varíe independientemente de los clientes que lo utilizan.

  • Context Delega trabajo a la estrategia asociada, pero mantiene el control sobre cómo y cuándo se llama a las estrategias. Puede proporcionar datos adicionales necesarios para la ejecución de la estrategia.
    • Ejemplo Una aplicación de navegación que puede calcular rutas utilizando diferentes algoritmos de estrategia. El contexto sería el planificador de rutas que decide cuándo y cómo calcular la ruta dependiendo de la estrategia seleccionada por el usuario (la más rápida, la más corta, la más económica, etc.).
  • Strategy Es una interfaz que define un método común para todas las estrategias concretas.
    • Ejemplo En el contexto de una aplicación de navegación, la interfaz Strategy podría definir un método calculateRoute(), que será implementado de diferente manera por cada estrategia concreta.
  • ConcreteStrategyA, ConcreteStrategyB, ConcreteStrategyC Son implementaciones específicas de la interfaz Strategy. Cada una proporciona un comportamiento concreto o un algoritmo específico
    • ConctreteStrategyA Podría ser una estrategia que calcula la ruta más rápida usando autopistas.
    • ConctreteStrategyB Podría calcular la ruta más corta, evitando autopistas
    • ConctreteStrategyC Podría optar por la ruta que consume menos combustible.

Ejemplo Practico

Context
public class Decodificador {
	private List<Pelicula> grilla;
	private List<Pelicula> reproducidas;
	private Sugerencia criterioSugerencia;
	
	public Decodificador() {
		this.grilla = new ArrayList<>();
		this.reproducidas = new ArrayList<>();
		this.criterioSugerencia = new SugerenciaNovedad();
	}
	public void agregarAGrilla(Pelicula pelicula) { this.grilla.add(pelicula); }
	public void agregarReproducida(Pelicula pelicula) { this.reproducidas.add(pelicula);}
	public void setCriterioSugerencia(Sugerencia sugerencia) { this.criterioSugerencia = sugerencia; }
	public List<Pelicula> obtenerSugerencias() {
		return this.criterioSugerencia.obtenerSugerencias(this);
	}
	public List<Pelicula> getGrilla() { return this.grilla; }
	public List<Pelicula> getReproducidas() { return this.reproducidas; }
}

Pelicula

public class Pelicula {
	private String titulo;
	private Year anioEstreno;
	private double puntaje;
	private List<Pelicula> peliculasSimilares;
	
	public Pelicula(String titulo, double puntaje, Year anioEstreno) {
		this.titulo = titulo;
		this.anioEstreno = anioEstreno;
		this.puntaje = puntaje;
		this.peliculasSimilares = new ArrayList<>();
	}
	public String getTitulo() { return titulo;}
	public Year getAnioEstreno() { return anioEstreno;}
	public double getPuntaje() { return puntaje;}

	public void establecerSimilitud(Pelicula pelicula) {
		if (!this.peliculasSimilares.contains(pelicula)) {
			this.peliculasSimilares.add(pelicula);
			pelicula.establecerSimilitud(this);
		}
	}
	public List<Pelicula> getPeliculasSimilares(){ return this.peliculasSimilares;}
}
Strategy
public abstract class Sugerencia {
	public List<Pelicula> obtenerSugerencias(Decodificador decodificador){
		return this.sugerirPeliculas(decodificador).stream()
				.filter(pelicula -> !decodificador.getReproducidas().contains(pelicula))
				.limit(3).collect(Collectors.toList());
	}	
	public abstract List<Pelicula> sugerirPeliculas(Decodificador decodificador);
}
ConcreteStrategyA
public class SugerenciaNovedad extends Sugerencia {

	public List<Pelicula> sugerirPeliculas(Decodificador decodificador) {
		return decodificador.getGrilla().stream()
				.sorted((p2,p1) -> p1.getAnioEstreno().compareTo(p2.getAnioEstreno()))
				.collect(Collectors.toList());
	}
}
ConcreteStrategyB
public class SugerenciaPuntaje extends Sugerencia {
	public List<Pelicula> sugerirPeliculas(Decodificador decodificador) {
		return decodificador.getGrilla().stream()
				.sorted((p1,p2) -> Double.compare(p2.getPuntaje(), p1.getPuntaje()))
				.collect(Collectors.toList());
	}
}
ConcreteStrategyC
public class SugerenciaSimilaridad extends Sugerencia {
	public List<Pelicula> sugerirPeliculas(Decodificador decodificador) {
		return decodificador.getReproducidas().stream()
				.map(pelicula -> pelicula.getPeliculasSimilares()).flatMap(lista -> lista.stream())
				.distinct()
				.collect(Collectors.toList());
	} 
}
DecodificadorTest
public class DecodificadorTest {
	Decodificador decodificador;
	Pelicula rocky1, rocky2, rocky3, rocky4, rocky5, terminator1, terminator2, terminator3;
	Sugerencia novedad, puntaje, similaridad;
	
	@BeforeEach
	void setUp() throws Exception{
		rocky1 = new Pelicula("Rocky 1", 10, Year.of(2000));
		rocky2 = new Pelicula("Rocky 2", 9, Year.of(2001));
		rocky3 = new Pelicula("Rocky 3", 8, Year.of(2002));
		rocky4 = new Pelicula("Rocky 4", 7, Year.of(2003));
		rocky5 = new Pelicula("Rocky 5", 6, Year.of(2004));
		
		//Se podria establecer la similitud con cada pelicula pero es mucho
		//Se hace con rocky2 porque busca las mimilares con las reproducidas
		rocky2.establecerSimilitud(rocky1);
		rocky2.establecerSimilitud(rocky3);
		rocky2.establecerSimilitud(rocky4);
		rocky2.establecerSimilitud(rocky5);
		
		terminator1 = new Pelicula("Terminator1", 1, Year.of(2020));
		terminator2 = new Pelicula("Terminator2", 2, Year.of(2021));
		terminator3 = new Pelicula("Terminator3", 3, Year.of(2022));
		
		decodificador = new Decodificador();
		decodificador.agregarAGrilla(rocky1);
		decodificador.agregarAGrilla(rocky2);
		decodificador.agregarAGrilla(rocky3);
		decodificador.agregarAGrilla(rocky4);
		decodificador.agregarAGrilla(rocky5);
		decodificador.agregarAGrilla(terminator1);
		decodificador.agregarAGrilla(terminator2);
		decodificador.agregarAGrilla(terminator3);
		
		decodificador.agregarReproducida(rocky2);
		decodificador.agregarReproducida(rocky4);
	}
	@Test
	public void testSugerenciaNovedad() {
		List<Pelicula> ultimas3 = new ArrayList<>();
		ultimas3.add(terminator3);
		ultimas3.add(terminator2);
		ultimas3.add(terminator1);
		assertEquals(ultimas3, decodificador.obtenerSugerencias());
	}
	@Test
	public void testSugerenciaPuntaje() {
		puntaje = new SugerenciaPuntaje();
		decodificador.setCriterioSugerencia(puntaje);
		List<Pelicula> masPuntaje = new ArrayList<>();
		masPuntaje.add(rocky1);
		masPuntaje.add(rocky3);
		masPuntaje.add(rocky5);
		assertEquals(masPuntaje, decodificador.obtenerSugerencias());
	}
	
	@Test
	public void testSugerenciaSimilaridad() {
		similaridad = new SugerenciaSimilaridad();
		decodificador.setCriterioSugerencia(similaridad);
		//Son las 3 similares sin reproducir
		List<Pelicula> similaresRocky = new ArrayList<>();
		similaresRocky.add(rocky1);
		similaresRocky.add(rocky3);
		similaresRocky.add(rocky5);
		assertEquals(similaresRocky, decodificador.obtenerSugerencias());
	}
}

State (Comportamiento)

Se utiliza para permitir a un objeto alterar su comportamiento cuando su estado interno cambia. El objeto parecerá cambiar su clase.

  • Context Accede a la interfaz de State para realizar su comportamiento, que cambia dinámicamente según el estado actual.
    • request() Este método debería delegar la operación a la instancia actual de State
    • Ejemplo Imagina una aplicación de procesamiento de pedidos donde el Context es un Pedido. El Pedido puede tener varios estados como Pendiente, Pagado, Enviado, y Entregado. Cada uno de estos estados alterará cómo se procesan ciertas operaciones (por ejemplo, no se puede enviar un pedido antes de que esté pagado).
  • State es una interfaz o una clase abstracta que define un método handle() que todas las clases concretas de estado implementarán.
    • Ejemplo En el sistema de pedidos, State tendría un método handle() que podría ser llamado algo así como procesarSiguientePaso(). Este método determinaría qué hacer a continuación con el pedido (por ejemplo, procesar el pago, enviar el pedido, etc.).
  • ConcreteStateA, ConcreteStateB Cada clase representa un estado específico del Context y proporciona su propia implementación del método handle().
    • ConcreteStateA (PedidoPagado): Este estado podría manejar la lógica de preparar el pedido para el envío. La implementación de handle() en este estado podría cambiar el estado del pedido a Enviado si todo está listo para el envío.
    • ConcreteStateB (PedidoEnviado): Este estado manejaría las acciones posteriores al envío, como notificar al cliente o cambiar el estado a Entregado. La implementación de handle() aquí podría involucrar la verificación del progreso del envío y la actualización del estado del pedido.
Preguntar ¿Cuando el objeto state debe tener una instancia del context

En el patrón de diseño State, es común que los objetos de estado (State) tengan acceso al objeto de contexto (Context) para poder realizar cambios en el estado del contexto directamente. Sin embargo, si los objetos State tienen una referencia directa al Context o no depende del diseño específico y de los requisitos del sistema. Aquí te detallo los dos enfoques posibles:

1. State con Referencia a Context

  • Descripción: En esta configuración, cada objeto State mantiene una referencia al Context. Esto les permite no solo manejar su comportamiento específico sino también cambiar el estado del Context directamente cuando se cumplan ciertas condiciones.
  • Ventajas:
    • Control Directo: Los estados pueden controlar las transiciones a otros estados sin involucrar al Context, lo que simplifica el código del Context.
    • Flexibilidad: Facilita la implementación de comportamientos complejos que dependen del estado y contexto actuales, como revertir a un estado anterior o saltar a estados no secuenciales.
  • Ejemplo: Un objeto State en un juego puede verificar si el jugador ha alcanzado ciertos puntos de logro y directamente cambiar el estado del juego para reflejar un nuevo nivel o modo de juego.

2. State sin Referencia Directa a Context

  • Descripción: En esta configuración, los objetos State no mantienen una referencia directa al Context. En su lugar, dependen de que el Context pase de alguna forma cualquier información necesaria y maneje explícitamente los cambios de estado.
  • Ventajas:
    • Desacoplamiento: Mayor desacoplamiento entre el estado y el contexto, lo que puede facilitar la prueba y mantenimiento de cada clase por separado.
    • Reusabilidad: Los objetos State pueden ser más fácilmente reutilizables en diferentes contextos si no están fuertemente acoplados a una clase de contexto específica.
  • Ejemplo: Un objeto State en una aplicación de procesamiento de documentos podría realizar operaciones como guardar o cargar archivos sin necesitar saber en qué estado específico de la UI se encuentra la aplicación.

Ejemplo Practico

Context
public class Excursion {
	private String nombre;
	private Estado estado;
	private List<Usuario> inscriptos;
	private List<Usuario> enEspera;
	private LocalDate fechaInicio;
	private LocalDate fechaFin;
	private String puntoEncuentro;
	private double costo;
	private int cupoMinimo;
	private int cupoMaximo;
	
	public Excursion(String nombre, LocalDate fechaInicio, LocalDate fechaFin, String puntoEncuentro, double costo,
			int cupoMinimo, int cupoMaximo) {
		this.nombre = nombre;
		this.estado = new Provisoria(this);
		this.inscriptos = new ArrayList<>();
		this.enEspera = new ArrayList<>();
		this.fechaInicio = fechaInicio;
		this.fechaFin = fechaFin;
		this.puntoEncuentro = puntoEncuentro;
		this.costo = costo;
		this.cupoMinimo = cupoMinimo;
		this.cupoMaximo = cupoMaximo;
	}
	
	public List<Usuario> getInscriptos() {
		return inscriptos;
	}

	public List<Usuario> getEnEspera() { 
		return enEspera;
	}

	public Estado getEstado() {
		return estado;
	}
	
	public void setEstado(Estado estado) {
		this.estado = estado;
	}

	public int getCupoMinimo() {
		return cupoMinimo;
	}

	public int getCupoMaximo() {
		return cupoMaximo;
	}

	public void inscribir (Usuario unUsuario) {
		this.estado.inscribir(unUsuario);
	}
	
	public boolean alcanzoMinimo() {
		return this.getInscriptos().size() >= this.cupoMinimo;
	}
	
	public boolean alcanzoMaximo() {
		return this.getInscriptos().size() >= this.cupoMaximo;
	}
	public String obtenerInformacion() {
		return "La excursion '" + this.nombre 
				+ "' tiene un costo de " + this.costo
				+ " con fecha de inicio " + this.fechaInicio.toString()
				+ " y fecha de fin " + this.fechaFin.toString()
				+ ".\nEl punto de encuentro es en '" + this.puntoEncuentro
				+ "'. " + this.estado.obtenerInformacion();
	}
	public String getMailsInscriptos() {
		return this.inscriptos.stream()
				.map(inscripto -> inscripto.getMail())
				.reduce("",(acumulator, element)-> acumulator +"\n" + element);
	}
}

Usuario

public class Usuario {
    private String nombre;
    private String apellido;
    private String mail;

    public Usuario(String nombre, String apellido, String mail) {
        this.nombre = nombre;
        this.apellido = apellido;
        this.mail = mail;
    }

    public String getNombre() {
        return nombre;
    }

    public String getApellido() {
        return apellido;
    }

    public String getMail() {
        return mail;
    }
}
State
public abstract class Estado {
    protected Excursion excursion;

    public Estado(Excursion excursion) {
        this.excursion = excursion;
    }

    public abstract void inscribir(Usuario unUsuario);
    public abstract String obtenerInformacion();
}
ConcreteStateA
public class Provisoria extends Estado {
    public Provisoria(Excursion excursion) {
        super(excursion);
    }

    public void inscribir(Usuario unUsuario) {
        if (!this.excursion.alcanzoMaximo()) {
            this.excursion.getInscriptos().add(unUsuario);
            if (this.excursion.alcanzoMinimo()) {
                this.excursion.setEstado(new Definitiva(this.excursion));
            }
        }
    }

    public String obtenerInformacion() {
        return "\nActualmente faltan " + (this.excursion.getCupoMinimo() - this.excursion.getInscriptos().size())
                + " personas para alcanzar el cupo mínimo.";
    }
}
ConcreteStateB
public class Definitiva extends Estado {
    public Definitiva(Excursion excursion) {
        super(excursion);
    }

    public void inscribir(Usuario unUsuario) {
        if (!this.excursion.alcanzoMaximo()) {
            this.excursion.getInscriptos().add(unUsuario);
        } else {
            this.excursion.setEstado(new Completa(this.excursion));
            this.excursion.inscribir(unUsuario); 
        }
    }

    public String obtenerInformacion() {
        return "\nLa excursión está confirmada y tiene espacio para más inscripciones.";
    }
}
ConcreteStateC
public class Completa extends Estado {
    public Completa(Excursion excursion) {
        super(excursion);
    }

    public void inscribir(Usuario unUsuario) {
        this.excursion.getEnEspera().add(unUsuario);
    }

    public String obtenerInformacion() {
        return "\nLa excursión está completa. Todos los nuevos inscriptos serán puestos en lista de espera.";
    }
}
ExcursionTest
public class ExcursionTest {
	Excursion excursion;
	Usuario usuario;
	
	@BeforeEach
	void setUp() throws Exception{
		usuario = new Usuario("Fabian", "Martinez", "fabian@gmail.com");
		excursion = new Excursion("Viaje", 
				LocalDate.of(2000, 1, 1), 
				LocalDate.of(2000,2,1), "La Ciudad", 100, 3, 6);
	}
	@Test
	public void testExcursion() {
		excursion.inscribir(usuario);
		excursion.inscribir(usuario);
		excursion.inscribir(usuario);
		assertEquals(3, excursion.getInscriptos().size());
		excursion.inscribir(usuario);
		excursion.inscribir(usuario);
		excursion.inscribir(usuario);
		assertEquals(6, excursion.getInscriptos().size());
		excursion.inscribir(usuario);
		assertEquals(1, excursion.getEnEspera().size());
	}
}

Tengo que consultar porque tengo bajo acoplamiento, pero no se si esta bien

Decorator (Estructural)

Permite añadir nuevas funcionalidades a objetos de manera dinámica, ofreciendo una alternativa flexible a la herencia para extender funcionalidades.

  • Component Interfaz para objetos que pueden tener responsabilidades añadidas dinámicamente.
    • Ejemplo En una aplicación de renderizado de texto, Component podría ser una interfaz Text con un método draw()
  • ConcreteComponent Implementa la interfaz Component, representando objetos a los que se añadirán responsabilidades.
    • Ejemplo PlainText podría ser una clase que implemente Text, y su método draw() simplemente muestra el texto sin ningún formato.
  • Decorator Es una clase abstracta que implementa la interfaz Component y mantiene una referencia a un objeto Component.
    • Ejemplo TextDecorator podría ser una clase abstracta que implementa Text y contiene un elemento Text que se decorará.
  • ConcreteDecoratorA, ConcreteDecoratorB Extienden la funcionalidad de Decorator añadiendo estado o comportamiento adicional.
    • ConcreteDecoratorA: BoldTextDecorator podría ser una clase que decora un texto agregando negrita. Su método draw() llamará draw() del Text decorado y luego aplicará un estilo de negrita al texto.
    • ConcreteDecoratorB: ItalicTextDecorator podría ser una clase que agrega funcionalidad para dibujar texto en cursiva.

Ejemplo Practico

Component
public interface FileAttributes {
    default String prettyPrint() { return ""; }
    default String getName() { return ""; }
    default String getExtension() { return ""; }
    default String getSize() { return ""; }
    default String getDateCreated() { return ""; }
    default String getDateModified() { return ""; }
    default String getPermissions() { return ""; }
}
ConcreteComponent
public class File implements FileAttributes {
    private String name;
    private String extension;
    private double size;
    private LocalDate dateCreated;
    private LocalDate dateModified;
    private String permissions;
    
    // Constructor para inicializar el archivo con todos los atributos necesarios
    public File(String name, String extension, double size, LocalDate dateCreated, LocalDate dateModified, String permissions) {
        this.name = name;
        this.extension = extension;
        this.size = size;
        this.dateCreated = dateCreated;
        this.dateModified = dateModified;
        this.permissions = permissions;
    }
    
    public String prettyPrint() {
        return "Archivo: " + getName() + " (." + getExtension() + "), Tamaño: " + getSize() + " MB\n" +
               "Creado el: " + getDateCreated() + "\n" +
               "Modificado el: " + getDateModified() + "\n" +
               "Permisos: " + getPermissions();
    }
    
    public String getName() {
        return name;
    }

    public String getExtension() {
        return extension;
    }

    public String getSize() {
        return String.format("%.2f", size);
    }

    public String getDateCreated() {
        return dateCreated.toString();
    }

    public String getDateModified() {
        return dateModified.toString();
    }

    public String getPermissions() {
        return permissions;
    }
}
Decorator
public abstract class Aspect implements FileAttributes {
    protected FileAttributes component;

    public Aspect(FileAttributes component) {
        this.component = component;
    }

    public String prettyPrint() {
        return component.prettyPrint();
    }
}
ConcreteDecoratorA
public class AspectDateCreated extends Aspect {
    public AspectDateCreated(FileAttributes component) {
        super(component);
    }
    
    public String prettyPrint() {
        return super.prettyPrint() + "Creado: " + getDateCreated() + "\n";
    }
}
ConcreteDecoratorB
public class AspectDateModified extends Aspect {
    public AspectDateModified(FileAttributes component) {
        super(component);
    }
    
    @Override
    public String prettyPrint() {
        return super.prettyPrint() + "Modificado: " + getDateModified() + "\n";
    }
}
ConcreteDecoratorC
public class AspectExtension extends Aspect {
    public AspectExtension(FileAttributes component) {
        super(component);
    }

    @Override
    public String prettyPrint() {
        return super.prettyPrint() + "Extensión: ." + getExtension() + "\n";
    }
}
ConcreteDecoratorD
public class AspectName extends Aspect {
    public AspectName(FileAttributes component) {
        super(component);
    }

    @Override
    public String prettyPrint() {
        return super.prettyPrint() + "Nombre: " + getName() + "\n";
    }
}
ConcreteDecoratorE
public class AspectPermissions extends Aspect {

    public AspectPermissions(FileAttributes component) {
        super(component);
    }
    
    @Override
    public String prettyPrint() {
        return super.prettyPrint() + "Permisos: " + getPermissions() + "\n";
    }
}
ConcreteDecoratorF
public class AspectSize extends Aspect {
    public AspectSize(FileAttributes component) {
        super(component);
    }

    @Override
    public String prettyPrint() {
        return super.prettyPrint() + "Tamaño: " + getSize() + " MB\n";
    }
}

Me rindo, despues pregunto en la clase.

Proxy (Estructural)

Es utilizado para proporcionar un sustituto o marcador de posición para otro objeto. Controla el acceso a este objeto original, permitiendo realizar operaciones antes o después de pasar la llamada al objeto original.

  1. Subject:
    • Descripción: Define la interfaz común para RealSubject y Proxy. Esta interfaz permite que un Proxy sea utilizado en lugar del objeto real.
    • Ejemplo: En un sistema de gestión de documentos, Subject podría ser una interfaz Document con métodos como display(), edit() y save().
  2. RealSubject:
    • Descripción: El objeto real que el proxy representa. Define el objeto real que contiene la lógica de negocio que debería ser controlada o mejorada por el proxy.
    • Ejemplo: DocumentImpl podría ser una implementación concreta de Document, gestionando documentos en un sistema de archivos.
  3. Proxy:
    • Descripción: Mantiene una referencia al RealSubject, controlando el acceso a este y pudiendo realizar tareas adicionales como cargar perezosa, control de acceso, logging, etc.
    • Función: Intercepta todas las llamadas dirigidas al RealSubject y puede realizar operaciones antes o después de pasar la llamada al RealSubject.
    • Ejemplo: DocumentProxy podría ser un proxy que controla el acceso a DocumentImpl. Antes de mostrar un documento, el proxy podría verificar si el usuario tiene los permisos necesarios.

Ejemplo Practico

Subject
public interface DatabaseAccess {
    Collection<String> getSearchResults(String queryString);
    int insertNewRow(List<String> rowData);
}
RealSubject
public class DatabaseRealAccess implements DatabaseAccess {
    private Map<String, List<String>> data;
    private int currentId;

    public DatabaseRealAccess() {
        super();
        this.data = new HashMap<>();
        this.currentId = 3;
        this.data.put("select * from comics where id=1", Arrays.asList("Spiderman", "Marvel"));
        this.data.put("select * from comics where id=2", Arrays.asList("Batman", "DC comics"));
    }

    public Collection<String> getSearchResults(String queryString) {
        return this.data.getOrDefault(queryString, Collections.emptyList());
    }

    public int insertNewRow(List<String> rowData) {
        this.data.put("select * from comics where id=" + this.currentId, rowData);
        this.currentId = this.currentId + 1;

        return this.currentId - 1;
    }
}
Proxy
public class DatabaseProxy implements DatabaseAccess{
	private DatabaseAccess database;
	private boolean isLog;
	
	public DatabaseProxy (DatabaseAccess database) {
		this.database = database;
		this.isLog = false;
	}

	public void logIn () {
		this.isLog = true;
	}
	
	public void closeSession() {
		this.isLog = false;
	}
	
	public Collection<String> getSearchResults(String queryString) {
		if (!this.isLog) {
			throw new RuntimeException("access denied"); 
		}
		return this.database.getSearchResults(queryString);
	}

	public int insertNewRow(List<String> rowData) {
		if (!this.isLog) {
			throw new RuntimeException("access denied"); 
		}
		return this.database.insertNewRow(rowData);
	}
}
ProxyTest
public class ProxyTest {
	DatabaseAcess base;
	DatabaseProxy proxy;
	List<String> pelis_id1 = Arrays.asList("Spiderman", "Marvel");
	List<String> pelis_id2 = Arrays.asList("Batman", "DC comics");
	List<String> agregadas = Arrays.asList("Rocky", "Rambo");
	
	
	@BeforeEach
	void setUp() throws Exception{
		base = new DatabaseRealAccess();
		proxy = new DatabaseProxy(base);
	}
	
	@Test
	public void testSinProxy() {		
		assertEquals(pelis_id1, base.getSearchResults("select * from comics where id=1"));
		assertEquals(pelis_id2, base.getSearchResults("select * from comics where id=2"));
		base.insertNewRow(agregadas);
		assertEquals(agregadas, base.getSearchResults("select * from comics where id=3"));
	}
	
	@Test
	public void testConProxy() {
	    Throwable exception = assertThrows(RuntimeException.class, () -> {
	        proxy.getSearchResults("select * from comics where id=1");
	    });
	    
	    assertEquals("access denied", exception.getMessage());
	    
	    proxy.logIn();
	    assertEquals(pelis_id1, base.getSearchResults("select * from comics where id=1"));
		assertEquals(pelis_id2, base.getSearchResults("select * from comics where id=2"));
		base.insertNewRow(agregadas);
		assertEquals(agregadas, base.getSearchResults("select * from comics where id=3"));
	}
}

Factory Method (Creacional)

Factory Method

  • Product Es la interfaz o clase abstracta que define el tipo de objeto que el método fábrica creará.
    • Ejemplo En un software de gestión de documentos, Document podría ser la interfaz Product, con métodos como open(), save(), y close()
  • ConcreteProduct Es una implementación específica de la interfaz Product, representando un producto específico creado por la fábrica concreta.
  • Creator
  • ConcreteCreator

About

☕ Patrones de Diseño, Refactoring y Frameworks

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages