Patrones de diseño de Java (3/23): patrón de decorador

definición

El patrón Decorator asigna dinámicamente responsabilidades a los objetos. Para ampliar la funcionalidad, los decoradores ofrecen una alternativa más flexible a la herencia .

Caso 1: ¿Qué hora son las tres?

Actualice el sistema de pedidos de la cadena de café, el diseño de la clase original:

Para expandir su negocio, las cafeterías permiten a los clientes agregar varios condimentos a sus bebidas, tales como:

  1. Leche al vapor
  2. Leche De Soya Soya
  3. Moca (sabor a chocolate) Moca
  4. cubrir la espuma de leche

Los condimentos agregados se cobran de manera diferente.

primer intento

explosión de clase

Esto viola dos principios de diseño serios :

  1. Usa más la composición, menos la herencia.
  2. Esfuércese por un diseño débilmente acoplado entre objetos que interactúan.

intentar otra vez

Usando variables de instancia y herencia, realice un seguimiento de estas especias:

Limitaciones de este intento:

  1. Los cambios en los precios de los condimentos harán que cambiemos el código existente.
  2. Una vez que aparece un nuevo condimento, debemos agregar un nuevo método y cambiar el método cost() en la superclase.
  3. Es posible que se desarrollen nuevas bebidas en el futuro. Para estas bebidas (p. ej., té helado), algunos condimentos pueden no ser adecuados, pero en este diseño, la subclase Tea (té) aún heredará aquellos métodos que no son adecuados, como: hasWhip() (con leche) Bubble) .
  4. ¿Qué pasa si un cliente quiere un moca doble?

Principio de diseño: las clases deben estar abiertas para la extensión y cerradas para la modificación

Nuestro objetivo es permitir que las clases se amplíen fácilmente para incorporar nuevos comportamientos sin modificar el código existente.

Si esto se puede lograr, dicho diseño es resistente al cambio y puede aceptar nuevas funciones para satisfacer las necesidades cambiantes.

Si bien puede parecer contradictorio, existen algunas técnicas que permiten extender el código sin modificarlo directamente.

Tenga cuidado al elegir las partes del código que deben extenderse. Aplicar el principio abierto-cerrado en todas partes es un desperdicio, innecesario y hace que el código sea complicado y difícil de entender.

(MyNote: Sea adaptable)

prueba el patrón decorador

Tomaremos la bebida como el cuerpo principal y luego "decoraremos" la bebida con especias en el tiempo de ejecución. Por ejemplo, si un cliente quiere
un café tostado oscuro con moca y espuma de leche, entonces:

1. Toma un objeto DarkRoast

2. Decóralo con objetos Moka

3. Decóralo con un objeto Whip

4. Llame al método cost() y confíe en que el delegado agregue el costo del condimento
.

Características del patrón Decorator

  • El decorador y el objeto decorado tienen el mismo supertipo.

  • Puede envolver un objeto con uno o más decoradores.

  • Dado que el decorador y el objeto decorado tienen el mismo supertipo, siempre que se requiera el objeto original (envuelto), se puede utilizar el objeto decorado en su lugar.

  • Un decorador puede agregar su propio comportamiento antes y/o después del comportamiento del decorador delegado para lograr un propósito específico.

  • Los objetos se pueden decorar en cualquier momento, por lo que puede decorar objetos de forma dinámica e ilimitada con sus decoradores favoritos en tiempo de ejecución.

Diagrama de clases para este ejemplo

calificación finalizada

categoría de bebida

public abstract class Beverage {
    
    
	String description = "Unknown Beverage";
  
	public String getDescription() {
    
    
		return description;
	}
 
	public abstract double cost();
}
Mezcla de la casa
public class HouseBlend extends Beverage {
    
    
	public HouseBlend() {
    
    
		description = "House Blend Coffee";
	}
 
	public double cost() {
    
    
		return .89;
	}
}
Tostado oscuro
public class DarkRoast extends Beverage {
    
    
	public DarkRoast() {
    
    
		description = "Dark Roast Coffee";
	}
 
	public double cost() {
    
    
		return .99;
	}
}
Café exprés
public class Espresso extends Beverage {
    
    
  
	public Espresso() {
    
    
		description = "Espresso";
	}
  
	public double cost() {
    
    
		return 1.99;
	}
}

Descafeinado
public class Decaf extends Beverage {
    
    
	public Decaf() {
    
    
		description = "Decaf Coffee";
	}
 
	public double cost() {
    
    
		return 1.05;
	}
}
Decoración de condimentos
public abstract class CondimentDecorator extends Beverage {
    
    
	Beverage beverage;
	public abstract String getDescription();
}
Leche
public class Milk extends CondimentDecorator {
    
    
	public Milk(Beverage beverage) {
    
    
		this.beverage = beverage;
	}

	public String getDescription() {
    
    
		return beverage.getDescription() + ", Milk";
	}

	public double cost() {
    
    
		return .10 + beverage.cost();
	}
}
Moca
public class Mocha extends CondimentDecorator {
    
    
	public Mocha(Beverage beverage) {
    
    
		this.beverage = beverage;
	}
 
	public String getDescription() {
    
    
		return beverage.getDescription() + ", Mocha";
	}
 
	public double cost() {
    
    
		return .20 + beverage.cost();
	}
}
Soja
public class Soy extends CondimentDecorator {
    
    
	public Soy(Beverage beverage) {
    
    
		this.beverage = beverage;
	}

	public String getDescription() {
    
    
		return beverage.getDescription() + ", Soy";
	}

	public double cost() {
    
    
		return .15 + beverage.cost();
	}
}
Látigo
public class Whip extends CondimentDecorator {
    
    
	public Whip(Beverage beverage) {
    
    
		this.beverage = beverage;
	}
 
	public String getDescription() {
    
    
		return beverage.getDescription() + ", Whip";
	}
 
	public double cost() {
    
    
		return .10 + beverage.cost();
	}
}

ejecutar clase de prueba

public class StarbuzzCoffee {
    
    
 
	public static void main(String args[]) {
    
    
		Beverage beverage = new Espresso();
		System.out.println(beverage.getDescription() 
				+ " $" + beverage.cost());
 
		System.out.println("---");
		
		
		Beverage beverage2 = new DarkRoast();
		beverage2 = new Mocha(beverage2);
		beverage2 = new Mocha(beverage2);
		beverage2 = new Whip(beverage2);
		System.out.println(beverage2.getDescription() 
				+ " $" + beverage2.cost());
		
		System.out.println("---");
 
		Beverage beverage3 = new HouseBlend();
		beverage3 = new Soy(beverage3);
		beverage3 = new Mocha(beverage3);
		beverage3 = new Whip(beverage3);
		System.out.println(beverage3.getDescription() 
				+ " $" + beverage3.cost());
	}
}

resultado de la operación:

Espresso $1.99
---
Dark Roast Coffee, Mocha, Mocha, Whip $1.49
---
House Blend Coffee, Soy, Mocha, Whip $1.34

Caso 2: escribir su propio decorador Java I/0

Descripción general de IO Streams en JDK

Muchas de las clases del paquete java.io son decoradores.

Aquí hay una colección típica de objetos, usando decoradores para combinar la funcionalidad
para leer datos de archivos:

Tanto BufferedInputStream como LineNumberInputStream se extienden desde FilterInputStream, y FilterInputStream es una clase de decoración abstracta.

java.io realmente no hace mucha diferencia. Hemos reducido la API de java.io para que le resulte más fácil ver su documentación y combinar varios decoradores de flujo de "entrada" para satisfacer sus propósitos.

Encontrará que OutputStream está diseñado de la misma manera. También puede encontrar que los flujos de Reader/Writer (como entrada y salida basada en datos de caracteres) y las clases InputStream/OutputStream son bastante similares (hay algunas diferencias e inconsistencias menores, pero son bastante similares, por lo que debería poder entender estos clases).

Sin embargo, Java I/O también presenta una desventaja del patrón decorador: el uso del patrón decorador a menudo da como resultado una gran cantidad de clases pequeñas en el diseño, y el número es demasiado grande, lo que puede causar problemas a los programadores que usan esta API.

Sin embargo, ahora que comprende cómo funcionan los decoradores, cuando utiliza la API muy decorada de otra persona, puede identificar fácilmente cómo se organizan sus clases de decoradores para obtener lo que desea de forma empaquetada.

calificación finalizada

¿Qué tal esta idea? Escribe un decorador que convierta todos los caracteres en mayúsculas dentro del flujo de entrada a minúsculas.

Ejemplo: Al leer "¡Conozco el patrón del decorador, por lo tanto, YO GOBERNO!", el decorador lo convertirá en "¡Conozco el patrón del decorador, por lo tanto, gobierno!"

Flujo de entrada en minúsculas

Extiende FilterInputStream, que es un decorador abstracto para todos los InputStreams:

import java.io.*;

public class LowerCaseInputStream extends FilterInputStream {
    
    

	public LowerCaseInputStream(InputStream in) {
    
    
		super(in);
	}
 
	public int read() throws IOException {
    
    
		int c = in.read();
		return (c == -1 ? c : Character.toLowerCase((char)c));
	}
		
	public int read(byte[] b, int offset, int len) throws IOException {
    
    
		int result = in.read(b, offset, len);
		for (int i = offset; i < offset+result; i++) {
    
    
			b[i] = (byte)Character.toLowerCase((char)b[i]);
		}
		return result;
	}
}
Cree un archivo de prueba D:\test.txt
I know the Decorator Pattern therefore I RULE!
ejecutar clase de prueba
import java.io.*;

public class InputTest {
    
    
	
	public static void main(String[] args) throws IOException {
    
    
		
		int c;
		InputStream in = null;
		try {
    
    
			in = 
				new LowerCaseInputStream( 
					new BufferedInputStream(
						new FileInputStream("D:\\text.txt")));

			while((c = in.read()) >= 0) {
    
    
				System.out.print((char)c);
			}
		} catch (IOException e) {
    
    
			e.printStackTrace();
		} finally {
    
    
			if (in != null) {
    
     in.close(); }
		}
		
		
		System.out.println();
		System.out.println("---");
		
		//
		try (InputStream in2 = 
				new LowerCaseInputStream(
					new BufferedInputStream(
						new FileInputStream("D:\\text.txt")))) 
		{
    
    
			while((c = in2.read()) >= 0) {
    
    
				System.out.print((char)c);
			}
		} catch (IOException e) {
    
    
			e.printStackTrace();
		}
	}
}

resultado de la operación:

i know the decorator pattern therefore i rule!
---
i know the decorator pattern therefore i rule!

Referencias

  1. "Patrones de diseño Head First"

Supongo que te gusta

Origin blog.csdn.net/u011863024/article/details/119843361
Recomendado
Clasificación