23种设计模式

36 阅读41分钟

一、前言

  1. 核心:设计模式提供相关问题的解决方案,使人们可以复用成功的设计和体系结构。
  2. 要素:模式名称、问题、解决方案、效果。
  3. 分类:创建型结构型行为型

image.png

二、创建型(5 种)

创建型模式
创建型模式:使用继承改变被实例化的类。
对象创建型模式:将实例化委托给另一个对象。

【0】简单工厂模式 [静态工厂方法]

属于创建型模式,但不属于23种设计模式之一。

  1. 定义一个工厂类,它可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类。
  2. 用于创建实例的方法通常是静态(static)方法。可以通过工厂类的静态方法来创建对象,不需要显式地使用 new 关键字来实例化对象。
  3. 主要角色:工厂类、抽象产品类、具体产品类。
  4. 结构图

image.png

  1. 示例代码
class Test {
    public static void main(String[] args) {
        Product product1 = SimpleFactory.makeProduct(1);
        Product product2 = SimpleFactory.makeProduct(2);
        product1.show();
        product2.show();
    }
}

// 工厂类(Factory):负责创建具体对象的逻辑实现。它通常包含一个静态方法,根据传入的参数来判断并创建不同的对象。
class SimpleFactory {
    public static Product makeProduct(int kind) {
        switch (kind) {
            case 1:
                return new ConcreteProduct1();
            case 2:
                return new ConcreteProduct2();
            default:
                return null;
        }
    }
}

// 抽象产品类(Product):定义了具体产品类的共同接口,客户端通过抽象产品类来调用具体产品的方法。
abstract class Product {
    public abstract void show();
}

// 具体产品类(ConcreteProduct):实现抽象产品类定义的接口,是工厂类所创建的对象。
class ConcreteProduct1 extends Product {
    @Override
    public void show() {
        System.out.println("具体产品1显示...");
    }
}

class ConcreteProduct2 extends Product {
    @Override
    public void show() {
        System.out.println("具体产品2显示...");
    }
}

  1. 缺点:比如在这个示例中,想要再添加一个具体产品类 ConcreteProduct3 ,则还需要在 SimpleFactory 里补充 return new ConcreteProduct3(),违反了 “开发-封闭原则(对扩展开放,对修改封闭)” 里的 “对修改封闭” 原则。

【1】工厂方法模式(Factory Method)

  1. 定义一个用于创建对象的接口,让子类决定实例化哪个类,使一个类的实例化延迟到子类
  2. 主要角色:抽象工厂、抽象产品、具体工厂、具体产品。
  3. 结构图

image.png

或者

image.png 4. 示例代码

class FactoryMethodPatternExample {
    public static void main(String[] args) {
        Factory factoryA = new FactoryA();
        Product product1 = factoryA.createProduct();
        product1.show();

        Factory factoryB = new FactoryB();
        Product product2 = factoryB.createProduct();
        product2.show();
    }
}

// 抽象工厂 Creator:声明了一个工厂方法,用于创建产品对象。它可以是一个接口或者是一个抽象类。
interface Factory {
    // 声明 Factory Method,返回一个 Product 类型的对象
    public Product createProduct();
}

// 抽象产品:定义了产品的接口,是具体产品类的共同父类。
interface Product {
    public void show();
}

// 具体工厂:实现了抽象工厂,负责具体产品对象的创建。
class FactoryA implements Factory {
    @Override
    public Product createProduct() {
        return new ConcreteProduct1();
    }
}

class FactoryB implements Factory {
    @Override
    public Product createProduct() {
        return new ConcreteProduct2();
    }
}

// 具体产品:实现了抽象产品接口,具体的产品对象由具体工厂创建。
class ConcreteProduct1 implements Product {
    @Override
    public void show() {
        System.out.println("具体产品1显示...");
    }
}

class ConcreteProduct2 implements Product {
    @Override
    public void show() {
        System.out.println("具体产品2显示...");
    }
}

  1. 工厂方法模式-适用于:
    (1)当一个类不知道它所必须创建的对象的类时;
    (2)当一个类希望由它的子类来指定它所创建的对象时;
    (3)当类将创建对象的职责委托给多个子类中的某一个时。

  2. 缺点:类的数量增加,增加了系统的复杂度。

【2】抽象工厂模式(Abstract Factory)

  1. 提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。将一系列相关的产品对象的创建集中到一起,便于进行统一管理和维护。
  2. 主要角色:抽象工厂、具体工厂、抽象产品族、具体产品族。
  3. 结构图

image.png

  1. 示例代码
// client 仅使用由 AbstractFctory 和 AbstractProductA、AbstractProductB 类声明的接口
class Client {
    public static void main(String[] args) {
        Factory factory1 = new Factory1();
        ProductA productA1 = factory1.createProductA();
        ProductB productB1 = factory1.createProductB();
        productA1.showA();
        productB1.showB();

        Factory factory2 = new Factory2();
        ProductA productA2 = factory2.createProductA();
        ProductB productB2 = factory2.createProductB();
        productA2.showA();
        productB2.showB();
    }
}

// 抽象工厂(将一系列相关的或相依赖的产品对象的创建集中到一起)
interface Factory {
    public ProductA createProductA();
    public ProductB createProductB();
}

// 具体工厂
class Factory1 implements Factory {
    @Override
    public ProductA createProductA() {
        return new ConcreteProductA1();
    }

    @Override
    public ProductB createProductB() {
        return new ConcreteProductB1();
    }
}

class Factory2 implements Factory {
    @Override
    public ProductA createProductA() {
        return new ConcreteProductA2();
    }

    @Override
    public ProductB createProductB() {
        return new ConcreteProductB2();
    }
}

// 抽象产品族
interface ProductA {
    public void showA();
}

interface ProductB {
    public void showB();
}

// 具体产品族
class ConcreteProductA1 implements ProductA {
    @Override
    public void showA() {
        System.out.println("具体产品A1显示...");
    }
}

class ConcreteProductA2 implements ProductA {
    @Override
    public void showA() {
        System.out.println("具体产品A2显示...");
    }
}

class ConcreteProductB1 implements ProductB {
    @Override
    public void showB() {
        System.out.println("具体产品B1显示...");
    }
}

class ConcreteProductB2 implements ProductB {
    @Override
    public void showB() {
        System.out.println("具体产品B2显示...");
    }
}

  1. 抽象工厂模式-适用于:
    (1)一个系统要独立于它的创建、组合和表示时
    (2)一个系统要由多个产品系列中的一个来配置时;
    (3)当要强调一系列相关的产品对象的设计以便进行联合使用时;
    (4)当提供一个产品类库,只想显示它们的接口而不是实现时;
    (5)比如,为图形用户界面组件定义不同平台的并行类层次结构,适合采用 抽象工厂 模式。

  2. 缺点:扩展性较差,增加新的产品等级结构(即新增产品种类)需要修改抽象工厂的接口以及所有具体工厂的实现。因此,抽象工厂模式更适合用于产品族固定、但需要支持多种产品变体的情况。

【3】生成器模式(Builder)

  1. 将一个复杂对象的构建和它的表示分离,使得同样的构建过程可以创建不同的表示
  2. 主要角色:指挥者(Director)、抽象生成器(Builder)、具体生成器、产品。
  3. 结构图

image.png

  1. 示例代码
import java.util.*;

class BuilderPatternExample {
    public static void main(String[] args) {
        Director director = new Director();

        Builder builder1 = new ConcreteBuilder1();
        director.construct(builder1);
        Product product1 = builder1.getResult();
        product1.show(); // 该产品包含以下部件:[部件A, 部件B, 部件C, 部件D, 部件E, 部件F]

        Builder builder2 = new ConcreteBuilder2();
        director.construct(builder2);
        Product product2 = builder2.getResult();
        product2.show(); // 该产品包含以下部件:[部件X, 部件Y, 部件Z]
    }
}

// 指挥者(Director):负责【使用 Builder 接口】来构建复杂对象。它并不知道具体产品的细节,只知道构建过程。
class Director {
    public void construct(Builder builder) {
        builder.buildPart();
    }
}

// 抽象生成器(Builder):定义了创建产品各个部分的抽象方法,通常包括创建产品的每个部分的操作。
abstract class Builder {
    public abstract void buildPart();

    public abstract Product getResult();
}

// 具体生成器(ConcreteBuilder):实现了抽象生成器接口,负责具体产品各个部分的创建,以及组装这些部分来构建完整的产品。
class ConcreteBuilder1 extends Builder {
    private Product product = new Product();

    @Override
    public void buildPart() {
        product.add("部件A");
        product.add("部件B");
        product.add("部件C");
        product.add("部件D");
        product.add("部件E");
        product.add("部件F");
    }

    @Override
    public Product getResult() {
        return product;
    }
}

class ConcreteBuilder2 extends Builder {
    private Product product = new Product();

    @Override
    public void buildPart() {
        product.add("部件X");
        product.add("部件Y");
        product.add("部件Z");
    }

    @Override
    public Product getResult() {
        return product;
    }
}

// 产品(Product):表示被构建的复杂对象,包含多个部分。
class Product {
    private List<String> parts = new ArrayList<>();

    public void add(String part) {
        parts.add(part);
    }

    public void show() {
        System.out.println("该产品包含以下部件:" + Arrays.toString(parts.toArray()));
    }
}

  1. 生成器模式-适用于:
    (1)当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时;
    (2)当构造过程必须允许被构造对象有不同的表示时。

  2. 优点:可以避免使用多个构造函数或者过多的参数来创建复杂对象,提高了代码的可读性。

【4】原型模式(Prototype)

  1. 用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。
  2. 主要角色:原型接口、具体原型类、客户端。
  3. 结构图 image.png
  4. 示例代码
class PrototypePatternExample {
    public static void main(String[] args) {
        Product product = new Product(1, 100);

        // Client:让一个原型复制自身,从而创建一个新的对象 【 3.调用 clone 】
        Product productClone = (Product) product.clone();
        System.out.println(productClone.getId()); // 1
        System.out.println(productClone.getPrice()); // 100.0
    }
}

// Prototype:声明一个复制自身的接口 【 1.定义 clone 】
interface Prototype {
    public Prototype clone();
}

// ConcretePrototype:实现一个复制自身的操作
class Product implements Prototype {
    private int id;
    private double price;

    // 无参构造函数
    public Product() {
    }

    // 有参构造函数
    public Product(int id, double price) {
        this.id = id;
        this.price = price;
    }

    public int getId() {
        return this.id;
    }

    public double getPrice() {
        return this.price;
    }

    // 实现一个复制自身的操作 【 2.实现 clone 】
    @Override
    public Prototype clone() {
        return new Product(this.id, this.price);
    }
}

  1. 原型模式-适用于:
    (1)当一个系统应该独立于它的产品的创建、构成和表示时;
    (2)当要实例化的类是在运行时刻指定时,例如,通过动态装载;
    (3)为了避免创建一个与产品类层次平行的工厂类层次时;
    (4)当一个类的实例只能有几个不同的状态组合中的一种时。建立相应数目的原型并克隆它们,可能比每次用合适的状态手工实例化该类更方便一些。

  2. 优点:当创建对象的过程比较复杂、耗时或者需要频繁创建对象时,可以使用原型模式。通过复制已有对象来创建新的对象,避免了重复的初始化过程,提高了性能。

【5】单例模式(Singleton)

  1. 保证一个类只有一个实例,并提供一个访问它的全局访问点。
  2. 结构图

image.png

  1. 示例代码
class SingletonPatternExample {
    public static void main(String[] args) {
        Singleton s1 = Singleton.getInstance();
        Singleton s2 = Singleton.getInstance();

        if (s1 == s2) {
            System.out.println("s1 and s2 are the same instance"); // s1 and s2 are the same instance
        } else {
            System.out.println("s1 and s2 are different instances");
        }
    }
}

class Singleton {
    // 保证类只有一个实例
    private static Singleton instance = new Singleton();

    // 限制外部不能通过 new 创建实例
    private Singleton() {
    }

    // 提供一个访问它的全局访问点
    public static Singleton getInstance() {
        return instance;
    }
}
  1. 单例模式-适用于:
    (1)当类只有一个实例,而且客户可以从一个众所周知的访问点访问它时;
    (2)当这个唯一实例应该是通过子类化可扩展的,并且客户无须改变代码就能使用一个扩展的实例时。

三、结构型(7 种)

涉及:如何组合类和对象以获得更大的结构?使用继承机制类组合接口和实现。有助于多个独立开发的类库协同工作。

【1】适配器模式(Adapter)

  1. 将一个类的接口转换成客户希望的另一个接口。使得原本由于接口不兼容而不能一起工作的那些类可以一起工作

  2. 结构图
    类适配器:使用多重继承对一个接口与另一个接口进行匹配。

    image.png

    对象适配器:依赖于对象组合。

    image.png

  3. 主要角色:目标接口(Target)、适配器(Adapter)、被适配者(Adaptee)

  4. 示例代码

class AdapterPatternExample {
    public static void main(String[] args) {
        Adaptee adaptee = new Adaptee();
        Target adapter = new Adapter(adaptee);  // △这里的类型是Target,没是 Adapter
        adapter.request();
    }
}

// 目标接口
interface Target {
    public void request();
}

// 被适配者
class Adaptee {
    public void specificRequest() {
        System.out.println("specificRequest");
    }
}

// 适配器
class Adapter implements Target {
    private Adaptee adaptee;  // △定义了但是还没有实例化

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

    @Override
    public void request() {
        adaptee.specificRequest();
    }
}
  1. 适配器模式-适用于:
    (1)想使用一个已经存在的类,而它的接口不符合要求时;
    (2)创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作。
    (3)(仅适用于对象 Adapter) 想使用一个已经存在的子类,但是不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配它的父类接口。

【2】桥接模式(Bridge)

  1. 将抽象部分与其实现部分分离,使它们可以独立地变化。
  2. 主要角色:抽象化(Abstraction)、实现化(Implementor)、具体抽象化(Refined Abstraction)、具体实现化(Concrete Implementor)。
  3. 结构图

image.png

  1. 示例代码
    示例1
class BridgePatternExample {
    public static void main(String[] args) {
        Implementor implementorA = new ConcreteImplementorA();
        Abstraction abstractionA = new RefinedAbstractionA(implementorA);
        abstractionA.operation(); // ConcreteImplementorA operationImpl

        Implementor implementorB = new ConcreteImplementorB();
        Abstraction abstractionB = new RefinedAbstractionB(implementorB);
        abstractionB.operation(); // ConcreteImplementorB operationImpl
    }
}

// 抽象化:定义[抽象部分]的接口,并维护一个指向[实现部分]对象的引用。通常抽象化角色会包含【高层业务逻辑】
abstract class Abstraction {
    protected Implementor implementor;

    public Abstraction(Implementor implementor) {
        this.implementor = implementor;
    }

    public abstract void operation();
}

// 具体抽象化:对抽象化角色进行扩展,可以实现抽象部分中的不同变化。它拥有一个具体的实现化对象。
class RefinedAbstractionA extends Abstraction {
    public RefinedAbstractionA(Implementor implementor) {
        super(implementor);
    }

    // 抽象化角色持有实现化角色的引用 implementor,通过委派实现化角色完成具体的实现
    public void operation() {
        implementor.operationImpl();
    }
}

class RefinedAbstractionB extends Abstraction {
    public RefinedAbstractionB(Implementor implementor) {
        super(implementor);
    }

    public void operation() {
        implementor.operationImpl();
    }
}

// 实现化:定义[实现部分]的接口,供具体实现类实现。实现化角色通常只关注【底层实现】细节
interface Implementor {
    public void operationImpl();
}

// 具体实现化:实现实现化角色的接口,并提供具体的实现细节
class ConcreteImplementorA implements Implementor {
    public void operationImpl() {
        System.out.println("ConcreteImplementorA operationImpl");
    }
}

class ConcreteImplementorB implements Implementor {
    public void operationImpl() {
        System.out.println("ConcreteImplementorB operationImpl");
    }
}

示例2

public class BridgePatternExample {
    public static void main(String[] args) {
        Shape skyBlueCircle = new CircleShape(100, 100, 50, new DrawingAPIA());
        skyBlueCircle.draw();

        Shape yellowCircle = new CircleShape(200, 200, 80, new DrawingAPIB());
        yellowCircle.draw();
    }
}

// 实现化角色接口
interface DrawingAPI {
    void drawCircle(double x, double y, double radius);
}

// 具体实现化角色A
class DrawingAPIA implements DrawingAPI {
    @Override
    public void drawCircle(double x, double y, double radius) {
        System.out.printf("使用DrawingAPIA绘制圆:中心点(%.1f, %.1f),半径 %.1f%n", x, y, radius);
    }
}

// 具体实现化角色B
class DrawingAPIB implements DrawingAPI {
    @Override
    public void drawCircle(double x, double y, double radius) {
        System.out.printf("使用DrawingAPIB绘制圆:中心点(%.1f, %.1f),半径 %.1f%n", x, y, radius);
    }
}

// 抽象化角色
abstract class Shape {
    protected DrawingAPI drawingAPI;

    public Shape(DrawingAPI drawingAPI) {
        this.drawingAPI = drawingAPI;
    }

    public abstract void draw();
}

// 具体抽象化角色:圆形
class CircleShape extends Shape {
    private double x, y, radius;

    public CircleShape(double x, double y, double radius, DrawingAPI drawingAPI) {
        super(drawingAPI);
        this.x = x;
        this.y = y;
        this.radius = radius;
    }

    @Override
    public void draw() {
        drawingAPI.drawCircle(x, y, radius);
    }
}

  1. 桥接模式-适用于:
    (1)不希望在抽象和它的实现部分之间有一个固定绑定关系时;
    (2)在许多类要生成的类层次结构时。

  2. 总结
    (1)在桥接模式中,抽象化和实现化是两个独立的类层级结构,抽象部分包含高层业务逻辑,而实现部分则包含底层具体实现,它们通过桥接接口进行关联。
    (2)抽象化角色持有实现化角色的引用,通过委派实现化角色完成具体的实现
    (3)这种组合关系让抽象部分和实现部分能够独立地变化和扩展。这种灵活性和可扩展性使得系统更加稳定和易于维护。

【3】组合模式(Composite)

  1. 将对象组合成树型结构以表示部分-整体的层次结构。使得用户对单个对象和组合对象的使用具有一致性
  2. 主要角色:抽象构件(Component)、叶子节点(Leaf)、容器节点(Composite)。
  3. 结构图

image.png

4.示例代码

比如文件树,文件夹和文件都是文件树的节点,文件夹下可以有文件夹和文件,文件夹相当于 Composite,文件相当于 Leaf。

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

class CompositePatternExample {
    public static void main(String[] args) {
        Component rootFolder = new Composite("文件树");

        Component file1 = new Leaf(10, "文件1.txt");
        Component file2 = new Leaf(20, "文件2.txt");
        rootFolder.addComponent(file1);
        rootFolder.addComponent(file2);

        Component subFolder = new Composite("文件夹");
        Component file3 = new Leaf(30, "文件3.txt");
        Component file4 = new Leaf(40, "文件4.txt");
        subFolder.addComponent(file3);
        subFolder.addComponent(file4);
        rootFolder.addComponent(subFolder);

        // 递归遍历-打印整个文件树
        rootFolder.print();
    }
}

// 抽象构件:定义了叶子节点和容器节点的共同接口,可以是抽象类或接口。它声明了组合对象的公共操作方法,包括操作自身以及管理子节点的方法。
// 单个对象和组合对象都实现了这个接口,这样就能对组合对象和单个对象进行一致处理。
abstract class Component {
    public abstract void addComponent(Component com);

    public abstract void removeComponent(Component com);

    public abstract Component getComponent(int index);

    public abstract void print();
}

// 叶子节点(比如文件):其下再无其它子节点,是组合结构的最小单位。
class Leaf extends Component {
    int size;
    String name;

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

    public void addComponent(Component com) {
        System.out.println("文件Leaf没有子节点");
    }

    public void removeComponent(Component com) {
        System.out.println("文件Leaf没有子节点");
    }

    public Component getComponent(int index) {
        System.out.println("文件Leaf没有子节点");
        return null;
    }

    public void print() {
        System.out.println("文件: " + name + " 大小: " + size + "KB");
    }

}

// 容器节点(比如文件夹):是组合结构中的容器对象,它包含了一个或多个子节点。实现了抽象构件接口,并具备管理子节点的能力,包括:添加子节点、移除子节点和获取子节点等方法。
class Composite extends Component {
    String name;
    List<Component> components = new ArrayList<>();

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

    public void addComponent(Component com) {
        components.add(com);
    }

    public void removeComponent(Component com) {
        components.remove(com);
    }

    public Component getComponent(int index) {
        return components.get(index);
    }

    public void print() {
        System.out.println("文件夹: " + name);
        for (Component com : components) {
            com.print();
        }
    }

}
  1. 组合模式-适用于:
    (1)想表示对象的部分-整体层次结构;
    (2)希望用户忽略单个对象与组合对象的不同,用户将统一地使用组合结构中的所有对象。

【4】装饰器模式(Decorator)

  1. 动态地给对象添加一些额外的职责,就增加功能而言,Decorator 比生成子类更加灵活。
  2. 主要角色:抽象构件、具体构件、抽象装饰器、具体装饰器。
  3. 结构图

image.png

  1. 示例代码
class DecoratorPatternExample {
    public static void main(String[] args) {
        // 写法1
        Message message1 = new TextMessage();
        message1 = new EncryptedMessageDecorator(message1);
        message1.send();

        // 同写法1
        Message message2 = new TextMessage();
        message2 = new EncryptedMessageDecorator(message2);
        message2 = new CompressedMessageDecorator(message2);
        message2.send();

        // 写法2
        Message message3 = new TextMessage();
        Message encryptedMessage = new EncryptedMessageDecorator(message3);
        Message compressedAndEncryptedMessage = new CompressedMessageDecorator(encryptedMessage);
        compressedAndEncryptedMessage.send();

        // 写法3
        Message message4 = new CompressedMessageDecorator(new EncryptedMessageDecorator(new TextMessage()));
        message4.send();
    }
}

// 抽象构件 (Component)
interface Message {
    void send();
}

// 具体构件 (ConcreteComponent)
class TextMessage implements Message {
    @Override
    public void send() {
        System.out.println("发送普通文本消息");
    }
}

// 抽象装饰器 (Director):维持一个指向构件对象的指针,并定义一个与抽象构件接口一致的接口
abstract class MessageDecorator implements Message {
    private Message decoratedMessage;

    public MessageDecorator(Message decoratedMessage) {
        this.decoratedMessage = decoratedMessage;
    }

    @Override
    public void send() {
        decoratedMessage.send(); // △ 当前示例中 Message 只有一个方法 send
    }
}

// 具体装饰器 (ConcreteDirector):负责给构件对象添加附加的功能
class EncryptedMessageDecorator extends MessageDecorator {
    public EncryptedMessageDecorator(Message decoratedMessage) {
        super(decoratedMessage);
    }

    @Override
    public void send() {
        super.send();
        encryptMessage();
    }

    private void encryptMessage() {
        System.out.println("附加发送加密消息");
    }
}

class CompressedMessageDecorator extends MessageDecorator {
    public CompressedMessageDecorator(Message decoratedMessage) {
        super(decoratedMessage);
    }

    @Override
    public void send() {
        super.send();
        compressMessage();
    }

    private void compressMessage() {
        System.out.println("附加发送压缩消息");
    }
}

  1. 装饰器模式-适用于:
    (1)在不影响其它对象的情况下,以动态、透明的方式给单个对象添加职责;
    (2)处理那些可以撤销的职责。
    (3)当不能采用生成子类的方式进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是,由于类定义被隐藏,或类定义不能用于生成子类。

【5】外观模式(Facade)

  1. 为子系统中的一组接口提供一个一致的界面,Facade 模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。(将一系列对象加以包装以简化其接口
  2. 主要角色:外观(Facade)、子系统(Subsystem)、客户端(Client)。
  3. 结构图

image.png 5. 示例代码

class FacadePatternExample {
    public static void main(String[] args) {
        // Client 通过 Facade 向子系统委派任务
        Facade facade = new Facade();
        facade.operate1();
        facade.operate2();
    }
}

// Facade:为多个子系统提供一个共同的接口,提供子系统的访问入口
class Facade {
    private SubSystem1 system1 = new SubSystem1();
    private SubSystem2 system2 = new SubSystem2();
    private SubSystem3 system3 = new SubSystem3();

    public void operate1() {
        system1.operate();
    }

    public void operate2() {
        system2.operate();
    }

    public void operate3() {
        system3.operate();
    }

    public void operateAll() {
        system1.operate();
        system2.operate();
        system3.operate();
    }
}

// 子系统:实现子系统的功能,处理由 Facade 指派的任务
class SubSystem1 {
    public void operate() {
        System.out.println("SubSystem1 operate");
    }
}

class SubSystem2 {
    public void operate() {
        System.out.println("SubSystem2 operate");
    }
}

class SubSystem3 {
    public void operate() {
        System.out.println("SubSystem3 operate");
    }
}

  1. 外观模式-适用于:
    (1)要为一个复杂的子系统提供一个简单接口时;
    (2)客户程序与抽象类的实现之间存在着很大的依赖时;
    (3)当需要构建一个层次结构的子系统时,使用 Facade 模式定义子系统中每层的入口点。
  2. 总结
    Facade 模式,通过创建一个外观类(也称为门面类),将客户端与子系统之间的复杂交互进行封装。客户端只需要与外观类进行交互,而不需要直接与子系统中的组件进行通信。外观类将客户端请求委派给适当的子系统对象,从而提供了简化的接口和更高层次的抽象。

【6】享元模式(Flyweight)

  1. 运用共亨技术有效地支持大量细粒度的对象。旨在通过共享对象,以减少对象数量来最小化内存使用和提高性能
  2. 主要角色:享元工厂、抽象享元类、具体享元类、客户端(Client)。
  3. 结构图

image.png 4. 示例代码

import java.util.HashMap;
import java.util.Map;

class FlyweightPatternExample {
    public static void main(String[] args) {
        // Client 维护对享元对象的引用,并根据需要传递外部状态

        FlyweightFactory factory = new FlyweightFactory();

        Flyweight flyweight1 = factory.getFlyweight("shared");
        flyweight1.operation("state 1");

        Flyweight flyweight2 = factory.getFlyweight("shared");
        flyweight2.operation("state 2");

        Flyweight flyweight3 = factory.getFlyweight("unique");
        flyweight3.operation("state 3");
    }
}

// 抽象享元接口:声明享元对象中需要外部状态的方法
interface Flyweight {
    void operation(String extrinsicState);
}

// 具体享元类:实现抽象享元接口,存储内部状态,并根据外部状态提供具体的业务逻辑
class ConcreteFlyweight implements Flyweight {
    private String intrinsicState;

    public ConcreteFlyweight(String intrinsicState) {
        this.intrinsicState = intrinsicState;
    }

    public void operation(String extrinsicState) {
        System.out.println("Intrinsic State: " + intrinsicState);
        System.out.println("Extrinsic State: " + extrinsicState);
    }
}

// 享元工厂:负责创建和管理享元对象。它维护一个享元池(Flyweight Pool)来存储已经创建的享元对象,并根据客户端请求返回相应的享元对象
class FlyweightFactory {
    private Map<String, Flyweight> flyweights = new HashMap<>();

    public Flyweight getFlyweight(String key) {
        if (flyweights.containsKey(key)) {
            return flyweights.get(key);
        } else {
            Flyweight flyweight = new ConcreteFlyweight(key);
            flyweights.put(key, flyweight);
            return flyweight;
        }
    }
}
  1. 享元模式-适用于:
    (1)一个应用程序使用了大量的对象时;
    (2)由于使用大量的对象,而造成很大的存储开销时;

【7】代理模式(Proxy)

  1. 为其它对象提供一种代理以控制对这个对象的访问。(通过提供与对象相同的接口,来控制对这个对象的访问。)
  2. 主要角色:抽象主题(Subject)、真实主题(RealSubject)、代理(Proxy)。
  3. 结构图

image.png 4. 示例代码

class ProxyPatternExample {
    public static void main(String[] args) {
        // Client 通过抽象主题接口来访问真实主题和代理,并不直接与真实主题交互
        Image image1 = new ProxyImage("image1.jpg");
        Image image2 = new ProxyImage("image2.jpg");

        image1.display(); // Loading image: image1.jpg and Displaying image: image1.jpg

        image2.display(); // Loading image: image2.jpg and Displaying image: image2.jpg

        // 图片1不会再次加载,而是直接从缓存中显示
        image1.display(); // Displaying image: image1.jpg
    }
}

// Subject:定义了真实主题和代理主题的共同接口,客户端通过该接口访问真实主题和代理主题
interface Image {
    void display();
}

// RealSubject:执行具体的业务逻辑
class RealImage implements Image {
    private String filename;

    public RealImage(String filename) {
        this.filename = filename;
        loadFromDisk();
    }

    private void loadFromDisk() {
        System.out.println("Loading image: " + filename);
    }

    public void display() {
        System.out.println("Displaying image: " + filename);
    }
}

// Proxy:实现了抽象主题接口,充当了真实主题的代理,负责控制和管理对真实主题对象的访问。代理可以对真实主题的访问进行增强、限制或优化
class ProxyImage implements Image {
    private String filename;
    private RealImage realImage;

    public ProxyImage(String filename) {
        this.filename = filename;
    }

    public void display() {
        if (realImage == null) {
            realImage = new RealImage(filename);
        }
        realImage.display();
    }
}
  1. 代理模式-适用于:
    在需要比较通用和复杂的对象指针代替简单的指针时。 在需要不改变原始对象的情况下,对其进行功能的扩展、限制或优化。常用场景: (1)远程代理(Remote Proxy):代理对象可以隐藏和封装原始对象的网络通信细节,使得客户端可以通过代理对象访问位于远程服务器上的对象。
    (2)虚拟代理(Virtual Proxy):代理对象充当了原始对象的占位符,延迟创建原始对象,直到真正需要时才会创建,可以提高系统性能。
    (3)安全代理(Protection Proxy):代理对象可以控制对原始对象的访问权限,限制客户端对某些敏感操作的调用。
    (4)智能代理(Smart Proxy):代理对象可以在调用原始对象之前或之后添加额外的逻辑,比如缓存结果、记录日志等。

四、行为型(11 种)

涉及算法和对象间职责的分配。使用继承机制在类之间分派行为。

【1】责任链模式(Chain of Responsibiility)

  1. 使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
  2. 它允许将请求沿着处理者链传递,直到有一个处理者能够处理该请求。每个处理者都可以选择将请求处理完毕或者将其传递给链中的下一个处理者。
  3. 主要角色:抽象处理者(Handler)、具体处理者(ConcreteHandler)、客户端(Client)。
  4. 结构图

image.png 5. 示例代码

class ChainOfResposibilityPatternExample {
    public static void main(String[] args) {
        // Client 向链上的具体处理者提交请求

        LeaveHandler director = new Director(); // 主管
        LeaveHandler manager = new Manager(); // 经理
        LeaveHandler hr = new HR(); // 人事

        director.setNextHandler(manager);
        manager.setNextHandler(hr);

        // 发送请假申请
        director.handleRequest(5); // Manager approves the leave.
        director.handleRequest(10); // HR approves the leave.
        director.handleRequest(20); // Leave application rejected.
    }
}

// 抽象处理者:定义一个处理请求的接口;如果需要,接口可以定义一个方法以设定和返回对下家的引用以实现后继链
abstract class LeaveHandler {
    protected LeaveHandler nextHandler;

    public void setNextHandler(LeaveHandler nextHandler) {
        this.nextHandler = nextHandler;
    }

    public abstract void handleRequest(int days);
}

// 具体处理者A:处理它所负责的请求,可访问它的后继者;如果可处理该请求,就处理之;否则将该请求转发给它的后继者
class Director extends LeaveHandler {
    public void handleRequest(int days) {
        if (days <= 3) {
            System.out.println("Director approves the leave.");
        } else if (nextHandler != null) {
            nextHandler.handleRequest(days);
        } else {
            System.out.println("Leave application rejected.");
        }
    }
}

// 具体处理者B
class Manager extends LeaveHandler {
    public void handleRequest(int days) {
        if (days <= 7) {
            System.out.println("Manager approves the leave.");
        } else if (nextHandler != null) {
            nextHandler.handleRequest(days);
        } else {
            System.out.println("Leave application rejected.");
        }
    }
}

// 具体处理者C
class HR extends LeaveHandler {
    public void handleRequest(int days) {
        if (days <= 15) {
            System.out.println("HR approves the leave.");
        } else {
            System.out.println("Leave application rejected.");
        }
    }
}
  1. 责任链模式-适用于:
    (1)有多个的对象可以处理一个请求,哪个对象处理该请求运行时刻自动确定;
    (2)想在不明确指定接收者的情况下向多个对象中的一个提交一个请求;
    (3)可处理一个请求的对象集合应被动态指定。

【2】命令模式(Command)

  1. 将一个请求封装为一个对象,从而使得可以用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。
  2. 旨在将请求与接收该请求的对象解耦,并通过将请求封装成一个对象来实现这种解耦。使得调用者只需关心执行命令的方式,而不需要关心具体的命令执行细节。
  3. 主要角色:命令(Command)、具体命令(ConcreteCommand)、调用者(Invoker)、接收者(Receiver)、客户端(Client)。
  4. 结构图

image.png 5. 示例代码
示例1:

class CommandPatternExample {
    public static void main(String[] args) {
        Receiver receiver = new Receiver();
        Command command = new ConcreteCommand(receiver);

        Invoker invoker = new Invoker();
        invoker.setCommand(command);
        invoker.executeCommand();
    }
}

// 命令:定义了执行操作的接口,通常包含一个执行操作的方法
interface Command {
    void execute();
}

// 具体命令:负责将请求传递给接收者,并调用接收者的相应方法来执行具体的操作
class ConcreteCommand implements Command {
    private Receiver receiver;

    public ConcreteCommand(Receiver receiver) {
        this.receiver = receiver;
    }

    @Override
    public void execute() {
        receiver.action();
    }
}

// 调用者:负责调用命令对象来执行请求。调用者并不知道具体的操作细节,它只知道如何调用命令对象来执行请求
class Invoker {
    private Command command;

    public void setCommand(Command command) {
        this.command = command;
    }

    public void executeCommand() {
        System.out.println("调用者发布了命令");
        command.execute();
    }
}

// 接收者:负责请求的具体实施和执行
class Receiver {
    public void action() {
        System.out.println("接收者执行和实施了请求");
    }
}

示例2: 遥控器、电视、开机、关机

class CommandPatternExample2 {
    public static void main(String[] args) {
        Television tv = new Television();

        Command turnOnCommand = new TurnOnCommand(tv);
        Command turnOffCommand = new TurnOffCommand(tv);

        RemoteControl remoteControl = new RemoteControl(turnOnCommand, turnOffCommand);

        remoteControl.pressOnButton();
        remoteControl.pressOffButton();
    }
}

// 命令接口
interface Command {
    void execute();
}

// 具体命令 - 开机
class TurnOnCommand implements Command {
    private Television tv;

    public TurnOnCommand(Television tv) {
        this.tv = tv;
    }

    @Override
    public void execute() {
        tv.turnOn();
    }
}

// 具体命令 - 关机
class TurnOffCommand implements Command {
    private Television tv;

    public TurnOffCommand(Television tv) {
        this.tv = tv;
    }

    @Override
    public void execute() {
        tv.turnOff();
    }
}

// 接收者 - 电视
class Television {
    public void turnOn() {
        System.out.println("电视已经打开");
    }

    public void turnOff() {
        System.out.println("电视已经关闭");
    }
}

// 调用者 - 遥控器
class RemoteControl {
    private Command onCommand;
    private Command offCommand;

    public RemoteControl(Command onCommand, Command offCommand) {
        this.onCommand = onCommand;
        this.offCommand = offCommand;
    }

    public void pressOnButton() {
        onCommand.execute();
    }

    public void pressOffButton() {
        offCommand.execute();
    }
}
  1. 命令模式-适用于:
    (1)抽象出待执行的动作以参数化某对象;
    (2)在不同的时刻指定、排列和执行请求一个Command对象可以有一个与初始请求无关的生存期;
    (3)支持取消操作。Command 的 execute 操作可在实施操作前将状态存储起来,在取消操作时这个状态用来消除该操作的影响;
    (4)支持修改日志。这样当系统崩溃时,这些修改可以被重做一遍;
    (5)用构建在原语操作上的高层操作构造一个系统。

【3】解释器模式(Interpreter)

  1. 给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
  2. 主要角色:抽象表达式(Abstract Expression)、终结符表达式(Terminal Expression)、非终结符表达式(Nonterminal Expression)、客户端(Client)。
  3. 结构图

image.png 4. 示例代码

class InterpreterPatternExample {
    public static void main(String[] args) {
        // 1 + 2 - 3 * 4
        Expression expression = new SubtractExpression(
                new AddExpression(
                        new NumberExpression(1),
                        new NumberExpression(2)),
                new MultiplyExpression(
                        new NumberExpression(3),
                        new NumberExpression(4)));

        System.out.println(expression.interpret());
    }
}

// 表达式接口 (声明了解释器的抽象方法,这个接口为抽象语法树中所有的结点所共享)
interface Expression {
    int interpret();
}

// 终结符表达式 (负责解释终结符表达式) - 数字
class NumberExpression implements Expression {
    private int number;

    public NumberExpression(int number) {
        this.number = number;
    }

    @Override
    public int interpret() {
        return number;
    }
}

// 非终结符表达式 (通常包含终结符表达式和其他非终结符表达式作为子表达式) - 加法
class AddExpression implements Expression {
    private Expression left;
    private Expression right;

    public AddExpression(Expression left, Expression right) {
        this.left = left;
        this.right = right;
    }

    @Override
    public int interpret() {
        return left.interpret() + right.interpret();
    }
}

// 非终结符表达式 - 减法
class SubtractExpression implements Expression {
    private Expression left;
    private Expression right;

    public SubtractExpression(Expression left, Expression right) {
        this.left = left;
        this.right = right;
    }

    @Override
    public int interpret() {
        return left.interpret() - right.interpret();
    }
}

// 非终结符表达式 - 乘法
class MultiplyExpression implements Expression {
    private Expression left;
    private Expression right;

    public MultiplyExpression(Expression left, Expression right) {
        this.left = left;
        this.right = right;
    }

    @Override
    public int interpret() {
        return left.interpret() * right.interpret();
    }
}

  1. 解释器模式-适用于:
    当有一个语言需要解释执行,并且可将该语言中的句子表示为一个抽象语法树时,以下情况效果最好:
    (1)该文法简单时;
    (2)效率不是一个关键问题时。

【4】迭代器模式(Iterator)

  1. 提供一种方法顺序访问一个聚合对象中的各个元素,且不需要暴露该对象的内部表示
  2. 主要角色:迭代器(Iterator)、具体迭代器(Concrete Iterator)、聚合对象(Aggregate)、具体聚合对象(Concrete Aggregate)。
  3. 结构图

image.png 4. 示例代码

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

class test {
    public static void main(String[] args) {
        ListAggregate listAggregate = new ListAggregate();
        listAggregate.add("A");
        listAggregate.add("B");
        listAggregate.add("C");

        Iterator iterator = listAggregate.createIterator();
        while (iterator.hasNext()) {
            Object item = iterator.next();
            System.out.println("Item: " + item); // Item: A Item: B Item: C
        }
    }
}

// 迭代器:定义访问和遍历元素的接口
interface Iterator {
    boolean hasNext();

    Object first();

    Object next();

    boolean isDone();

    Object currentItem();

}

// 聚合对象:定义创建相应迭代器对象的接口
interface Aggregate {
    Iterator createIterator();
}

// 具体迭代器:实现迭代器接口,对该聚合遍历时跟踪当前位置
class ListIterator implements Iterator {
    private List<Object> list;
    private int index;

    public ListIterator(List<Object> list) {
        this.list = list;
        this.index = 0;
    }

    @Override
    public boolean hasNext() {
        return index < list.size();
    }

    @Override
    public Object first() {
        return list.get(0);
    }

    @Override
    public Object next() {
        if (hasNext()) {
            return list.get(index++);
        } else {
            throw new NoSuchElementException();
        }
    }

    @Override
    public boolean isDone() {
        return index >= list.size();
    }

    @Override
    public Object currentItem() {
        return list.get(index);
    }
}

// 具体聚合对象:实现创建相应迭代器对象的接口,该操作返回 ConcreteIterator 的一个适当的实例
class ListAggregate implements Aggregate {
    private List<Object> list;

    public ListAggregate() {
        this.list = new ArrayList<>();
    }

    public void add(Object item) {
        list.add(item);
    }

    @Override
    public Iterator createIterator() {
        return new ListIterator(list);
    }
}
  1. 迭代器模式-适用于:
    (1)访问一个聚合对象的内容而无须暴露它的内部表示时;
    (2)支持对聚合对象的多种遍历时;
    (3)为遍历不同的聚合结构提供一个统一的接口。

【5】中介者模式(Mediator)

  1. 用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
  2. 主要角色:中介者(Mediator)、具体中介者(Concrete Mediator)、同事对象(Colleague)、具体同事对象(Concrete Colleague)。
  3. 结构图

image.png 4. 示例代码
假设有一个简单的聊天室程序,其中有多个用户对象,它们之间需要进行互相发送消息的功能。我们可以使用中介者模式来实现此聊天室程序。

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

class MediatorPatternExample {
    public static void main(String[] args) {

        ChatMediator chatMediator = new ChatMediator();

        User user1 = new User(chatMediator, "User1");
        User user2 = new User(chatMediator, "User2");
        User user3 = new User(chatMediator, "User3");

        chatMediator.register(user1);
        chatMediator.register(user2);
        chatMediator.register(user3);

        user1.send("Hello everyone!");
        // Output:
        // User1 sends: Hello everyone!
        // User2 receives: Hello everyone!
        // User3 receives: Hello everyone!
    }
}

// 中介者接口:定义了对象之间的交互通信接口,通常包括注册对象、发送消息等方法
interface Mediator {
    void register(Colleague colleague);

    void sendMessage(String message, Colleague sender);
}

// 同事对象接口:每个对象都是同事对象,它们之间通过中介者进行通信
interface Colleague {
    void send(String message);

    void receive(String message);
}

// 具体中介者:实现了中介者接口,负责协调对象之间的交互
class ChatMediator implements Mediator {
    private List<Colleague> colleagues;

    public ChatMediator() {
        this.colleagues = new ArrayList<>();
    }

    @Override
    public void register(Colleague colleague) {
        colleagues.add(colleague);
    }

    @Override
    public void sendMessage(String message, Colleague sender) {
        for (Colleague colleague : colleagues) {
            if (colleague != sender) {
                colleague.receive(message);
            }
        }
    }
}

// 具体同事对象:每个对象都是同事对象,它们之间通过中介者进行通信
class User implements Colleague {
    private Mediator mediator;
    private String name;

    public User(Mediator mediator, String name) {
        this.mediator = mediator;
        this.name = name;
    }

    @Override
    public void send(String message) {
        System.out.println(name + " sends: " + message);
        mediator.sendMessage(message, this);
    }

    @Override
    public void receive(String message) {
        System.out.println(name + " receives: " + message);
    }
}
  1. 中介者模式-适用于:
    (1)一个对象引用其他很多对象并且直接与这些对象通信,导致难以复用该对象时;
    (2)一组对象以定义良好但是复杂的方式进行通信,产生的相互依赖关系结构混乱且难以理解时;
    (3)减少多个对象或类之间的通信复杂性。

【6】备忘录模式(Memento)

  1. 在不破坏封装性的前提下捕获一个对象的内部状态,并在对象之外保存这个状态。这样以后就可以将对象恢复到原先保存的状态。
  2. 主要角色:发起人(Originator)、备忘录(Memento)、管理者(Caretaker)。
  3. 结构图

image.png 4. 示例代码

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

class MementoPatternExample {
    public static void main(String[] args) {
        Originator textEditor = new Originator();
        Caretaker caretaker = new Caretaker();

        textEditor.setText("Hello, World!"); // 设置初始文本

        caretaker.saveMemento(textEditor.save()); // 保存初始状态

        textEditor.setText("Hello, GPT-3.5!"); // 修改文本
        System.out.println(textEditor.getText()); // 输出:Hello, GPT-3.5!

        textEditor.restore(caretaker.getMemento()); // 恢复到初始状态
        System.out.println(textEditor.getText()); // 输出:Hello, World!
    }
}

// 管理者 Caretaker(备忘录管理):负责保存和管理备忘录对象,不会对备忘录进行操作或检查
class Caretaker {
    private Memento memento;

    public void saveMemento(Memento memento) {
        this.memento = memento;
    }

    public Memento getMemento() {
        return memento;
    }
}

// 备忘录 Memento(比如文本编辑器的状态):存储发起人对象的内部状态。发起人根据需要决定备忘录存储发起人的哪些内部状态。防止发起人以外的其它对象访问备忘录
class Memento {
    private String text;

    public Memento(String text) {
        this.text = text;
    }

    public String getText() {
        return text;
    }
}

// 发起人 Originator(比如文本编辑器):负责创建一个备忘录对象,并将其状态保存到备忘录中
class Originator {
    private String text;

    public void setText(String text) {
        this.text = text;
    }

    public String getText() {
        return text;
    }

    public Memento save() {
        return new Memento(text);
    }

    public void restore(Memento memento) {
        this.text = memento.getText();
    }
}

  1. 备忘录模式-适用于:
    (1)必须保存一个对象在某一个时刻的(部分)状态,这样以后需要时它才能恢复到先前的状态;
    (2)如果一个用接口来让其它对象直接得到这些状态,将会暴露对象的实现细节并破坏对象的封装性。

【7】观察者模式(Observer)

  1. 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。(将主题对象和观察者对象解耦,使所要交互的对象尽量松耦合
  2. 主要角色:抽象主题(Subject)、抽象观察者(Observer)、具体主题(Concrete Subject)、具体观察者(Concrete Observer)。
  3. 结构图

image.png 4. 示例代码
比如:订阅者订阅最新的新闻

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

class ObserverPatternExample {
    public static void main(String[] args) {

        Newsletter newsletter = new Newsletter();

        Subscriber subscriber1 = new Subscriber("Alice");
        newsletter.registerObserver(subscriber1);

        Subscriber subscriber2 = new Subscriber("Bob");
        newsletter.registerObserver(subscriber2);

        newsletter.setNews("Today's news: AI assistant reached a new milestone!");

        newsletter.removeObserver(subscriber2);

        newsletter.setNews("Breaking news: Scientists discovered a new planet!");

        // Output:
        // Alice received the latest news: Today's news: AI assistant reached a new milestone!
        // Bob received the latest news: Today's news: AI assistant reached a new milestone!
        
        // Alice received the latest news: Breaking news: Scientists discovered a new planet!
    }
}

// 目标:【Subject 知道它的观察者】。可以有任意多个观察者观察同一个目标。提供注册和删除观察者对象的接口。
interface Subject {
    void registerObserver(Observer observer);

    void removeObserver(Observer observer);

    void notifyObservers();
}

// 观察者:为那些在目标发生改变时需要获得通知的对象定义一个更新接口。
interface Observer {
    void update(String news);
}

// 具体目标(ConcreteSubject):将有关状态存入各 ConcreteObserver 对象。当它的状态发生改变时,【ConcreteSubject 向它的各个观察者发出通知】。
class Newsletter implements Subject {
    private List<Observer> observers = new ArrayList<>();
    private String news;

    public void setNews(String news) {
        this.news = news;
        notifyObservers();
    }

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

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

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

// 具体观察者:维护一个指向 ConcreteSubject 对象的引用。存储有关状态,这些状态应与目标的状态保持一致。实现 Observer 的更新接口以使自身状态与目标的状态保持一致。
class Subscriber implements Observer {
    private String name;

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

    @Override
    public void update(String news) {
        System.out.println(name + " received the latest news: " + news);
    }
}
  1. 观察者模式-适用于:
    (1)当一个抽象模型有两个方面,其中一个方面依赖于另一个方面,将这两者封装在独立的对象中以使它们可以各自独立地改变和复用;
    (2)当对一个对象的改变需要同时改变其他对象,而不知道具体有多少对象有待改变时;
    (3)当一个对象必须通知其他对象,而它又不能假定其他对象是谁,即不希望这些对象是紧耦合的。

【8】状态模式(State)

  1. 允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。
  2. 主要角色:上下文环境(Context)、抽象状态(State)、具体状态(Concrete State)。
  3. 结构图

image.png 4. 示例代码

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

class StatePatternExample {
    public static void main(String[] args) {
        Context order = new Context();

        order.request(); // 输出:Order is being processed.
        order.request(); // 输出:Order has been processed and shipped.
        order.request(); // 输出:Order has already been shipped.
    }
}

// 上下文环境:定义客户端感兴趣的接口,维护一个具体状态类的实例,将与状态相关的操作委托给当前的具体状态对象来处理。
class Context {
    private State state;

    public Context() {
        state = new NewState();
    }

    public void setState(State state) {
        this.state = state;
    }

    public void request() {
        state.handle(this);
    }
}

// 抽象状态:定义一个接口以封装与 Context 的一个特定状态相关的行为。
interface State {
    void handle(Context order);
}

// 具体状态:每一个具体状态类都实现了上下文环境(Context)的一个状态所对应的行为。
class NewState implements State {
    @Override
    public void handle(Context order) {
        System.out.println("Context is being processed.");
        order.setState(new ProcessedState());
    }
}

class ProcessedState implements State {
    @Override
    public void handle(Context order) {
        System.out.println("Context has been processed and shipped.");
        order.setState(new ShippedState());
    }
}

class ShippedState implements State {
    @Override
    public void handle(Context order) {
        System.out.println("Context has already been shipped.");
    }
}
  1. 状态模式-适用于:
    (1)一个对象的行为决定于它的状态,并且它必须在运行时刻根据状态改变它的行为。
    (2)一个操作中含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态。这个状态常用一个或多个枚举常量表示。通常,有多个操作包含这一相同的条件结构。State模式将每一个条件分支放入一个独立的类中。这使得开发者可以根据对象自身的情况将对象的状态作为一个对象,这一对象可以不依赖于其他对象独立变化。

【9】策略模式(Strategy)

  1. 定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。此模式使得算法可以独立于使用它们的客户而变化。
  2. 主要角色:上下文环境(Context)、抽象策略(Strategy)、具体策略(Concrete Strategy)。
  3. 结构图

image.png 4. 示例代码

class StrategyPatternExample {
    public static void main(String[] args) {
        ScrollContext scrollContext = new ScrollContext();

        // 使用 NoopScrollStrategy
        scrollContext.setStrategy(new NoopScrollStrategy());
        scrollContext.performScroll(100); // 不产生实际的滚动操作

        // 使用 BlockScrollStrategy
        scrollContext.setStrategy(new BlockScrollStrategy());
        scrollContext.performScroll(200); // 模拟阻塞式滚动操作
    }
}

// 上下文环境:用一个 ConcreteStrategy 对象来配置;维护一个对 Strategy 对象的引用;可定义一个接口来让 Strategy 访问它的数据
class ScrollContext {
    private ScrollStrategy strategy;

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

    public void performScroll(int position) {
        strategy.scrollTo(position);
    }
}

// 策略接口:定义所有支持的算法的公共接口。Context 使用这个接口来调用某 ConcreteStrategy 定义的算法
interface ScrollStrategy {
    void scrollTo(int position);
}

// 具体策略:实现了策略接口的具体算法
class NoopScrollStrategy implements ScrollStrategy {
    @Override
    public void scrollTo(int position) {
        // Do nothing
    }
}

class BlockScrollStrategy implements ScrollStrategy {
    @Override
    public void scrollTo(int position) {
        System.out.println("Blocking scrolling...");
        try {
            Thread.sleep(2000); // 模拟阻塞滚动操作
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Scrolling to position: " + position);
    }
}

  1. 策略模式-适用于:
    (1)许多相关的类仅仅是行为有异。“策略”提供了一种用多个行为中的一个行为来配置一个类的方法。
    (2)需要使用一个算法的不同变体。例如,定义一些反映不同空间的空间/时间权衡的算法。当这些变体实现为一个算法的类层次时,可以使用策略模式。 (3)算法使用客户不应该知道的数据。可使用策略模式以避免暴露复杂的、与算法相关的数据结构。 (4)一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现,将相关的条件分支移入它们各自的 Strategy 类中,以代替这些条件语句。
  2. 示例:某旅游公司欲开发一套软件系统,要求能根据季节、节假日等推出不同的旅行定价包,如淡季打折、一口价等。“淡季打折”、“一口价”均属于不同的算法。

【10】模板方法模式(Template Method)

  1. 定义一个操作中的算法骨架,而将一些步骤延迟到子类中。Template Method 使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
  2. 主要角色:抽象类、具体类。
  3. 结构图

image.png 4. 示例代码

class TemplateMethodPatternExample {
    public static void main(String[] args) {
        CoffeeMaker espressoMaker = new EspressoMaker();
        espressoMaker.makeCoffee();

        System.out.println();

        CoffeeMaker cappuccinoMaker = new CappuccinoMaker();
        cappuccinoMaker.makeCoffee();
    }
}

// Abstract
// class:定义抽象的原语操作,具体的子类将重定义它们以实现一个算法的各步骤;实现模板方法,该模板方法不仅调用原语操作,也调用定义在抽象类或其它对象中的其他操作。
abstract class CoffeeMaker {
    // 制作咖啡的算法骨架
    public final void makeCoffee() {
        boilWater();
        brewCoffee();
        pourIntoCup();
        addCondiments();
    }

    // 煮水
    public void boilWater() {
        System.out.println("Boiling water");
    }

    // 冲泡咖啡
    public abstract void brewCoffee();

    // 倒入杯中
    public void pourIntoCup() {
        System.out.println("Pouring into cup");
    }

    // 添加调料
    public abstract void addCondiments();
}

// Concrete class:实现原语操作以完成算法中与特定子类相关的步骤
class EspressoMaker extends CoffeeMaker {
    @Override
    public void brewCoffee() {
        System.out.println("Brewing espresso");
    }

    @Override
    public void addCondiments() {
        System.out.println("No condiments needed for espresso");
    }
}

class CappuccinoMaker extends CoffeeMaker {
    @Override
    public void brewCoffee() {
        System.out.println("Brewing cappuccino");
    }

    @Override
    public void addCondiments() {
        System.out.println("Adding milk and sugar for cappuccino");
    }
}
  1. 模板方法模式-适用于:
    (1)一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。
    (2)各子类中公共的行为应被提取出来并集中到一个公共父类中,以避免代码重复。
    (3)控制子类扩展。模板方法旨在特定点调用“hook”操作(默认的行为,子类可以在必要时进行重定义扩展),这就只允许在这些点进行扩展。

【11】访问者模式(Visitor)

  1. 用于将算法对象结构分离,表示一个作用于某对象结构中的各元素的操作。它允许在不改变各元素的类的前提下,定义作用于这些元素的新操作。
  2. 主要角色:访问者(Visitor)、具体访问者(Concrete Visitor)、元素(Element)、具体元素(Concrete Element)、对象结构(Object Structure)。
  3. 结构图

image.png 4. 示例代码

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

class VistorPatternExample {
    public static void main(String[] args) {
        // 往购物车里添加很多商品
        ShoppingCart shoppingCart = new ShoppingCart();
        shoppingCart.addItem(new Fruit("Apple"));
        shoppingCart.addItem(new Clothing("Shirt", "M"));
        shoppingCart.addItem(new Clothing("Jeans", "L"));

        // 客户(Vistor)是一个具体访问者
        Customer customer = new Customer();

        // 购物车(Object Structure)接收客户的访问
        shoppingCart.accept(customer);

        // Outtput:
        // Customer is buying Apple
        // Customer is buying Shirt M
        // Customer is buying Jeans L
    }
}

// 访问者接口:为该 ObjectStructure 中 ConcreteElement 的每一个类声明一个 visit 操作。该操作的名字和特征标识了发送 visit 请求给该访问者的那个类,这使得访问者可以确定正被访问元素的具体的类。这样访问者就可以通过该元素的特定接口直接访问它。
interface Visitor {
    // 重载
    void visit(Fruit fruit);

    void visit(Clothing clothing);
}

// 具体访问者:实现每个由 Visitor 声明的操作,每个操作实现本算法的一部分,而该算法片段乃是对应于结构中对象的类。ConcreteVisitor 为该算法提供了上下文并存储它的局部状态。这一状态常常在遍历该结构的过程中累积结果。
class Customer implements Visitor {
    @Override
    public void visit(Fruit fruit) {
        System.out.println("Customer is buying " + fruit.getName());
    }

    @Override
    public void visit(Clothing clothing) {
        System.out.println("Customer is buying " + clothing.getType() + " " + clothing.getSize());
    }
}

// 元素接口:定义以一个访问者为参数 accept 操作
interface ShopItem {
    void accept(Visitor visitor);
}

// 具体元素:实现以一个访问者为参数的 accept 操作
class Fruit implements ShopItem {
    private String name;

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

    public String getName() {
        return name;
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

// 具体元素
class Clothing implements ShopItem {
    private String type;
    private String size;

    public Clothing(String type, String size) {
        this.type = type;
        this.size = size;
    }

    public String getType() {
        return type;
    }

    public String getSize() {
        return size;
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

// 对象结构(Object Structure):能枚举它的元素,可以提供一个高层的接口以允许该访问者访问它的元素。可以是一个组合或是一个集合,如一个列表或一个无序集合。
class ShoppingCart {
    private List<ShopItem> items;

    public ShoppingCart() {
        items = new ArrayList<>();
    }

    public void addItem(ShopItem item) {
        items.add(item);
    }

    public void accept(Visitor visitor) {
        for (ShopItem item : items) {
            item.accept(visitor);
        }
    }
}
  1. 访问者模式-适用于:
    (1)一个对象结构包含很多类对象,它们有不同的接口,而用户想对这些对象实施一些依赖于其具体类的操作。
    (2)需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而又想要避免这些操作“污染”这些对象的类。Visitor 使得用户可以将相关的操作集中起来定义在一个类中。当该对象结构被很多应用共享时,用 Visitor 模式让每个应用仅包含需要用到的操作。
    (3)定义对象结构的类很少改变,但经常需要在此结构上定义新的操作。改变对象结构类需要重定义对所有访问者的接口,这可能需要很大的代价。如果对象结构类经常改变,那么可能还是在这些类中定义这些操作较好。

五、在 Angular 框架中的应用和体现

  1. 组合模式(Composite Pattern):Angular 的组件树结构可以看作是组合模式的应用,其中父组件包含子组件,并通过组件树形成层次结构。

  2. 依赖注入模式(Dependency Injection Pattern):Angular 使用依赖注入模式来管理组件、服务等之间的依赖关系。通过装饰器和注入器,Angular 可以自动解析和注入依赖项。

  3. 装饰器模式(Decorator Pattern):在 Angular 中,装饰器(如@Component 和 @Injectable)用于为类提供额外的元数据,从而改变其行为或添加额外的功能。

  4. 观察者模式(Observer Pattern):Angular 的事件机制使用了观察者模式,通过订阅和发布事件来实现组件之间的通信。

  5. 工厂方法模式(Factory Method Pattern):尽管 Angular 框架本身没有直接使用工厂方法模式,但通过依赖注入机制,可以实现类似于工厂方法模式的行为。

  6. 单例模式(Singleton Pattern):在 Angular 中,服务(Service)通常被设计为单例模式,即在整个应用中只有一个实例。这样做可以确保服务在不同组件之间共享数据和状态,并且避免了不必要的资源浪费。

  7. 策略模式(Strategy Pattern):在 Angular 中,可以使用策略模式来传递不同的行为或算法给组件或服务,在不修改其代码的情况下改变其行为。