一文讲完Java常用设计模式(全23种)

文章目录

  • 介绍
  • 设计模式的六大原则
  • 一、创建型模式
    • 1、单例模式(Singleton Pattern)
      • 1)饿汉式
      • 2)懒汉式,双检锁
      • 3)静态内部类
      • 4)枚举
    • 2、原型模式(Prototype Pattern)
    • 3、工厂模式(Factory Pattern)
    • 4、抽象工厂模式(Abstract Factory Pattern)
    • 5、建造者模式(Builder Pattern)
  • 二、结构型模式
    • 1、适配器模式(Adapter Pattern)
    • 2、组合模式(Composite Pattern)
    • 3、装饰器模式(Decorator Pattern)
    • 4、外观模式(Facade Pattern)
    • 5、享元模式(Flyweight Pattern)
    • 6、代理模式(Proxy Pattern)
      • 1)静态代理
      • 2)jdk动态代理
      • 3)cglib动态代理
    • 7、桥接模式(Bridge Pattern)
  • 三、行为型模式
    • 1、责任链模式(Chain of Responsibility Pattern)
    • 2、命令模式(Command Pattern)
    • 3、解释器模式(Interpreter Pattern)
    • 4、迭代器模式(Iterator Pattern)
    • 5、观察者模式(Observer Pattern)
      • Java自带观察者模式介绍
    • 6、状态模式(State Pattern)
    • 7、模板模式(Template Pattern)
    • 8、中介者模式(Mediator Pattern)
    • 9、备忘录模式(Memento Pattern)
    • 10、访问者模式(Visitor Pattern)
    • 11、策略模式(Strategy Pattern)
  • 其他
    • 1、过滤器模式(Filter Pattern)
    • 2、空对象模式(Null Object Pattern)

介绍

设计模式的起源可以追溯到20世纪80年代,当时面向对象编程开始流行。在这个时期,一些软件开发者开始注意到他们在不同的项目中遇到了相同的问题,并且他们开始寻找可重用的解决方案。这些解决方案被称为设计模式。最早提出设计模式的人是Erich Gamma、Richard Helm、Ralph Johnson和John Vlissides,他们在1994年出版了一本名为《设计模式:可复用面向对象软件的基础》的书,这本书成为了设计模式领域的经典著作。自那时以来,设计模式已经成为软件开发中非常重要的概念,并且在不同的编程语言和开发框架中都得到了广泛的应用。

四位作者合称 GOF(四人帮,全拼 Gang of Four)。他们所提出的设计模式主要是基于以下的面向对象设计原则。

  • 对接口编程而不是对实现编程。
  • 优先使用对象组合而不是继承。

设计模式是常见的软件设计问题的可重用解决方案。它们提供了一种解决开发人员在设计软件应用程序时经常遇到的问题的方法。设计模式有几种类型,包括创建型、结构型和行为型模式。

  • 创建型模式用于以灵活和高效的方式创建对象。包括Singleton模式、工厂模式和抽象工厂模式等。
  • 结构型模式用于组合类和对象以形成更大的结构。包括适配器模式、桥接模式和装饰器模式等。
  • 行为型模式用于处理类或对象之间的通信和控制流。包括观察者模式、策略模式和模板方法模式。

设计模式是软件开发中非常有用的工具,可以提高代码的可重用性和可维护性,同时也可以提高开发人员的工作效率。

设计模式的六大原则

  1. 单一职责原则(SRP):一个类只应该有一个引起它变化的原因。
  2. 开放封闭原则(OCP):软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。
  3. 里氏替换原则(LSP):子类型必须能够替换掉它们的父类型。
  4. 依赖倒置原则(DIP):高层模块不应该依赖于低层模块,两者都应该依赖于抽象接口;抽象接口不应该依赖于具体实现,具体实现应该依赖于抽象接口。
  5. 接口隔离原则(ISP):不应该强迫一个类实现它不需要的接口,应该将接口拆分成更小和更具体的部分,以便客户端只需要知道它们感兴趣的部分。
  6. 迪米特法则(LOD):一个对象应该对其他对象有尽可能少的了解,通常称为“最少知识原则”。

一、创建型模式

1、单例模式(Singleton Pattern)

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一,有以下特点:

  • 单例类只能有一个实例。
  • 单例类必须自己创建自己的唯一实例。
  • 单例类必须给所有其他对象提供这一实例。

1)饿汉式

这种方式比较常用,但容易产生垃圾对象
优点:没有加锁,执行效率会提高。
缺点:类加载时就初始化,浪费内存。

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

2)懒汉式,双检锁

在懒汉式基础上加入双重检验锁,保证线程安全和性能。

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

3)静态内部类

使用静态内部类来实现懒汉式单例模式,保证线程安全和性能。这种方式能达到双检锁方式一样的功效,但实现更简单。

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

4)枚举

使用枚举来实现单例模式,保证线程安全和防止反射攻击。
1)将已经有的class转换成枚举单例
PersonEnum.INSTANCE.getInstance()

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

public enum PersonEnum {
    INSTANCE;

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

    public Person getInstance() {
        return instance;
    }
}

2)直接新建枚举类
PersonSingleton.INSTANCE

public enum PersonSingleton {
    INSTANCE;

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

2、原型模式(Prototype Pattern)

原型设计模式允许通过复制现有对象来创建新对象,而不是通过实例化类来创建新对象。
在需要创建大量相似对象时非常有用,它可以避免重复创建对象,从而提高性能,并且可以根据需要实现浅拷贝或深拷贝。
在Java中,原型模式的实现通常涉及到实现Cloneable接口和重写clone()方法。
一文讲完Java常用设计模式(全23种)_第1张图片

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

在上面的代码中,Shape是一个抽象类,它实现了Cloneable接口并重写了clone()方法。Circle和Square是Shape的具体子类,它们实现了draw()方法。ShapeCache类是一个缓存,它存储了Shape对象的副本。PrototypePatternDemo类是一个演示类,它使用ShapeCache来获取Shape对象的副本。

在loadCache()方法中,我们创建了两个Shape对象的副本,并将它们存储在shapeMap中。在main()方法中,我们使用getShape()方法来获取Shape对象的副本,并输出它们的类型。由于我们使用了原型模式,所以我们可以通过复制现有对象来创建新对象,而无需实例化类。

3、工厂模式(Factory Pattern)

通过一个工厂类来实现对象的创建,而无需直接暴露对象的创建逻辑给客户端。
简单工厂模式的优点在于客户端无需了解具体产品类的创建细节,只需通过工厂类来创建对象,并且工厂类可以根据客户端的需求来动态创建不同类型的对象。但是缺点也比较明显,如果需要创建的产品类数量较多,则工厂类的代码会变得很臃肿,不便于维护。
一文讲完Java常用设计模式(全23种)_第2张图片

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

4、抽象工厂模式(Abstract Factory Pattern)

通过定义一个创建对象的接口来创建对象,但将具体实现的决定留给子类来决定。
在抽象工厂模式中,接口是负责创建一个相关对象的工厂,不需要显式指定它们的类。每个生成的工厂都能按照工厂模式提供对象。
一文讲完Java常用设计模式(全23种)_第3张图片

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

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

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

5、建造者模式(Builder Pattern)

建造者模式是一种创建型设计模式,它允许您通过一步一步地构建复杂对象来创建不同类型的对象。它使用一个建造者类来封装对象的创建过程并将其分解为多个简单的步骤。这使得您可以通过更改这些步骤来创建不同类型的对象。
一文讲完Java常用设计模式(全23种)_第4张图片

在这个示例中,我们创建了Car类作为我们想要构建的复杂对象。然后,我们创建了CarBuilder类,该类逐步构建Car对象,并最终返回它。最后,我们使用CarBuilder来构建Car对象。

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

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

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

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

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

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

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

二、结构型模式

1、适配器模式(Adapter Pattern)

Java适配器模式是一种结构型设计模式,它允许不兼容的接口之间进行通信。适配器模式通过将一个类的接口转换为客户端所期望的另一个接口来实现这一点。这种模式可以在不修改现有代码的情况下重用现有类。
适配器模式可以帮助我们在不修改现有代码的情况下重用现有类,并且可以使不兼容的接口之间进行通信。

主要作用:适配器模式将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

在下面的示例中,我们有一个目标接口 Target ,一个源接口 Adaptee 和一个适配器类 Adapter 。适配器类实现了目标接口,并且将请求转发到源接口的特定请求方法。客户端代码创建一个适配器对象,并使用它来调用目标接口的方法。
一文讲完Java常用设计模式(全23种)_第5张图片

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

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

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

2、组合模式(Composite Pattern)

组合模式是一种结构型设计模式,它允许你将对象组合成树形结构以表示“部分-整体”的层次结构。这种模式使得客户端可以统一对待单个对象和对象组合。

在组合模式中,有两种基本类型的对象:叶节点和组合节点。叶节点表示树形结构中的单个对象,而组合节点则表示树形结构中的对象组合。组合节点可以包含其他组合节点和/或叶节点,形成一个递归的树形结构。
一文讲完Java常用设计模式(全23种)_第6张图片

以下是一个使用Java实现组合模式的示例代码,其中我们使用一个抽象类Component来表示树形结构中的节点,包括叶节点和组合节点。组合节点包含子节点,可以通过add()和remove()方法来添加和删除子节点。叶节点没有子节点,但可以实现共同的操作方法。具体的组合节点继承自Component类,实现自己的操作方法。

public interface IComponent {
   void display();
}

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

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

    public abstract void add(IComponent component);

    public abstract void remove(IComponent component);

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

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

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

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

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

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

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

3、装饰器模式(Decorator Pattern)

装饰器模式是一种结构性设计模式,它允许您在不影响同一类的其他对象的行为的情况下,静态或动态地向单个对象添加行为。 当您想要在运行时添加或删除对象的功能时,或者当您想要减少创建不同行为组合所需的子类数量时,此模式非常有用。

在Java中,使用继承和组合的结合来实现装饰器模式。 具体来说,您需要创建一个基类或接口来定义对象的核心行为,然后创建一个或多个装饰器类来向对象添加附加行为。 每个装饰器类都具有对其装饰的对象的引用,并且它可以在委托给对象的原始行为之前或之后修改对象的行为。

装饰器模式适用于以下场景:

  1. 在不修改现有代码的情况下,向现有类添加新的功能。
  2. 在运行时动态地向对象添加新的行为。
  3. 以不同的方式组合对象,以实现不同的行为。

使用装饰器模式时需要注意以下几点:

  1. 装饰器类需要实现与被装饰对象相同的接口,以便可以对被装饰对象进行包装。
  2. 装饰器类应该在调用被装饰对象的方法之前或之后添加新的行为。
  3. 不要创建过多的装饰器对象,否则会导致代码变得复杂难以维护。
    一文讲完Java常用设计模式(全23种)_第7张图片
public interface Pizza {
    public String getDescription();
    public double getCost();
}
 // 具体组件
public class PlainPizza implements Pizza {
    public String getDescription() {
        return "薄饼";
    }
    public double getCost() {
        return 4.00;
    }
}
 // 装饰器
public abstract class ToppingDecorator implements Pizza {
    protected Pizza pizza;
    public ToppingDecorator(Pizza pizza) {
        this.pizza = pizza;
    }
    public String getDescription() {
        return pizza.getDescription();
    }
    public double getCost() {
        return pizza.getCost();
    }
}
 // 具体装饰器
public class Cheese extends ToppingDecorator {
    public Cheese(Pizza pizza) {
        super(pizza);
    }
    public String getDescription() {
        return pizza.getDescription() + ",马苏里拉奶酪";
    }
    public double getCost() {
        return pizza.getCost() + 0.50;
    }
}
 // 具体装饰器
public class Pepperoni extends ToppingDecorator {
    public Pepperoni(Pizza pizza) {
        super(pizza);
    }
    public String getDescription() {
        return pizza.getDescription() + ",意大利辣香肠";
    }
    public double getCost() {
        return pizza.getCost() + 1.00;
    }
}
 // 客户端代码
public class PizzaShop {
    public static void main(String[] args) {
        Pizza pizza = new PlainPizza();
        pizza = new Cheese(pizza);
        pizza = new Pepperoni(pizza);
        System.out.println(pizza.getDescription());
        System.out.println("成本:$" + pizza.getCost());
    }
}

在此示例中,我们有一个 Pizza 接口,它定义了Pizza 的核心行为,包括其描述和成本。 然后我们有一个 PlainPizza 类,它实现了 Pizza 接口。

接下来,我们创建一个抽象的 ToppingDecorator 类,它实现 Pizza 接口并具有对其装饰的 Pizza 对象的引用。 这允许我们向 Pizza 对象添加附加行为而不修改原始对象。

最后,我们创建了两个具体的装饰器类 Cheese 和 Pepperoni ,它们向 Pizza 对象添加附加行为。 每个装饰器类修改 getDescription() 和 getCost() 方法以在委托给原始对象之前或之后添加自己的行为。

在客户端代码中,我们创建一个 PlainPizza 对象,然后用 Cheese 对象和 Pepperoni 对象装饰它。 然后打印出Pizza 的描述和成本以验证装饰器是否修改了原始对象的行为。

4、外观模式(Facade Pattern)

外观模式(Facade Pattern)是一种结构型设计模式,它提供了一个简单的接口来访问复杂系统中的子系统,从而隐藏了子系统的复杂性。外观模式属于对象型模式,它通过创建一个外观类,将客户端与子系统解耦,使得客户端只需要与外观类交互即可完成操作。
一文讲完Java常用设计模式(全23种)_第8张图片

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

在这个示例中,我们有三个子系统类: CPU , Memory 和 HardDrive 。每个类都执行系统的不同功能,例如处理数据,加载内存和读取硬盘数据。

然后,我们创建了一个 ComputerFacade 类,它封装了这三个子系统类,并提供了一个简单的 start() 方法,客户端可以使用该方法启动计算机。在 start() 方法中,我们按照正确的顺序调用每个子系统的方法,以确保计算机正确启动。

最后,在客户端代码中,我们创建了一个 ComputerFacade 对象,并使用 start() 方法启动计算机。由于外观类隐藏了系统的复杂性,客户端代码非常简单。

5、享元模式(Flyweight Pattern)

享元模式(Flyweight Pattern)是一种结构型设计模式,它通过共享对象来减少内存使用和对象创建的开销。享元模式属于对象型模式,它通过创建一个享元工厂来管理共享对象,并在需要时返回已经存在的对象,从而减少对象的创建和销毁次数。
一文讲完Java常用设计模式(全23种)_第9张图片

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

在这个示例中,我们有一个 Shape 接口,它定义了一个 draw() 方法,用于绘制图形。

然后,我们创建了一个具体的享元类 Circle ,它实现了 Shape 接口,并存储了圆的颜色、位置和半径等信息。
接下来,我们创建了一个享元工厂类 ShapeFactory ,它维护了一个 Map ,用于存储已经创建的圆对象。在客户端需要绘制圆时,我们可以通过 ShapeFactory 获取已经存在的圆对象,如果不存在,则创建一个新的圆对象,并将其存储在 Map 中。

最后,在客户端代码中,我们随机生成20个圆,每个圆的颜色、位置和半径都是随机的。通过 ShapeFactory 获取圆对象,并调用它的 draw() 方法来绘制图形。由于相同颜色的圆对象被共享,因此减少了内存使用和对象创建的开销。

6、代理模式(Proxy Pattern)

代理模式是项目中常用的一种设计模式。提供了间接访问目标对象的一种方式;即通过代理对象访问目标对象。
这样做的好处是,可以在不改变原有目标对象的基础上,对目标对象增加额外的扩展功能。
代理模式又分为静态代理、jdk动态代理、cglib动态代理三种实现方式。

三种实现方式各有优点,以及适用的场景:

  • 静态代理:代理类必须非常明确,所以无法做到通用,但是效率也是最高的
  • jdk动态代理:必须基于接口代理,有一定局限性;动态生成字节码文件,可以用于通用业务(性能日志等)
  • cglig动态代理:也是动态生成字节码文件,生成的代理类继承了目标对象
  • spring aop默认代理策略是:如果目标对象实现了接口,则使用jdk动态代理,否则使用cglib代理
  • jdk8之后,jdk动态代理效率要高于cglib代理

1)静态代理

被代理对象与代理对象需要实现相同的接口或者是继承相同父类,因此要定义一个接口或抽象类。

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

    private IHello hello;

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

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

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

2)jdk动态代理

jdk动态代理是基于接口的一种代理方式,目标对象一定要实现接口。

原理是,利用反射机制,动态生成匿名类继承Proxy类并且实现了要代理的接口,由于java不支持多继承,所以JDK动态代理不能代理类

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


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

    private Object target;

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

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

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

/**测试*/
public class Demo2Test {

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

3)cglib动态代理

目标对象可以不用实现接口,不能针对final类进行代理。
原理是,动态生成class继承目标对象。使用cglib必须引入对应的jar包

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

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

    private Object target;

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

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

/**测试*/
public class Demo3Test {

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

7、桥接模式(Bridge Pattern)

桥接模式是一种将抽象化和实现化解耦的设计模式。它使用了封装、聚合以及继承等基本技术,将两个独立变化的维度通过抽象化的方式进行桥接,从而降低了它们之间的耦合度,使得系统更加灵活。
一文讲完Java常用设计模式(全23种)_第10张图片

下面是 Java 中桥接模式实现的示例代码:

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

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

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

输出结果如下:

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

这是一个简单的桥接模式实现,它允许我们在运行时动态地改变 Shape 类的颜色而不用影响到 Shape 子类,同时也允许我们增加新的颜色和形状类而无需改变其它现有的类。

三、行为型模式

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

责任链模式是一种行为设计模式,它允许将请求沿着处理链进行传递,直到有一个处理程序处理它为止。在Java中实现责任链模式通常需要以下几个步骤:

  1. 定义一个处理器接口,该接口包含一个处理请求的方法。
  2. 创建一个抽象处理器类,它实现处理器接口,并包含一个指向下一个处理器的引用。
  3. 创建具体的处理器类,它们继承自抽象处理器类,并实现处理请求的方法。
  4. 在客户端代码中,创建处理器链,并将请求发送到链的第一个处理器。

以下是一个简单的Java实现责任链模式的示例:
一文讲完Java常用设计模式(全23种)_第11张图片

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

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

    public Handler getNextHandler() {
        return nextHandler;
    }
}

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

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

    public String getType() {
        return type;
    }

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

输入结果:

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

以上代码演示了如何创建一个处理器链,并将请求沿着链传递,直到有一个处理程序处理它为止。在这个例子中,ConcreteHandler1、ConcreteHandler2和ConcreteHandler3都是具体的处理器类,它们继承自AbstractHandler类,并实现handleRequest方法。客户端代码创建了一个处理器链,并将请求发送到链的第一个处理器。当请求到达处理器时,它会检查请求类型是否与处理器可以处理的类型匹配。如果是,处理器将处理请求。否则,它将请求传递给链中的下一个处理器,直到有一个处理程序处理它为止。

2、命令模式(Command Pattern)

命令模式是一种行为设计模式,它允许将请求封装为一个对象,从而使不同的请求可以使用不同的参数进行参数化,队列或日志请求,以及支持可撤销的操作。在Java中,命令模式的实现通常涉及一个命令接口和一个或多个具体命令类,这些类实现了命令接口,并在其execute()方法中定义了实际的命令逻辑。此外,命令模式还可能涉及一个调用者类,该类将一个或多个命令对象与接收者对象关联起来,并在需要时调用它们的execute()方法。命令模式是一种非常灵活和可扩展的模式,可以用于许多不同的应用程序场景。
一文讲完Java常用设计模式(全23种)_第12张图片

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

执行输出:

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

3、解释器模式(Interpreter Pattern)

Java解释器模式是一种行为设计模式,它定义了一种语言和该语言的解释器,使得可以使用该语言来表达一些特定的操作。该模式适用于需要解释一些特定语言的场景,例如编译器、表达式计算器等。

在Java中,可以使用抽象语法树(AST)来表示语言表达式,并使用解释器来执行这些表达式。解释器模式通常包括以下几个组件:

  • 抽象表达式(Abstract Expression):定义了一个抽象的解释器接口,该接口包含了解释器需要实现的方法。
  • 终结符表达式(Terminal Expression):实现了抽象表达式接口的终结符,用于表示语言中的基本操作或值。
  • 非终结符表达式(Non-Terminal Expression):实现了抽象表达式接口的非终结符,用于表示语言中的复杂操作。
  • 上下文(Context):包含了解释器需要的一些全局信息,例如变量、函数等。
  • 解释器(Interpreter):使用上述组件来解释语言表达式,并执行相应的操作。
    一文讲完Java常用设计模式(全23种)_第13张图片

以下是一个简单的Java解释器模式示例代码:

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

在上面的示例代码中,我们定义了两个终结符表达式(NumberExpression)和一个非终结符表达式(AddExpression),用于表示加法操作。我们还定义了一个上下文(Context),用于存储变量和函数,以及一个解释器(Interpreter),用于执行表达式。最后,我们使用解释器执行了一个简单的加法表达式,并打印出了结果。

4、迭代器模式(Iterator Pattern)

Java迭代器模式是一种行为设计模式,它提供了一种访问集合对象元素的方法,而不需要暴露该对象的内部表示。该模式适用于需要遍历集合对象的场景,例如数组、列表、树等。
一文讲完Java常用设计模式(全23种)_第14张图片

下面是一个简单的Java迭代器模式示例代码,使用数组存储,实现了一个数组迭代器:

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

在上面的示例代码中,我们定义了抽象迭代器接口(Iterator),用于表示迭代器对象。我们还定义了具体迭代器实现类(ArrayIterator),用于实现数组迭代器。最后,我们使用迭代器遍历了一个整型数组,并打印出了每个元素的值。

5、观察者模式(Observer Pattern)

Java观察者模式是一种行为设计模式,它定义了一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都会得到通知并自动更新。该模式适用于需要在对象间建立动态的、松散耦合的关系的场景,例如事件处理等。

观察者模式适用于以下场景:

  1. 当一个对象的状态发生改变时,需要通知其他对象并更新它们的状态。
  2. 当一个对象需要将自己的状态变化通知给其他对象,但是又不希望与这些对象之间产生紧密的耦合关系。
  3. 当一个对象的改变需要同时改变其他对象的状态,而且不知道具体有多少对象需要改变。
  4. 当一个对象的改变需要其他对象的协助,但是不希望这些对象与自己产生紧密的耦合关系。
  5. 当一个对象的改变会引起一系列的连锁反应,而且这些连锁反应的具体实现不希望被该对象所知道。
  6. 当一个对象需要向其他对象提供一种数据更新的方式,而且这种更新方式需要在不同的时间点通知不同的对象。

总之,观察者模式适用于需要在对象间建立动态的、松散耦合的关系的场景,例如事件处理、GUI编程、消息队列等。
一文讲完Java常用设计模式(全23种)_第15张图片

下面是一个简单的Java观察者模式示例代码,实现了一个气象站,当气象数据发生改变时,会通知所有的观察者对象:

import java.util.ArrayList;
import java.util.List;
 // 抽象主题接口
interface Subject {
    void registerObserver(Observer observer);
    void removeObserver(Observer observer);
    void notifyObservers();
}
// 具体观察者实现类
class Display implements Observer {
    private float temperature;
    private float humidity;
    private float pressure;
    private String name;

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

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

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

    public void display() {
        System.out.println("Temperature: " + temperature);
        System.out.println("Humidity: " + humidity);
        System.out.println("Pressure: " + pressure);
    }
}
 // 抽象观察者接口
interface Observer {
    void update(float temperature, float humidity, float pressure);
    String name();
}
 // 具体观察者实现类
class Display implements Observer {
    private float temperature;
    private float humidity;
    private float pressure;
     public void update(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        display();
    }
     public void display() {
        System.out.println("Temperature: " + temperature);
        System.out.println("Humidity: " + humidity);
        System.out.println("Pressure: " + pressure);
    }
}
 // 使用观察者模式实现气象站
public class Main {
    public static void main(String[] args) {
        WeatherStation weatherStation = new WeatherStation();
        Display display1 = new Display();
        Display display2 = new Display();
        weatherStation.registerObserver(display1);
        weatherStation.registerObserver(display2);
        weatherStation.setMeasurements(25.0f, 60.0f, 1013.0f);
        weatherStation.removeObserver(display2);
        weatherStation.setMeasurements(26.0f, 65.0f, 1012.0f);
    }
}

执行输入结果:

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

在上面的示例代码中,我们定义了抽象主题接口(Subject)和抽象观察者接口(Observer),用于表示主题和观察者对象。我们还定义了具体主题实现类(WeatherStation)和具体观察者实现类(Display),用于实现气象站和显示器对象。最后,我们使用观察者模式实现了一个气象站,当气象数据发生改变时,会通知所有的观察者对象,并更新显示器的数据。

Java自带观察者模式介绍

Java提供的一种内置的观察者模式实现。它使用了Java中的Observable类和Observer接口来实现观察者模式。

Observable类是一个抽象类,它表示一个可观察的对象,具有添加、删除和通知观察者的方法。当Observable对象的状态发生改变时,会调用它的notifyObservers()方法,通知所有的观察者对象,并更新它们的状态。Observable类还提供了setChanged()方法和clearChanged()方法,用于标记Observable对象的状态是否发生了改变。

Observer接口表示观察者对象,具有更新状态的方法update()。当Observable对象的状态发生改变时,会调用观察者对象的update()方法,传递更新的数据。
一文讲完Java常用设计模式(全23种)_第16张图片

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

在上面的示例代码中,我们使用了Observable类和Observer接口来实现气象站和显示器对象。当气象数据发生改变时,Observable对象会调用notifyObservers()方法,通知所有的观察者对象,并更新它们的状态。观察者对象实现Observer接口的update()方法,用于更新自己的状态。

6、状态模式(State Pattern)

Java状态模式是一种行为型设计模式,它允许对象在内部状态改变时改变它的行为。状态模式将状态封装成独立的类,并将请求委托给当前状态对象,从而实现状态的切换和状态行为的变化。

使用状态模式的场景包括:

  1. 当一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为时。
  2. 当一个对象需要根据状态改变它的数据和方法时。
  3. 当一个对象需要在多个状态之间切换,并且每个状态都有不同的行为时。

注意事项:

  1. 状态模式可以增加类的数量,因此需要在设计时考虑类的数量和复杂性。
  2. 状态模式需要对状态进行封装,因此需要在设计时考虑状态的可扩展性和可维护性。
    一文讲完Java常用设计模式(全23种)_第17张图片

下面是一个使用Java状态模式实现的示例代码:

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

在上面的示例代码中,我们定义了一个状态接口State和两个具体状态类ConcreteState1和ConcreteState2。我们还定义了一个环境类Context,它包含一个状态对象,并定义了一个请求方法request(),用于调用当前状态对象的handle()方法。在客户端代码中,我们创建了一个Context对象,并设置它的状态为ConcreteState1,然后调用request()方法,输出"ConcreteState1 is handling.“。接着,我们将Context的状态设置为ConcreteState2,并再次调用request()方法,输出"ConcreteState2 is handling.”。

通过使用状态模式,我们可以将状态和行为分离,使得对象的行为可以随着状态的改变而改变,从而实现更加灵活的设计。

7、模板模式(Template Pattern)

Java模板模式是一种行为设计模式,它定义了一个操作中的程序骨架,将一些步骤延迟到子类中实现。这使得子类可以在不改变程序结构的情况下重新定义程序中的某些步骤。

在Java模板模式中,有两种类型的方法:抽象方法和具体方法。抽象方法由子类实现,而具体方法由父类实现。模板方法是由具体方法和抽象方法组成的,它定义了程序的骨架,而具体方法则实现了算法的部分步骤。

Java模板模式适用于以下场景:

  1. 当需要定义一组算法,并且这些算法的结构相似,但是实现细节不同时,可以使用模板模式。
  2. 当需要控制算法的流程,并且在算法的不同阶段需要不同的行为时,可以使用模板模式。
  3. 当需要在不影响算法整体结构的情况下,对算法的某些步骤进行定制化时,可以使用模板模式。
  4. 当需要在多个类中使用相同的算法时,可以使用模板模式,避免代码重复。

总之,Java模板模式适用于那些需要在程序中定义骨架,并且在子类中实现具体步骤的情况。它可以提高代码的复用性和可维护性,同时也可以使代码更加灵活和可扩展。
一文讲完Java常用设计模式(全23种)_第18张图片

下面是一个简单的Java模板模式示例代码:

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

在上面的代码中,Game类是一个抽象类,定义了一个play()方法作为模板方法。Cricket和Football类是具体的实现类,它们实现了抽象类中定义的抽象方法。在main()方法中,我们创建了一个Cricket对象和一个Football对象,并调用它们的play()方法。这样我们就可以在不改变算法结构的情况下重新定义算法中的某些步骤。

8、中介者模式(Mediator Pattern)

Java中介者模式是一种行为设计模式,它允许对象之间通过中介者对象进行通信,从而避免对象之间的直接耦合。中介者模式将对象之间的复杂关系转化为中介者和对象之间的简单关系,从而提高了系统的灵活性和可维护性。

在Java中介者模式中,中介者对象负责协调对象之间的通信,它通常包含一些公共方法,用于处理对象之间的交互。对象之间的通信通过中介者对象进行,从而避免了对象之间的直接耦合。
一文讲完Java常用设计模式(全23种)_第19张图片

下面是一个简单的Java中介者模式示例代码:

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

在上面的代码中,Mediator接口定义了send()方法,用于处理对象之间的交互。Colleague抽象类表示对象,它包含一个Mediator对象,用于处理对象之间的通信。ConcreteColleague1和ConcreteColleague2是具体的对象实现类,它们实现了Colleague抽象类中的方法。ConcreteMediator是具体的中介者实现类,它负责协调对象之间的通信。

在main()方法中,我们创建了一个ConcreteMediator对象和两个ConcreteColleague对象,并调用它们的send()方法进行通信。通过中介者对象进行通信,避免了对象之间的直接耦合。

9、备忘录模式(Memento Pattern)

Java备忘录模式是一种行为设计模式,它允许在不破坏封装性的情况下捕获和恢复对象的内部状态。备忘录模式通常用于需要撤销操作或恢复先前状态的情况下。该模式包括三个主要组件:原始对象、备忘录对象和负责管理备忘录对象的对象。
一文讲完Java常用设计模式(全23种)_第20张图片

下面是一个简单的Java备忘录模式示例代码:

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

在上面的代码中,Originator类表示原始对象,它包含需要保存的状态。createMemento()方法创建备忘录对象,并将当前状态保存到备忘录对象中。restore()方法用于从备忘录对象中恢复先前的状态。

Memento类表示备忘录对象,它包含需要保存的状态。

Caretaker类负责管理备忘录对象,它包含一个Memento对象。

在main()方法中,我们创建了一个Originator对象和一个Caretaker对象,并调用它们的方法进行状态保存和恢复。通过备忘录模式,我们可以在不破坏封装性的情况下捕获和恢复对象的内部状态。

10、访问者模式(Visitor Pattern)

Java访问者模式是一种行为型设计模式,它允许你在不修改对象结构的前提下定义新的操作。访问者模式将对象结构和操作分离开来,使得操作可以独立地应用于对象结构中的元素。在访问者模式中,有两个主要角色:访问者和元素。访问者定义了对元素进行操作的方法,而元素则提供了接受访问者的方法。

下面是一个访问者模式的示例代码,其中我们将实现一个简单的计算器,它可以对表达式进行计算。我们将使用访问者模式来遍历表达式树,并对每个节点执行相应的操作。

访问者模式适用于以下场景:

  1. 对象结构比较稳定,但经常需要在此对象结构上定义新的操作。
  2. 需要对一个对象结构中的对象进行很多不同的且不相关的操作,而不希望这些操作“污染”这些对象的类。
  3. 对象结构中对象的类很少改变,但经常需要在这些对象上定义新的操作。
  4. 需要在运行时根据对象的类型执行不同的操作。
  5. 需要对对象结构中的所有对象进行某种类型的处理,但是对象所采取的处理方式根据对象类型不同而异。

常见的使用访问者模式的场景包括:编译器的语法分析、XML文档解析、静态分析器、模型验证器、模型转换器等。

一文讲完Java常用设计模式(全23种)_第21张图片

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

输出结果:

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

上面的例子中,我们定了一个表达式接口Expression,并提供了两个表达式实现类数字表达式NumberExpression和加法表达式AdditionExpression,定义了一个访问者接口Visitor,以及两个具体访问者CalculatorVisitor、PrinterVisitor,两个访问者接收表达式对象,并在访问者中对表达式实现具体操作,分别是表达式运算以及表达式打印。上面例子没有改变具体的表达式类,且定义了新的操作。

11、策略模式(Strategy Pattern)

策略模式是一种行为型设计模式,它允许您定义一系列算法,将每个算法封装起来,并使它们可以互换使用。这种模式使得算法可以独立于使用它们的客户端而变化。

在Java中,策略模式通常由一个接口和多个实现该接口的类组成。客户端将使用该接口来调用算法,而不是直接调用实现类。这样,客户端就可以在运行时选择不同的算法实现,而不需要修改代码。
一文讲完Java常用设计模式(全23种)_第22张图片

下面的例子展示了如何使用策略模式来实现一个简单的支付系统。我们定义了一个接口 PaymentStrategy ,并创建了多个实现该接口的类,每个类代表一种不同的支付方式。客户端可以根据需要选择使用哪种支付方式。

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

执行输出:

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

在上面的示例中, PaymentStrategy 接口定义了一种支付方式,并包含一个 pay 方法,该方法接受一个金额参数。我们创建了三个实现该接口的类,分别代表信用卡支付、PayPal支付和现金支付。 PaymentProcessor 类接受一个 PaymentStrategy 实例作为参数,并使用它来执行支付操作。在 main 方法中,我们创建了一个 PaymentProcessor 实例,并使用不同的支付方式来进行支付。

其他

下面再介绍两种常用的设计模式

1、过滤器模式(Filter Pattern)

Java过滤器设计模式是一种常用的设计模式,用于在请求到达目标对象之前或之后,对请求进行处理或过滤。该模式可以用于实现不同的功能,如验证、授权、日志记录、压缩等,将不同的操作当作过滤链中的一个过滤器。
一文讲完Java常用设计模式(全23种)_第23张图片
下面是一个简单的Java过滤器设计模式的示例代码:

  • 首先,定义一个接口Filter,该接口包含一个方法doFilter,用于处理请求
  • 然后,实现两个过滤器类,一个用于验证请求,另一个用于记录日志
  • 最后,定义一个FilterChain类,用于将多个过滤器链接在一起
public interface Filter {
    public void doFilter(String request);
}
// 定义授权过滤器
public class AuthenticationFilter implements Filter {
    public void doFilter(String request) {
        System.out.println("Authenticating request: " + request);
    }
}
// 定义日志过滤器
public class LoggingFilter implements Filter {
    public void doFilter(String request) {
        System.out.println("Logging request: " + request);
    }
}
// 定义过滤器链
public class FilterChain {
    private List<Filter> filters = new ArrayList<Filter>();
    private int index = 0;

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

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

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

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

        chain.doFilter("request");
    }
}

上述示例代码展示了Java过滤器设计模式的基本实现方法。通过定义不同的过滤器类来链接它们,可以实现不同的功能。

2、空对象模式(Null Object Pattern)

Java空对象模式是一种行为设计模式,它允许我们在不返回null的情况下提供默认行为。这种模式通常用于需要处理null对象的情况下,以避免NullPointerException异常。该模式包括两个主要组件:抽象类和具体类。

下面是一个简单的Java空对象模式示例代码:
一文讲完Java常用设计模式(全23种)_第24张图片

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

在上面的代码中,我们定义了一个接口User,它表示一个用户,并包含两个方法:getName()和hasAccess()。接着,我们定义了一个具体类RealUser,它表示一个真实的用户,并实现了User接口的两个方法。我们还定义了一个空对象NullUser,它表示一个空的用户,并实现了User接口的两个方法。最后,我们定义了一个工厂类UserFactory,用于创建用户。如果传入的名称为null,则返回一个空对象NullUser,否则返回一个真实的用户RealUser。

在main()方法中,我们使用UserFactory创建了两个用户user1和user2,并打印它们的名称和是否有权限。由于user2的名称为null,因此它将返回一个空对象NullUser,并打印出"Guest"字符串和false布尔值。通过空对象模式,我们可以在不返回null的情况下提供默认行为,避免NullPointerException异常。

你可能感兴趣的:(java,设计模式)