¡Hola! Hoy continuaremos estudiando patrones de diseño y discutiremos el patrón de fábrica abstracto . Esto es lo que cubriremos en la lección:
- Discutiremos qué es una fábrica abstracta y qué problema resuelve este patrón.
- Crearemos el esqueleto de una aplicación multiplataforma para pedir café a través de una interfaz de usuario
- Estudiaremos las instrucciones sobre cómo usar este patrón, incluido mirar un diagrama y código
- Y como extra, esta lección incluye un huevo de Pascua oculto que te ayudará a aprender a usar Java para determinar el nombre del sistema operativo y, según el resultado, realizar una u otra acción.
- herencia en java
- Clases abstractas y métodos en Java.
¿Qué problemas resuelve una fábrica abstracta?
Una fábrica abstracta, como todos los patrones de fábrica, nos ayuda a garantizar que los nuevos objetos se creen correctamente. Lo usamos para administrar la "producción" de varias familias de objetos interconectados. Varias familias de objetos interconectados... ¿Qué significa eso? No te preocupes: en la práctica, todo es más sencillo de lo que parece. Para empezar, ¿qué podría ser una familia de objetos interconectados? Supongamos que estamos desarrollando una estrategia militar que involucra varios tipos de unidades:- infantería
- caballería
- arqueros
Sigamos automatizando nuestra cafetería
En la última lección, estudiamos el patrón del método de fábrica. Lo usamos para expandir nuestro negocio de café y abrir varias ubicaciones nuevas. Hoy continuaremos modernizando nuestro negocio. Usando el patrón de fábrica abstracto, sentaremos las bases para una nueva aplicación de escritorio para pedir café en línea. Al escribir una aplicación de escritorio, siempre debemos pensar en el soporte multiplataforma. Nuestra aplicación debe funcionar tanto en macOS como en Windows (spoiler: el soporte para Linux se deja para que lo implementes como tarea). ¿Cómo será nuestra aplicación? Bastante simple: será un formulario que consistirá en un campo de texto, un campo de selección y un botón. Si tiene experiencia en el uso de diferentes sistemas operativos, seguramente habrá notado que los botones en Windows se representan de manera diferente a los de una Mac. Como todo lo demás... Bueno, comencemos.- botones
- campos de texto
- campos de selección
onClick
, onValueChanged
o onInputChanged
. En otras palabras, podríamos definir métodos que nos permitan manejar varios eventos (presionar un botón, ingresar texto, seleccionar un valor en un cuadro de selección). Todo esto se omite deliberadamente aquí para no sobrecargar el ejemplo y hacerlo más claro a medida que estudiamos el patrón de fábrica. Definamos interfaces abstractas para nuestros productos:
public interface Button {}
public interface Select {}
public interface TextField {}
Para cada sistema operativo, debemos crear elementos de interfaz al estilo del sistema operativo. Estaremos escribiendo código para Windows y MacOS. Vamos a crear implementaciones para Windows:
public class WindowsButton implements Button {
}
public class WindowsSelect implements Select {
}
public class WindowsTextField implements TextField {
}
Ahora hacemos lo mismo para MacOS:
public class MacButton implements Button {
}
public class MacSelect implements Select {
}
public class MacTextField implements TextField {
}
Excelente. Ahora podemos pasar a nuestra fábrica de resúmenes, que creará todos los tipos de productos de resúmenes disponibles:
public interface GUIFactory {
Button createButton();
TextField createTextField();
Select createSelect();
}
Magnífico. Como puede ver, no hemos hecho nada complicado todavía. Todo lo que sigue es también sencillo. Por analogía con los productos, creamos varias implementaciones de fábrica para cada sistema operativo. Comencemos con Windows:
public class WindowsGUIFactory implements GUIFactory {
public WindowsGUIFactory() {
System.out.println("Creating GUIFactory for Windows OS");
}
public Button createButton() {
System.out.println("Creating Button for Windows OS");
return new WindowsButton();
}
public TextField createTextField() {
System.out.println("Creating TextField for Windows OS");
return new WindowsTextField();
}
public Select createSelect() {
System.out.println("Creating Select for Windows OS");
return new WindowsSelect();
}
}
Hemos agregado algunos resultados de la consola dentro de los métodos y el constructor para ilustrar mejor lo que está sucediendo. Ahora para macOS:
public class MacGUIFactory implements GUIFactory {
public MacGUIFactory() {
System.out.println("Creating GUIFactory for macOS");
}
@Override
public Button createButton() {
System.out.println("Creating Button for macOS");
return new MacButton();
}
@Override
public TextField createTextField() {
System.out.println("Creating TextField for macOS");
return new MacTextField();
}
@Override
public Select createSelect() {
System.out.println("Creating Select for macOS");
return new MacSelect();
}
}
Tenga en cuenta que cada firma de método indica que el método devuelve un tipo abstracto. Pero dentro de los métodos, estamos creando implementaciones específicas de los productos. Este es el único lugar donde controlamos la creación de instancias específicas. Ahora es el momento de escribir una clase para el formulario. Esta es una clase de Java cuyos campos son elementos de la interfaz:
public class CoffeeOrderForm {
private final TextField customerNameTextField;
private final Select coffeeTypeSelect;
private final Button orderButton;
public CoffeeOrderForm(GUIFactory factory) {
System.out.println("Creating coffee order form");
customerNameTextField = factory.createTextField();
coffeeTypeSelect = factory.createSelect();
orderButton = factory.createButton();
}
}
Una fábrica abstracta que crea elementos de interfaz se pasa al constructor del formulario. Pasaremos la implementación de fábrica necesaria al constructor para crear elementos de interfaz para un sistema operativo en particular.
public class Application {
private CoffeeOrderForm coffeeOrderForm;
public void drawCoffeeOrderForm() {
// Determine the name of the operating system through System.getProperty()
String osName = System.getProperty("os.name").toLowerCase();
GUIFactory guiFactory;
if (osName.startsWith("win")) { // For Windows
guiFactory = new WindowsGUIFactory();
} else if (osName.startsWith("mac")) { // For Mac
guiFactory = new MacGUIFactory();
} else {
System.out.println("Unknown OS. Unable to draw form :(");
return;
}
coffeeOrderForm = new CoffeeOrderForm(guiFactory);
}
public static void main(String[] args) {
Application application = new Application();
application.drawCoffeeOrderForm();
}
}
Si ejecutamos la aplicación en Windows, obtenemos el siguiente resultado:
Creating GUIFactory for Windows OS
Creating coffee order form
Creating TextField for Windows OS
Creating Select for Windows OS
Creating Button for Windows OS
En una Mac, la salida será la siguiente:
Creating GUIFactory for macOS
Creating coffee order form
Creating TextField for macOS
Creating Select for macOS
Creating Button for macOS
En Linux:
Unknown OS. Unable to draw form :(
Y ahora resumimos. Escribimos el esqueleto de una aplicación basada en GUI en la que los elementos de la interfaz se crean específicamente para el sistema operativo relevante. Repetiremos de manera concisa lo que hemos creado:
- Una familia de productos que consta de un campo de entrada, un campo de selección y un botón.
- Diferentes implementaciones de la familia de productos para Windows y macOS.
- Una fábrica abstracta que define una interfaz para crear nuestros productos.
- Dos implementaciones de nuestra fábrica, cada una responsable de crear una familia específica de productos.
- Un formulario (una clase de Java) cuyos campos son elementos de interfaz abstractos que se inicializan con los valores necesarios en el constructor utilizando una fábrica abstracta.
- Clase de aplicación Dentro de esta clase, creamos un formulario, pasando la implementación de fábrica deseada a su constructor.
Fábrica abstracta: cómo usar
Una fábrica abstracta es un patrón de diseño para administrar la creación de varias familias de productos sin estar vinculado a clases de productos concretas. Al usar este patrón, debe:- Definir familias de productos. Supongamos que tenemos dos de ellos:
SpecificProductA1
,SpecificProductB1
SpecificProductA2
,SpecificProductB2
- Para cada producto dentro de la familia, defina una clase abstracta (interfaz). En nuestro caso, tenemos:
ProductA
ProductB
- Dentro de cada familia de productos, cada producto debe implementar la interfaz definida en el paso 2.
- Cree una fábrica abstracta, con métodos para crear cada producto definidos en el paso 2. En nuestro caso, estos métodos serán:
ProductA createProductA();
ProductB createProductB();
- Cree implementaciones de fábrica abstractas para que cada implementación controle la creación de productos de una sola familia. Para hacer esto, dentro de cada implementación de la fábrica abstracta, debe implementar todos los métodos de creación para que creen y devuelvan implementaciones de productos específicas.
// Define common product interfaces
public interface ProductA {}
public interface ProductB {}
// Create various implementations (families) of our products
public class SpecificProductA1 implements ProductA {}
public class SpecificProductB1 implements ProductB {}
public class SpecificProductA2 implements ProductA {}
public class SpecificProductB2 implements ProductB {}
// Create an abstract factory
public interface AbstractFactory {
ProductA createProductA();
ProductB createProductB();
}
// Implement the abstract factory in order to create products in family 1
public class SpecificFactory1 implements AbstractFactory {
@Override
public ProductA createProductA() {
return new SpecificProductA1();
}
@Override
public ProductB createProductB() {
return new SpecificProductB1();
}
}
// Implement the abstract factory in order to create products in family 2
public class SpecificFactory2 implements AbstractFactory {
@Override
public ProductA createProductA() {
return new SpecificProductA2();
}
@Override
public ProductB createProductB() {
return new SpecificProductB2();
}
}
Tarea
Para reforzar el material, puedes hacer 2 cosas:- Refine la aplicación de pedido de café para que también funcione en Linux.
- Crea tu propia fábrica abstracta para producir unidades involucradas en cualquier estrategia militar. Esta puede ser una estrategia militar histórica que involucre ejércitos reales o una fantasía con orcos, gnomos y elfos. Lo importante es elegir algo que te interese. ¡Sea creativo, imprima mensajes en la consola y disfrute aprendiendo sobre patrones!
GO TO FULL VERSION