Un artículo completa los patrones de diseño comunes de Java (23 tipos en total)

introducir

Los orígenes de los patrones de diseño se remontan a la década de 1980, cuando la programación orientada a objetos se hizo popular. Durante este período, algunos desarrolladores de software comenzaron a notar que encontraban los mismos problemas en diferentes proyectos y comenzaron a buscar soluciones reutilizables. Estas soluciones se denominan patrones de diseño. Las primeras personas que propusieron patrones de diseño fueron Erich Gamma, Richard Helm, Ralph Johnson y John Vlissides, quienes publicaron un libro llamado "Patrones de diseño: la base del software orientado a objetos reutilizable" en 1994. Este libro se ha convertido en un clásico en el campo de los patrones de diseño. Desde entonces, los patrones de diseño se han convertido en un concepto muy importante en el desarrollo de software y son muy utilizados en diferentes lenguajes de programación y frameworks de desarrollo.

Los cuatro autores se denominan colectivamente GOF (Gang of Four). Los patrones de diseño que propusieron se basan principalmente en los siguientes principios de diseño orientado a objetos.

  • Programa a la interfaz en lugar de la implementación.
  • Prefiere la composición de objetos a la herencia.

Los patrones de diseño son soluciones reutilizables para problemas comunes de diseño de software. Proporcionan una forma de resolver problemas que los desarrolladores suelen encontrar al diseñar aplicaciones de software. Hay varios tipos de patrones de diseño, incluidos los patrones de creación, estructurales y de comportamiento.

  • Los patrones de creación se utilizan para crear objetos de una manera flexible y eficiente. Incluyendo el modo Singleton, el modo de fábrica y el modo de fábrica abstracto.
  • Los patrones estructurales se utilizan para combinar clases y objetos para formar estructuras más grandes. Incluyendo modo adaptador, modo puente y modo decorador, etc.
  • Los patrones de comportamiento se utilizan para gestionar la comunicación y controlar el flujo entre clases u objetos. Incluyendo el modo de observador, el modo de estrategia y el modo de método de plantilla.

Los patrones de diseño son herramientas muy útiles en el desarrollo de software, que pueden mejorar la reutilización y mantenibilidad del código y, al mismo tiempo, pueden mejorar la productividad de los desarrolladores.

Seis principios de patrones de diseño

  1. Principio de responsabilidad única (SRP): una clase debe tener solo una razón para cambiarla.
  2. Principio abierto cerrado (OCP): las entidades de software (clases, módulos, funciones, etc.) deben estar abiertas para la extensión y cerradas para la modificación.
  3. Principio de sustitución de Liskov (LSP): los subtipos deben poder reemplazar a sus supertipos.
  4. Principio de inversión de dependencia (DIP): los módulos de alto nivel no deben depender de los módulos de bajo nivel, ambos deben depender de interfaces abstractas; las interfaces abstractas no deben depender de implementaciones concretas y las implementaciones concretas deben depender de interfaces abstractas.
  5. Principio de segregación de interfaz (ISP): una clase no debe verse obligada a implementar una interfaz que no necesita, la interfaz debe dividirse en partes más pequeñas y específicas para que los clientes solo necesiten conocer las partes que les interesan.
  6. Ley de Deméter (LOD): un objeto debe saber lo menos posible sobre otros objetos, a menudo llamado el "principio del mínimo conocimiento".

1. Modo de creación

1. Patrón único

Singleton Pattern (Patrón Singleton) es uno de los patrones de diseño más simples en Java, el cual tiene las siguientes características:

  • Una clase singleton solo puede tener una instancia.
  • Una clase singleton debe crear su propia instancia única.
  • Una clase singleton debe proporcionar esta instancia a todos los demás objetos.

1) estilo chino hambriento

Este método es el más utilizado, pero es fácil generar objetos basura.Ventajas
: Sin bloqueo, se mejorará la eficiencia de ejecución.
Desventajas: Inicializar cuando se carga la clase, desperdiciando memoria.

public class Singleton {
    
    
    private static Singleton instance = new Singleton();
    private Singleton() {
    
    }
    public static Singleton getInstance() {
    
    
        return instance;
    }
}

2) Estilo perezoso, bloqueo de doble verificación

Agregue bloqueos de verificación doble sobre la base del hombre perezoso para garantizar la seguridad y el rendimiento del subproceso.

public class Singleton {
    
    
    private volatile static Singleton instance = null;
    private Singleton() {
    
    }
    public static Singleton getInstance() {
    
    
        if (instance == null) {
    
    
            synchronized (Singleton.class) {
    
    
                if (instance == null) {
    
    
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

3) Clase interna estática

Use una clase interna estática para implementar el patrón de singleton perezoso para garantizar la seguridad y el rendimiento del subproceso. Este método puede lograr el mismo efecto que el método de bloqueo de doble verificación, pero la implementación es más simple.

public class Singleton {
    
    
    private Singleton() {
    
    }
    private static class SingletonHolder {
    
    
        private static final Singleton INSTANCE = new Singleton();
    }
    public static Singleton getInstance() {
    
    
        return SingletonHolder.INSTANCE;
    }
}

4) enumeración

Utilice la enumeración para implementar el modo singleton, garantizar la seguridad de subprocesos y evitar ataques de reflexión.
1) Convertir la clase existente en un singleton de enumeración
PersonEnum.INSTANCE.getInstance()

@Data
public class Person {
    
    
    private String name;
    private int age;
}

public enum PersonEnum {
    
    
    INSTANCE;

    private Person instance;
    private PersonEnum(){
    
    
        instance = new Person();
    }

    public Person getInstance() {
    
    
        return instance;
    }
}

2) Crear directamente una nueva clase de enumeración
PersonSingleton.INSTANCE

public enum PersonSingleton {
    
    
    INSTANCE;

    private String name;
    public String getName() {
    
    
        return name;
    }
}

2. Patrón prototipo

El patrón de diseño de prototipos permite crear nuevos objetos copiando objetos existentes en lugar de instanciar una clase.
Muy útil cuando necesita crear una gran cantidad de objetos similares, puede evitar la creación repetida de objetos, mejorando así el rendimiento, y puede implementar copias superficiales o profundas según sea necesario.
En Java, la implementación del patrón prototipo generalmente implica implementar la interfaz Cloneable y anular el método clone().
patrón de prototipo

public abstract class Shape implements Cloneable {
    
    
    private String id;
    protected String type;
    public String getId() {
    
    
        return id;
    }
    public void setId(String id) {
    
    
        this.id = id;
    }
    public String getType() {
    
    
        return type;
    }
    public void setType(String type) {
    
    
        this.type = type;
    }
    public abstract void draw();
    @Override
    public Object clone() {
    
    
        Object clone = null;
        try {
    
    
            clone = super.clone();
        } catch (CloneNotSupportedException e) {
    
    
            e.printStackTrace();
        }
        return clone;
    }
}
public class Circle extends Shape {
    
    
    public Circle() {
    
    
        type = "Circle";
    }
    @Override
    public void draw() {
    
    
        System.out.println("Inside Circle::draw() method.");
    }
}
public class Square extends Shape {
    
    
    public Square() {
    
    
        type = "Square";
    }
    @Override
    public void draw() {
    
    
        System.out.println("Inside Square::draw() method.");
    }
}
public class ShapeCache {
    
    
    private static Map<String, Shape> shapeMap = new HashMap<>();
    public static Shape getShape(String shapeId) {
    
    
        Shape cachedShape = shapeMap.get(shapeId);
        return (Shape) cachedShape.clone();
    }
    // For each shape run database query and create shape
    // shapeMap.put(shapeKey, shape);
    // for example, we are adding three shapes
    public static void loadCache() {
    
    
        Circle circle = new Circle();
        circle.setId("1");
        shapeMap.put(circle.getId(), circle);
        Square square = new Square();
        square.setId("2");
        shapeMap.put(square.getId(), square);
    }
}
public class PrototypePatternDemo {
    
    
    public static void main(String[] args) {
    
    
        ShapeCache.loadCache();
        Shape clonedShape = ShapeCache.getShape("1");
        System.out.println("Shape : " + clonedShape.getType());
        Shape clonedShape2 = ShapeCache.getShape("2");
        System.out.println("Shape : " + clonedShape2.getType());
    }
}

En el código anterior, Shape es una clase abstracta que implementa la interfaz Cloneable y anula el método clone(). Circle y Square son subclases concretas de Shape que implementan el método draw(). La clase ShapeCache es un caché que almacena copias de objetos Shape. La clase PrototypePatternDemo es una clase de demostración que usa ShapeCache para obtener una copia del objeto Shape.

En el método loadCache(), creamos dos copias del objeto Shape y las almacenamos en el shapeMap. En el método main(), usamos el método getShape() para obtener una copia de los objetos Shape y mostrar sus tipos. Como estamos usando el patrón prototipo, podemos crear nuevos objetos copiando objetos existentes sin instanciar la clase.

3. Patrón de fábrica

Cree objetos a través de una clase de fábrica sin exponer directamente la lógica de creación de objetos a los clientes.
La ventaja del modelo de fábrica simple es que el cliente no necesita conocer los detalles de la creación de clases de productos específicas, sino que solo necesita crear objetos a través de la clase de fábrica, y la clase de fábrica puede crear dinámicamente diferentes tipos de objetos según las necesidades del cliente. Pero las desventajas también son obvias: si hay que crear muchas clases de productos, el código de la clase de fábrica se hinchará y no será fácil de mantener.
Patrón de fábrica simple

abstract class Animal {
    
    
    public abstract void sound();
}
class Cat extends Animal {
    
    
    @Override
    public void sound() {
    
    
        System.out.println("喵喵喵");
    }
}
class Dog extends Animal {
    
    
    @Override
    public void sound() {
    
    
        System.out.println("汪汪汪");
    }
}
// 创建一个工厂类
class AnimalFactory {
    
    
    // 定义一个静态方法,根据传入的参数创建具体的产品类对象
    public static Animal createAnimal(String type) {
    
    
        if (type.equalsIgnoreCase("dog")) {
    
    
            return new Dog();
        } else if (type.equalsIgnoreCase("cat")) {
    
    
            return new Cat();
        } else {
    
    
            throw new IllegalArgumentException("Invalid animal type: " + type);
        }
    }
}
// 客户端代码
public class Main {
    
    
    public static void main(String[] args) {
    
    
        // 使用工厂类创建不同的 Animal 对象
        Animal dog = AnimalFactory.createAnimal("dog");
        dog.sound();
        Animal cat = AnimalFactory.createAnimal("cat");
        cat.sound();
    }
}

4. Patrón de fábrica abstracto

Los objetos se crean definiendo una interfaz para crearlos, pero dejando la decisión de implementación a las subclases.
En el patrón Abstract Factory, las interfaces son fábricas responsables de crear un objeto relacionado sin especificar su clase explícitamente. Cada fábrica generada puede proporcionar objetos según el patrón de fábrica.
patrón de fábrica abstracto

// 创建一个抽象产品类
abstract class Animal {
    
    
    public abstract void sound();
}
class Cat extends Animal {
    
    
    @Override
    public void sound() {
    
    
        System.out.println("喵喵喵");
    }
}
// 创建具体产品类,继承自 Animal 类
class Dog extends Animal {
    
    
    @Override
    public void sound() {
    
    
        System.out.println("汪汪汪");
    }
}

abstract class AnimalFactory {
    
    
    // 定义一个抽象方法,用于创建 Animal 对象
    public abstract Animal createAnimal();
}
class CatFactory extends AnimalFactory {
    
    
    @Override
    public Animal createAnimal() {
    
    
        return new Cat();
    }
}
// 创建具体工厂类,实现创建 Animal 对象的接口
class DogFactory extends AnimalFactory {
    
    
    @Override
    public Animal createAnimal() {
    
    
        return new Dog();
    }
}
// 客户端代码
public class Main {
    
    
    public static void main(String[] args) {
    
    
        // 创建一个 Dog 对象
        AnimalFactory dogFactory = new DogFactory();
        Animal dog = dogFactory.createAnimal();
        dog.sound();

        // 创建一个 Cat 对象
        AnimalFactory catFactory = new CatFactory();
        Animal cat = catFactory.createAnimal();
        cat.sound();
    }
}

5. Patrón de constructor

El patrón de construcción es un patrón de diseño creativo que le permite crear diferentes tipos de objetos construyendo objetos complejos paso a paso. Utiliza una clase constructora para encapsular el proceso de creación de objetos y dividirlo en pasos simples. Esto le permite crear diferentes tipos de objetos cambiando estos pasos.
modo constructor

En este ejemplo, creamos la clase Car como el objeto complejo que queremos construir. Luego creamos la clase CarBuilder, que crea de forma incremental el objeto Car y lo devuelve al final. Finalmente, usamos CarBuilder para construir el objeto Car.

public class Car {
    
    
    private String make;
    private String model;
    private int year;
    private String engine;
    private int seats;
    public Car(String make, String model, int year, String engine, int seats) {
    
    
        this.make = make;
        this.model = model;
        this.year = year;
        this.engine = engine;
        this.seats = seats;
    }
	// ... getter setter ... //
}
public class CarBuilder {
    
    
    private String make;
    private String model;
    private int year;
    private String engine;
    private int seats;

    public CarBuilder setMake(String make) {
    
    
        this.make = make;
        return this;
    }

    public CarBuilder setModel(String model) {
    
    
        this.model = model;
        return this;
    }

    public CarBuilder setYear(int year) {
    
    
        this.year = year;
        return this;
    }

    public CarBuilder setEngine(String engine) {
    
    
        this.engine = engine;
        return this;
    }

    public CarBuilder setSeats(int seats) {
    
    
        this.seats = seats;
        return this;
    }

    public Car build() {
    
    
        return new Car(make, model, year, engine, seats);
    }
}

2. Modelo estructural

1. Patrón de adaptador

El patrón de adaptador de Java es un patrón de diseño estructural que permite la comunicación entre interfaces incompatibles. El patrón Adapter hace esto convirtiendo la interfaz de una clase a otra interfaz que esperan los clientes. Este patrón permite la reutilización de clases existentes sin modificar el código existente.
El patrón de adaptador nos ayuda a reutilizar las clases existentes sin modificar el código existente y permite la comunicación entre interfaces incompatibles.

Función principal: El modo adaptador convierte la interfaz de una clase en otra interfaz que el cliente quiera, de modo que aquellas clases que no pudieron trabajar juntas debido a la incompatibilidad de la interfaz puedan trabajar juntas.

En el siguiente ejemplo, tenemos una interfaz de destino Target, una interfaz de origen Adaptee y una clase de adaptador Adapter. La clase de adaptador implementa la interfaz de destino y reenvía la solicitud al método de solicitud específico de la interfaz de origen. El código del cliente crea un objeto de adaptador y lo usa para llamar a métodos de la interfaz de destino.
patrón de adaptador

// 源接口
public class Adaptee {
    
    
    public void specificRequest() {
    
    
        System.out.println("Adaptee's specific request");
    }
}
public interface Target {
    
    
    public void request();
}
// 适配器类
public class Adapter implements Target {
    
    
    private Adaptee adaptee;

    public Adapter(Adaptee adaptee) {
    
    
        this.adaptee = adaptee;
    }

    public void request() {
    
    
        adaptee.specificRequest();
    }
}
// 客户端代码
public class Client {
    
    
    public static void main(String[] args) {
    
    
        Adaptee adaptee = new Adaptee();
        Target target = new Adapter(adaptee);
        target.request();
    }
}

2. Patrón compuesto

El patrón compuesto es un patrón de diseño estructural que le permite componer objetos en estructuras de árbol para representar jerarquías de "parte-todo". Este patrón permite a los clientes tratar objetos individuales y combinaciones de objetos de manera uniforme.

En el modo compuesto, hay dos tipos básicos de objetos: nodos hoja y nodos compuestos. Un nodo de hoja representa un solo objeto en la estructura de árbol, mientras que un nodo compuesto representa un grupo de objetos en la estructura de árbol. Los nodos de composición pueden contener otros nodos de composición y/o nodos hoja, formando una estructura de árbol recursiva.
modo de combinación

El siguiente es un código de muestra que usa Java para implementar el modo de combinación, en el que usamos un Componente de clase abstracta para representar los nodos en la estructura de árbol, incluidos los nodos de hoja y los nodos de combinación. Un nodo compuesto contiene nodos secundarios, y los nodos secundarios se pueden agregar y eliminar a través de los métodos add() y remove(). Los nodos de hoja no tienen nodos secundarios, pero pueden implementar métodos de operación comunes. El nodo de combinación específico hereda de la clase Componente y realiza su propio método de operación.

public interface IComponent {
    
    
   void display();
}

// Component.java
public abstract class Component implements IComponent {
    
    
    protected String name;

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

    public abstract void add(IComponent component);

    public abstract void remove(IComponent component);

}
// Composite.java
public class Composite extends Component {
    
    
    private List<IComponent> children = new ArrayList<>();

    public Composite(String name) {
    
    
        super(name);
    }

    @Override
    public void add(IComponent component) {
    
    
        children.add(component);
    }

    @Override
    public void remove(IComponent component) {
    
    
        children.remove(component);
    }

    @Override
    public void display() {
    
    
        System.out.println("Composite: " + name);
        for (IComponent component : children) {
    
    
            component.display();
        }
    }
}
// Leaf.java
public class Leaf implements IComponent {
    
    
    private String name;

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

    @Override
    public void display() {
    
    
        System.out.println("Leaf: " + name);
    }
}
// Client.java
public class Client {
    
    
    public static void main(String[] args) {
    
    
        Component root = new Composite("root");
        Component branch1 = new Composite("branch1");
        Component branch2 = new Composite("branch2");
        Component leaf1 = new Leaf("leaf1");
        Component leaf2 = new Leaf("leaf2");
        Component leaf3 = new Leaf("leaf3");
        root.add(branch1);
        root.add(branch2);
        branch1.add(leaf1);
        branch2.add(leaf2);
        branch2.add(leaf3);
        root.display();
    }
}

3. Patrón de decorador

El patrón decorador es un patrón de diseño estructural que le permite agregar comportamiento a un solo objeto, de forma estática o dinámica, sin afectar el comportamiento de otros objetos de la misma clase. Este patrón es útil cuando desea tener la capacidad de agregar o eliminar objetos en tiempo de ejecución, o cuando desea reducir la cantidad de subclases necesarias para crear combinaciones de diferentes comportamientos.

En Java, el patrón decorador se implementa mediante una combinación de herencia y composición. Específicamente, crea una clase base o interfaz para definir el comportamiento central del objeto y luego crea una o más clases de decorador para agregar un comportamiento adicional al objeto. Cada clase de decorador tiene una referencia al objeto que decora y puede modificar el comportamiento del objeto antes o después de delegar el comportamiento original del objeto.

El patrón decorador es adecuado para los siguientes escenarios:

  1. Agregue nueva funcionalidad a las clases existentes sin modificar el código existente.
  2. Agregue un nuevo comportamiento a los objetos dinámicamente en tiempo de ejecución.
  3. Combinar objetos de diferentes maneras para lograr diferentes comportamientos.

Hay algunas cosas a tener en cuenta al usar el patrón decorador:

  1. La clase decorador necesita implementar la misma interfaz que el objeto decorado para que el objeto decorado pueda envolverse.
  2. Una clase decoradora debe agregar un nuevo comportamiento antes o después de invocar el método del objeto decorado.
  3. No cree demasiados objetos decoradores, de lo contrario, el código se volverá complicado y difícil de mantener.
    patrón decorador
public interface Pizza {
    
    
    public String getDescription();
    public double getCost();
}
 // 具体组件
public class PlainPizza implements Pizza {
    
    
    public String getDescription() {
    
    
        return "薄饼";
    }
    public double getCost() {
    
    
        return 4.00;
    }
}
 // 装饰器
public abstract class ToppingDecorator implements Pizza {
    
    
    protected Pizza pizza;
    public ToppingDecorator(Pizza pizza) {
    
    
        this.pizza = pizza;
    }
    public String getDescription() {
    
    
        return pizza.getDescription();
    }
    public double getCost() {
    
    
        return pizza.getCost();
    }
}
 // 具体装饰器
public class Cheese extends ToppingDecorator {
    
    
    public Cheese(Pizza pizza) {
    
    
        super(pizza);
    }
    public String getDescription() {
    
    
        return pizza.getDescription() + ",马苏里拉奶酪";
    }
    public double getCost() {
    
    
        return pizza.getCost() + 0.50;
    }
}
 // 具体装饰器
public class Pepperoni extends ToppingDecorator {
    
    
    public Pepperoni(Pizza pizza) {
    
    
        super(pizza);
    }
    public String getDescription() {
    
    
        return pizza.getDescription() + ",意大利辣香肠";
    }
    public double getCost() {
    
    
        return pizza.getCost() + 1.00;
    }
}
 // 客户端代码
public class PizzaShop {
    
    
    public static void main(String[] args) {
    
    
        Pizza pizza = new PlainPizza();
        pizza = new Cheese(pizza);
        pizza = new Pepperoni(pizza);
        System.out.println(pizza.getDescription());
        System.out.println("成本:$" + pizza.getCost());
    }
}

En este ejemplo, tenemos una interfaz de Pizza que define el comportamiento central de Pizza, incluida su descripción y costo. Luego tenemos una clase PlainPizza que implementa la interfaz Pizza.

A continuación, creamos una clase abstracta ToppingDecorator que implementa la interfaz Pizza y tiene una referencia al objeto Pizza que decora. Esto nos permite agregar un comportamiento adicional al objeto Pizza sin modificar el objeto original.

Finalmente, creamos dos clases de decoradores concretas Cheese y Pepperoni que agregan un comportamiento adicional al objeto Pizza. Cada clase de decorador modifica los métodos getDescription() y getCost() para agregar su propio comportamiento antes o después de delegar en el objeto original.

En el código del cliente, creamos un objeto PlainPizza y lo decoramos con objetos Cheese y Pepperoni. Luego imprima la descripción y el costo de Pizza para verificar que el decorador modifica el comportamiento del objeto original.

4. Patrón de fachada

Facade Pattern es un patrón de diseño estructural que proporciona una interfaz simple para acceder a los subsistemas en un sistema complejo, ocultando así la complejidad de los subsistemas. El modo Facade es un modo basado en objetos que desacopla al cliente del subsistema mediante la creación de una clase de fachada, de modo que el cliente solo necesita interactuar con la clase de fachada para completar la operación.
modo de apariencia

class CPU {
    
    
    public void processData() {
    
    
        System.out.println("正在处理数据...");
    }
}
 class Memory {
    
    
    public void load() {
    
    
        System.out.println("正在加载内存...");
    }
}
 class HardDrive {
    
    
    public void readData() {
    
    
        System.out.println("正在读取硬盘数据...");
    }
}
 // 外观类
class ComputerFacade {
    
    
    private CPU cpu;
    private Memory memory;
    private HardDrive hardDrive;
     public ComputerFacade() {
    
    
        cpu = new CPU();
        memory = new Memory();
        hardDrive = new HardDrive();
    }
     public void start() {
    
    
        System.out.println("启动计算机...");
        cpu.processData();
        memory.load();
        hardDrive.readData();
        System.out.println("计算机启动完毕!");
    }
}
 // 客户端代码
public class FacadePatternDemo {
    
    
    public static void main(String[] args) {
    
    
        ComputerFacade computer = new ComputerFacade();
        computer.start();
    }
}

En este ejemplo tenemos tres clases de subsistemas: CPU, Memory y HardDrive. Cada clase realiza una función diferente del sistema, como procesar datos, cargar memoria y leer datos del disco duro.

Luego creamos una clase ComputerFacade que encapsula las tres clases del subsistema y proporciona un método start() simple que los clientes pueden usar para iniciar la computadora. En el método start(), llamamos a los métodos de cada subsistema en el orden correcto para garantizar que la computadora se inicia correctamente.

Finalmente, en el código del cliente, creamos un objeto ComputerFacade y usamos el método start() para iniciar la computadora. Dado que las clases de fachada ocultan las complejidades del sistema, el código del cliente es muy simple.

5. Patrón de peso mosca

Flyweight Pattern es un patrón de diseño estructural que reduce el uso de memoria y la sobrecarga de creación de objetos al compartir objetos. El patrón Flyweight pertenece al patrón de tipo objeto.Administra los objetos compartidos mediante la creación de una fábrica Flyweight y devuelve los objetos existentes cuando es necesario, lo que reduce la cantidad de creación y destrucción de objetos.
Modo de peso mosca

interface Shape {
    
    
    void draw();
}
// 具体享元类
class Circle implements Shape {
    
    
    private String color;
    private int x;
    private int y;
    private int radius;
     public Circle(String color) {
    
    
        this.color = color;
    }
     public void setX(int x) {
    
    
        this.x = x;
    }
     public void setY(int y) {
    
    
        this.y = y;
    }
     public void setRadius(int radius) {
    
    
        this.radius = radius;
    }
     @Override
    public void draw() {
    
    
        System.out.println("画了一个" + color + "的圆,半径为" + radius + ",位置为(" + x + "," + y + ")");
    }
}
// 享元工厂类
class ShapeFactory {
    
    
    private static final Map<String, Shape> circleMap = new HashMap<>();
     public static Shape getCircle(String color) {
    
    
        Circle circle = (Circle) circleMap.get(color);
         if (circle == null) {
    
    
            circle = new Circle(color);
            circleMap.put(color, circle);
            System.out.println("创建了一个" + color + "的圆");
        }
         return circle;
    }
}
// 客户端代码
public class FlyweightPatternDemo {
    
    
    private static final String[] colors = {
    
     "红色", "绿色", "蓝色", "黄色", "黑色" };
     public static void main(String[] args) {
    
    
        for (int i = 0; i < 20; i++) {
    
    
            Circle circle = (Circle) ShapeFactory.getCircle(getRandomColor());
            circle.setX(getRandomX());
            circle.setY(getRandomY());
            circle.setRadius(100);
            circle.draw();
        }
    }
     private static String getRandomColor() {
    
    
        return colors[(int) (Math.random() * colors.length)];
    }
     private static int getRandomX() {
    
    
        return (int) (Math.random() * 100);
    }
     private static int getRandomY() {
    
    
        return (int) (Math.random() * 100);
    }
}

En este ejemplo, tenemos una interfaz Shape que define un método draw() para dibujar formas.

Luego, creamos un círculo de clase de peso mosca específico, que implementa la interfaz Shape y almacena información como el color, la posición y el radio del círculo.
A continuación, creamos una clase de fábrica de peso mosca ShapeFactory, que mantiene un mapa para almacenar objetos circulares creados. Cuando el cliente necesita dibujar un círculo, podemos obtener el objeto de círculo existente a través de ShapeFactory, si no existe, crear un nuevo objeto de círculo y almacenarlo en el Mapa.

Finalmente, en el código del cliente, generamos aleatoriamente 20 círculos, cada uno con un color, posición y radio aleatorios. Obtenga el objeto circular a través de ShapeFactory y llame a su método draw() para dibujar la forma. Dado que los objetos circulares del mismo color se comparten, se reducen el uso de memoria y la sobrecarga de creación de objetos.

6. Patrón de representación

El patrón proxy es un patrón de diseño comúnmente utilizado en proyectos. Proporciona una forma de acceder indirectamente al objeto de destino, es decir, a través del objeto proxy para acceder al objeto de destino.
La ventaja de esto es que, sin cambiar el objeto de destino original, se pueden agregar funciones de extensión adicionales al objeto de destino.
El modo proxy se divide en tres métodos de implementación: proxy estático, proxy dinámico jdk y proxy dinámico cglib.

Los tres métodos de implementación tienen sus propias ventajas y escenarios aplicables:

  • Proxy estático: la clase de proxy debe ser muy clara, por lo que no puede ser universal, pero también es la más eficiente
  • Proxy dinámico jdk: debe basarse en el proxy de interfaz, que tiene ciertas limitaciones; generar dinámicamente archivos de código de bytes, que se pueden usar para negocios generales (registros de rendimiento, etc.)
  • proxy dinámico cglig: también genera dinámicamente archivos de código de bytes, y la clase de proxy generada hereda el objeto de destino
  • La estrategia de proxy predeterminada de Spring AOP es: si el objeto de destino implementa la interfaz, use el proxy dinámico jdk; de lo contrario, use el proxy cglib
  • Después de jdk8, la eficiencia del proxy dinámico jdk es mayor que la del proxy cglib

1) proxy estático

El objeto proxy y el objeto proxy deben implementar la misma interfaz o heredar la misma clase principal, por lo que se debe definir una interfaz o una clase abstracta.

/**代理接口*/
public interface IHello {
    
    
    String hi(String key);
}
/**代理接口实现类*/
public class HelloImpl implements IHello {
    
    
    @Override
    public String hi(String key) {
    
    
        String str = "hello:" + key;
        System.out.println("HelloImpl! " + str);
        return str;
    }
}
/**静态代理类*/
public class HelloStaticProxy implements IHello {
    
    

    private IHello hello;

    public HelloStaticProxy(IHello hello) {
    
    
        this.hello = hello;
    }

    @Override
    public String hi(String key) {
    
    
        System.out.println(">>> static proxy start");
        String result = hello.hi(key);
        System.out.println(">>> static proxy end");
        return result;
    }
}
/**测试*/
public class DemoTest {
    
    

    public static void main(String[] args) {
    
    
        IHello helloProxy = new HelloStaticProxy(new HelloImpl());
        helloProxy.hi("world");
    }
}

2) proxy dinámico jdk

El proxy dinámico jdk es un método de proxy basado en interfaz y el objeto de destino debe implementar la interfaz.

El principio es que mediante el uso del mecanismo de reflexión, se genera dinámicamente una clase anónima para heredar la clase Proxy e implementar la interfaz que se va a utilizar como proxy. Dado que Java no admite la herencia múltiple, el proxy dinámico JDK no puede representar la clase como proxy.

/**代理接口*/
public interface IHello {
    
    
    String hi(String key);
}
/**代理接口实现类*/
public class HelloImpl implements IHello {
    
    
    @Override
    public String hi(String key) {
    
    
        String str = "hello:" + key;
        System.out.println("HelloImpl! " + str);
        return str;
    }
}


/**jdk动态代理类*/
public class JdkProxy implements InvocationHandler {
    
    

    private Object target;

    public JdkProxy(Object target) {
    
    
        this.target = target;
    }

    /**
     * 获取被代理接口实例对象
     *
     * @param <T>
     * @return
     */
    public <T> T getProxy() {
    
    
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
        System.out.println(">>> JdkProxy start");
        Object result = method.invoke(target, args);
        System.out.println(">>> JdkProxy end");
        return result;
    }
}

/**测试*/
public class Demo2Test {
    
    

    public static void main(String[] args) {
    
    
        JdkProxy proxy = new JdkProxy(new HelloImpl());
        IHello helloProxy = proxy.getProxy();
        helloProxy.hi(" jdk proxy !");
    }
}

3) proxy dinámico cglib

El objeto de destino no necesita implementar la interfaz y no puede actuar como un proxy para la clase final.
El principio es que la clase generada dinámicamente hereda el objeto de destino. Se debe introducir el paquete jar correspondiente para usar cglib

 <dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.2.7</version>
</dependency>
/**目标类*/
public class HelloImpl {
    
    
    public String hi(String key) {
    
    
        String str = "hello:" + key;
        System.out.println("HelloImpl! " + str);
        return str;
    }
}

/**cglib代理类*/
public class CglibProxy implements InvocationHandler {
    
    

    private Object target;

    /**
     * 获取被代理接口实例对象
     */
    public <T> T getProxy() {
    
    
        //1创建增强器对象
        Enhancer e = new Enhancer();
        //2设置增强器的类加载器
        e.setClassLoader(target.getClass().getClassLoader());
        //3设置代理对象父类类型
        e.setSuperclass(target.getClass());
        //4设置回调函数
        e.setCallback(this);
        //5创建代理对象
        return (T) e.create();
    }

    public CglibProxy(Object target) {
    
    
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
        System.out.println(">>> cglib start");
        Object obj = method.invoke(target, args);
        System.out.println(">>> cglib end");
        return obj;
    }
}

/**测试*/
public class Demo3Test {
    
    

    public static void main(String[] args) {
    
    
        HelloImpl hello = new HelloImpl();
        CglibProxy cglibProxy = new CglibProxy(hello);
        HelloImpl proxy = cglibProxy.getProxy();
        proxy.hi(" cglib ");
    }
}

7. Patrón de puente

El patrón de puente es un patrón de diseño que desacopla la abstracción y la implementación. Utiliza tecnologías básicas como la encapsulación, la agregación y la herencia para unir dos dimensiones que cambian de forma independiente de forma abstracta, lo que reduce el grado de acoplamiento entre ellas y hace que el sistema sea más flexible.
Modo Puente

El siguiente es un código de muestra para la implementación del patrón de puente en Java:

// 首先,我们定义一个 Color 接口,它表示颜色:
public interface Color {
    
    
    void applyColor();
}
// 然后,我们定义一个 Shape 抽象类,它包含了一个 Color 对象:
public abstract class Shape {
    
    
    protected Color color;
    public Shape(Color color) {
    
    
        this.color = color;
    }
    public abstract void applyColor();
}
// 接下来,我们定义两个实现了 Color 接口的具体类:
public class Red implements Color {
    
    
    @Override
    public void applyColor() {
    
    
        System.out.println("Applying red color");
    }
}

public class Blue implements Color {
    
    
    @Override
    public void applyColor() {
    
    
    System.out.println("Applying blue color");
    }
}
// 最后,我们定义两个实现了 Shape 抽象类的具体类:
public class Circle extends Shape {
    
    
    public Circle(Color color) {
    
    
        super(color);
    }
    @Override
    public void applyColor() {
    
    
        System.out.print("Circle applying color: ");
        color.applyColor();
    }
}

public class Square extends Shape {
    
    
    public Square(Color color) {
    
    
        super(color);
    }
    @Override
    public void applyColor() {
    
    
        System.out.print("Square applying color: ");
        color.applyColor();
    }
}
// 现在,我们可以使用这些类来创建出对应的对象并调用它们的方法:
public class Test {
    
    
    public static void main(String[] args) {
    
    
        Color blue = new Blue();
        Shape square = new Square(new Red());
        Shape circle = new Circle(blue);
        square.applyColor();
        circle.applyColor();
    }
}

La salida es la siguiente:

Square applying color: Applying red color
Circle applying color: Applying blue color

Esta es una implementación de patrón de puente simple que nos permite cambiar dinámicamente el color de la clase Shape en tiempo de ejecución sin afectar las subclases Shape, y también nos permite agregar nuevas clases de color y forma sin cambiar otras clases existentes.

3. Modelo de comportamiento

1. Patrón de cadena de responsabilidad

El patrón Cadena de responsabilidad es un patrón de diseño de comportamiento que permite pasar una solicitud a lo largo de una cadena de procesamiento hasta que un controlador la maneje. La implementación del patrón Cadena de responsabilidad en Java generalmente requiere los siguientes pasos:

  1. Define una interfaz de controlador que contiene un método para manejar solicitudes.
  2. Cree una clase de controlador abstracto que implemente la interfaz del controlador y contenga una referencia al siguiente controlador.
  3. Cree clases de controlador concretas que hereden de clases de controlador abstractas e implemente métodos para manejar solicitudes.
  4. En el código del cliente, cree una cadena de controladores y envíe la solicitud al primer controlador de la cadena.

La siguiente es una implementación Java simple del patrón Cadena de responsabilidad:
modelo de cadena de responsabilidad

public interface Handler {
    
    
    Handler setNextHandler(Handler nextHandler);
    void handleRequest(Request request);
}
// 创建抽象处理器类
public abstract class AbstractHandler implements Handler {
    
    
    private Handler nextHandler;

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

    public Handler getNextHandler() {
    
    
        return nextHandler;
    }
}

 // 创建具体的处理器类
public class ConcreteHandler1 extends AbstractHandler {
    
    
    public void handleRequest(Request request) {
    
    
        if (request.getType().equals("Type1")) {
    
    
            System.out.println("ConcreteHandler1 handles request " + request);
        } else {
    
    
            getNextHandler().handleRequest(request);
        }
    }
}
 public class ConcreteHandler2 extends AbstractHandler {
    
    
    public void handleRequest(Request request) {
    
    
        if (request.getType().equals("Type2")) {
    
    
            System.out.println("ConcreteHandler2 handles request " + request);
        } else {
    
    
            getNextHandler().handleRequest(request);
        }
    }
}
 public class ConcreteHandler3 extends AbstractHandler {
    
    
    public void handleRequest(Request request) {
    
    
        if (request.getType().equals("Type3")) {
    
    
            System.out.println("ConcreteHandler3 handles request " + request);
        } else {
    
    
            getNextHandler().handleRequest(request);
        }
    }
}
// 创建请求类
public class Request {
    
    
    private String type;

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

    public String getType() {
    
    
        return type;
    }

    public String toString() {
    
    
        return "Request [type=" + type + "]";
    }
}
// 客户端代码
public class Client {
    
    
    public static void main(String[] args) {
    
    
        Handler handler1 = new ConcreteHandler1();
        Handler handler2 = new ConcreteHandler2();
        Handler handler3 = new ConcreteHandler3();
        handler1.setNextHandler(handler2)
                .setNextHandler(handler3);
        handler1.handleRequest(new Request("Type1"));
        handler1.handleRequest(new Request("Type2"));
        handler1.handleRequest(new Request("Type3"));
    }
}

Introduce el resultado:

ConcreteHandler1 handles request Request [type=Type1]
ConcreteHandler2 handles request Request [type=Type2]
ConcreteHandler3 handles request Request [type=Type3]

El código anterior demuestra cómo crear una cadena de controladores y pasar la solicitud a lo largo de la cadena hasta que un controlador la maneje. En este ejemplo, ConcreteHandler1, ConcreteHandler2 y ConcreteHandler3 son clases controladoras concretas que heredan de la clase AbstractHandler e implementan el método handleRequest. El código del cliente crea una cadena de controladores y envía la solicitud al primer controlador de la cadena. Cuando llega una solicitud a un procesador, verifica que el tipo de solicitud coincida con un tipo que el procesador pueda manejar. En caso afirmativo, el controlador procesará la solicitud. De lo contrario, pasa la solicitud al siguiente controlador de la cadena hasta que un controlador la maneja.

2. Patrón de comando

El patrón de comando es un patrón de diseño de comportamiento que permite la encapsulación de solicitudes como un objeto para que las diferentes solicitudes se puedan parametrizar con diferentes parámetros, poner en cola o registrar solicitudes y admitir operaciones que se pueden deshacer. En Java, la implementación del patrón de comandos generalmente implica una interfaz de comandos y una o más clases de comandos concretas, que implementan la interfaz de comandos y definen la lógica de comandos real en su método de ejecución(). Además, el patrón de comando puede incluir una clase de invocador que asocie uno o más objetos de comando con objetos de receptor e invoque sus métodos de ejecución () cuando sea necesario. El patrón de comando es un patrón muy flexible y extensible que se puede usar en muchos escenarios de aplicación diferentes.
modo de comando

interface Command {
    
    
    void execute(String[] args);
}
 // 定义具体命令
class CreateFileCommand implements Command {
    
    
    public void execute(String[] args) {
    
    
        // 根据给定的名称和内容创建文件的代码
        System.out.println("创建文件 : " + String.join(", ", args));
    }
}
 class DeleteFileCommand implements Command {
    
    
    public void execute(String[] args) {
    
    
        // 根据给定的名称删除文件的代码
        System.out.println("删除文件 : "+String.join(",",args) );
    }
}
 // 定义命令执行者
class CommandExecutor {
    
    
    private Map<String, Command> commands = new HashMap<>();
     public CommandExecutor() {
    
    
        // 将具体命令与命令名称关联起来
        commands.put("create", new CreateFileCommand());
        commands.put("delete", new DeleteFileCommand());
    }
     public void executeCommand(String commandName, String[] args) {
    
    
        // 查找对应的命令并执行
        Command command = commands.get(commandName);
        if (command != null) {
    
    
            command.execute(args);
        } else {
    
    
            System.out.println("Unknown command: " + commandName);
        }
    }
}
 // 使用命令执行者执行命令
public class Main {
    
    
    public static void main(String[] args) {
    
    
        CommandExecutor executor = new CommandExecutor();
        executor.executeCommand("create", new String[]{
    
    "file.txt", "Hello World!"});
        executor.executeCommand("delete", new String[]{
    
    "file.txt"});
        executor.executeCommand("unknown", new String[]{
    
    });
    }
}

Salida de ejecución:

创建文件 : file.txt, Hello World!
删除文件 : file.txt
Unknown command: unknown

3. Patrón de intérprete

El patrón de intérprete de Java es un patrón de diseño de comportamiento que define un idioma y un intérprete para ese idioma para que algunas operaciones específicas se puedan expresar usando el idioma. Este modo es adecuado para escenarios que necesitan interpretar algunos lenguajes específicos, como compiladores, calculadoras de expresiones, etc.

En Java, las expresiones del lenguaje se pueden representar usando un árbol de sintaxis abstracta (AST), y se usa un intérprete para ejecutar esas expresiones. Los patrones de intérprete suelen incluir los siguientes componentes:

  • Expresión abstracta: define una interfaz de intérprete abstracta que contiene métodos que el intérprete necesita implementar.
  • Terminal Expression: Una terminal que implementa la interfaz de expresión abstracta y se utiliza para representar operaciones básicas o valores en el lenguaje.
  • Expresión no terminal (Non-Terminal Expression): un símbolo no terminal que implementa la interfaz de expresión abstracta y se utiliza para representar operaciones complejas en el lenguaje.
  • Contexto: contiene información global que necesita el intérprete, como variables, funciones, etc.
  • Intérprete: use los componentes anteriores para interpretar expresiones del lenguaje y realizar las operaciones correspondientes.
    modo intérprete

El siguiente es un código de muestra para un modo de intérprete de Java simple:

interface Expression {
    
    
    int interpret(Context context);
}
 // 终结符表达式
class NumberExpression implements Expression {
    
    
    private int value;
     public NumberExpression(int value) {
    
    
        this.value = value;
    }
     public int interpret(Context context) {
    
    
        return value;
    }
}
 // 非终结符表达式
class AddExpression implements Expression {
    
    
    private Expression left;
    private Expression right;
     public AddExpression(Expression left, Expression right) {
    
    
        this.left = left;
        this.right = right;
    }
     public int interpret(Context context) {
    
    
        return left.interpret(context) + right.interpret(context);
    }
}
 // 上下文
class Context {
    
    
    private Map<String, Integer> variables = new HashMap<>();
     public void setVariable(String name, int value) {
    
    
        variables.put(name, value);
    }
     public int getVariable(String name) {
    
    
        return variables.get(name);
    }
}
 // 解释器
class Interpreter {
    
    
    private Expression expression;
     public Interpreter(Expression expression) {
    
    
        this.expression = expression;
    }
     public int interpret(Context context) {
    
    
        return expression.interpret(context);
    }
}
 // 使用解释器执行表达式
public class Main {
    
    
    public static void main(String[] args) {
    
    
        // 创建上下文
        Context context = new Context();
        context.setVariable("a", 10);
        context.setVariable("b", 20);
         // 创建表达式
        Expression expression = new AddExpression(
            new NumberExpression(context.getVariable("a")),
            new NumberExpression(context.getVariable("b"))
        );
         // 创建解释器并执行表达式
        Interpreter interpreter = new Interpreter(expression);
        int result = interpreter.interpret(context);
        System.out.println("Result: " + result);
    }
}

En el código de muestra anterior, definimos dos expresiones terminales (NumberExpression) y una expresión no terminal (AddExpression) para representar la operación de suma. También definimos un contexto (Context), que se utiliza para almacenar variables y funciones, y un intérprete (Interpreter), que se utiliza para ejecutar expresiones. Finalmente, ejecutamos una expresión de suma simple usando el intérprete e imprimimos el resultado.

4. Patrón de iterador

El patrón Java Iterator es un patrón de diseño de comportamiento que proporciona una forma de acceder a los elementos de un objeto de colección sin exponer la representación interna de ese objeto. Este modo es adecuado para escenarios que necesitan atravesar objetos de colección, como arreglos, listas, árboles, etc.
patrón de iterador

El siguiente es un código de muestra simple del patrón de iterador de Java, que implementa un iterador de matriz utilizando el almacenamiento de matriz:

interface Iterator<T> {
    
    
    boolean hasNext();
    T next();
}
 // 具体迭代器实现类
class ArrayIterator<T> implements Iterator<T> {
    
    
    private T[] array;
    private int currentIndex;
     public ArrayIterator(T[] array) {
    
    
        this.array = array;
        this.currentIndex = 0;
    }
     public boolean hasNext() {
    
    
        return currentIndex < array.length;
    }
     public T next() {
    
    
        if (!hasNext()) {
    
    
            throw new NoSuchElementException();
        }
        T element = array[currentIndex];
        currentIndex++;
        return element;
    }
}
 // 使用迭代器遍历数组
public class Main {
    
    
    public static void main(String[] args) {
    
    
        Integer[] array = {
    
    1, 2, 3, 4, 5};
        Iterator<Integer> iterator = new ArrayIterator<>(array);
        while (iterator.hasNext()) {
    
    
            System.out.println(iterator.next());
        }
    }
}

En el código de muestra anterior, definimos una interfaz iteradora abstracta (Iterator) para representar un objeto iterador. También definimos una clase de implementación de iterador específica (ArrayIterator) para implementar iteradores de matriz. Finalmente, iteramos a través de una matriz de enteros e imprimimos el valor de cada elemento.

5. Patrón de observador

El patrón de observador de Java es un patrón de diseño de comportamiento que define una relación de dependencia de uno a muchos. Cuando cambia el estado de un objeto, todos los objetos que dependen de él serán notificados y actualizados automáticamente. Este modo es adecuado para escenarios que necesitan establecer relaciones dinámicas y poco acopladas entre objetos, como el procesamiento de eventos.

El patrón Observer es adecuado para los siguientes escenarios:

  1. Cuando cambia el estado de un objeto, debe notificar a otros objetos y actualizar su estado.
  2. Cuando un objeto necesita notificar a otros objetos sobre sus cambios de estado, pero no quiere tener una relación de acoplamiento estrecha con estos objetos.
  3. Cuando el cambio de un objeto necesita cambiar el estado de otros objetos al mismo tiempo, y no se sabe cuántos objetos deben cambiarse.
  4. Cuando el cambio de un objeto requiere la asistencia de otros objetos, pero no desea que estos objetos estén estrechamente acoplados entre sí.
  5. Cuando un objeto cambia, provocará una serie de reacciones en cadena, y la realización específica de estas reacciones en cadena no quiere ser conocida por el objeto.
  6. Cuando un objeto necesita proporcionar un método de actualización de datos a otros objetos, y este método de actualización necesita notificar a diferentes objetos en diferentes momentos.

En resumen, el patrón de observador es adecuado para escenarios que necesitan establecer una relación dinámica y débilmente acoplada entre objetos, como el procesamiento de eventos, la programación de GUI, las colas de mensajes, etc.
Patrón de observador

El siguiente es un código de muestra de patrón de observador de Java simple que implementa una estación meteorológica que notifica a todos los objetos de observador cuando cambian los datos meteorológicos:

// 抽象主题接口
interface Subject {
    
    
    void registerObserver(Observer observer);
    void removeObserver(Observer observer);
    void notifyObservers();
}
// 具体主题实现类
class WeatherStation implements Subject {
    
    
    private List<Observer> observers;
    private float temperature;
    private float humidity;
    private float pressure;

    public WeatherStation() {
    
    
        this.observers = new ArrayList<>();
    }

    public void registerObserver(Observer observer) {
    
    
        observers.add(observer);
    }

    public void removeObserver(Observer observer) {
    
    
        observers.remove(observer);
    }

    public void notifyObservers() {
    
    
        System.out.println(">> 通知所有观察者 <<");
        for (Observer observer : observers) {
    
    
            System.out.println("------观察者:" + observer.name() + "-----------");
            observer.update(temperature, humidity, pressure);
        }
    }

    public void setMeasurements(float temperature, float humidity, float pressure) {
    
    
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        notifyObservers();
    }
}
 // 抽象观察者接口
interface Observer {
    
    
    void update(float temperature, float humidity, float pressure);
    String name();
}
// 具体观察者实现类
class Display implements Observer {
    
    
    private float temperature;
    private float humidity;
    private float pressure;
    private String name;

    @Override
    public String name() {
    
    
        return this.name;
    }

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

    public void update(float temperature, float humidity, float pressure) {
    
    
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        display();
    }

    public void display() {
    
    
        System.out.println("Temperature: " + temperature);
        System.out.println("Humidity: " + humidity);
        System.out.println("Pressure: " + pressure);
    }
}
// 使用观察者模式实现气象站
public class Main {
    
    
    public static void main(String[] args) {
    
    
        WeatherStation weatherStation = new WeatherStation();
        Display display1 = new Display("01");
        Display display2 = new Display("02");
        weatherStation.registerObserver(display1);
        weatherStation.registerObserver(display2);
        weatherStation.setMeasurements(25.0f, 60.0f, 1013.0f);
        weatherStation.removeObserver(display2);
        weatherStation.setMeasurements(26.0f, 65.0f, 1012.0f);
    }
}

Ejecute el resultado de entrada:

>> 通知所有观察者 <<
------观察者:01-----------
Temperature: 25.0
Humidity: 60.0
Pressure: 1013.0
------观察者:02-----------
Temperature: 25.0
Humidity: 60.0
Pressure: 1013.0
>> 通知所有观察者 <<
------观察者:01-----------
Temperature: 26.0
Humidity: 65.0
Pressure: 1012.0

En el código de ejemplo anterior, definimos una interfaz de sujeto abstracto (Sujeto) y una interfaz de observador abstracto (Observador), que se utilizan para representar los objetos sujeto y observador. También definimos una clase de implementación de sujeto concreto (WeatherStation) y una clase de implementación de observador concreto (Display) para implementar la estación meteorológica y los objetos de visualización. Finalmente, implementamos una estación meteorológica utilizando el patrón Observer. Cuando los datos meteorológicos cambian, se notifica a todos los objetos del observador y se actualizan los datos en la pantalla.

Introducción al propio modo observador de Java

Una implementación de patrón de observador integrada proporcionada por Java. Utiliza la clase Observable y la interfaz Observer en Java para implementar el patrón de observador.

La clase Observable es una clase abstracta que representa un objeto observable con métodos para agregar, eliminar y notificar a los observadores. Cuando cambia el estado del objeto Observable, se llama a su método de notificaciónObservers() para notificar a todos los objetos observadores y actualizar su estado. La clase Observable también proporciona el método setChanged() y el método clearChanged() para marcar si el estado del objeto Observable ha cambiado.

La interfaz Observer representa un objeto observador, que tiene un método update() para actualizar el estado. Cuando cambia el estado del objeto Observable, se llamará al método update() del objeto observador para pasar los datos actualizados.
JDK viene con modo observador

import java.util.Observable;
import java.util.Observer;
 
// 具体主题类
class WeatherStation extends Observable {
    
    
    private float temperature;
    private float humidity;
    private float pressure;
 
    public void setMeasurements(float temperature, float humidity, float pressure) {
    
    
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        setChanged();
        notifyObservers();
    }
 
    public float getTemperature() {
    
    
        return temperature;
    }
 
    public float getHumidity() {
    
    
        return humidity;
    }
 
    public float getPressure() {
    
    
        return pressure;
    }
}
 
// 具体观察者类
class Display implements Observer {
    
    
    private float temperature;
    private float humidity;
    private float pressure;
 
    public void update(Observable o, Object arg) {
    
    
        if (o instanceof WeatherStation) {
    
    
            WeatherStation weatherStation = (WeatherStation) o;
            this.temperature = weatherStation.getTemperature();
            this.humidity = weatherStation.getHumidity();
            this.pressure = weatherStation.getPressure();
            display();
        }
    }
 
    public void display() {
    
    
        System.out.println("Temperature: " + temperature);
        System.out.println("Humidity: " + humidity);
        System.out.println("Pressure: " + pressure);
    }
}
 
// 使用JDK自带观察者模式实现气象站
public class Main {
    
    
    public static void main(String[] args) {
    
    
        WeatherStation weatherStation = new WeatherStation();
        Display display1 = new Display();
        Display display2 = new Display();
        weatherStation.addObserver(display1);
        weatherStation.addObserver(display2);
        weatherStation.setMeasurements(25.0f, 60.0f, 1013.0f);
        weatherStation.deleteObserver(display2);
        weatherStation.setMeasurements(26.0f, 65.0f, 1012.0f);
    }
}

En el código de ejemplo anterior, hemos utilizado la clase Observable y la interfaz Observer para implementar la estación meteorológica y mostrar objetos. Cuando los datos meteorológicos cambien, el objeto Observable llamará al método notificarObservers() para notificar a todos los objetos observadores y actualizar su estado. El objeto Observer implementa el método update() de la interfaz Observer para actualizar su propio estado.

6. Patrón de estado

El patrón de estado de Java es un patrón de diseño de comportamiento que permite que un objeto cambie su comportamiento cuando cambia su estado interno. El patrón de estado encapsula el estado en una clase independiente y delega la solicitud al objeto de estado actual, para realizar el cambio de estado y el cambio de comportamiento del estado.

Los escenarios para usar el patrón de estado incluyen:

  1. Cuando el comportamiento de un objeto depende de su estado y debe cambiar su comportamiento en tiempo de ejecución en función del estado.
  2. Cuando un objeto necesita cambiar sus datos y métodos según el estado.
  3. Cuando un objeto necesita cambiar entre varios estados, cada uno con un comportamiento diferente.

Precauciones:

  1. El patrón de estado puede aumentar el número de clases, por lo que debe diseñarse teniendo en cuenta el número y la complejidad de las clases.
  2. El patrón de estado debe encapsular el estado, por lo que la escalabilidad y la mantenibilidad del estado deben tenerse en cuenta en el diseño.
    modo de estado

Aquí hay un código de muestra implementado usando el patrón de estado de Java:

interface State {
    
    
    void handle();
}
 // 具体状态类1
class ConcreteState1 implements State {
    
    
    public void handle() {
    
    
        System.out.println("ConcreteState1 is handling.");
    }
}
 // 具体状态类2
class ConcreteState2 implements State {
    
    
    public void handle() {
    
    
        System.out.println("ConcreteState2 is handling.");
    }
}
 // 环境类
class Context {
    
    
    private State state;
     public void setState(State state) {
    
    
        this.state = state;
    }
     public void request() {
    
    
        state.handle();
    }
}
 // 使用状态模式实现的客户端代码
public class Main {
    
    
    public static void main(String[] args) {
    
    
        Context context = new Context();
        State state1 = new ConcreteState1();
        State state2 = new ConcreteState2();
        context.setState(state1);
        context.request();
        context.setState(state2);
        context.request();
    }
}

En el código de ejemplo anterior, definimos una interfaz de estado State y dos clases de estado concretas ConcreteState1 y ConcreteState2. También definimos un contexto de clase de entorno, que contiene un objeto de estado y define un método de solicitud request(), que se utiliza para llamar al método handle() del objeto de estado actual. En el código del cliente, creamos un objeto de contexto, establecemos su estado en ConcreteState1 y luego llamamos al método request () para generar "ConcreteState1 está manejando". A continuación, establecemos el estado de Contexto en ConcreteState2 y volvemos a llamar al método request(), con el resultado "ConcreteState2 está manejando".

Al usar el patrón de estado, podemos separar el estado y el comportamiento, de modo que el comportamiento del objeto pueda cambiar a medida que cambia el estado, logrando así un diseño más flexible.

7. Patrón de plantilla

El patrón de plantilla de Java es un patrón de diseño de comportamiento que define el esqueleto del programa en una operación, aplazando algunos pasos a la implementación de la subclase. Esto permite que las subclases redefinan ciertos pasos en el programa sin cambiar la estructura del programa.

En el patrón de plantilla de Java, hay dos tipos de métodos: métodos abstractos y métodos concretos. Los métodos abstractos son implementados por subclases mientras que los métodos concretos son implementados por superclases. El método plantilla se compone de métodos concretos y métodos abstractos, que definen el esqueleto del programa, mientras que los métodos concretos implementan algunos pasos del algoritmo.

El patrón de plantilla de Java es adecuado para los siguientes escenarios:

  1. Utilice el patrón de plantilla cuando necesite definir un conjunto de algoritmos que sean similares en estructura pero que difieran en los detalles de implementación.
  2. El patrón de plantilla se puede usar cuando necesita controlar el flujo de un algoritmo y necesita diferentes comportamientos en diferentes etapas del algoritmo.
  3. Cuando es necesario personalizar algunos pasos del algoritmo sin afectar la estructura general del algoritmo, se puede usar el modo de plantilla.
  4. Cuando necesite usar el mismo algoritmo en varias clases, puede usar el patrón de plantilla para evitar la duplicación de código.

En resumen, el patrón de plantilla de Java es adecuado para quienes necesitan definir el esqueleto en el programa e implementar los pasos específicos en la subclase. Mejora la reutilización y la capacidad de mantenimiento del código, al mismo tiempo que hace que el código sea más flexible y extensible.
patrón de plantilla

El siguiente es un código de muestra de patrón de plantilla de Java simple:

abstract class Game {
    
    
    abstract void initialize();
    abstract void startPlay();
    abstract void endPlay();
     // 模板方法
    public final void play() {
    
    
        // 初始化游戏
        initialize();
         // 开始游戏
        startPlay();
         // 结束游戏
        endPlay();
    }
}
 class Cricket extends Game {
    
    
    @Override
    void endPlay() {
    
    
        System.out.println("Cricket Game Finished!");
    }
     @Override
    void initialize() {
    
    
        System.out.println("Cricket Game Initialized! Start playing.");
    }
     @Override
    void startPlay() {
    
    
        System.out.println("Cricket Game Started. Enjoy the game!");
    }
}
 class Football extends Game {
    
    
    @Override
    void endPlay() {
    
    
        System.out.println("Football Game Finished!");
    }
     @Override
    void initialize() {
    
    
        System.out.println("Football Game Initialized! Start playing.");
    }
     @Override
    void startPlay() {
    
    
        System.out.println("Football Game Started. Enjoy the game!");
    }
}
 public class TemplatePatternDemo {
    
    
    public static void main(String[] args) {
    
    
        Game game = new Cricket();
        game.play();
         System.out.println();
         game = new Football();
        game.play();
    }
}

En el código anterior, la clase Game es una clase abstracta que define un método play() como método de plantilla. Las clases Cricket y Football son clases de implementación concreta que implementan los métodos abstractos definidos en la clase abstracta. En el método main(), creamos un objeto Cricket y un objeto Football y llamamos a su método play(). De esta manera podemos redefinir algunos pasos en el algoritmo sin cambiar la estructura del algoritmo.

8. Patrón mediador

El patrón Java Mediator es un patrón de diseño de comportamiento que permite que los objetos se comuniquen a través de un objeto Mediator, evitando así el acoplamiento directo entre objetos. El patrón de mediador transforma la relación compleja entre objetos en una relación simple entre mediadores y objetos, mejorando así la flexibilidad y mantenibilidad del sistema.

En el patrón de intermediario de Java, el objeto intermediario es responsable de coordinar la comunicación entre objetos y, por lo general, contiene algunos métodos públicos para manejar la interacción entre objetos. La comunicación entre objetos se realiza a través de un objeto mediador, evitando así el acoplamiento directo entre objetos.
patrón mediador

El siguiente es un código de muestra de patrón de Java Mediator simple:

// Mediator接口定义了send()方法,用于处理对象之间的交互。
interface Mediator {
    
    
    void send(String message, Colleague colleague);
}
 // Colleague抽象类表示对象,它包含一个Mediator对象,用于处理对象之间的通信。
abstract class Colleague {
    
    
    protected Mediator mediator;
     public Colleague(Mediator mediator) {
    
    
        this.mediator = mediator;
    }
     public abstract void receive(String message);
     public abstract void send(String message);
}
 // ConcreteColleague1和ConcreteColleague2是具体的对象实现类,它们实现了Colleague抽象类中的方法。
class ConcreteColleague1 extends Colleague {
    
    
    public ConcreteColleague1(Mediator mediator) {
    
    
        super(mediator);
    }
     @Override
    public void receive(String message) {
    
    
        System.out.println("Colleague1 received message: " + message);
    }
     @Override
    public void send(String message) {
    
    
        System.out.println("Colleague1 sends message: " + message);
        mediator.send(message, this);
    }
}
 class ConcreteColleague2 extends Colleague {
    
    
    public ConcreteColleague2(Mediator mediator) {
    
    
        super(mediator);
    }
     @Override
    public void receive(String message) {
    
    
        System.out.println("Colleague2 received message: " + message);
    }
     @Override
    public void send(String message) {
    
    
        System.out.println("Colleague2 sends message: " + message);
        mediator.send(message, this);
    }
}
 // ConcreteMediator是具体的中介者实现类,它负责协调对象之间的通信。
class ConcreteMediator implements Mediator {
    
    
    private ConcreteColleague1 colleague1;
    private ConcreteColleague2 colleague2;
     public void setColleague1(ConcreteColleague1 colleague1) {
    
    
        this.colleague1 = colleague1;
    }
     public void setColleague2(ConcreteColleague2 colleague2) {
    
    
        this.colleague2 = colleague2;
    }
     @Override
    public void send(String message, Colleague colleague) {
    
    
        if (colleague == colleague1) {
    
    
            colleague2.receive(message);
        } else {
    
    
            colleague1.receive(message);
        }
    }
}
 public class MediatorPatternDemo {
    
    
    public static void main(String[] args) {
    
    
        ConcreteMediator mediator = new ConcreteMediator();
         ConcreteColleague1 colleague1 = new ConcreteColleague1(mediator);
        ConcreteColleague2 colleague2 = new ConcreteColleague2(mediator);
         mediator.setColleague1(colleague1);
        mediator.setColleague2(colleague2);
         colleague1.send("Hello, Colleague2.");
        colleague2.send("Hello, Colleague1.");
    }
}

En el código anterior, la interfaz Mediator define el método send(), que se usa para manejar la interacción entre objetos. La clase abstracta Colleague representa objetos y contiene un objeto Mediator para manejar la comunicación entre objetos. ConcreteColleague1 y ConcreteColleague2 son clases de implementación de objetos concretos que implementan métodos en la clase abstracta Colleague. ConcreteMediator es una clase de implementación intermediaria específica, que es responsable de coordinar la comunicación entre objetos.

En el método main(), creamos un objeto ConcreteMediator y dos objetos ConcreteColleague, y llamamos a su método send() para comunicarnos. La comunicación se realiza a través de objetos intermediarios, evitando el acoplamiento directo entre objetos.

9. Patrón de recuerdo

El patrón Java Memento es un patrón de diseño de comportamiento que permite capturar y restaurar el estado interno de un objeto sin romper la encapsulación. El patrón memento se usa a menudo en situaciones en las que es necesario deshacer una acción o restaurar un estado anterior. El patrón consta de tres componentes principales: objetos primitivos, objetos de recuerdo y objetos responsables de gestionar objetos de recuerdo.
modo memo

El siguiente es un código de muestra para un patrón de recuerdo de Java simple:

// Originator类表示原始对象,它包含需要保存的状态。
class Originator {
    
    
    private String state;
     public void setState(String state) {
    
    
        this.state = state;
    }
     public String getState() {
    
    
        return state;
    }
     // createMemento()方法创建备忘录对象,并将当前状态保存到备忘录对象中。
    public Memento createMemento() {
    
    
        return new Memento(state);
    }
     // restore()方法用于从备忘录对象中恢复先前的状态。
    public void restore(Memento memento) {
    
    
        state = memento.getState();
    }
}
 // Memento类表示备忘录对象,它包含需要保存的状态。
class Memento {
    
    
    private String state;
     public Memento(String state) {
    
    
        this.state = state;
    }
     public String getState() {
    
    
        return state;
    }
}
 // Caretaker类负责管理备忘录对象,它包含一个Memento对象。
class Caretaker {
    
    
    private Memento memento;
     public void setMemento(Memento memento) {
    
    
        this.memento = memento;
    }
     public Memento getMemento() {
    
    
        return memento;
    }
}
 public class MementoPatternDemo {
    
    
    public static void main(String[] args) {
    
    
        Originator originator = new Originator();
        Caretaker caretaker = new Caretaker();
         // 保存原始对象的状态到备忘录对象中
        originator.setState("State 1");
        caretaker.setMemento(originator.createMemento());
         // 修改原始对象的状态
        originator.setState("State 2");
         // 从备忘录对象中恢复先前的状态
        originator.restore(caretaker.getMemento());
         System.out.println("Current state: " + originator.getState());
    }
}

En el código anterior, la clase Originator representa el objeto original, que contiene el estado que debe guardarse. El método createMemento() crea un objeto de recuerdo y guarda el estado actual en el objeto de recuerdo. El método restore() se usa para restaurar el estado anterior del objeto memento.

La clase Memento representa un objeto Memento, que contiene el estado que debe guardarse.

La clase Caretaker es responsable de administrar el objeto Memento, que contiene un objeto Memento.

En el método main(), creamos un objeto Originator y un objeto Caretaker, y llamamos a sus métodos para la conservación y restauración del estado. Con el patrón Memento, podemos capturar y restaurar el estado interno de un objeto sin romper la encapsulación.

10. Patrón de visitante

El patrón de visitante de Java es un patrón de diseño de comportamiento que le permite definir nuevas operaciones sin modificar la estructura del objeto. El patrón de visitante separa la estructura del objeto y las operaciones para que las operaciones se puedan aplicar de forma independiente a los elementos de la estructura del objeto. En el patrón Visitor, hay dos actores principales: el visitante y el elemento. Los visitantes definen métodos para operar en elementos, y los elementos proporcionan métodos que aceptan visitantes.

A continuación se muestra un código de muestra del patrón de visitante, en el que implementaremos una calculadora simple que puede evaluar expresiones. Usaremos el patrón visitante para recorrer el árbol de expresión y realizar la operación correspondiente en cada nodo.

El patrón de visitante es adecuado para los siguientes escenarios:

  1. La estructura del objeto es relativamente estable, pero a menudo es necesario definir nuevas operaciones en esta estructura del objeto.
  2. Necesita realizar muchas operaciones diferentes y no relacionadas en los objetos en una estructura de objeto, y no desea que estas operaciones "contaminen" las clases de estos objetos.
  3. Las clases de objetos en la estructura del objeto rara vez cambian, pero a menudo es necesario definir nuevas operaciones en estos objetos.
  4. Se deben realizar diferentes operaciones en tiempo de ejecución según el tipo de objeto.
  5. Todos los objetos en una estructura de objeto deben procesarse de alguna manera, pero la forma en que se procesan los objetos varía según el tipo de objeto.

Los escenarios comunes para usar el patrón de visitante incluyen: análisis de sintaxis del compilador, análisis de documentos XML, analizador estático, validador de modelos, convertidor de modelos, etc.

patrón de visitante

interface Expression {
    
    
    void accept(Visitor visitor);
}
class NumberExpression implements Expression {
    
    
    private int value;
    public NumberExpression(int value) {
    
    
        this.value = value;
    }
    public int getValue() {
    
    
        return value;
    }
    public void accept(Visitor visitor) {
    
    
        visitor.visit(this);
    }
}
// 定义具体的元素类:加法表达式
class AdditionExpression implements Expression {
    
    
    private Expression left;
    private Expression right;
    public AdditionExpression(Expression left, Expression right) {
    
    
        this.left = left;
        this.right = right;
    }
    public Expression getLeft() {
    
    
        return left;
    }
    public Expression getRight() {
    
    
        return right;
    }
    public void accept(Visitor visitor) {
    
    
        visitor.visit(this);
    }
}
interface Visitor {
    
    
    void visit(NumberExpression expression);
    void visit(AdditionExpression expression);
}
// 定义具体的访问者类:打印访问者
class PrinterVisitor implements Visitor {
    
    
    public void visit(NumberExpression expression) {
    
    
        System.out.print(expression.getValue());
    }
    public void visit(AdditionExpression expression) {
    
    
        System.out.print("(");
        expression.getLeft().accept(this);
        System.out.print("+");
        expression.getRight().accept(this);
        System.out.print(")");
    }
}
class CalculatorVisitor implements Visitor {
    
    
    private int result;
    public void visit(NumberExpression expression) {
    
    
        result = expression.getValue();
    }
    public void visit(AdditionExpression expression) {
    
    
        expression.getLeft().accept(this);
        int leftValue = result;
        expression.getRight().accept(this);
        int rightValue = result;
        result = leftValue + rightValue;
    }
    public int getResult() {
    
    
        return result;
    }
}
public class Client {
    
    
    public static void main(String[] args) {
    
    
        // 构建表达式树:1 + (2 + 3)
        Expression expression = new AdditionExpression(
                new NumberExpression(1),
                new AdditionExpression(
                        new NumberExpression(2),
                        new NumberExpression(3)
                )
        );
        // 计算表达式的值
        CalculatorVisitor calculator = new CalculatorVisitor();
        expression.accept(calculator);
        System.out.println("Result: " + calculator.getResult());
        // 打印表达式的字符串表示
        PrinterVisitor printer = new PrinterVisitor();
        expression.accept(printer);
    }
}

Resultado de salida:

Result: 6
(1+(2+3))

En el ejemplo anterior, definimos una expresión de interfaz de expresión y proporcionamos dos implementaciones de expresión, NumberExpression y AdditionExpression, y definimos una interfaz de visitante Visitor, y dos visitantes específicos, CalculatorVisitor e PrinterVisitor.Los dos visitantes reciben el objeto de expresión e implementan operaciones específicas en la expresión en el visitante, a saber, operación de expresión e impresión de expresión. El ejemplo anterior no cambia la clase de expresión concreta y define nuevas operaciones.

11. Patrón de estrategia

El patrón de estrategia es un patrón de diseño de comportamiento que le permite definir una familia de algoritmos, encapsular cada algoritmo y hacerlos intercambiables. Este patrón permite que los algoritmos varíen independientemente de los clientes que los utilicen.

En Java, el patrón de estrategia generalmente consta de una interfaz y varias clases que implementan la interfaz. El cliente utilizará esta interfaz para llamar al algoritmo en lugar de llamar directamente a la clase de implementación. De esta forma, el cliente puede elegir una implementación de algoritmo diferente en tiempo de ejecución sin modificar el código.
patrón de estrategia

El siguiente ejemplo muestra cómo usar el patrón de estrategia para implementar un sistema de pago simple. Definimos una interfaz PaymentStrategy y creamos múltiples clases que implementan esta interfaz, y cada clase representa un método de pago diferente. Los clientes pueden elegir qué método de pago utilizar según sus necesidades.

interface PaymentStrategy {
    
    
    void pay(double amount);
}
 class CreditCardPayment implements PaymentStrategy {
    
    
    private String cardNumber;
    private String cvv;
    private String expiryDate;
     public CreditCardPayment(String cardNumber, String cvv, String expiryDate) {
    
    
        this.cardNumber = cardNumber;
        this.cvv = cvv;
        this.expiryDate = expiryDate;
    }
     public void pay(double amount) {
    
    
        System.out.println("Paying " + amount + " using credit card.");
    }
}
 class PayPalPayment implements PaymentStrategy {
    
    
    private String email;
    private String password;
     public PayPalPayment(String email, String password) {
    
    
        this.email = email;
        this.password = password;
    }
     public void pay(double amount) {
    
    
        System.out.println("Paying " + amount + " using PayPal.");
    }
}
 class CashPayment implements PaymentStrategy {
    
    
    public void pay(double amount) {
    
    
        System.out.println("Paying " + amount + " using cash.");
    }
}
 class PaymentProcessor {
    
    
    private PaymentStrategy strategy;
     public PaymentProcessor(PaymentStrategy strategy) {
    
    
        this.strategy = strategy;
    }
     public void setStrategy(PaymentStrategy strategy) {
    
    
        this.strategy = strategy;
    }
     public void processPayment(double amount) {
    
    
        strategy.pay(amount);
    }
}
 public class PaymentSystem {
    
    
    public static void main(String[] args) {
    
    
        PaymentProcessor processor = new PaymentProcessor(new CreditCardPayment("1234 5678 9012 3456", "123", "12/23"));
        processor.processPayment(100.0);
         processor.setStrategy(new PayPalPayment("[email protected]", "password"));
        processor.processPayment(50.0);
         processor.setStrategy(new CashPayment());
        processor.processPayment(25.0);
    }
}

Salida de ejecución:

Paying 100.0 using credit card.
Paying 50.0 using PayPal.
Paying 25.0 using cash.

En el ejemplo anterior, la interfaz de PaymentStrategy define un método de pago y contiene un método de pago que acepta un parámetro de cantidad. Creamos tres clases que implementan esta interfaz, que representan el pago con tarjeta de crédito, el pago con PayPal y el pago en efectivo, respectivamente. La clase PaymentProcessor toma una instancia de PaymentStrategy como parámetro y la usa para realizar operaciones de pago. En el método principal, creamos una instancia de PaymentProcessor y usamos diferentes métodos de pago para realizar los pagos.

otro

Se introducen los siguientes dos patrones de diseño de uso común

1. Patrón de filtro

El patrón de diseño de filtro de Java es un patrón de diseño de uso común para procesar o filtrar solicitudes antes o después de que lleguen al objeto de destino. Este modo se puede utilizar para implementar diferentes funciones, como autenticación, autorización, registro, compresión, etc., y tratar diferentes operaciones como un filtro en la cadena de filtros.
modo de filtro
Aquí hay un código de muestra para un patrón de diseño de filtro Java simple:

  • Primero, defina un filtro de interfaz, que contiene un método doFilter para procesar solicitudes
  • Luego, implemente dos clases de filtro, una para autenticar solicitudes y otra para iniciar sesión
  • Finalmente, defina una clase FilterChain para encadenar varios filtros juntos
public interface Filter {
    
    
    public void doFilter(String request);
}
// 定义授权过滤器
public class AuthenticationFilter implements Filter {
    
    
    public void doFilter(String request) {
    
    
        System.out.println("Authenticating request: " + request);
    }
}
// 定义日志过滤器
public class LoggingFilter implements Filter {
    
    
    public void doFilter(String request) {
    
    
        System.out.println("Logging request: " + request);
    }
}
// 定义过滤器链
public class FilterChain {
    
    
    private List<Filter> filters = new ArrayList<Filter>();
    private int index = 0;

    public void addFilter(Filter filter) {
    
    
        filters.add(filter);
    }

    public void doFilter(String request) {
    
    
        if (index == filters.size()) {
    
    
            return;
        }

        Filter filter = filters.get(index);
        index++;

        filter.doFilter(request);
        doFilter(request);
    }
}
public class Main {
    
    
    public static void main(String[] args) {
    
    
        FilterChain chain = new FilterChain();
        chain.addFilter(new AuthenticationFilter());
        chain.addFilter(new LoggingFilter());

        chain.doFilter("request");
    }
}

El código de ejemplo anterior muestra la implementación básica del patrón de diseño de filtro de Java. Se pueden lograr diferentes funciones definiendo diferentes clases de filtros para encadenarlos.

2. Patrón de objeto nulo

El patrón de objeto nulo de Java es un patrón de diseño de comportamiento que nos permite proporcionar un comportamiento predeterminado sin devolver un valor nulo. Este patrón generalmente se usa en situaciones en las que se deben manejar objetos nulos para evitar excepciones NullPointerException. El patrón consta de dos componentes principales: clases abstractas y clases concretas.

El siguiente es un código de muestra simple para el patrón de objeto nulo de Java:
patrón de objeto vacío

interface User {
    
    
    String getName();
    boolean hasAccess();
}
 // 定义一个具体类,表示一个真实的用户
class RealUser implements User {
    
    
    private String name;
    private boolean hasAccess;
     public RealUser(String name, boolean hasAccess) {
    
    
        this.name = name;
        this.hasAccess = hasAccess;
    }
     public String getName() {
    
    
        return name;
    }
     public boolean hasAccess() {
    
    
        return hasAccess;
    }
}
 // 定义一个空对象,表示一个空的用户
class NullUser implements User {
    
    
    public String getName() {
    
    
        return "Guest";
    }
     public boolean hasAccess() {
    
    
        return false;
    }
}
 // 定义一个工厂类,用于创建用户
class UserFactory {
    
    
    // 根据名称和权限创建一个用户
    public static User getUser(String name, boolean hasAccess) {
    
    
        if (name == null) {
    
    
            return new NullUser();
        }
        return new RealUser(name, hasAccess);
    }
}
 public class NullObjectPatternDemo {
    
    
    public static void main(String[] args) {
    
    
        User user1 = UserFactory.getUser("Alice", true);
        User user2 = UserFactory.getUser(null, false);
         System.out.println("User 1: " + user1.getName() + ", has access: " + user1.hasAccess());
        System.out.println("User 2: " + user2.getName() + ", has access: " + user2.hasAccess());
    }
}

En el código anterior, definimos una interfaz Usuario, que representa a un usuario y contiene dos métodos: getName() y hasAccess(). A continuación, definimos una clase concreta RealUser, que representa un usuario real e implementa dos métodos de la interfaz de usuario. También definimos un objeto vacío NullUser, que representa un usuario vacío e implementa dos métodos de la interfaz de usuario. Finalmente, definimos una clase de fábrica UserFactory para crear usuarios. Si el nombre entrante es nulo, devuelve un objeto vacío NullUser; de lo contrario, devuelve un usuario real RealUser.

En el método main(), usamos UserFactory para crear dos usuarios, usuario1 y usuario2, e imprimir sus nombres y permisos. Dado que el nombre de usuario2 es nulo, devolverá un objeto vacío NullUser e imprimirá la cadena "Invitado" y el valor booleano de falso. A través del patrón de objeto vacío, podemos proporcionar un comportamiento predeterminado sin devolver un valor nulo, evitando NullPointerException.

Supongo que te gusta

Origin blog.csdn.net/wlddhj/article/details/131071730
Recomendado
Clasificación