Patrones de diseño conductual de patrones de diseño

1. Introducción

        Los patrones de diseño de comportamiento son una clase de patrones de diseño que se centran en la comunicación y la interacción entre objetos, y se utilizan para resolver el problema de la asignación de responsabilidades y la colaboración entre objetos. Estos patrones describen cómo los objetos colaboran para realizar una sola tarea y cómo mantener los objetos acoplados libremente.

        Los patrones de diseño de comportamiento se pueden dividir en las siguientes categorías:

        1. Patrón de método de plantilla : defina un marco de algoritmo que permita que las subclases proporcionen implementaciones para uno o más pasos.

        2. Patrón de observador (Observer Pattern) : defina dependencias de uno a muchos entre objetos, de modo que cuando cambie el estado de un objeto, todos los objetos que dependen de él serán notificados y actualizados automáticamente.

        3. Patrón de estrategia (Strategy Pattern) : define una serie de algoritmos, encapsúlalos en clases independientes y haz que se reemplacen entre sí, de modo que los cambios de los algoritmos sean independientes de los clientes que los usan.

        4. Patrón de comando : encapsule solicitudes en objetos, lo que permite usar diferentes solicitudes, colas o registros para parametrizar otros objetos. El modo de comando también admite operaciones que se pueden deshacer.

        5. State Pattern (Patrón de estado) : Permite que un objeto cambie su comportamiento cuando cambia su estado interno. El objeto parece haber modificado su clase.

        6. Patrón de cadena de responsabilidad : organice los procesadores de solicitudes a través de una estructura de cadena, de modo que cada solicitud pueda procesarse varias veces hasta que un procesador la maneje.

        7. Patrón de intérprete : defina un idioma, analice una representación del idioma y realice las operaciones representadas.

        8. Patrón Visitante (Visitor Pattern) : Definir una nueva operación y aplicarla a un conjunto de objetos. El patrón visitante puede realizar operaciones independientes de la clase del objeto.

2. Patrón de método de plantilla

        El patrón de método de plantilla es un patrón de diseño de comportamiento que define el marco de un algoritmo y difiere la implementación de algunos pasos a las subclases, de modo que las diferentes subclases puedan implementar estos pasos de acuerdo con sus necesidades reales.

        En el patrón de método de plantilla, se define una clase abstracta que contiene uno o más métodos abstractos, que pueden implementarse mediante subclases, y también contiene un método de plantilla concreto, que llama al método abstracto para completar un flujo de algoritmo específico.

        Aquí hay un código de muestra simple:

abstract class AbstractClass {
    public void templateMethod() {
        // step 1
        operation1();
        // step 2
        operation2();
        // step 3
        operation3();
    }

    protected abstract void operation1();
    protected abstract void operation2();

    protected void operation3() {
        // 默认实现,子类可以选择重写
    }
}

class ConcreteClass1 extends AbstractClass {
    @Override
    protected void operation1() {
        System.out.println("ConcreteClass1 operation1");
    }

    @Override
    protected void operation2() {
        System.out.println("ConcreteClass1 operation2");
    }
}

class ConcreteClass2 extends AbstractClass {
    @Override
    protected void operation1() {
        System.out.println("ConcreteClass2 operation1");
    }

    @Override
    protected void operation2() {
        System.out.println("ConcreteClass2 operation2");
    }

    @Override
    protected void operation3() {
        System.out.println("ConcreteClass2 operation3");
    }
}

        En este código de ejemplo, AbstractClasses una clase abstracta, que define un método de plantilla templateMethod(), que contiene tres pasos: operation1(), operation2(), operation3(). Donde operation1()y operation2()son métodos abstractos que las subclases deben implementar, pero operation3()es un método de implementación predeterminado y las subclases pueden optar por anularlos o no.

  ConcreteClass1y son dos subclases concretas ConcreteClass2respectivamente , que heredan AbstractClasse implementan los métodos abstractos en él. El métodoConcreteClass2 se anula , anulando la implementación predeterminada.operation3()

        El patrón de método de plantilla se usa a menudo en el diseño del marco.El marco define el marco del algoritmo, y la implementación específica se realiza mediante subclases. Por ejemplo, en la especificación de Servlet en Java, se define un doGet()método , que incluye todo el proceso de procesamiento de solicitudes HTTP, y la subclase de Servlet completa la implementación específica.

        Otro ejemplo del uso del patrón de método de plantilla es la clase de ventana en la biblioteca GUI. La clase de ventana define el marco para abrir, cerrar, renderizar y otras operaciones, y la implementación específica se realiza mediante subclases.

        En resumen, el patrón del método de plantilla puede ayudarnos a implementar algoritmos complejos y puede garantizar la coherencia y la escalabilidad del algoritmo durante el proceso de implementación.

3. Patrón de observador

        El Patrón de Observador (Observer Pattern) es un patrón de diseño de comportamiento, que define una relación de dependencia de uno a muchos. Cuando el estado del objeto observado cambia, todos sus dependientes serán notificados y actualizados automáticamente.

El patrón de observador consta de los siguientes roles:

  • Asunto: el objeto observado define la interfaz para agregar, eliminar y notificar a los observadores.
  • Observer: Observer define una interfaz para recibir notificaciones y actualizar el estado.
  • ConcreteSubject: el objeto observado específico mantiene su propia lista de observadores y es responsable de notificar al observador los cambios de estado.
  • ConcreteObserver: el observador específico mantiene una referencia al objeto observado específico e implementa su propio método de actualización.

Aquí hay un código de muestra para un patrón de observador simple:

import java.util.ArrayList;
import java.util.List;

interface Observer {
    void update(String message);
}

interface Subject {
    void attach(Observer observer);
    void detach(Observer observer);
    void notifyObservers(String message);
}

class ConcreteSubject implements Subject {
    private List<Observer> observers = new ArrayList<>();

    @Override
    public void attach(Observer observer) {
        observers.add(observer);
    }

    @Override
    public void detach(Observer observer) {
        observers.remove(observer);
    }

    @Override
    public void notifyObservers(String message) {
        for (Observer observer : observers) {
            observer.update(message);
        }
    }
}

class ConcreteObserver implements Observer {
    private String name;

    public ConcreteObserver(String name) {
        this.name = name;
    }

    @Override
    public void update(String message) {
        System.out.println(name + " received message: " + message);
    }
}

public class ObserverDemo {
    public static void main(String[] args) {
        Subject subject = new ConcreteSubject();
        Observer observer1 = new ConcreteObserver("Observer1");
        Observer observer2 = new ConcreteObserver("Observer2");

        subject.attach(observer1);
        subject.attach(observer2);

        subject.notifyObservers("Hello World!");

        subject.detach(observer2);

        subject.notifyObservers("Goodbye World!");
    }
}

        En este ejemplo, ConcreteSubject actúa como observador y ConcreteObserver actúa como observador. Cuando cambia el estado de ConcreteSubject, notifica a todos los observadores y llama a su método de actualización.

En Java, el patrón de observador también tiene muchos escenarios de aplicación, los siguientes son algunos ejemplos:

        1. El propio mecanismo de monitoreo de eventos de Java

Tanto las bibliotecas de componentes Java AWT como Swing utilizan el patrón de observador para implementar el mecanismo de escucha de eventos. Cuando ocurre un evento, Java invoca el método del oyente registrado en el origen del evento.

        2. El mecanismo de escucha de eventos del framework Spring

En el marco Spring, se implementa un mecanismo flexible de monitoreo de eventos mediante el uso de las interfaces ApplicationEvent y ApplicationListener. Cuando ocurre un evento en la aplicación, el contenedor Spring notifica a todos los oyentes registrados.

        3. Autobús de eventos de guayaba

        Se proporciona un marco de bus de eventos en la biblioteca de Google Guava, que permite a los desarrolladores implementar más fácilmente la programación basada en eventos. El marco del bus de eventos se basa en el patrón del observador, utiliza tecnologías como la reflexión y el proxy dinámico, y realiza un mecanismo de distribución de eventos eficiente.

3. Modo de estrategia

        El patrón de estrategia es un patrón de diseño de comportamiento que define una serie de algoritmos y encapsula cada algoritmo para que puedan ser reemplazados entre sí. Esto permite que los algoritmos cambien independientemente de los clientes que los utilicen.

El siguiente es un código de muestra del patrón de estrategia:

// 策略接口
public interface SortingStrategy {
    public void sort(int[] arr);
}

// 快速排序策略
public class QuickSortStrategy implements SortingStrategy {
    @Override
    public void sort(int[] arr) {
        // 实现快速排序算法
    }
}

// 归并排序策略
public class MergeSortStrategy implements SortingStrategy {
    @Override
    public void sort(int[] arr) {
        // 实现归并排序算法
    }
}

// 策略上下文
public class Sorter {
    private SortingStrategy strategy;

    public void setStrategy(SortingStrategy strategy) {
        this.strategy = strategy;
    }

    public void sort(int[] arr) {
        strategy.sort(arr);
    }
}

// 客户端代码
public static void main(String[] args) {
    int[] arr = {5, 2, 8, 6, 1, 9};

    Sorter sorter = new Sorter();
    sorter.setStrategy(new QuickSortStrategy());
    sorter.sort(arr); // 快速排序

    sorter.setStrategy(new MergeSortStrategy());
    sorter.sort(arr); // 归并排序
}

        En el código de muestra anterior, definimos una SortingStrategyinterfaz y dos clases de algoritmos concretos QuickSortStrategyy MergeSortStrategyambas implementan SortingStrategyla interfaz. Luego definimos una Sorterclase, que contiene una SortingStrategyvariable miembro de tipo interfaz, que puede establecer diferentes implementaciones de algoritmos. Finalmente, en el código del cliente, podemos establecer diferentes algoritmos llamando a métodos Sorteren el objeto y luego llamando a métodos para ordenar.setStrategy()sort()

        La belleza del patrón de estrategia es que permite que el algoritmo cambie independientemente del cliente que lo use. El código del cliente puede usar un algoritmo diferente sin cambiar su propia implementación. Además, el patrón de estrategia también puede evitar el uso de una gran cantidad de declaraciones condicionales, lo que hace que el código sea más conciso y legible.

        Un ejemplo de una aplicación práctica del patrón Estrategia es el sistema de pago de un sitio de compras en línea. El sistema de liquidación necesita calcular el precio del producto según el método de pago seleccionado por el usuario. Diferentes métodos de pago pueden tener diferentes políticas preferenciales, por lo que se pueden implementar diferentes métodos de pago como diferentes clases de estrategia y luego usar el modo de estrategia en el sistema de liquidación para seleccionar la estrategia de pago adecuada para el cálculo. Esto facilita agregar o modificar métodos de pago sin modificar el código del sistema de facturación.

4. Modelo de Cadena de Responsabilidad

        El patrón Cadena de responsabilidad es un patrón de diseño de comportamiento que le permite enviar solicitudes a través de una cadena de controladores hasta que un controlador maneja la solicitud. Cada manejador tiene una referencia al siguiente manejador, formando así una cadena.

        En el patrón Cadena de responsabilidad, un cliente envía una solicitud al primer procesador de la cadena y luego cada procesador de la cadena tiene la oportunidad de procesar la solicitud. Si el procesador puede manejar la solicitud, procesa la solicitud y devuelve el resultado; de lo contrario, pasa la solicitud al siguiente procesador en la cadena hasta que se procesa la solicitud.

        Un ejemplo simple del patrón Cadena de responsabilidad podría ser el sistema anti-trampas de un sitio web. Cuando los usuarios realizan ciertas actividades en el sitio, como enviar comentarios, publicar publicaciones, etc., el sistema los verifica para garantizar que no sean robots. El sistema generalmente consta de múltiples etapas, cada una con un mecanismo de verificación diferente. Si falla la validación en una etapa, la solicitud pasa a la siguiente etapa para su procesamiento hasta que se valida la solicitud o todas las etapas fallan en la validación.

        Aquí hay un ejemplo simple de implementación de Java:

public abstract class Handler {
    private Handler nextHandler;

    public Handler(Handler nextHandler) {
        this.nextHandler = nextHandler;
    }

    public void handleRequest(Request request) {
        if (canHandleRequest(request)) {
            handle(request);
        } else if (nextHandler != null) {
            nextHandler.handleRequest(request);
        } else {
            System.out.println("No handler found for the request.");
        }
    }

    protected abstract boolean canHandleRequest(Request request);

    protected abstract void handle(Request request);
}

public class Request {
    private String message;

    public Request(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }
}

public class AuthenticationHandler extends Handler {
    public AuthenticationHandler(Handler nextHandler) {
        super(nextHandler);
    }

    @Override
    protected boolean canHandleRequest(Request request) {
        return request.getMessage().startsWith("authenticate:");
    }

    @Override
    protected void handle(Request request) {
        System.out.println("Authenticating the request: " + request.getMessage());
    }
}

public class AuthorizationHandler extends Handler {
    public AuthorizationHandler(Handler nextHandler) {
        super(nextHandler);
    }

    @Override
    protected boolean canHandleRequest(Request request) {
        return request.getMessage().startsWith("authorize:");
    }

    @Override
    protected void handle(Request request) {
        System.out.println("Authorizing the request: " + request.getMessage());
    }
}

public class Main {
    public static void main(String[] args) {
        Handler handlerChain = new AuthenticationHandler(new AuthorizationHandler(null));

        Request request1 = new Request("authenticate:user1");
        handlerChain.handleRequest(request1);

        Request request2 = new Request("authorize:user2");
        handlerChain.handleRequest(request2);

        Request request3 = new Request("invalid_request");
        handlerChain.handleRequest(request3);
    }
}

        El patrón Chain of Responsibility se usa ampliamente en muchos marcos de código abierto, aquí hay algunos ejemplos:

  1. El modo de filtro en la API de Java Servlet, donde cada filtro puede interceptar solicitudes y procesarlas. La solicitud es procesada primero por el primer filtro y luego pasa al siguiente filtro hasta que se procesa la solicitud.

  2. AOP (Programación Orientada a Aspectos) en Spring Framework se implementa en base al patrón de Cadena de Responsabilidad. Spring AOP permite separar una preocupación transversal (como la gestión de transacciones, el registro, etc.) de la lógica comercial de la aplicación y entretejerlos en la llamada de método del objeto de destino a través de una serie de potenciadores.

  3. Modo válvula en Apache Tomcat. Tomcat es un contenedor web de código abierto, que utiliza un patrón llamado Valve para implementar muchas funciones, como el filtrado de solicitudes HTTP, el control de acceso, etc. Cada válvula puede procesar la solicitud y luego pasar la solicitud a la siguiente válvula hasta que se procese.

  4. Patrón ChannelPipeline en Netty. Netty es un marco de aplicación de red de alto rendimiento basado en NIO, que adopta el modelo de cadena de responsabilidad para realizar el procesamiento y la transmisión de datos. ChannelPipeline en Netty contiene una serie de ChannelHandlers, cada controlador puede procesar datos y pasar los datos al siguiente controlador.

        En resumen, el patrón de cadena de responsabilidad es un patrón de diseño muy común que puede ayudarnos a dividir el procesamiento de solicitudes en varios pasos y permitirnos modificar dinámicamente el flujo de procesamiento. Esto puede hacer que el sistema sea más flexible y ampliable, y mejorar la capacidad de mantenimiento y reutilización del sistema.

5. Modo intérprete

        El patrón de intérprete es un patrón de diseño de comportamiento que se utiliza para resolver algunos problemas específicos. La idea central de este patrón es definir una gramática del idioma y definir un intérprete para interpretar las declaraciones en el idioma. El modo de intérprete consta principalmente de las siguientes dos partes:

  1. Abstract Expression (Expresión abstracta): define la interfaz del intérprete, que generalmente contiene solo un método interpret().
  2. Expresión concreta: implemente la interfaz de expresión abstracta y explique la expresión de manera concreta.

        Aquí hay un ejemplo simple del modo intérprete:

// 抽象表达式
interface Expression {
    boolean interpret(String context);
}

// 具体表达式
class TerminalExpression implements Expression {
    private String data;

    public TerminalExpression(String data) {
        this.data = data;
    }

    @Override
    public boolean interpret(String context) {
        if (context.contains(data)) {
            return true;
        }
        return false;
    }
}

// 或表达式
class OrExpression implements Expression {
    private Expression expression1;
    private Expression expression2;

    public OrExpression(Expression expression1, Expression expression2) {
        this.expression1 = expression1;
        this.expression2 = expression2;
    }

    @Override
    public boolean interpret(String context) {
        return expression1.interpret(context) || expression2.interpret(context);
    }
}

// 与表达式
class AndExpression implements Expression {
    private Expression expression1;
    private Expression expression2;

    public AndExpression(Expression expression1, Expression expression2) {
        this.expression1 = expression1;
        this.expression2 = expression2;
    }

    @Override
    public boolean interpret(String context) {
        return expression1.interpret(context) && expression2.interpret(context);
    }
}

// 客户端
public class Client {
    public static void main(String[] args) {
        Expression robert = new TerminalExpression("Robert");
        Expression john = new TerminalExpression("John");
        Expression orExpression = new OrExpression(robert, john);
        Expression julie = new TerminalExpression("Julie");
        Expression married = new TerminalExpression("Married");
        Expression andExpression = new AndExpression(julie, married);
        System.out.println(orExpression.interpret("John"));
        System.out.println(andExpression.interpret("Julie Married"));
    }
}

        El modo de intérprete rara vez se usa en Java, pero aún se aplica en el análisis de algunos lenguajes específicos de dominio, como el análisis en lenguajes de consulta de bases de datos, el análisis de expresiones regulares, etc.

        Tomando las expresiones regulares como ejemplo, java.util.regexlos paquetes en Java se implementan en función del modo de intérprete. En este paquete, Patternuna clase representa un patrón de expresión regular y Matcheruna clase representa el resultado de hacer coincidir un patrón. Al hacer coincidir, Patternla expresión regular se compila en un objeto intérprete y luego Matcherla cadena de entrada se analiza y se compara a través de la clase.

        Específicamente, Patternla clase usa el modo de combinación en el modo de intérprete para combinar múltiples objetos de intérprete, construyendo así un árbol de objetos de intérprete para toda la expresión regular. Esta estructura de árbol incluye varios nodos de hoja, y cada nodo de hoja representa un elemento de sintaxis de expresión regular básico, como un carácter o una clase de carácter, mientras que los nodos que no son de hoja representan una combinación de varios elementos básicos, como uno o más caracteres Una repetición o una unión de múltiples clases de caracteres. MatcherLa clase usa el patrón de iterador para atravesar el árbol de objetos del intérprete, analizando y haciendo coincidir la cadena de entrada.

        En resumen, la aplicación del modo intérprete en Java es relativamente específica y generalmente se usa en el análisis de lenguajes específicos de dominio o en escenarios específicos como expresiones regulares.

Supongo que te gusta

Origin blog.csdn.net/zz18532164242/article/details/130395027
Recomendado
Clasificación