常用的23种设计模式

设计模式

  1. 创建型模式(Creational Patterns)
    • 工厂方法模式(Factory Method Pattern):通过定义一个创建对象的接口,但是让子类决定实例化哪个类来创建对象。
    • 抽象工厂模式(Abstract Factory Pattern):提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
    • 单例模式(Singleton Pattern):确保一个类只有一个实例,并提供一个全局访问点。
    • 原型模式(Prototype Pattern):通过复制现有对象来创建新对象。
    • 建造者模式(Builder Pattern):将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
  2. 结构型模式(Structural Patterns)
    • 适配器模式(Adapter Pattern):将一个类的接口转换成客户希望的另一个接口。
    • 桥接模式(Bridge Pattern):将抽象部分与它的实现部分分离,使它们可以独立地变化。
    • 组合模式(Composite Pattern):将对象组合成树形结构以表示"部分-整体"的层次结构。
    • 装饰者模式(Decorator Pattern):动态地给一个对象添加一些额外的职责。
    • 外观模式(Facade Pattern):为子系统中的一组接口提供一个统一的接口。
    • 享元模式(Flyweight Pattern):运用共享技术有效地支持大量细粒度的对象。
  3. 行为型模式(Behavioral Patterns)
    • 责任链模式(Chain of Responsibility Pattern):为解除请求的发送者和接收者之间耦合而创建的一系列对象。
    • 命令模式(Command Pattern):将一个请求封装为一个对象,从而使你可以用不同的请求对客户进行参数化。
    • 解释器模式(Interpreter Pattern):给定一个语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。
    • 迭代器模式(Iterator Pattern):提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。
    • 中介者模式(Mediator Pattern):用一个中介对象来封装一系列的对象交互。
    • 备忘录模式(Memento Pattern):在不违反封装的情况下,捕获一个对象的内部状态,并在该对象之外保存这个状态。
    • 观察者模式(Observer Pattern):定义对象间的一种一对多的依赖关系,使得当一个对象状态改变时,所有依赖它的对象都会得到通知并自动更新。
    • 状态模式(State Pattern):允许一个对象在其内部状态改变时改变它的行为。
    • 策略模式(Strategy Pattern):定义一系列算法,将每个算法封装起来,并使它们可以互相替换。
    • 模板方法模式(Template Method Pattern):定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。
    • 访问者模式(Visitor Pattern):表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。

一、创建型模式(Creational Patterns)

1. 单例模式

​ 单例模式是创建型模式之一,它保证一个类只有一个实例,并提供一个全局访问点来访问该实例。在Java中,单例模式通常有两种实现方式:懒汉式和饿汉式。下面我们将介绍这两种实现方式。

  1. 懒汉式(Lazy Initialization) 懒汉式单例模式指的是在需要使用时才会创建实例,也就是延迟初始化。懒汉式单例模式的关键在于私有化构造函数和一个静态方法getInstance(),该方法通过延迟初始化来实现单例模式。
public class LazySingleton {

    private static LazySingleton instance;

    private LazySingleton() {}

    public static LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
}

懒汉式单例模式适用于以下一些业务场景:

资源消耗较大的对象:如果单例对象的创建和初始化过程比较耗费资源,例如数据库连接、文件操作等,可以考虑使用懒汉式单例模式。懒汉式单例模式 可以延迟对象的初始化,只有在需要使用时才进行初始化,从而减少了资源的消耗。

资源初始化需要延迟加载:有些情况下,单例对象的初始化可能需要延迟加载,例如在启动时不需要立即初始化,而是在第一次使用时才进行初始化。懒 汉式单例模式正好可以满足这种需求,只有在第一次调用获取单例对象的方法时才进行初始化。

避免不必要的初始化:有些情况下,单例对象可能永远不会被使用,或者在应用的生命周期中只会被间断性地使用。使用懒汉式单例模式可以避免在不必 要的情况下进行对象的初始化,从而节省资源。

线程安全性要求不高:懒汉式单例模式在多线程环境下可能存在线程安全性问题,但如果业务场景对线程安全性要求不高,或者可以通过加锁等手段解决 线程安全性问题,那么可以考虑使用懒汉式单例模式。

  1. 饿汉式(Eager Initialization)

饿汉式单例模式指的是在类加载时就创建实例,也就是立即初始化。饿汉式单例模式的关键在于私有化构造函数和一个静态变量,该变量在类加载时就被初始化为实例。

public class EagerSingleton {

    private static EagerSingleton instance = new EagerSingleton();

    private EagerSingleton() {}

    public static EagerSingleton getInstance() {
        return instance;
    }
}

以上是懒汉式和饿汉式两种实现方式的示例代码。在实现单例模式时,需要注意线程安全和序列化等问题,这些问题可以使用各种技术来解决。

饿汉式单例模式适用于以下一些业务场景:

  1. 资源消耗较小的对象:如果单例对象的创建和初始化过程比较简单,并且不会消耗大量的资源,可以考虑使用饿汉式单例模式。因为饿汉式单例模式在类加载时就会初始化单例对象,不会延迟对象的创建,因此适用于资源消耗较小的对象。
  2. 资源初始化不需要延迟加载:有些情况下,单例对象的初始化不需要延迟加载,可以在应用启动时就进行初始化。例如,应用启动时需要初始化一些全局配置信息或共享资源,可以使用饿汉式单例模式进行初始化。
  3. 线程安全性要求较高:饿汉式单例模式在类加载时就会创建单例对象,因此不存在线程安全性问题。如果业务场景对线程安全性要求较高,或者不希望在多线程环境下出现竞态条件等问题,可以考虑使用饿汉式单例模式。
  4. 简化代码逻辑:饿汉式单例模式相对比较简单,不需要考虑线程安全性问题,也不需要进行额外的同步控制。如果业务场景不需要延迟加载单例对象,并且对代码逻辑的简化有要求,可以考虑使用饿汉式单例模式。

下面是一个线程安全的单例模式的示例代码(懒汉式):

public class Singleton implements Serializable  {
    //使用volatile修饰变量使线程的线程的修改结果对所有线程都可见
    private static volatile Singleton instance;

    private Singleton() {
        // 私有构造函数
    }

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

    private Object readResolve() throws ObjectStreamException {
        return instance;
    }
}

​ 这里采用了双重检查锁定(Double-Checked Locking)的方式来保证线程安全性和性能。在 getInstance() 方法中,首先判断 instance 是否为 null,如果为 null,则进入同步代码块,在同步代码块内再次判断 instance 是否为 null,如果为 null,则创建一个新的实例。由于在同步代码块内使用了双重检查,所以可以保证线程安全性和较高的性能。

​ 需要注意的是,在使用双重检查锁定时,需要将 instance 声明为 volatile,这是因为 Java 中的指令重排可能会导致线程安全问题。使用 volatile 可以禁止指令重排,确保线程安全。在实现 Serializable 接口时,需要定义一个 readResolve() 方法,以防止在反序列化时创建新的实例,而是返回原来的实例。

2. 工厂方法模式

工厂方法模式是一种创建型设计模式,它定义了一个用于创建对象的接口,但是让子类决定实例化哪个类。因此,工厂方法模式将对象的实例化推迟到子类中进行。

下面是使用 Java 实现工厂方法模式的示例代码:

首先定义一个抽象产品类 Product,它有一个抽象方法 use,用于描述产品的使用方法:

public abstract class Product {
    public abstract void use();
}

然后定义两个具体的产品类 ProductAProductB,它们都继承自抽象产品类 Product,并实现了 use 方法:

public class ProductA extends Product {
    @Override
    public void use() {
        System.out.println("Product A is used.");
    }
}

public class ProductB extends Product {
    @Override
    public void use() {
        System.out.println("Product B is used.");
    }
}

接下来定义一个工厂接口 Factory,其中包含一个创建产品的抽象方法 createProduct

public interface Factory {
    Product createProduct();
}

然后分别定义两个具体的工厂类 FactoryAFactoryB,它们都实现了工厂接口 Factory,并分别负责创建 ProductAProductB

public class FactoryA implements Factory {
    @Override
    public Product createProduct() {
        return new ProductA();
    }
}

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

最后,在客户端代码中使用工厂类来创建产品。首先创建一个工厂实例,并使用它来创建产品:

public class MethodFactory{
    
    public static void main(String[] args){
        Factory factoryA = new FactoryA();
		Product productA = factoryA.createProduct();
		productA.use();

		Factory factoryB = new FactoryB();
		Product productB = factoryB.createProduct();
		productB.use();
    }
    
}

​ 这样,客户端就可以通过工厂类来创建产品实例了。由于工厂方法模式将产品的实例化过程和客户端解耦,因此客户端无需关心产品是如何创建的,只需要通过工厂类来获取产品即可。同时,工厂方法模式也符合开闭原则,因为在新增产品时只需要添加一个新的具体产品类和一个对应的具体工厂类即可,不需要修改已有代码。

工厂方法模式适用于以下一些业务场景:

  1. 对象创建需要隐藏具体实现:当需要隐藏对象的具体实现,并且希望客户端通过一个公共的接口来创建对象时,可以考虑使用工厂方法模式。工厂方法模式将对象的创建过程封装在工厂类中,客户端只需要通过工厂类的接口来创建对象,而无需关心具体的实现细节。
  2. 对象的类型不确定,由子类决定:当需要根据不同的条件创建不同类型的对象时,可以使用工厂方法模式。工厂方法模式定义了一个抽象的工厂接口,具体的对象创建由子类来实现,客户端根据不同的条件选择不同的工厂类来创建对象。
  3. 遵循开闭原则:工厂方法模式可以很好地满足开闭原则,即对扩展开放,对修改关闭。通过定义抽象的工厂接口和具体的工厂子类,可以方便地扩展新的产品类型,而无需修改现有的代码。
  4. 类的实例化延迟到子类:有些情况下,对象的实例化过程可能需要延迟到子类中去进行,例如根据配置文件或用户输入的参数来确定具体的对象类型。工厂方法模式可以很好地满足这种需求,将对象的实例化延迟到子类中去实现。
  5. 对象的创建逻辑复杂或需要封装:当对象的创建逻辑比较复杂,或者需要封装对象的创建过程时,可以使用工厂方法模式。工厂方法模式将对象的创建过程封装在工厂类中,客户端无需关心具体的创建过程,从而简化了客户端的代码逻辑。
3. 抽象工厂模式

抽象工厂模式是一种提供接口用于创建相关或依赖对象的工厂模式,该模式的目的是将具体实现细节与客户端代码分离。以下是Java中实现抽象工厂模式的示例:

首先定义产品族接口,如下所示:

public interface Animal {
    String getAnimal();
    String makeSound();
}

接下来定义产品族的实现类:

public class Cat implements Animal {

    @Override
    public String getAnimal() {
        return "Cat";
    }

    @Override
    public String makeSound() {
        return "Meow";
    }
}

public class Dog implements Animal {

    @Override
    public String getAnimal() {
        return "Dog";
    }

    @Override
    public String makeSound() {
        return "Woof";
    }
}

然后定义抽象工厂接口:

public interface AnimalFactory {
    Animal createAnimal();
}

接下来定义具体工厂类,实现抽象工厂接口,如下所示:

public class DogFactory implements AnimalFactory {

    @Override
    public Animal createAnimal() {
        return new Dog();
    }
}

public class CatFactory implements AnimalFactory {

    @Override
    public Animal createAnimal() {
        return new Cat();
    }
}

最后,客户端代码中可以使用具体工厂类创建相关的对象:

public class Main {
    public static void main(String[] args) {
        AnimalFactory dogFactory = new DogFactory();
        Animal dog = dogFactory.createAnimal();
        System.out.println("Dog says " + dog.makeSound());

        AnimalFactory catFactory = new CatFactory();
        Animal cat = catFactory.createAnimal();
        System.out.println("Cat says " + cat.makeSound());
    }
}

这样就实现了一个简单的抽象工厂模式,其中所有的类都是线程安全的,因为它们都没有可变状态。如果需要添加新的产品族,只需要添加新的实现类和相应的工厂类即可,不需要修改已有的代码。

抽象工厂模式适用于以下一些业务场景:

  1. 需要创建一组相关或相互依赖的产品对象:当需要创建一组相关或相互依赖的产品对象时,可以使用抽象工厂模式。抽象工厂模式定义了一个抽象的工厂接口,可以创建一组相关的产品对象,这些产品对象之间可能存在一定的依赖关系,例如汽车工厂可以创建轮胎、发动机等相关的产品对象。

  2. 希望更容易地交换产品系列:抽象工厂模式可以很容易地交换产品系列,即替换具体的工厂类就可以生产不同系列的产品。例如,可以定义一个具体的汽车工厂和一个具体的飞机工厂,分别生产汽车和飞机相关的产品,通过替换工厂类就可以在不改变客户端代码的情况下切换生产不同系列的产品。

  3. 需要满足开闭原则:抽象工厂模式可以很好地满足开闭原则,即对扩展开放,对修改关闭。通过定义抽象的工厂接口和具体的工厂子类,可以方便地扩展新的产品系列,而无需修改现有的代码。

  4. 需要保证产品对象的一致性:当需要保证创建的产品对象之间的一致性或兼容性时,可以使用抽象工厂模式。抽象工厂模式可以确保创建的产品对象都是属于同一产品系列的,从而保证了产品对象之间的一致性。

  5. 需要对产品对象进行组合:有些情况下,需要对产品对象进行组合,即将不同类型的产品对象组合在一起使用。抽象工厂模式可以很方便地实现对产品对象的组合,客户端只需要关心产品对象的接口,而无需关心具体的实现细节。

4. 建造者模式

建造者模式是一种用于创建复杂对象的创建型模式,它允许你将一个复杂对象的构建过程分解为多个简单的步骤,从而更加灵活地创建对象。以下是Java中实现建造者模式的示例:

首先定义产品类:

public class Product {
    private String partA;
    private String partB;
    private String partC;

    public void setPartA(String partA) {
        this.partA = partA;
    }

    public void setPartB(String partB) {
        this.partB = partB;
    }

    public void setPartC(String partC) {
        this.partC = partC;
    }

    public String getPartA() {
        return partA;
    }

    public String getPartB() {
        return partB;
    }

    public String getPartC() {
        return partC;
    }
}

接下来定义抽象建造者类:

public abstract class Builder {
    protected Product product = new Product();

    public abstract void buildPartA();
    public abstract void buildPartB();
    public abstract void buildPartC();

    public Product getProduct() {
        return product;
    }
}

然后定义具体建造者类,实现抽象建造者接口:

public class ConcreteBuilder extends Builder {

    @Override
    public void buildPartA() {
        product.setPartA("Part A");
    }

    @Override
    public void buildPartB() {
        product.setPartB("Part B");
    }

    @Override
    public void buildPartC() {
        product.setPartC("Part C");
    }
}

最后定义指导者类,它负责使用建造者对象来构建产品:

public class Director {
    private Builder builder;

    public Director(Builder builder) {
        this.builder = builder;
    }

    public void constructProduct() {
        builder.buildPartA();
        builder.buildPartB();
        builder.buildPartC();
    }

    public Product getProduct() {
        return builder.getProduct();
    }
}

客户端代码中可以使用指导者对象来构建产品:

public class Main {
    public static void main(String[] args) {
        Builder builder = new ConcreteBuilder();
        Director director = new Director(builder);
        director.constructProduct();
        Product product = director.getProduct();
        System.out.println(product.getPartA() + ", " + product.getPartB() + ", " + product.getPartC());
    }
}

这样就实现了一个简单的建造者模式,其中所有的类都是线程安全的,因为它们都没有可变状态。如果需要创建不同类型的产品,只需要添加新的产品类和相应的建造者类即可,不需要修改已有的代码。

建造者模式适用于以下一些业务场景:

  1. 对象的构建过程比较复杂:当对象的构建过程比较复杂,并且需要按照一定的步骤来构建对象时,可以考虑使用建造者模式。建造者模式将对象的构建过程分解成多个简单的步骤,并通过一个指导者来组装这些步骤,从而简化了对象的构建过程。
  2. 对象的属性较多,且可变性较大:当对象的属性较多,并且这些属性的可变性较大时,可以考虑使用建造者模式。建造者模式可以将对象的属性封装在具体的建造者类中,并提供一系列的方法来设置对象的属性,从而灵活地构建不同属性的对象。
  3. 需要构建不同表示的对象:当需要构建不同表示的对象时,例如XML文档、JSON对象等,可以使用建造者模式。建造者模式可以根据不同的表示形式来构建不同的对象,从而方便地扩展和维护代码。
  4. 需要控制对象的创建过程:当需要对对象的创建过程进行精确控制时,可以使用建造者模式。建造者模式将对象的创建过程分解成多个步骤,并通过一个指导者来组装这些步骤,可以灵活地控制对象的创建过程,满足不同的需求。
  5. 需要避免构造方法参数过多:当对象的构造方法参数过多时,会导致构造方法的参数列表变得很长,不易理解和维护。建造者模式可以将对象的构建过程分解成多个步骤,并通过一系列的方法来设置对象的属性,从而避免了构造方法参数过多的问题。
5. 原型模式

在 Java 中,实现深拷贝的原型模式需要在 clone() 方法中手动复制对象的引用类型属性,以确保每个属性都是独立的。以下是一个实现深拷贝的示例代码:

public class Circle implements Cloneable {
    private int x;
    private int y;
    private int radius;
    private List<Point> points;

    public Circle(int x, int y, int radius, List<Point> points) {
        this.x = x;
        this.y = y;
        this.radius = radius;
        this.points = points;
    }

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }

    public int getRadius() {
        return radius;
    }

    public void setRadius(int radius) {
        this.radius = radius;
    }

    public List<Point> getPoints() {
        return points;
    }

    public void setPoints(List<Point> points) {
        this.points = points;
    }

    @Override
    public Circle clone() throws CloneNotSupportedException {
        Circle clonedCircle = (Circle) super.clone();
        List<Point> clonedPoints = new ArrayList<>();
        for (Point point : this.points) {
            clonedPoints.add(new Point(point.getX(), point.getY()));
        }
        clonedCircle.setPoints(clonedPoints);
        return clonedCircle;
    }
}

在这个示例中,Circle 类中包含了一个 List 类型的引用类型属性 points,为了实现深拷贝,需要手动复制 points 的值。在 clone() 方法中,我们首先调用 super.clone() 创建一个 Circle 对象的浅拷贝,然后创建一个新的 List 对象 clonedPoints,并将 points 中每个 Point 对象的副本添加到 clonedPoints 中。最后,将 clonedPoints 设置为 clonedCircle 对象的 points 属性,返回 clonedCircle 对象。这样,我们就实现了 Circle 对象的深拷贝。

原型模式适用于以下一些业务场景:

  1. 对象的创建过程比较复杂或耗时:当对象的创建过程比较复杂或耗时时,可以考虑使用原型模式。原型模式通过复制现有对象的原型来创建新的对象,避免了复杂的对象初始化过程,提高了对象的创建效率。
  2. 需要避免创建过多相似对象:当需要创建大量相似但不完全相同的对象时,可以使用原型模式。原型模式通过复制现有对象的原型来创建新的对象,避免了重复创建相似对象的开销,提高了系统的性能和资源利用率。
  3. 需要动态创建对象,并且对象类型不确定:当需要动态创建对象,并且对象的类型在运行时才能确定时,可以使用原型模式。原型模式允许在运行时动态指定要创建的对象类型,并通过复制原型对象来创建新的对象,从而满足了对象类型不确定的需求。
  4. 需要保护对象的构造过程:当需要保护对象的构造过程,防止客户端直接访问对象的构造方法时,可以使用原型模式。原型模式将对象的构造过程封装在原型类中,客户端只需要通过原型对象来创建新的对象,无需关心具体的构造过程。
  5. 需要实现快速创建和销毁对象:当需要快速创建和销毁对象,并且对象的创建过程比较耗时时,可以使用原型模式。原型模式允许在运行时动态复制对象,从而避免了对象的创建和销毁过程,提高了系统的性能和资源利用率。

二、结构型模式

1. Adapter Pattern(适配器模式)

适配器模式是一种结构型设计模式,允许类的接口与另一个类的接口不兼容而无需修改原始代码。在 Java 中,适配器模式可以通过实现一个适配器类来实现。

首先定义目标接口:

// 目标接口
public interface MediaPlayer {
    void play(String audioType, String fileName);
}

然后定义需要适配的类:

// 需要适配的类
public class AdvancedMediaPlayer {
    public void playVlc(String fileName) {
        System.out.println("Playing vlc file. Name: " + fileName);
    }

    public void playMp4(String fileName) {
        System.out.println("Playing mp4 file. Name: " + fileName);
    }
}

接着定义适配器类:

// 适配器类
public class MediaAdapter implements MediaPlayer {
    private AdvancedMediaPlayer advancedMediaPlayer;

    public MediaAdapter(String audioType) {
        if (audioType.equalsIgnoreCase("vlc")) {
            advancedMediaPlayer = new AdvancedMediaPlayer();
        }
    }

    public void play(String audioType, String fileName) {
        if (audioType.equalsIgnoreCase("vlc")) {
            advancedMediaPlayer.playVlc(fileName);
        } else {
            System.out.println("Invalid media. " + audioType + " format not supported.");
        }
    }
}

最后,在客户端代码中使用适配器:

public class AudioPlayer implements MediaPlayer {
    MediaAdapter mediaAdapter;

    public void play(String audioType, String fileName) {
        if (audioType.equalsIgnoreCase("mp3")) {
            System.out.println("Playing mp3 file. Name: " + fileName);
        } else if (audioType.equalsIgnoreCase("vlc")) {
            mediaAdapter = new MediaAdapter(audioType);
            mediaAdapter.play(audioType, fileName);
        } else {
            System.out.println("Invalid media. " + audioType + " format not supported.");
        }
    }
}

执行结果如下

Playing mp3 file. Name: song.mp3
Playing vlc file. Name: movie.vlc
Invalid media. avi format not supported.

这样,通过适配器模式,我们可以使用 MediaPlayer 接口来播放 MP3 和 VLC 文件。如果我们想要播放其他格式的文件,我们只需要在适配器中添加相应的逻辑即可。

适配器模式适用于以下一些业务场景:

  1. 集成旧系统或遗留代码:当需要集成旧系统或遗留代码到新系统中,并且旧系统的接口与新系统的接口不兼容时,可以使用适配器模式。适配器模式可以将旧系统的接口适配成新系统的接口,从而实现系统之间的无缝集成。
  2. 兼容多个接口:当需要使用的接口有多个不同的实现,并且需要统一访问这些接口时,可以使用适配器模式。适配器模式可以将多个不同接口的实现适配成统一的接口,使得客户端可以统一访问这些接口。
  3. 封装第三方库或服务:当需要使用第三方库或服务,并且其接口与系统的接口不兼容时,可以使用适配器模式。适配器模式可以将第三方库或服务的接口适配成系统的接口,使得系统可以方便地使用第三方库或服务。
  4. 实现特定功能:当需要实现特定功能,并且已有类的接口不符合要求时,可以使用适配器模式。适配器模式可以将已有类的接口适配成符合要求的接口,从而实现特定功能。
  5. 解耦系统中的组件:当系统中的各个组件之间的耦合度较高,并且需要解耦时,可以使用适配器模式。适配器模式可以将系统中各个组件的接口适配成统一的接口,从而降低组件之间的耦合度。
2. Bridge Pattern(桥接模式)

桥接模式(Bridge Pattern)用于将抽象部分与其实现部分分离,使它们可以独立变化。这种模式涉及一个接口类,该接口类充当一个桥,使得具体类的功能独立于接口实现。以下是一个简单的Java实现桥接模式的示例:

  • 实现消息发送的接口
interface MessageSender {
    void sendMessage(String message);
}
  • 具体的消息发送实现类:发送邮件
class EmailSender implements MessageSender {
    @Override
    public void sendMessage(String message) {
        System.out.println("Sending Email: " + message);
    }
}
  • 具体的消息发送实现类:发送短信
class SmsSender implements MessageSender {
    @Override
    public void sendMessage(String message) {
        System.out.println("Sending SMS: " + message);
    }
}
  • 消息抽象类
abstract class Message {
    protected MessageSender messageSender;

    protected Message(MessageSender messageSender) {
        this.messageSender = messageSender;
    }
	//消息发送接口
    public abstract void send();
}
  • 具体的消息类:普通消息
class TextMessage extends Message {
    public TextMessage(MessageSender messageSender) {
        super(messageSender);
    }

    @Override
    public void send() {
        messageSender.sendMessage("This is a text message");
    }
}
  • // 具体的消息类:加急消息
class UrgentMessage extends Message {
    public UrgentMessage(MessageSender messageSender) {
        super(messageSender);
    }

    @Override
    public void send() {
        messageSender.sendMessage("This is an urgent message");
    }
}
// 测试类
public class BridgePatternDemo {
    public static void main(String[] args) {
        // 使用 Email 发送消息
        Message emailMessage = new TextMessage(new EmailSender());
        emailMessage.send();

        // 使用 SMS 发送消息
        Message smsMessage = new UrgentMessage(new SmsSender());
        smsMessage.send();
    }
}

在这个示例中,MessageSender 是一个接口,定义了发送消息的方法。EmailSenderSmsSender 是两个具体的消息发送实现类,分别用于发送邮件和短信。

Message 是一个抽象类,它包含了一个对 MessageSender 的引用,以及一个抽象的发送消息方法 send()TextMessageUrgentMessage 是两个具体的消息类,分别用于发送普通消息和加急消息。

BridgePatternDemo 类中,创建了一个使用 Email 发送普通消息的示例,以及一个使用 SMS 发送加急消息的示例。通过桥接模式,消息发送的实现与消息类型解耦,使得它们可以独立变化。

桥接模式适用于以下一些业务场景:

  1. 抽象与实现分离:当需要将抽象部分与实现部分分离,并且可以独立地变化时,可以使用桥接模式。桥接模式可以将抽象部分与实现部分分离开来,使它们可以独立地变化,从而提高了系统的灵活性和可扩展性。
  2. 多维度的变化:当一个类存在多个维度的变化,并且希望这些变化可以独立地进行扩展时,可以使用桥接模式。桥接模式可以将各个维度的变化分离开来,并通过桥接接口来组合这些变化,从而实现各个维度的独立扩展。
  3. 数据库驱动程序:数据库驱动程序通常需要支持多种不同的数据库产品,并且数据库产品与应用程序之间存在一定的差异。可以使用桥接模式将数据库驱动程序的抽象部分与具体的数据库产品进行分离,使其可以独立地进行扩展和演化。
  4. 图形界面库:图形界面库通常需要支持多种不同的操作系统和窗口系统,并且操作系统和窗口系统之间存在一定的差异。可以使用桥接模式将图形界面库的抽象部分与具体的操作系统和窗口系统进行分离,使其可以独立地进行扩展和演化。
  5. 远程控制器:远程控制器通常需要支持多种不同的设备和通信协议,并且设备和通信协议之间存在一定的差异。可以使用桥接模式将远程控制器的抽象部分与具体的设备和通信协议进行分离,使其可以独立地进行扩展和演化。
3. Composite Pattern(组合模式)
4. Decorator Pattern(装饰者模式)

装饰者模式是一种结构型设计模式,它允许你通过将对象包装在具有相同接口的其他对象中来动态地修改其行为。

// 定义一个抽象组件接口
interface Component {
    void operation();
}

// 实现组件的具体类
class ConcreteComponent implements Component {
    public void operation() {
        System.out.println("ConcreteComponent operation");
    }
}

// 定义装饰者类的抽象基类,它实现了Component接口
abstract class Decorator implements Component {
    private Component component;

    public Decorator(Component component) {
        this.component = component;
    }

    public void operation() {
        component.operation();
    }
}

// 具体装饰者类A
class ConcreteDecoratorA extends Decorator {
    public ConcreteDecoratorA(Component component) {
        super(component);
    }

    public void operation() {
        super.operation();
        System.out.println("ConcreteDecoratorA operation");
    }
}

// 具体装饰者类B
class ConcreteDecoratorB extends Decorator {
    public ConcreteDecoratorB(Component component) {
        super(component);
    }

    public void operation() {
        super.operation();
        System.out.println("ConcreteDecoratorB operation");
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        Component component = new ConcreteComponent();
        Component decoratorA = new ConcreteDecoratorA(component);
        Component decoratorB = new ConcreteDecoratorB(decoratorA);
        decoratorB.operation();
    }
}

在这个示例中,Component是组件接口,ConcreteComponent是具体组件类,Decorator是装饰者抽象基类,ConcreteDecoratorAConcreteDecoratorB是具体装饰者类。客户端代码创建一个具体组件对象并将其传递给两个装饰者对象,最终调用decoratorBoperation()方法。

当执行decoratorB.operation()时,将首先调用ConcreteDecoratorAoperation()方法,然后调用ConcreteDecoratorBoperation()方法。ConcreteDecoratorAoperation()方法将调用其父类Decoratoroperation()方法,Decoratoroperation()方法又将调用ConcreteComponentoperation()方法,这样就实现了装饰者模式的动态修改行为的功能。

5. Facade Pattern(外观模式)
6. Flyweight Pattern(享元模式)
7. Proxy Pattern(代理模式)

代理模式是一种结构型设计模式,其目的是为其他对象提供一种代理以控制对这个对象的访问。在代理模式中,我们创建一个代理对象,该代理对象可以充当另一个对象的替代者,以控制对这个对象的访问。代理模式允许我们在访问对象时引入一些额外的功能,而不必修改原始对象的代码。

// 定义接口
interface Image {
    void display();
}

// 创建实际的类
class RealImage implements Image {
    private String filename;

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

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

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

// 创建代理类
class ProxyImage implements Image {
    private RealImage realImage;
    private String filename;

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

    public void display() {
        if (realImage == null) {
            realImage = new RealImage(filename);
        }
        realImage.display();
    }
}

// 测试代理模式
public class ProxyPatternDemo {
    public static void main(String[] args) {
        // 创建代理对象
        Image image = new ProxyImage("test.jpg");

        // 图像将从磁盘加载
        image.display();
        System.out.println();

        // 图像不需要从磁盘加载
        image.display();
    }
}

三、Behavioral Patterns(行为型模式)

1. Chain of Responsibility Pattern(责任链模式)

责任链模式用于处理请求的责任链。它允许多个处理器处理一个请求,直到其中一个处理器能够处理它为止。在这种模式中,请求沿着处理器链向下传递,直到被某个处理器处理为止,或者达到链的末端而无法被任何处理器处理。

下面是一个简单的责任链模式实现示例:

// 抽象处理器类
public abstract class Handler {
    private Handler next;

    public Handler setNext(Handler next) {
        this.next = next;
        return next;
    }

    public void handle(Request request) {
        if (canHandle(request)) {
            doHandle(request);
        } else if (next != null) {
            next.handle(request);
        } else {
            // 所有处理器都无法处理该请求,进行相应处理
            System.out.println("Request cannot be handled");
        }
    }

    // 判断是否能够处理该请求
    protected abstract boolean canHandle(Request request);

    // 处理请求的方法
    protected abstract void doHandle(Request request);
}

// 具体处理器类A
public class ConcreteHandlerA extends Handler {
    @Override
    protected boolean canHandle(Request request) {
        return request.getType() == RequestType.TYPE_A;
    }

    @Override
    protected void doHandle(Request request) {
        // 处理请求
    }
}

// 具体处理器类B
public class ConcreteHandlerB extends Handler {
    @Override
    protected boolean canHandle(Request request) {
        return request.getType() == RequestType.TYPE_B;
    }

    @Override
    protected void doHandle(Request request) {
        // 处理请求
    }
}

// 请求类
public class Request {
    private RequestType type;
    private String content;

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

    public RequestType getType() {
        return type;
    }

    public String getContent() {
        return content;
    }
}

// 请求类型枚举
public enum RequestType {
    TYPE_A,
    TYPE_B
}

在上面的示例中,抽象处理器类Handler定义了一个指向下一个处理器的引用,它实现了handle()方法来处理请求。当请求到达一个处理器时,如果它能够处理该请求,它将执行doHandle()方法来处理请求;否则,它将把请求传递给下一个处理器。具体处理器类ConcreteHandlerAConcreteHandlerB实现了canHandle()doHandle()方法来判断和处理请求。请求类Request表示一个请求,它包含了请求的类型和内容。RequestType枚举表示请求的类型。

可以通过以下方式来使用责任链模式:

Handler handlerA = new ConcreteHandlerA();
Handler handlerB = new ConcreteHandlerB();

handlerA.setNext(handlerB);

Request requestA = new Request(RequestType.TYPE_A, "requestA");
handlerA.handle(requestA);

Request requestB = new Request(RequestType.TYPE_B, "requestB");
handlerA.handle(requestB);

责任链的另外一种实现方式

// 请求处理者接口
public interface Handler {
    void handleRequest(String request);
}

// 具体请求处理者1
public class ConcreteHandler1 implements Handler {
    private Handler nextHandler;

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

    public void handleRequest(String request) {
        if (request.equals("1")) {
            System.out.println("ConcreteHandler1 handles the request " + request);
        } else {
            nextHandler.handleRequest(request);
        }
    }
}

// 具体请求处理者2
public class ConcreteHandler2 implements Handler {
    private Handler nextHandler;

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

    public void handleRequest(String request) {
        if (request.equals("2")) {
            System.out.println("ConcreteHandler2 handles the request " + request);
        } else {
            nextHandler.handleRequest(request);
        }
    }
}

// 具体请求处理者3
public class ConcreteHandler3 implements Handler {
    private Handler nextHandler;

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

    public void handleRequest(String request) {
        if (request.equals("3")) {
            System.out.println("ConcreteHandler3 handles the request " + request);
        } else {
            nextHandler.handleRequest(request);
        }
    }
}

// 客户端
public class Client {
    public static void main(String[] args) {
        Handler handler1 = new ConcreteHandler1();
        Handler handler2 = new ConcreteHandler2();
        Handler handler3 = new ConcreteHandler3();

        handler1.setNextHandler(handler2);
        handler2.setNextHandler(handler3);

        handler1.handleRequest("1");
        handler1.handleRequest("2");
        handler1.handleRequest("3");
        handler1.handleRequest("4");
    }
}
2. Command Pattern(命令模式)

命令模式(Command Pattern)是一种行为型设计模式,用于将请求封装为一个对象,从而使您可以将命令的发送者与接收者分开。这种模式背后的思想是将命令的发送者和执行者解耦,并将它们彼此分离,从而提高代码的灵活性和可扩展性。

下面是Java实现命令模式的示例代码:

// Command 接口定义了执行命令的方法
public interface Command {
    void execute();
}

// Receiver 类定义了接收者的行为
public class Receiver {
    public void action() {
        System.out.println("Receiver is doing something...");
    }
}

// ConcreteCommand 类将命令和接收者绑定在一起
public class ConcreteCommand implements Command {
    private Receiver receiver;
    public ConcreteCommand(Receiver receiver) {
        this.receiver = receiver;
    }
    public void execute() {
        receiver.action();
    }
}

// Invoker 类定义了命令的发送者,并在需要的时候将命令发送给接收者
public class Invoker {
    private Command command;
    public void setCommand(Command command) {
        this.command = command;
    }
    public void executeCommand() {
        command.execute();
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        Receiver receiver = new Receiver();
        Command command = new ConcreteCommand(receiver);
        Invoker invoker = new Invoker();
        invoker.setCommand(command);
        invoker.executeCommand();
    }
}
3. Interpreter Pattern(解释器模式)

解释器模式是一种行为设计模式,它定义了一种语言和它的解释器,用于解释和执行该语言中的表达式。在Java中,我们可以使用以下步骤实现解释器模式:

  1. 创建表达式接口:定义解释器要处理的表达式的公共接口。该接口通常包含一个解释方法。
interface Expression {
    boolean interpret(String context);
}
  1. 创建具体表达式类:实现表达式接口,并定义解释器如何处理特定的表达式。
class TerminalExpression implements Expression {
    private String data;

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

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

class OrExpression implements Expression {
    private Expression expr1;
    private Expression expr2;

    public OrExpression(Expression expr1, Expression expr2) {
        this.expr1 = expr1;
        this.expr2 = expr2;
    }

    public boolean interpret(String context) {
        return expr1.interpret(context) || expr2.interpret(context);
    }
}

class AndExpression implements Expression {
    private Expression expr1;
    private Expression expr2;

    public AndExpression(Expression expr1, Expression expr2) {
        this.expr1 = expr1;
        this.expr2 = expr2;
    }

    public boolean interpret(String context) {
        return expr1.interpret(context) && expr2.interpret(context);
    }
}
  1. 创建解释器类:将表达式解释为操作序列,并执行它们。
class Interpreter {
    private Expression expression;

    public Interpreter(Expression expression) {
        this.expression = expression;
    }

    public boolean interpret(String context) {
        return expression.interpret(context);
    }
}
  1. 在客户端代码中使用解释器:
public class Client {
    public static void main(String[] args) {
        Expression person1 = new TerminalExpression("Tom");
        Expression person2 = new TerminalExpression("Jerry");
        Expression isSingle = new OrExpression(person1, person2);

        Expression married = new TerminalExpression("married");
        Expression isMarried = new AndExpression(isSingle, married);

        Interpreter interpreter = new Interpreter(isMarried);

        System.out.println("Tom is single? " + interpreter.interpret("Tom"));
        System.out.println("Jerry is single? " + interpreter.interpret("Jerry"));
        System.out.println("Tom is married? " + interpreter.interpret("Tom married"));
    }
}

在这个示例中,我们使用解释器模式实现了一个简单的语言来描述人的婚姻状态,并使用解释器来解释和执行这个语言中的表达式。

4. Iterator Pattern(迭代器模式)

​ 迭代器模式(Iterator Pattern)是一种行为型设计模式,它提供一种访问集合对象元素的方式,而不需要暴露集合对象的内部结构。使用迭代器模式,您可以遍历集合并访问集合中的元素,而无需知道集合对象的底层实现。

下面是Java实现迭代器模式的示例代码:

// Iterator 接口定义了迭代器的行为
public interface Iterator {
    boolean hasNext();
    Object next();
}

// ConcreteIterator 类实现了迭代器接口
public class ConcreteIterator implements Iterator {
    private List<Object> list;
    private int index;
    public ConcreteIterator(List<Object> list) {
        this.list = list;
        this.index = 0;
    }
    public boolean hasNext() {
        return index < list.size();
    }
    public Object next() {
        Object obj = list.get(index);
        index++;
        return obj;
    }
}

// Aggregate 接口定义了聚合对象的行为
public interface Aggregate {
    Iterator iterator();
}

// ConcreteAggregate 类实现了聚合接口,返回一个具体的迭代器对象
public class ConcreteAggregate implements Aggregate {
    private List<Object> list;
    public ConcreteAggregate() {
        this.list = new ArrayList<Object>();
    }
    public void add(Object obj) {
        list.add(obj);
    }
    public Iterator iterator() {
        return new ConcreteIterator(list);
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        Aggregate aggregate = new ConcreteAggregate();
        aggregate.add("item1");
        aggregate.add("item2");
		aggregate.add("item3");
		Iterator iterator = aggregate.iterator();
		while (iterator.hasNext()) {
		Object obj = iterator.next();
		System.out.println(obj);
				}
			}
		}

​ 在上面的示例代码中,Iterator 接口定义了迭代器的行为,ConcreteIterator 类实现了 Iterator 接口,用于迭代具体的集合对象。Aggregate 接口定义了聚合对象的行为,ConcreteAggregate 类实现了 Aggregate 接口,返回一个具体的迭代器对象。客户端代码使用聚合对象和迭代器对象来遍历集合并访问集合中的元素。

迭代器模式适用于以下一些业务场景:

  1. 遍历集合:迭代器模式最常见的用途是遍历集合或聚合对象中的元素。通过提供一个统一的接口,客户端可以访问集合中的元素,而无需了解集合的内部结构。
  2. 封装集合:迭代器模式可以将集合的内部结构和遍历逻辑分离开来,从而提高了集合的封装性和安全性。集合只需要提供一个迭代器接口,客户端通过迭代器来访问集合中的元素,而不需要了解集合的具体实现细节。
  3. 支持多种遍历方式:迭代器模式可以支持多种遍历方式,例如顺序遍历、逆序遍历、深度优先遍历、广度优先遍历等。通过提供不同的迭代器实现,可以实现不同的遍历方式,从而满足不同的需求。
  4. 分离遍历算法:迭代器模式将遍历算法封装在迭代器中,使得客户端可以通过统一的接口来访问集合中的元素,而不必关心遍历算法的实现细节。这样就实现了遍历算法和集合的分离,提高了系统的灵活性和可维护性。
  5. 支持多种集合类型:迭代器模式可以支持多种集合类型,例如列表、树、图等。通过定义统一的迭代器接口和多个具体的迭代器实现,可以方便地支持不同类型的集合。
5. Mediator Pattern(中介者模式)

中介者模式(Mediator Pattern)用于通过一个中介对象来封装一系列对象之间的交互,使得这些对象不需要显式地相互引用,从而降低了它们之间的耦合度。以下是一个简单的Java实现中介者模式的示例:

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

/**
 * 中介者接口
 */
interface Mediator {
    /**
     * 消息发送接口
     *
     * @param message   消息内容
     * @param colleague 同事类
     */
    void sendMessage(String message, Colleague colleague);
}

/**
 * 具体中介者
 */
class ConcreteMediator implements Mediator {

    private final List<Colleague> colleagueList;

    public ConcreteMediator() {
        this.colleagueList = new ArrayList<>();
    }

    /**
     * 添加同事
     *
     * @param colleague 同事信息
     */
    public void addColleague(Colleague colleague) {
        colleagueList.add(colleague);
    }

    /**
     * 移除同事信息
     *
     * @param colleague 同事信息
     */
    public void removeColleague(Colleague colleague) {
        colleagueList.remove(colleague);
    }

    /**
     * 消息发送接口
     *
     * @param message   消息内容
     * @param colleague 同事类
     */
    @Override
    public void sendMessage(String message, Colleague colleague) {
        for (Colleague col : colleagueList) {
            if (col != colleague) {
                col.receiveMessage(message);
            }
        }
    }
}

/**
 * 抽象同事类
 */
abstract class Colleague {

    protected Mediator mediator;

    public Colleague(Mediator mediator) {
        this.mediator = mediator;
    }

    /**
     * 发送消息
     *
     * @param message 消息内容
     */
    public abstract void send(String message);

    /**
     * 接收消息
     *
     * @param message 消息内容
     */
    public abstract void receiveMessage(String message);

}

/**
 * 具体同事类
 */
class ConcreteColleague extends Colleague {
    public ConcreteColleague(Mediator mediator) {
        super(mediator);
    }

    /**
     * 发送消息
     *
     * @param message 消息内容
     */
    @Override
    public void send(String message) {
        System.out.println("send message :" + message);
        mediator.sendMessage(message, this);

    }

    /**
     * 接收消息
     *
     * @param message 消息内容
     */
    @Override
    public void receiveMessage(String message) {
        System.out.println("receive message" + message);

    }
}


/**
 * @author jackie
 */
public class MediatorPatternDemo {

    public static void main(String[] args) {
        ConcreteMediator mediator = new ConcreteMediator();
        ConcreteColleague colleague1 = new ConcreteColleague(mediator);
        ConcreteColleague colleague2 = new ConcreteColleague(mediator);
        ConcreteColleague colleague3 = new ConcreteColleague(mediator);

        mediator.addColleague(colleague1);
        mediator.addColleague(colleague2);
        mediator.addColleague(colleague3);

        colleague1.send("this is colleague1,I send a message for everyone");

    }
}

6. Memento Pattern(备忘录模式)

备忘录模式(Memento Pattern)用于在不违反封装的情况下捕获一个对象的内部状态,并在该对象之外保存这个状态。

以下是一个简单的Java实现备忘录模式的示例:

/**
 * 备忘录类
 */
class Memento {
    /**
     * 当前状态
     */
    private final String state;

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

    /**
     * 获取状态信息
     *
     * @return
     */
    public String getState() {
        return state;
    }

}

/**
 * 管理者类
 */
class Caretaker {
    private final Stack<Memento> mementos = new Stack<>();

    /**
     * 存档
     *
     * @param memento 存档
     */
    public void save(Memento memento) {
        mementos.push(memento);
    }

    /**
     * 恢复到上一个存档
     *
     * @return 上一个存档信息
     */
    public Memento restore() {
        if (!mementos.isEmpty()) {
            return mementos.pop();
        }
        return null;
    }
}

/**
 * 发起人类
 */
class Originator {
    private String state;

    public String getState() {
        return state;
    }

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

    /**
     * 创建备忘录
     *
     * @return Memento
     */
    public Memento createMemento() {
        return new Memento(state);
    }

    /**
     * 从备忘录种恢复存档信息
     *
     * @param memento 状态
     */
    public void restoreFromMemento(Memento memento) {
        state = memento.getState();
    }
}


/**
 * 备忘录模式
 *
 * @author jackie
 */
public class MementoPatternDemo {
    public static void main(String[] args) {
        Originator originator = new Originator();
        Caretaker caretaker = new Caretaker();

        // 设置发起人的状态并存档
        originator.setState("State 1");
        caretaker.save(originator.createMemento());

        // 修改发起人的状态并再次存档
        originator.setState("State 2");
        caretaker.save(originator.createMemento());

        // 恢复到上一个存档
        Memento memento = caretaker.restore();
        if (memento != null) {
            originator.restoreFromMemento(memento);
        }
        System.out.println("Current State: " + originator.getState());
    }

}

备忘录模式适用于以下业务场景:

  1. 撤销操作:备忘录模式常用于实现撤销操作。例如,在文本编辑器中,用户对文本进行了修改,可以通过备忘录模式保存修改前的状态,如果用户希望撤销修改,可以从备忘录中恢复到之前的状态。
  2. 事务回滚:在数据库系统中,备忘录模式可以用于实现事务的回滚机制。当执行一系列数据库操作时,可以将每个操作前的数据库状态保存到备忘录中,在事务失败或者需要回滚时,可以从备忘录中恢复到之前的状态。
  3. 游戏存档:在游戏开发中,备忘录模式可以用于实现游戏存档功能。当玩家希望保存游戏进度时,可以将当前游戏状态保存到备忘录中,然后在需要恢复游戏进度时,可以从备忘录中读取之前的游戏状态。
  4. 编辑历史记录:在图形编辑器或者绘图软件中,备忘录模式可以用于实现编辑历史记录功能。用户对图形进行了一系列编辑操作,可以将每个操作前的图形状态保存到备忘录中,然后可以随时回退到之前的状态。
  5. 工作流程管理:在工作流程管理系统中,备忘录模式可以用于实现任务的撤销和恢复功能。当某个任务出现错误或者需要修改时,可以从备忘录中恢复到之前的状态,然后重新执行任务。
7. Observer Pattern(观察者模式)

观察者模式(Observer Pattern)是一种行为设计模式,用于定义对象之间的一对多依赖关系,当一个对象的状态发生变化时,其所有依赖对象都会收到通知并自动更新。

以下是一个简单的Java实现观察者模式的示例:

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

// 主题接口
interface Subject {
    void attach(Observer observer);

    void detach(Observer observer);

    void notifyObservers();
}

// 观察者接口
interface Observer {
    void update();
}

/**
 * 测试类
 *
 * @author jackie
 */
public class ObserverPatternDemo {
    public static void main(String[] args) {
        // 创建具体主题对象
        ConcreteSubject subject = new ConcreteSubject();

        // 创建具体观察者对象并订阅主题
        ConcreteObserver observer1 = new ConcreteObserver("Observer 1", subject);
        ConcreteObserver observer2 = new ConcreteObserver("Observer 2", subject);
        ConcreteObserver observer3 = new ConcreteObserver("Observer 3", subject);

        // 主题状态变化,通知观察者
        subject.attach(observer1);
        subject.attach(observer2);
        subject.attach(observer3);
        subject.setState(5);

        // 移除观察者
        subject.detach(observer2);
        subject.setState(10);
    }
}

// 具体主题类
class ConcreteSubject implements Subject {
    private final List<Observer> observers = new ArrayList<>();
    private int state;

    public int getState() {
        return state;
    }

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

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

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

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

// 具体观察者类
class ConcreteObserver implements Observer {
    private final String name;
    private final ConcreteSubject subject;

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

    @Override
    public void update() {
        System.out.println(name + " received update, new state: " + subject.getState());
    }
}

在这个示例中:

  • Subject 是主题接口,定义了注册、注销和通知观察者的方法。
  • ConcreteSubject 是具体主题类,包含了状态和观察者列表,并实现了主题接口的方法。
  • Observer 是观察者接口,定义了更新的方法。
  • ConcreteObserver 是具体观察者类,包含了名称和对主题的引用,并实现了观察者接口的方法。
  • 在测试类中,我们创建了一个具体主题对象和三个具体观察者对象,然后主题对象状态发生变化时通知观察者,并可以动态添加或移除观察者。

通过观察者模式,我们可以实现对象之间的解耦,当一个对象的状态发生变化时,其他依赖该对象的对象都会自动更新,从而实现了对象之间的一对多依赖关系。

观察者模式适用于许多不同的业务场景,其中包括但不限于以下情况:

  1. GUI开发:在图形用户界面(GUI)开发中,观察者模式常用于实现用户界面和数据模型之间的交互。例如,当数据模型发生变化时,可以通知相关的界面组件自动更新显示。
  2. 事件驱动系统:在事件驱动的系统中,观察者模式被广泛应用。例如,Java中的事件监听器机制就是观察者模式的一种实现。
  3. 消息队列:观察者模式可以用于实现消息队列系统,其中订阅者订阅特定类型的消息,当消息到达时,通知所有订阅者进行处理。
  4. 发布-订阅模式:观察者模式的一种变体是发布-订阅模式,其中发布者(或称为主题)负责发布事件,而订阅者(或称为观察者)订阅感兴趣的事件。
  5. 通知系统:在通知系统中,观察者模式可以用于实现消息通知的发布和订阅。例如,电子邮件订阅服务可以通知用户收到的新邮件。
  6. 监控系统:在监控系统中,观察者模式可以用于监控系统中的各种指标或事件,并及时通知管理员或用户。
  7. 数据同步:在分布式系统中,观察者模式可以用于实现数据同步机制。例如,当一个节点更新数据时,可以通知其他节点进行相应的更新。
  8. 游戏开发:在游戏开发中,观察者模式常用于实现游戏中的事件和状态的管理。例如,玩家角色的属性变化可以通知其他玩家或NPC进行相应的处理。
8. State Pattern(状态模式)

状态模式(State Pattern)是一种行为设计模式,它允许对象在其内部状态改变时改变其行为。状态模式将对象的行为封装在不同的状态类中,并将对象的行为委托给当前状态对象。

以下是一个简单的Java实现状态模式的示例:

// 状态接口
interface TransactionState {
    void process();
}

// 具体状态类:待处理状态
class PendingState implements TransactionState {
    @Override
    public void process() {
        System.out.println("Transaction is pending...");
    }
}

// 具体状态类:处理中状态
class ProcessingState implements TransactionState {
    @Override
    public void process() {
        System.out.println("Transaction is processing...");
    }
}

// 具体状态类:成功状态
class SuccessState implements TransactionState {
    @Override
    public void process() {
        System.out.println("Transaction is successful!");
    }
}

// 具体状态类:失败状态
class FailureState implements TransactionState {
    @Override
    public void process() {
        System.out.println("Transaction failed!");
    }
}

// 环境类
class TransactionContext {
    private TransactionState state;

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

    public void processTransaction() {
        state.process();
    }
}

// 测试类
public class TransactionStateDemo {
    public static void main(String[] args) {
        // 创建交易环境对象
        TransactionContext context = new TransactionContext();

        // 初始状态:待处理状态
        TransactionState pendingState = new PendingState();
        context.setState(pendingState);
        context.processTransaction();

        // 切换状态:处理中状态
        TransactionState processingState = new ProcessingState();
        context.setState(processingState);
        context.processTransaction();

        // 切换状态:成功状态
        TransactionState successState = new SuccessState();
        context.setState(successState);
        context.processTransaction();

        // 切换状态:失败状态
        TransactionState failureState = new FailureState();
        context.setState(failureState);
        context.processTransaction();
    }
}

状态模式适用于以下一些业务场景:

  1. 订单状态管理:在电子商务系统中,订单的生命周期包括创建、支付、配送、完成等不同的状态。使用状态模式可以将每个订单状态封装为一个状态类,从而实现订单状态的动态管理和转换。
  2. 工作流程管理:在工作流程管理系统中,每个工作流程可以处于不同的状态,例如待办、进行中、已完成等。使用状态模式可以将每个工作流程状态封装为一个状态类,从而实现工作流程状态的动态管理和流转。
  3. 游戏角色状态:在游戏开发中,游戏角色可以处于不同的状态,例如正常、受伤、死亡等。使用状态模式可以将每个角色状态封装为一个状态类,从而实现角色状态的动态管理和转换。
  4. 网络连接状态:在网络编程中,网络连接可以处于不同的状态,例如连接中、已连接、断开等。使用状态模式可以将每个连接状态封装为一个状态类,从而实现网络连接状态的动态管理和转换。
  5. 交通信号灯状态:在交通管理系统中,交通信号灯可以处于红灯、黄灯、绿灯等不同的状态。使用状态模式可以将每个信号灯状态封装为一个状态类,从而实现交通信号灯状态的动态管理和转换。
9. Strategy Pattern(策略模式)

策略模式(Strategy Pattern)是一种行为设计模式,它定义了一系列算法,并将每个算法封装起来,使它们可以互相替换。策略模式使得算法可以独立于客户端而变化。

以下是一个简单的Java实现策略模式的示例:

// 策略接口
interface PaymentStrategy {
    void pay(double amount);
}

// 具体策略类:支付宝支付
class AlipayStrategy implements PaymentStrategy {
    @Override
    public void pay(double amount) {
        System.out.println("Paid " + amount + " via Alipay");
    }
}

// 具体策略类:微信支付
class WechatPayStrategy implements PaymentStrategy {
    @Override
    public void pay(double amount) {
        System.out.println("Paid " + amount + " via WeChat Pay");
    }
}

// 上下文类
class PaymentContext {
    private PaymentStrategy strategy;

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

    public void executePayment(double amount) {
        if (strategy != null) {
            strategy.pay(amount);
        } else {
            System.out.println("No payment strategy set!");
        }
    }
}

// 测试类
public class StrategyPatternDemo {
    public static void main(String[] args) {
        // 创建支付上下文对象
        PaymentContext context = new PaymentContext();

        // 设置具体支付策略:支付宝支付
        context.setStrategy(new AlipayStrategy());
        // 执行支付
        context.executePayment(100);

        // 设置具体支付策略:微信支付
        context.setStrategy(new WechatPayStrategy());
        // 执行支付
        context.executePayment(200);
    }
}
  • PaymentStrategy 是策略接口,定义了支付的方法 pay(double amount)
  • AlipayStrategyWechatPayStrategy 是具体策略类,分别表示支付宝支付和微信支付,实现了 PaymentStrategy 接口。
  • PaymentContext 是上下文类,包含了一个策略对象,并提供了设置策略和执行支付的方法。
  • 在测试类中,我们创建了一个支付上下文对象,并分别设置了支付宝支付和微信支付两种具体支付策略,然后执行了相应的支付操作。

策略模式适用于以下一些业务场景:

  1. 支付方式选择:在电子商务系统中,用户可以选择不同的支付方式进行支付,例如支付宝、微信、信用卡等。使用策略模式可以将每种支付方式封装为一个具体的支付策略类,然后根据用户的选择动态设置支付策略。
  2. 排序算法:在排序算法中,不同的排序算法具有不同的性能和适用场景,例如冒泡排序、快速排序、归并排序等。使用策略模式可以将每种排序算法封装为一个具体的排序策略类,并根据需求动态选择合适的排序策略。
  3. 数据校验:在数据校验系统中,可能会有不同的数据校验规则,例如校验邮箱格式、校验手机号格式、校验身份证号格式等。使用策略模式可以将每种校验规则封装为一个具体的校验策略类,并根据需要动态选择合适的校验策略。
  4. 商业规则引擎:在企业应用中,常常需要根据不同的商业规则进行业务处理,例如折扣计算、奖励发放、积分计算等。使用策略模式可以将每种商业规则封装为一个具体的策略类,并根据业务需要动态选择合适的策略。
  5. 缓存策略:在缓存系统中,可能会有不同的缓存策略,例如先进先出(FIFO)、最近最少使用(LRU)、最不常用(LFU)等。使用策略模式可以将每种缓存策略封装为一个具体的策略类,并根据需求动态选择合适的缓存策略。
10. Template Method Pattern(模板方法模式)

模板方法模式(Template Method Pattern)是一种行为设计模式,它定义了一个算法的框架,并允许子类在不改变该算法结构的情况下重写算法的某些步骤。

以下是一个简单的Java实现模板方法模式的示例:

// 抽象类
abstract class Game {
    // 模板方法,定义了游戏的基本流程
    public final void play() {
        initialize();
        startPlay();
        endPlay();
    }

    // 抽象方法,子类必须实现
    abstract void initialize();
    abstract void startPlay();
    abstract void endPlay();
}

// 具体子类:篮球游戏
class BasketballGame extends Game {
    @Override
    void initialize() {
        System.out.println("Basketball Game Initialized! Start playing.");
    }

    @Override
    void startPlay() {
        System.out.println("Basketball Game Started. Enjoy the game!");
    }

    @Override
    void endPlay() {
        System.out.println("Basketball Game Finished!");
    }
}

// 具体子类:足球游戏
class FootballGame extends Game {
    @Override
    void initialize() {
        System.out.println("Football Game Initialized! Start playing.");
    }

    @Override
    void startPlay() {
        System.out.println("Football Game Started. Enjoy the game!");
    }

    @Override
    void endPlay() {
        System.out.println("Football Game Finished!");
    }
}

// 测试类
public class TemplateMethodPatternDemo {
    public static void main(String[] args) {
        Game basketballGame = new BasketballGame();
        basketballGame.play();

        System.out.println();

        Game footballGame = new FootballGame();
        footballGame.play();
    }
}
  • Game 是抽象类,定义了一个模板方法 play() 和一些抽象方法,子类必须实现这些抽象方法。
  • BasketballGameFootballGame 是具体子类,分别表示篮球游戏和足球游戏,实现了 Game 抽象类中的抽象方法。
  • 在测试类中,我们创建了一个篮球游戏对象和一个足球游戏对象,并调用了它们的 play() 方法。

模板方法模式适用于以下一些业务场景:

  1. 框架设计:在框架设计中,通常会定义一些基础类或接口,其中可能包含一些通用的算法或流程,而具体的实现细节留给子类来实现。使用模板方法模式可以将这些通用的算法或流程封装在模板方法中,从而方便框架的使用者扩展或定制。
  2. 软件开发流程:在软件开发流程中,通常会定义一些开发规范或流程,例如代码编写、编译、测试、部署等。使用模板方法模式可以将这些开发流程封装在模板方法中,从而规范开发流程并提高开发效率。
  3. 数据库操作:在数据库操作中,通常会有一些公共的操作流程,例如连接数据库、执行SQL语句、关闭连接等。使用模板方法模式可以将这些公共的操作流程封装在模板方法中,从而简化数据库操作的代码编写。
  4. 商业流程:在企业应用中,可能会有一些标准的商业流程,例如销售流程、采购流程、审批流程等。使用模板方法模式可以将这些商业流程封装在模板方法中,从而规范企业的运营流程并提高效率。
  5. 游戏开发:在游戏开发中,可能会有一些通用的游戏流程,例如游戏初始化、游戏开始、游戏结束等。使用模板方法模式可以将这些通用的游戏流程封装在模板方法中,从而提高游戏开发的效率。
11. Visitor Pattern(访问者模式)

访问者模式(Visitor Pattern)是一种行为设计模式,它允许你定义一个新操作,而无需修改被操作的元素类。以下是一个简单的Java实现访问者模式的示例:

假设我们有一个表示不同类型商品的类层次结构:

// 商品接口
interface Product {
    void accept(Visitor visitor);
}

// 具体商品类:图书
class Book implements Product {
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }

    public void bookInfo() {
        System.out.println("This is a book.");
    }
}

// 具体商品类:电子产品
class Electronic implements Product {
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }

    public void electronicInfo() {
        System.out.println("This is an electronic product.");
    }
}

// 访问者接口
interface Visitor {
    void visit(Book book);
    void visit(Electronic electronic);
}

// 具体访问者类:购物车访问者
class ShoppingCartVisitor implements Visitor {
    @Override
    public void visit(Book book) {
        System.out.println("Adding book to shopping cart.");
        book.bookInfo();
    }

    @Override
    public void visit(Electronic electronic) {
        System.out.println("Adding electronic product to shopping cart.");
        electronic.electronicInfo();
    }
}

// 测试类
public class VisitorPatternDemo {
    public static void main(String[] args) {
        Product[] products = {new Book(), new Electronic()};

        ShoppingCartVisitor visitor = new ShoppingCartVisitor();

        for (Product product : products) {
            product.accept(visitor);
        }
    }
}
  • Product 是商品接口,定义了一个接受访问者的方法 accept(Visitor visitor)
  • BookElectronic 是具体商品类,实现了 Product 接口,并实现了 accept 方法,将访问操作委托给传入的访问者。
  • Visitor 是访问者接口,定义了访问不同商品的方法 visit
  • ShoppingCartVisitor 是具体访问者类,实现了 Visitor 接口,提供了访问不同商品的具体逻辑。
  • 在测试类中,我们创建了一些商品对象,并通过访问者访问这些商品。

访问者模式适用于以下一些业务场景:

  1. 复杂对象结构:当对象结构比较复杂,并且包含多种不同类型的对象时,访问者模式可以帮助我们轻松地在对象结构上执行各种操作,而无需修改对象本身的结构。
  2. 操作和数据分离:当需要在不同的对象上执行相似但不同的操作时,可以使用访问者模式将操作从对象中分离出来,以便在不同的对象上执行不同的操作,从而避免了操作与对象之间的紧耦合。
  3. 数据结构稳定,但操作频繁变化:当对象结构相对稳定,但需要经常添加新的操作或功能时,访问者模式可以帮助我们方便地添加新的访问者类,而无需修改现有的对象结构。
  4. 编译器、解析器:在编译器、解析器等语法分析应用中,访问者模式可以帮助我们轻松地遍历语法树,并在不同的节点上执行不同的操作,例如语法分析、优化、生成代码等。
  5. GUI组件操作:在图形用户界面(GUI)开发中,访问者模式可以用于遍历GUI组件树,并在不同的组件上执行不同的操作,例如渲染、事件处理等。
  6. 数据结构算法:在数据结构算法中,访问者模式可以用于对不同类型的数据结构执行不同的算法,例如遍历、搜索、排序等。
posted @ 2024-03-19 15:07  开发者404  阅读(2353)  评论(0编辑  收藏  举报