En este punto, probablemente ya haya encontrado patrones de diseño. Por ejemplo, singleton .

Recordemos qué son los patrones, por qué son necesarios y qué son los patrones de creación (singleton es un ejemplo). También estudiaremos un nuevo patrón: el método de fábrica.

En el desarrollo de software, un patrón de diseño es una construcción arquitectónica repetible que representa una solución a un problema de diseño dentro de un contexto recurrente.

Por lo general, un patrón no es una solución final que se pueda convertir directamente en código. Es solo una solución modelo a un problema que se puede usar en varias situaciones.

Los patrones de creación son patrones de diseño que se ocupan del proceso de creación de objetos. Hacen posible crear un sistema que es independiente del método utilizado para crear, componer y presentar objetos.

Un método de fábrica es un patrón de diseño de creación que define una interfaz común para crear objetos en una clase principal, dando a sus descendientes la capacidad de crear estos objetos. En el momento de la creación, los descendientes pueden determinar qué clase crear.

¿Qué problema resuelve el patrón?

Imagine que decide crear un programa de entrega. Inicialmente, contratará mensajeros con automóviles y utilizará unAutoobjeto para representar un vehículo de reparto en el programa. Los mensajeros entregan paquetes del punto A al punto B y así sucesivamente. Pan comido.

El programa está ganando popularidad. Su negocio está creciendo y desea expandirse a nuevos mercados. Por ejemplo, también podría comenzar a entregar alimentos y enviar fletes. En este caso, los mensajeros pueden entregar los alimentos a pie, en scooters y en bicicletas, pero se necesitan camiones para el transporte de mercancías.

Ahora debe realizar un seguimiento de varias cosas (cuándo, a quién, qué y cuánto se entregará), incluido cuánto puede llevar cada mensajero. Los nuevos modos de transporte tienen diferentes velocidades y capacidades. Entonces nota que la mayoría de las entidades en su programa están fuertemente ligadas a laAutoclase. Se da cuenta de que para que su programa funcione con otros métodos de entrega, tendrá que volver a escribir el código base existente y volver a hacerlo cada vez que agregue un nuevo vehículo.

El resultado es un código horrendo lleno de declaraciones condicionales que realizan diferentes acciones según el tipo de transporte.

La solución

El patrón del método de fábrica sugiere crear objetos llamando a un método de fábrica especial en lugar de usar directamente el operador new . Las subclases de la clase que tiene el método de fábrica pueden modificar los objetos creados de los vehículos específicos. A primera vista, esto puede parecer inútil: simplemente hemos movido la llamada al constructor de un lugar a otro en el programa. Pero ahora puede anular el método de fábrica en una subclase para cambiar el tipo de transporte que se crea.

Veamos el diagrama de clases para este enfoque:

Para que este sistema funcione, todos los objetos devueltos deben tener una interfaz común. Las subclases podrán producir objetos de diferentes clases que implementen esta interfaz.

Por ejemplo, las clases Truck y Car implementan la interfaz CourierTransport con un método de entrega . Cada una de estas clases implementa el método de una manera diferente: los camiones entregan carga, mientras que los automóviles entregan alimentos, paquetes, etc. El método de fábrica en la clase TruckCreator devuelve un objeto de camión y la clase CarCreator devuelve un objeto de automóvil.

Para el cliente del método de fábrica, no hay diferencia entre estos objetos, ya que los tratará como una especie de CourierTransport abstracto . Al cliente le importará mucho que el objeto tenga un método para entregarlo, pero no es importante cómo funciona exactamente ese método.

Implementación en Java:


public interface CourierTransport {
	void deliver();
}
public class Car implements CourierTransport {
	@Override
	public void deliver() {
    		System.out.println("The package is being delivered by car");
	}
}
public class Truck implements CourierTransport {
	@Override
	public void deliver() {
    		System.out.println("The freight is being delivered by truck");
	}
}
public abstract class CourierTransportCreator {
	public abstract CourierTransport createTransport();
}
public class CarCreator extends CourierTransportCreator {
	@Override
	public CourierTransport createTransport() {
    		return new Car();
	}
}
public class TruckCreator extends CourierTransportCreator {
	@Override
	public CourierTransport createTransport() {
    		return new Truck();
	}
}
 
public class Delivery {
	private String address;
	private CourierTransport courierTransport;
 
	public void Delivery() {
	}
 
	public Delivery(String address, CourierTransport courierTransport) {
    	this.address = address;
    	this.courierTransport = courierTransport;
	}
 
	public CourierTransport getCourierTransport() {
    		return courierTransport;
	}
 
	public void setCourierTransport(CourierTransport courierTransport) {
    		this.courierTransport = courierTransport;
	}
 
	public String getAddress() {
    		return address;
	}
 
	public void setAddress(String address) {
    		this.address = address;
	}
}
public static void main(String[] args) {
    	// Accept a new type of order from the database (pseudocode)
    	String type = database.getTypeOfDeliver();
 
    	Delivery delivery = new Delivery();
    	
    	// Set the transport for delivery
        delivery.setCourierTransport(getCourierTransportByType(type));
    	
    	// Make the delivery
        delivery.getCourierTransport().deliver();
 
	}
 
	public static CourierTransport getCourierTransportByType(String type) {
    	switch (type) {
        	case "CarDelivery":
            	return new CarCreator().createTransport();
        	case "TruckDelivery":
            	return new TruckCreator().createTransport();
        	default:
            	throw new RuntimeException();
	    }
	}
    

Si queremos crear un nuevo objeto de entrega, el programa crea automáticamente un objeto de transporte adecuado.

¿Cuándo debemos aplicar este patrón?

1. Cuando no conoces de antemano los tipos y dependencias de los objetos con los que tu código necesita trabajar.

El método de fábrica separa el código para producir formas de transporte del código que usa el transporte. Como resultado, el código para crear objetos se puede ampliar sin tocar el resto del código.

Por ejemplo, para agregar soporte para un nuevo tipo de transporte, debe crear una nueva subclase y definir un método de fábrica en ella que devuelva una instancia del nuevo transporte.

2. Cuando desee ahorrar recursos del sistema reutilizando objetos existentes en lugar de crear otros nuevos.

Este problema suele ocurrir cuando se trabaja con objetos que consumen muchos recursos, como conexiones a bases de datos, sistemas de archivos, etc.

Piense en los pasos que debe seguir para reutilizar objetos existentes:

  1. Primero, debe crear un repositorio compartido para almacenar todos los objetos que cree.

  2. Al solicitar un nuevo objeto, debe buscar en el repositorio y verificar si contiene un objeto disponible.

  3. Devuelve el objeto al código del cliente.

  4. Pero si no hay objetos disponibles, cree uno nuevo y agréguelo al repositorio.

Todo este código debe colocarse en algún lugar que no abarrote el código del cliente. El lugar más conveniente sería el constructor, ya que solo necesitamos todas estas comprobaciones al crear objetos. Por desgracia, un constructor siempre crea un nuevo objeto, no puede devolver un objeto existente.

Eso significa que se necesita otro método que pueda devolver objetos nuevos y existentes. Este será el método de fábrica.

3. Cuando desee permitir que los usuarios amplíen partes de su marco o biblioteca.

Los usuarios pueden ampliar sus clases de marco a través de la herencia. Pero, ¿cómo hace que el marco cree objetos de estas nuevas clases en lugar de las estándar?

La solución es permitir que los usuarios amplíen no solo los componentes, sino también las clases que crean esos componentes. Y para ello, las clases creadoras deben tener métodos de creación específicos que se puedan definir.

Ventajas

  • Separa una clase de clases de transporte específicas.
  • Mantiene el código para crear formas de transporte en un solo lugar, lo que facilita el mantenimiento del código.
  • Simplifica la adición de nuevos modos de transporte al programa.
  • Implementa el principio abierto-cerrado.

Desventajas

Puede dar lugar a grandes jerarquías de clases paralelas, ya que cada clase de producto debe tener su propia subclase creadora.

vamos a resumir

Aprendió sobre el patrón del método de fábrica y vio una posible implementación. Este patrón se usa a menudo en varias bibliotecas que proporcionan objetos para crear otros objetos.

Utilice el patrón de método de fábrica cuando desee agregar fácilmente nuevos objetos de subclases de clases existentes para interactuar con su lógica comercial principal y no inflar su código debido a diferentes contextos.