写给我的朋友-《Java当中的设计模式》

667 阅读30分钟

java的设计模式有哪些?

Java 的设计模式按照 Gang of Four(GoF)的分类方式可以分为以下三类:

  1. 创建型模式:这些模式涉及对象的创建机制,包括对象的实例化,通过子类化来创建对象等。

    • 工厂方法模式(Factory Method Pattern)
    • 抽象工厂模式(Abstract Factory Pattern)
    • 单例模式(Singleton Pattern)
    • 建造者模式(Builder Pattern)
    • 原型模式(Prototype Pattern)
  2. 结构型模式:这些模式涉及如何将类或对象组合成更大的结构,以满足新的功能需求。

    • 适配器模式(Adapter Pattern)
    • 桥接模式(Bridge Pattern)
    • 组合模式(Composite Pattern)
    • 装饰器模式(Decorator Pattern)
    • 外观模式(Facade Pattern)
    • 享元模式(Flyweight Pattern)
    • 代理模式(Proxy Pattern)
  3. 行为型模式:这些模式涉及对象之间的通信机制,以及如何灵活地将责任分配给对象。

    • 职责链模式(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)
常用的设计模式有哪些
Tomcat中常用的设计模式
  1. 责任链模式(Chain of Responsibility Pattern):Tomcat中的过滤器(Filter)机制就是基于责任链模式实现的。过滤器将请求和响应传递给下一个过滤器,最终传递给Servlet或JSP进行处理。
  2. 适配器模式(Adapter Pattern):Tomcat中的Servlet容器和JSP引擎支持多种Servlet API和JSP API的版本。使用适配器模式,Tomcat可以适应不同的API版本,从而兼容不同的Web应用程序。
  3. 工厂模式(Factory Pattern):Tomcat使用工厂模式来创建和管理Servlet实例、JSP实例和其他组件。
  4. 单例模式(Singleton Pattern):Tomcat中的多个组件都使用了单例模式,例如连接池、线程池、缓存等。
  5. 命令模式(Command Pattern):Tomcat中的Web应用程序框架使用命令模式来处理HTTP请求和响应。每个HTTP请求都被封装成一个命令对象,该对象包含了请求的参数、HTTP方法、URI等信息,同时还包含了处理该请求的Servlet或JSP的引用。
  6. 观察者模式(Observer Pattern):Tomcat中的Session管理机制使用观察者模式来实现Session的销毁和超时。当Session销毁或超时时,观察者对象将被通知并执行相应的操作。 以下是Spring中常用的设计模式:
Spring中常用的设计模式
  1. 工厂模式(Factory Pattern):Spring框架使用工厂模式来创建和管理对象。例如,Spring容器使用BeanFactory来创建和管理Bean对象。
  2. 代理模式(Proxy Pattern):Spring框架使用代理模式来实现AOP(面向切面编程)功能。Spring AOP通过代理对象来实现对目标对象的增强功能。
  3. 模板方法模式(Template Method Pattern):Spring框架中的JdbcTemplate和HibernateTemplate都使用了模板方法模式来简化对数据库的访问。
  4. 观察者模式(Observer Pattern):Spring框架的事件机制使用观察者模式来实现。应用程序可以注册监听器来监听特定的事件,当事件发生时,观察者对象将被通知并执行相应的操作。
  5. 适配器模式(Adapter Pattern):Spring框架的MVC框架使用适配器模式来支持不同类型的请求处理器。适配器将请求委派给正确的处理器,从而使得应用程序能够处理多种类型的请求。
工厂方法模式
工厂方法模式是什么

工厂方法模式它定义了一个用于创建对象的接口,但是让子类决定实例化哪个类。换句话说,工厂方法模式提供了一种封装对象创建过程的方式。

在工厂方法模式中,通常有一个抽象工厂类或接口来定义工厂方法,而具体的工厂子类会实现这个工厂方法以创建产品对象。这个工厂方法可以是一个简单的工厂方法,也可以是一个工厂方法模式,这取决于具体的实现。

工厂方法模式的优点在于它能够很好地封装对象的创建过程,使得调用方不需要关心对象的创建细节,只需要通过工厂方法得到需要的对象即可。此外,工厂方法模式也符合开闭原则,即对扩展开放,对修改关闭,因为在需要添加新的产品时,只需要添加一个新的具体产品类和对应的具体工厂类即可,不需要修改已有的代码。

一个简单的工厂方法模式示例是创建一个“水果工厂”,可以有不同的具体水果工厂(例如苹果工厂、香蕉工厂、橙子工厂等),每个具体工厂可以生产不同的水果(例如苹果、香蕉、橙子等),调用方只需要通过工厂方法(例如getFruit())得到需要的水果即可。

如何在代码中实现工厂模式

在 Java 代码中,实现工厂方法模式需要以下步骤:

  1. 创建一个抽象产品类或接口,它定义了产品的基本属性和方法。
public interface Product {
    void use();
}
  1. 创建一个具体产品类,它实现了抽象产品类或接口中定义的方法。
public class ConcreteProduct implements Product {
    public void use() {
        System.out.println("使用具体产品");
    }
}
  1. 创建一个抽象工厂类或接口,它声明了工厂方法,用于创建产品对象。
public interface Factory {
    Product createProduct();
}
  1. 创建具体工厂类,它实现了抽象工厂类或接口中的工厂方法,并且返回一个具体产品对象。
public class ConcreteFactory implements Factory {
    public Product createProduct() {
        return new ConcreteProduct();
    }
}
  1. 在调用方使用工厂方法来创建具体的产品对象,而不是直接实例化具体产品类。
public class Test {
    public static void main(String[] args) {
        Factory factory = new ConcreteFactory();
        Product product = factory.createProduct();
        product.use();
    }
}

这样,调用方只需要使用工厂方法即可创建产品对象,而不需要知道具体的产品类或工厂类的实现细节。同时,如果需要添加新的产品类,只需要创建新的具体产品类和对应的具体工厂类即可,无需修改已有的代码。

抽象工厂模式
抽象工厂模式是什么

抽象工厂模式(Abstract Factory Pattern),它提供了一种封装一组相关或相互依赖对象的方式,而不需要指定它们具体的类。抽象工厂模式可以用于创建一组具有相同主题的产品,而这些产品可以是单个对象或对象的家族。

在抽象工厂模式中,通常有一个抽象工厂类或接口来定义工厂方法,而具体的工厂子类会实现这个工厂方法以创建产品对象。这个工厂方法可以创建一组相关的产品对象,而不是单个产品对象。

抽象工厂模式的优点在于它能够很好地封装对象的创建过程,使得调用方不需要关心对象的创建细节,只需要通过工厂方法得到需要的对象即可。此外,抽象工厂模式也符合开闭原则,即对扩展开放,对修改关闭,因为在需要添加新的产品时,只需要添加一个新的具体产品类和对应的具体工厂类即可,不需要修改已有的代码。

一个简单的抽象工厂模式示例是创建一个“水果工厂”,可以有不同的具体水果工厂(例如苹果工厂、香蕉工厂、橙子工厂等),每个具体工厂可以生产不同的水果(例如苹果、香蕉、橙子等),同时每个具体工厂也可以生产对应的“包装盒”,这些产品可以被视为一个产品家族。调用方只需要通过工厂方法(例如getFruitFactory())得到需要的水果工厂,然后使用工厂方法(例如createFruit())创建具体的水果和包装盒即可。

如何在java代码中实现抽象工厂模式

在 Java 代码中,实现抽象工厂模式需要以下步骤:

  1. 创建抽象产品类或接口,它定义了产品的基本属性和方法。
public interface Product {
    void use();
}
  1. 创建具体产品类,它实现了抽象产品类或接口中定义的方法。
public class ConcreteProductA implements Product {
    public void use() {
        System.out.println("使用具体产品A");
    }
}

public class ConcreteProductB implements Product {
    public void use() {
        System.out.println("使用具体产品B");
    }
}
  1. 创建抽象工厂类或接口,它定义了工厂方法,用于创建一组相关的产品对象。
public interface Factory {
    Product createProductA();
    Product createProductB();
}
  1. 创建具体工厂类,它实现了抽象工厂类或接口中的工厂方法,并且返回一组相关的具体产品对象。
public class ConcreteFactory implements Factory {
    public Product createProductA() {
        return new ConcreteProductA();
    }

    public Product createProductB() {
        return new ConcreteProductB();
    }
}
  1. 在调用方中使用抽象工厂对象来创建一组相关的产品对象,而不需要知道具体的产品类或工厂类的实现细节。
public class Client {
    public static void main(String[] args) {
        Factory factory = new ConcreteFactory();
        Product productA = factory.createProductA();
        Product productB = factory.createProductB();
        productA.use();
        productB.use();
    }
}

这样,调用方只需要通过抽象工厂对象来创建一组相关的产品对象即可,而不需要知道具体的产品类或工厂类的实现细节。如果需要添加新的产品类或工厂类,只需要创建新的具体产品类和对应的具体工厂类即可,无需修改已有的代码。

单例模式
什么是单例模式

单例模式(Singleton Pattern)它确保一个类只有一个实例,并提供了一个全局访问点,使得该实例可以被其他对象轻松访问。

单例模式常常用于管理共享资源或限制某些操作的实例数量。它能够避免不必要的内存分配和对象复制,提高系统性能和可维护性。

单例模式通常有两种实现方式:懒汉式和饿汉式。懒汉式是在第一次使用时才创建实例,而饿汉式则是在类加载时就创建实例。

懒汉式的实现通常需要考虑线程安全问题,因为在多线程环境下,可能会出现多个线程同时创建实例的情况。为了解决这个问题,可以使用同步锁或双重检查锁定等方式。

下面是一个简单的饿汉式单例模式的示例代码:

public class Singleton {
    // 饿汉式
    private static Singleton instance = new Singleton();

    private Singleton() {}

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

在上述示例代码中,我们使用了饿汉式的方式来创建单例对象。由于在类加载时就创建了实例,因此在 getInstance() 方法中直接返回该实例即可。在此示例中,我们使用了 private 访问修饰符来防止其他类创建该类的实例,并将构造方法设为私有,使得该类只能在内部创建实例。

下面是一个使用懒汉式实现的单例模式示例代码:

public class Singleton {
    // 懒汉式,需要考虑线程安全问题
    private static Singleton instance;

    private Singleton() {}

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

在上述示例代码中,我们使用了懒汉式的方式来创建单例对象。由于在第一次调用 getInstance() 方法时才创建实例,因此需要考虑线程安全问题。为了避免多个线程同时创建实例,我们使用 synchronized 关键字来同步 getInstance() 方法。这样可以确保在同一时刻只有一个线程能够进入该方法,从而避免了多个线程同时创建实例的问题。

需要注意的是,虽然使用 synchronized 关键字可以解决线程安全问题,但是它会导致性能下降。因为每次调用 getInstance() 方法都需要获取同步锁,而同步锁是一种较重的操作。因此,当程序需要频繁创建实例时,建议使用饿汉式的方式来创建单例对象,而当程序需要延迟加载实例时,可以使用懒汉式的方式。

建造者模式
什么是建造者模式

建造者模式(Builder Pattern)是一种创建型设计模式,它可以将一个复杂对象的构建过程和其表示分离开来,使得同样的构建过程可以创建不同的表示。换句话说,建造者模式将一个复杂对象的创建过程分解为一系列简单的步骤,以便于控制复杂对象的创建过程。

在建造者模式中,通常会定义一个 Builder 接口,该接口定义了创建复杂对象所需的一系列方法,例如 setXXX() 方法用于设置对象的属性值,build() 方法用于创建对象等。同时还会定义一个 Director 类,该类负责组合 Builder 中的各个方法,以便于创建复杂对象。具体的 Builder 实现类则根据具体的业务需求来实现 Builder 接口中的方法。

如何在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 interface Builder {
    void buildPartA(String partA);
    void buildPartB(String partB);
    void buildPartC(String partC);
    Product build();
}

接着,我们实现具体的建造者类,例如:

public class ConcreteBuilder implements Builder {
    private Product product;

    public ConcreteBuilder() {
        this.product = new Product();
    }

    @Override
    public void buildPartA(String partA) {
        product.setPartA(partA);
    }

    @Override
    public void buildPartB(String partB) {
        product.setPartB(partB);
    }

    @Override
    public void buildPartC(String partC) {
        product.setPartC(partC);
    }

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

在上述代码中,我们实现了 Builder 接口,并定义了一个具体的建造者类 ConcreteBuilder。在 ConcreteBuilder 中,我们首先初始化一个 Product 对象,然后根据不同的业务需求实现了 buildPartA、buildPartB 和 buildPartC 方法来设置 Product 对象的各个部分,最后通过 build 方法返回创建好的 Product 对象。

最后,我们定义一个指挥者类,该类负责使用 Builder 来创建复杂对象,例如:

public class Director {
    private Builder builder;

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

    public void construct() {
        builder.buildPartA("Part A");
        builder.buildPartB("Part B");
        builder.buildPartC("Part C");
    }
}

在上述代码中,我们定义了一个 Director 类,并在该类的 construct 方法中使用具体的 Builder 来创建复杂对象。最后,我们可以通过如下代码来使用建造者模式创建复杂对象:

Builder builder = new ConcreteBuilder();
Director director = new Director(builder);
director.construct();
Product product = builder.build();

在上述代码中,我们首先创建了一个具体的 Builder 对象 ConcreteBuilder,然后将该对象传递给 Director 构造函数中。接着,我们调用 Director 的 construct 方法来组合 Builder 中的各个方法,最后通过 Builder 的 build 方法来获取创建好的 Product 对象。

原型模式
什么是原型模式

原型模式(Prototype Pattern)其核心思想是通过克隆已有的对象来创建新的对象。在原型模式中,我们定义一个原型类,它包含一个 clone() 方法,该方法可以用于克隆当前对象并返回一个新的对象。使用原型模式创建对象时,我们首先创建一个原型对象,然后通过克隆这个原型对象来创建新的对象,从而避免了重复的对象创建过程。

原型模式的优点在于,它能够避免在每次创建对象时都进行初始化操作,从而提高了对象的创建效率。同时,由于使用原型模式创建的对象都是通过克隆已有的对象来创建的,因此它们的属性值和方法都已经经过了测试和调试,能够保证对象的正确性和稳定性。

原型模式的缺点在于,由于 Java 语言中的对象克隆方法是在 Object 类中定义的,因此需要对每个要克隆的类进行深入的理解和修改,才能实现正确的对象克隆操作。另外,由于克隆方法是在 Object 类中定义的,因此它只能克隆基本类型和引用类型的值,而不能克隆类的状态和行为,这也是原型模式的一个局限。

在 Java 中,原型模式的实现需要实现 Cloneable 接口,并重写 clone() 方法,以便实现对象的克隆操作。同时,我们还需要注意在实现克隆方法时,要考虑到对象的深克隆和浅克隆问题。

如何在代码中实现原型模式

实现原型模式需要注意以下几个步骤:

  1. 首先,在需要实现原型模式的类中实现 Cloneable 接口,并重写 clone() 方法。
  2. 在 clone() 方法中,首先调用 super.clone() 方法,以获得一个浅克隆的对象。
  3. 如果需要进行深克隆操作,则需要在 clone() 方法中将需要克隆的属性值也进行克隆。
  4. 在调用方代码中,通过调用原型对象的 clone() 方法来创建新的对象。

代码示例:

// 实现原型模式的类需要实现 Cloneable 接口
public class Prototype implements Cloneable {
    private String name;
    private int age;

    public Prototype(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // 重写 clone() 方法
    @Override
    public Prototype clone() {
        try {
            // 调用 super.clone() 方法,获得一个浅克隆的对象
            Prototype clone = (Prototype) super.clone();
            // 如果需要进行深克隆操作,可以在此处进行克隆
            return clone;
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
    }

    // getter 和 setter 方法
    // ...
}


public class Test {
    public static void main(String[] args) {
        Prototype prototype = new Prototype("张三", 20);
        Prototype clone = prototype.clone();
        System.out.println(clone.getName()); // 输出 "张三"
        System.out.println(clone.getAge()); // 输出 "20"
    }
}

在上面的示例代码中,我们实现了一个 Prototype 类,并重写了它的 clone() 方法,以便实现对象的克隆操作。在测试代码中,我们首先创建一个原型对象 prototype,然后通过调用 prototype 的 clone() 方法来创建一个新的对象 clone。由于 clone() 方法是原型模式的核心方法,它会根据原型对象创建一个新的对象并返回,因此我们可以通过调用 clone 对象的 getter 方法来获取克隆后的属性值。

适配器模式
什么是适配器模式

适配器模式它允许将一个类的接口转换成另一个调用方所期望的接口,从而使原本不兼容的类可以协同工作。适配器模式的核心思想是创建一个适配器类,该类将调用方所期望的接口转换成被适配者的接口,从而使得调用方可以通过适配器调用被适配者的方法。

适配器模式包含以下几个角色:

  1. 目标接口(Target):调用方所期望的接口,也是适配器所要实现的接口。
  2. 被适配者(Adaptee):需要被适配的类,它所提供的接口与调用方所期望的接口不同。
  3. 适配器(Adapter):实现调用方所期望的接口,同时包含一个被适配者的引用,通过调用被适配者的方法来实现调用方所期望的接口。

适配器模式的实现方式通常有两种:类适配器和对象适配器。类适配器使用继承来实现适配器的功能,它将适配器类继承自被适配者类和目标接口,从而实现调用方所期望的接口。对象适配器使用组合来实现适配器的功能,它将适配器类作为一个包含被适配者对象的属性,从而实现调用方所期望的接口。

适配器模式通常用于以下场景:

  1. 当调用方需要访问一个现有的类,而这个类的接口与调用方所期望的接口不兼容时,可以使用适配器模式来实现接口的转换。
  2. 当需要使用一个已经存在的类,但是由于其接口与其他类不兼容时,可以使用适配器模式来适配该类。
  3. 当需要重用一些现有的类,但是其接口与系统中的其他类不一致时,可以使用适配器模式来统一接口,以方便系统的维护和扩展。
如何在代码中实现适配器模式

适配器模式,通常有两种方式:类适配器模式和对象适配器模式

类适配器模式,代码示例:

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

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

// 适配器类
public class Adapter extends Adaptee implements Target {
    @Override
    public void request() {
        System.out.println("Adapter.request() called");
        specificRequest();
    }
}

public class test {
    public static void main(String[] args) {
        Target target = new Adapter();
        target.request();
    }
}

对象适配器模式,代码示例:

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

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

// 适配器类
public class Adapter implements Target {
    private Adaptee adaptee;

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

    @Override
    public void request() {
        System.out.println("Adapter.request() called");
        adaptee.specificRequest();
    }
}


public class test {
    public static void main(String[] args) {
        Adaptee adaptee = new Adaptee();
        Target target = new Adapter(adaptee);
        target.request();
    }
}

适配器模式的优缺点:

优点:

  • 可以让不兼容的类和接口一起工作。
  • 可以在不修改原有代码的情况下,增加新的适配器类,增加代码的灵活性和可扩展性。

缺点:

  • 对象适配器模式需要额外的对象,可能会增加代码复杂性。
  • 适配器模式会增加系统的类和对象的数量,增加系统的复杂性。

适用场景:

  • 已经存在的类和接口不兼容。
  • 希望增加新的适配器类,而不是修改原有代码。
  • 希望在多个类中共用同一个适配器对象。

总结: 适配器模式它可以让不兼容的接口一起工作。在 Java 中,可以通过类适配器模式或对象适配器模式来实现适配器模式,选择合适的方式取决于具体情况。适配器模式可以增加代码的灵活性和可扩展性,但也可能会增加代码的复杂性,因此在使用适配器模式时需要权衡利弊。

桥接模式
什么是桥接模式

桥接模式(Bridge Pattern)它通过将抽象和实现分离开来,使得它们可以独立地变化而不互相影响。

在 Java 中,桥接模式的实现通常使用接口来表示抽象和实现,并使用组合来将它们连接起来。抽象类通常包含一个对实现接口的引用,并将抽象操作委托给实现对象。

桥接模式的核心思想是将抽象与实现分离,以便它们可以独立地变化。这种设计模式通常用于处理多个维度的变化,例如多个平台、多个数据库等。它可以使代码更加灵活、可扩展和易于维护。

如何在代码中实现桥接模式
  1. 定义抽象类和实现接口 定义抽象类和实现接口,其中抽象类持有一个对实现接口的引用,并将抽象操作委托给实现对象。例如:

    
    public abstract class AbstractShape {
        protected DrawAPI drawAPI;
    
        protected AbstractShape(DrawAPI drawAPI){
            this.drawAPI = drawAPI;
        }
    
        public abstract void draw();
    }
    
    public interface DrawAPI {
        public void drawShape(int x, int y, int radius);
    }
    
  2. 实现具体的抽象类和实现类 实现具体的抽象类和实现类,它们都继承或实现相应的抽象类或接口,并实现抽象方法。例如:

    
    public class Circle extends AbstractShape {
        private int x, y, radius;
    
        public Circle(int x, int y, int radius, DrawAPI drawAPI) {
            super(drawAPI);
            this.x = x;
            this.y = y;
            this.radius = radius;
        }
    
        public void draw() {
            drawAPI.drawShape(x, y, radius);
        }
    }
    
    public class RedDraw implements DrawAPI {
        public void drawShape(int x, int y, int radius) {
            System.out.println("Drawing red circle at (" + x + ", " + y + "), radius " + radius);
        }
    }
    
  3. 使用桥接模式 在代码中创建具体的抽象类和实现类对象,并将它们连接起来。例如:

    DrawAPI redDraw = new RedDraw();
    Circle circle = new Circle(100, 100, 50, redDraw);
    circle.draw();
    

    运行结果:

    scssCopy code
    Drawing red circle at (100, 100), radius 50
    

通过上述步骤,就可以在 Java 中实现桥接模式。这种设计模式可以让抽象和实现的变化独立,使得代码更加灵活、可扩展和易于维护。

桥接模式它的优缺点如下:

优点:

  1. 分离抽象和实现:通过将抽象和实现分离,使得它们可以独立地变化,不会相互影响。这使得系统更加灵活,易于扩展和维护。
  2. 细粒度控制:桥接模式可以让抽象部分和实现部分分别扩展,从而可以实现更细粒度的控制。
  3. 提高系统可扩展性:由于抽象和实现分离,所以可以通过继承或实现不同的抽象和实现类,来实现更多的变化和组合。这样可以大大提高系统的可扩展性。

缺点:

  1. 增加代码复杂性:桥接模式需要定义抽象和实现接口,增加了代码的复杂性,使得代码难以理解和维护。
  2. 增加系统开销:由于需要在抽象和实现之间建立桥梁,所以会增加系统的开销和复杂性。
  3. 不适用于简单的系统:桥接模式适用于复杂的系统,对于简单的系统来说,可能会增加不必要的复杂性和开销。
组合模式
什么是组合模式

组合模式它允许你将对象组合成树形结构来表现“部分-整体”的层次结构,使得调用方可以统一地处理单个对象和组合对象。

组合模式由以下两种角色组成:

  1. Component(组件):定义组合中所有对象的通用接口,可以是抽象类或者接口,提供默认行为和管理子组件的操作。
  2. Leaf(叶子):表示组合中的叶子节点对象,叶子节点没有子节点。
  3. Composite(复合):表示组合中的复合节点对象,复合节点包含一组子节点,通常实现了Component接口的所有方法。

组合模式通过将单个对象和组合对象统一对待,使得调用方可以在不知道对象类型的情况下,对对象进行操作,从而提高了代码的可复用性和可扩展性。

举个例子,一个组织机构可以被看作是一个树形结构,由一个根节点(公司)和一些子节点(部门、员工)组成。这个组织机构可以使用组合模式来实现,根节点是复合节点,子节点可以是叶子节点或复合节点。调用方可以通过调用根节点的方法来访问整个组织机构,也可以通过调用子节点的方法来访问子组件。

组合模式的优点:

  1. 可以统一地处理单个对象和组合对象,提高代码的复用性和可扩展性。
  2. 可以简化调用方代码,使调用方不需要知道对象的具体类型,从而降低了调用方与对象的耦合度。
  3. 可以很方便地增加新的组件和叶子对象,从而使得系统更加灵活和可扩展。

组合模式的缺点:

  1. 在一些情况下,可能会让系统变得更加复杂,因为需要增加额外的抽象层次。
  2. 可能会导致系统的性能降低,因为需要进行额外的类型检查和转换。
如何在代码中实现组合模式

首先,我们定义一个抽象类Component,表示组合中所有对象的通用接口。其中包括一些默认实现和管理子组件的方法。


public abstract class Component {
    protected String name;

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

    public void add(Component component) {
        throw new UnsupportedOperationException();
    }

    public void remove(Component component) {
        throw new UnsupportedOperationException();
    }

    public Component getChild(int index) {
        throw new UnsupportedOperationException();
    }

    public abstract void display();
}

接下来,我们定义两个子类,分别表示组合中的叶子节点和复合节点。


public class Leaf extends Component {
    public Leaf(String name) {
        super(name);
    }

    public void display() {
        System.out.println("Leaf: " + name);
    }
}

public class Composite extends Component {
    private List<Component> children = new ArrayList<>();

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

    public void add(Component component) {
        children.add(component);
    }

    public void remove(Component component) {
        children.remove(component);
    }

    public Component getChild(int index) {
        return children.get(index);
    }

    public void display() {
        System.out.println("Composite: " + name);
        for (Component component : children) {
            component.display();
        }
    }
}

Leaf表示叶子节点,没有子节点,而Composite表示复合节点,包含一组子节点。Composite类中包含了一个List,用于存储子节点。

最后,我们可以使用这些类来构建一个组织机构的例子。


public class OrganizationDemo {
    public static void main(String[] args) {
        Composite company = new Composite("ABC Company");

        Composite department1 = new Composite("IT Department");
        department1.add(new Leaf("Bob"));
        department1.add(new Leaf("Alice"));

        Composite department2 = new Composite("Sales Department");
        department2.add(new Leaf("Tom"));
        department2.add(new Leaf("Jerry"));

        company.add(department1);
        company.add(department2);

        company.display();
    }
}

在这个例子中,我们创建了一个ABC Company的组织机构,其中包含了两个部门:IT Department和Sales Department。每个部门都包含了一些员工。我们通过调用Composite类的add方法来添加子节点,通过调用Composite类的display方法来展示整个组织机构的结构。

装饰器模式
什么是装饰器模式

装饰器模式它允许在不修改现有对象结构的情况下,动态地添加额外的行为。该模式的基本思想是,将对象包装在一个装饰器中,从而在运行时动态地添加、删除或修改对象的行为。

装饰器模式通常用于以下两种情况:

  1. 在不修改现有对象结构的情况下,动态地添加、删除或修改对象的行为。
  2. 在不使用子类的情况下,扩展现有对象的功能。

装饰器模式的核心是装饰器类。装饰器类继承自被装饰的对象,并在其中包含了一个被装饰的对象的引用。装饰器类通常实现了与被装饰对象相同的接口,并在其中包含了一些额外的行为。这样,我们就可以使用装饰器类来扩展现有对象的功能。

如何在代码中实现装饰器模式

实现装饰器模式的步骤如下:

  1. 创建一个接口或抽象类,定义被装饰者和装饰者的公共行为。
  2. 创建一个具体的被装饰者类,实现接口或抽象类。
  3. 创建一个抽象的装饰者类,实现接口或抽象类,并在其中保存一个被装饰者对象的引用,以便能够调用被装饰者的方法。
  4. 创建具体的装饰者类,继承自抽象装饰者类,并在其中实现装饰者特有的行为,如添加额外的功能、修改被装饰者的行为等。

示例代码:


// 创建接口或抽象类
public interface Component {
    void operation();
}

// 创建具体的被装饰者类
public class ConcreteComponent implements Component {
    @Override
    public void operation() {
        System.out.println("执行具体的操作");
    }
}

// 创建抽象的装饰者类
public abstract class Decorator implements Component {
    private Component component;

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

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

// 创建具体的装饰者类
public class ConcreteDecorator extends Decorator {
    public ConcreteDecorator(Component component) {
        super(component);
    }

    @Override
    public void operation() {
        super.operation();
        System.out.println("添加额外的功能");
    }
}


public class test {
    public static void main(String[] args) {
        // 创建被装饰者对象
        Component component = new ConcreteComponent();

        // 创建具体的装饰者对象,并将被装饰者对象传入构造函数
        ConcreteDecorator decorator = new ConcreteDecorator(component);

        // 调用装饰者的方法,实现装饰效果
        decorator.operation();
    }
}

在上述示例代码中,我们定义了一个Component接口和一个ConcreteComponent类作为被装饰者,一个Decorator抽象类和一个ConcreteDecorator类作为装饰者。调用方代码通过创建具体的装饰者对象来动态地添加额外的功能。

外观模式
什么是外观模式

外观模式(Facade Pattern)用于封装一些复杂的子系统,提供一个简单的接口给调用方来访问子系统的功能,从而隐藏子系统的复杂性。外观模式在 Java 中通常由一个外观类来实现,这个类可以访问多个子系统,并提供一些简单的方法,供调用方来访问子系统的功能。外观类通常会封装一些复杂的系统逻辑,提供一个高层次的接口,使得调用方可以更加方便地使用子系统的功能。在 Java 中,外观模式广泛应用于各种框架、库以及系统中,比如 Spring 框架中的 Facade 接口。

如何在代码中实现外观模式
  1. 定义子系统的接口和实现类:即定义多个类,每个类负责实现子系统的一个功能。
  2. 定义外观类:外观类封装了子系统的所有功能,并对外提供一个简单的接口,调用方只需要通过外观类即可访问子系统的功能。
  3. 使用外观类:调用方不需要直接访问子系统的实现类,而是通过访问外观类提供的接口来使用子系统的功能。

下面是一个简单的 Java 代码示例,用于演示外观模式的实现过程:


// 定义子系统接口
interface SubSystemA {
    void methodA();
}

interface SubSystemB {
    void methodB();
}

interface SubSystemC {
    void methodC();
}

// 实现子系统接口
class SubSystemAImpl implements SubSystemA {
    @Override
    public void methodA() {
        System.out.println("执行子系统A中的methodA方法");
    }
}

class SubSystemBImpl implements SubSystemB {
    @Override
    public void methodB() {
        System.out.println("执行子系统B中的methodB方法");
    }
}

class SubSystemCImpl implements SubSystemC {
    @Override
    public void methodC() {
        System.out.println("执行子系统C中的methodC方法");
    }
}

// 定义外观类
class Facade {
    private SubSystemA subSystemA;
    private SubSystemB subSystemB;
    private SubSystemC subSystemC;

    public Facade() {
        subSystemA = new SubSystemAImpl();
        subSystemB = new SubSystemBImpl();
        subSystemC = new SubSystemCImpl();
    }

    // 对外提供简单的接口
    public void method() {
        subSystemA.methodA();
        subSystemB.methodB();
        subSystemC.methodC();
    }
}

// 调用方使用外观类
public class Client {
    public static void main(String[] args) {
        Facade facade = new Facade();
        facade.method();
    }
}

在上面的代码示例中,SubSystemASubSystemBSubSystemC 是子系统的接口,SubSystemAImplSubSystemBImplSubSystemCImpl 是子系统的实现类。Facade 是外观类,它封装了子系统的所有功能,并对外提供一个简单的接口。Client 是调用方类,它通过访问 Facade 提供的接口来使用子系统的功能。

享元模式
什么是享元模式

享元模式它通过共享已经存在的对象来降低内存使用和对象创建的开销,以提高系统性能。

在享元模式中,将对象分为内部状态和外部状态。内部状态是指对象可共享出来的信息,存储在享元对象内部并且不会随环境变化而改变,因此可以被多个对象共享;外部状态是随环境变化而改变的、不可共享的信息,它通过参数传递给享元对象使用。

通过使用享元模式,可以大大减少需要创建的对象数量,提高系统的性能和效率。常见的应用场景包括字符编码、线程池、连接池等。

如何在代码中实现享元模式
  1. 定义享元接口(Flyweight),声明可以共享的操作。
public interface Flyweight {
    void operation();
}
  1. 定义具体享元类(ConcreteFlyweight),实现共享接口,并保存内部状态。
public class ConcreteFlyweight implements Flyweight {
    private String intrinsicState;

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

    @Override
    public void operation() {
        System.out.println("ConcreteFlyweight with intrinsic state: " + intrinsicState);
    }
}
  1. 定义享元工厂类(FlyweightFactory),用于创建和管理共享的享元对象。

public class FlyweightFactory {
    private static final Map<String, Flyweight> flyweights = new HashMap<>();

    public static Flyweight getFlyweight(String key) {
        Flyweight flyweight = flyweights.get(key);
        if (flyweight == null) {
            flyweight = new ConcreteFlyweight(key);
            flyweights.put(key, flyweight);
        }
        return flyweight;
    }
}
  1. 在调用方代码中,通过享元工厂获取享元对象,并调用共享的操作。
Flyweight flyweight = FlyweightFactory.getFlyweight("key");
flyweight.operation();

在实现中,通过共享已经创建的对象,避免了创建大量相同对象的开销,提高了系统性能。需要注意的是,享元模式通常需要使用单例模式,以确保工厂只创建一个实例。

代理模式
什么是代理模式

Java中的代理模式指的是在不改变原有类结构的情况下,通过引入一个代理类来控制对原有类的访问。代理类和原有类具有相同的接口,当调用方访问代理类时,代理类会将请求转发给原有类,同时在转发前或转发后执行一些附加的操作,比如权限控制、缓存、延迟加载等。代理模式可以增加代码的灵活性和可维护性。

如何在代码中实现代理模式

首先,定义一个共同的接口 Subject,用来定义被代理对象和代理对象的共同行为:

public interface Subject {
    void request();
}

然后,实现一个真实的对象 RealSubject,它实现了 Subject 接口的 request() 方法:


public class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("RealSubject: Handling request.");
    }
}

接下来,实现一个代理对象 Proxy,它也实现了 Subject 接口的 request() 方法,但在调用真实对象的 request() 方法之前或之后,它可以添加一些额外的逻辑:


public class Proxy implements Subject {
    private RealSubject realSubject;

    public Proxy(RealSubject realSubject) {
        this.realSubject = realSubject;
    }

    @Override
    public void request() {
        // 添加额外的逻辑
        System.out.println("Proxy: Logging before request.");

        // 调用真实对象的方法
        realSubject.request();

        // 添加额外的逻辑
        System.out.println("Proxy: Logging after request.");
    }
}

最后,我们可以在调用方代码中创建一个代理对象,并通过它来访问真实对象:


public class Client {
    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();
        Proxy proxy = new Proxy(realSubject);

        // 通过代理对象访问真实对象
        proxy.request();
    }
}

当调用方通过代理对象来访问真实对象时,代理对象会先执行一些额外的逻辑,然后再将请求转发给真实对象来处理。这样就实现了对真实对象的访问控制。

职责链模式
什么是职责链模式

它允许你将请求沿着处理者链进行发送,直到有一个处理者能够处理它。职责链模式使得多个对象都有机会处理请求,从而避免了请求发送者和接收者之间的耦合关系。

在职责链模式中,每个处理者都拥有下一个处理者的引用。如果当前处理者无法处理请求,它将请求转发给下一个处理者。这样,请求将沿着处理者链向下传递,直到有一个处理者能够处理它为止。

职责链模式适用于以下场景:

  • 有多个对象可以处理请求,但调用方不知道具体的处理对象。
  • 处理一个请求的对象集合应该被动态指定。
  • 在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
  • 可以处理请求的对象应该以链式结构组织起来。

职责链模式是一种非常有用的模式,特别适用于处理复杂请求的情况。它通过将请求发送者和接收者解耦,使得系统更加灵活、可扩展和易于维护。

如何在Java代码中实现职责链模式

假设有一个订单处理系统,每个订单需要依次进行以下处理流程:验证订单 -> 计算价格 -> 生成发货单 -> 发货。每个流程都可以单独处理,但必须按照指定顺序进行,如果有流程处理失败,整个订单处理流程将被中断。

代码如下:

javaCopy code
// 订单处理接口,定义订单处理流程
public interface OrderHandler {
    // 处理订单流程
    void handle(Order order, OrderHandler next);
}

// 验证订单处理类
public class ValidateOrderHandler implements OrderHandler {
    @Override
    public void handle(Order order, OrderHandler next) {
        System.out.println("正在验证订单:" + order.getOrderNo());
        // TODO: 实现订单验证逻辑
        if (next != null) {
            // 调用下一个处理器处理订单
            next.handle(order, next.getNext());
        }
    }
    @Override
    public OrderHandler getNext() {
        return new CalculatePriceHandler();
    }
}

// 计算价格处理类
public class CalculatePriceHandler implements OrderHandler {
    @Override
    public void handle(Order order, OrderHandler next) {
        System.out.println("正在计算订单价格:" + order.getOrderNo());
        // TODO: 实现计算订单价格逻辑
        if (next != null) {
            // 调用下一个处理器处理订单
            next.handle(order, next.getNext());
        }
    }
    @Override
    public OrderHandler getNext() {
        return new GenerateShippingOrderHandler();
    }
}

// 生成发货单处理类
public class GenerateShippingOrderHandler implements OrderHandler {
    @Override
    public void handle(Order order, OrderHandler next) {
        System.out.println("正在生成发货单:" + order.getOrderNo());
        // TODO: 实现生成发货单逻辑
        if (next != null) {
            // 调用下一个处理器处理订单
            next.handle(order, next.getNext());
        }
    }
    @Override
    public OrderHandler getNext() {
        return new ShippingHandler();
    }
}

// 发货处理类
public class ShippingHandler implements OrderHandler {
    @Override
    public void handle(Order order, OrderHandler next) {
        System.out.println("正在发货:" + order.getOrderNo());
        // TODO: 实现发货逻辑
    }
    @Override
    public OrderHandler getNext() {
        return null;
    }
}

// 订单类
public class Order {
    private String orderNo;
    public Order(String orderNo) {
        this.orderNo = orderNo;
    }
    public String getOrderNo() {
        return orderNo;
    }
}

// 测试代码
public class Main {
    public static void main(String[] args) {
        // 创建订单
        Order order = new Order("20220329001");

        // 构造订单处理流程
        OrderHandler handler = new ValidateOrderHandler();

        // 调用订单处理流程
        handler.handle(order, handler.getNext());
    }
}
命令模式
什么是命令模式

它允许将请求封装成一个对象,从而使您能够将不同的请求参数化并对请求排队、记录请求日志、以及支持可撤销的操作。

在命令模式中,请求方(称为Client)通过创建一个命令对象(称为Command),将其与一个执行该命令的对象(称为Receiver)相关联,并将其放入一个队列中。当执行该命令时,命令对象将调用接收器的一个或多个操作,完成请求的处理。由于命令对象包含了请求的全部信息,因此可以随时将其保存下来,以便进行日志记录、撤销操作等。

命令模式的主要优点是:

  • 可以将命令的执行者和请求者解耦,从而提高系统的灵活性。
  • 可以方便地添加新的命令,而无需修改现有代码。
  • 可以支持撤销操作,从而提高系统的可靠性。
  • 可以支持事务操作,即多个命令可以组合成一个事务,以确保操作的原子性
如何在Java代码中实现命令模式
  1. 定义命令接口(Command),该接口应该包含一个执行(execute)方法,用于执行具体的命令操作。
public interface Command {
    void execute();
}
  1. 创建一个或多个命令类,实现命令接口并实现具体的操作。
public class ConcreteCommand implements Command {
    private Receiver receiver;
    public ConcreteCommand(Receiver receiver) {
        this.receiver = receiver;
    }
    @Override
    public void execute() {
        receiver.action();
    }
}
  1. 创建命令接收者(Receiver),它包含了具体的操作方法,命令对象将调用该方法来执行具体操作。

public class Receiver {
    public void action() {
        // 执行具体操作
    }
}
  1. 创建命令调用者(Invoker),它接收并执行命令。

public class Invoker {
    private Command command;
    public void setCommand(Command command) {
        this.command = command;
    }
    public void executeCommand() {
        command.execute();
    }
}
  1. 创建测试代码,创建命令对象并将其与接收者相关联,将命令对象传递给调用者并执行。

public class Test {
    public static void main(String[] args) {
        Receiver receiver = new Receiver();
        Command command = new ConcreteCommand(receiver);
        Invoker invoker = new Invoker();
        invoker.setCommand(command);
        invoker.executeCommand();
    }
}

这样,当需要执行命令时,调用方只需创建相应的命令对象并将其传递给调用者即可,而不需要了解具体的命令实现和接收者实现。这样可以有效地实现命令的解耦。

解释器模式
什么是解释器模式

解释器模式是一种设计模式,用于将一个语言的语法规则表示为一个类的实例,并提供一种递归解释的方法。它适用于需要解释执行一种语言的场景,例如编译器、数据库查询等。该模式包含抽象表达式、终结符表达式、非终结符表达式和上下文等角色。其中,抽象表达式定义了解释器所需的所有方法,终结符表达式表示语言中的基本元素,非终结符表达式表示语言中的复合元素,上下文包含解释器执行的上下文信息。

如何在Java代码中实现解释器模式

首先定义表达式接口:

public interface Expression {
    boolean interpret(String context);
}

然后定义具体的表达式实现类,比如这里实现一个判断字符串是否包含特定关键字的表达式:


public class KeywordExpression implements Expression {
    private String keyword;

    public KeywordExpression(String keyword) {
        this.keyword = keyword;
    }

    @Override
    public boolean interpret(String context) {
        if (context == null || context.isEmpty()) {
            return false;
        }
        return context.contains(keyword);
    }
}

接下来,我们定义一个解释器类,用于解释表达式:


public class Interpreter {
    private Expression expression;

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

    public boolean interpret(String context) {
        return expression.interpret(context);
    }
}

最后,我们可以在调用方代码中使用解释器:


public class Client {
    public static void main(String[] args) {
        Expression expression1 = new KeywordExpression("hello");
        Expression expression2 = new KeywordExpression("world");
        Interpreter interpreter1 = new Interpreter(expression1);
        Interpreter interpreter2 = new Interpreter(expression2);

        String context = "hello world";
        System.out.println(interpreter1.interpret(context)); // 输出true
        System.out.println(interpreter2.interpret(context)); // 输出true
    }
}
迭代器模式
什么是迭代器模式

迭代器模它允许调用方通过迭代器逐一访问一个聚合对象中的元素,而无需暴露聚合对象的内部结构。简而言之,迭代器模式提供了一种方式来遍历复杂数据结构中的元素,而无需了解其内部实现细节。

在迭代器模式中,聚合对象实现一个名为Iterator的接口,该接口定义了用于访问聚合对象中元素的方法。迭代器对象包含迭代器的当前位置,调用方可以使用这些方法来逐个访问聚合对象中的元素。

通过使用迭代器模式,可以实现对聚合对象的访问方式的抽象化,从而使得代码更加模块化和灵活。

如何在Java代码中实现迭代器模式

下面是一个简单的迭代器模式的demo,它可以遍历一个数组并输出每个元素的值:

javaCopy code
import java.util.*;

// 定义一个迭代器接口
interface Iterator {
    boolean hasNext();
    Object next();
}

// 定义一个集合接口
interface Collection {
    Iterator createIterator();
}

// 实现一个数组集合类
class MyCollection implements Collection {
    private int[] arr = {1, 2, 3, 4, 5};

    @Override
    public Iterator createIterator() {
        return new MyIterator();
    }

    // 实现一个迭代器类
    private class MyIterator implements Iterator {
        private int index = 0;

        @Override
        public boolean hasNext() {
            return index < arr.length;
        }

        @Override
        public Object next() {
            if (this.hasNext()) {
                return arr[index++];
            }
            return null;
        }
    }
}

public class IteratorDemo {
    public static void main(String[] args) {
        Collection collection = new MyCollection();
        Iterator iterator = collection.createIterator();

        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }
}
中介者模式
什么是中介者模式

它允许对象之间通过中介者对象进行通信,而不是直接相互引用。这种模式有助于减少对象之间的耦合性,从而使代码更易于维护和扩展。

在中介者模式中,中介者对象充当了协调者的角色,负责处理对象之间的通信。对象之间不再直接相互引用,而是通过中介者对象进行通信。这种模式可以减少对象之间的依赖关系,从而使代码更加灵活和可扩展。

如何在Java代码中实现中介者模式
// 定义中介者接口
interface Mediator {
    void sendMessage(String message, Colleague colleague);
}

// 定义同事接口
interface Colleague {
    void receiveMessage(String message);
}

// 定义具体中介者类
class ConcreteMediator implements Mediator {
    private Colleague colleague1;
    private Colleague colleague2;

    public void setColleague1(Colleague colleague1) {
        this.colleague1 = colleague1;
    }

    public void setColleague2(Colleague colleague2) {
        this.colleague2 = colleague2;
    }

    public void sendMessage(String message, Colleague colleague) {
        if (colleague == colleague1) {
            colleague2.receiveMessage(message);
        } else {
            colleague1.receiveMessage(message);
        }
    }
}

// 定义具体同事类
class ConcreteColleague1 implements Colleague {
    private Mediator mediator;

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

    public void send(String message) {
        mediator.sendMessage(message, this);
    }

    public void receiveMessage(String message) {
        System.out.println("ConcreteColleague1 received message: " + message);
    }
}

class ConcreteColleague2 implements Colleague {
    private Mediator mediator;

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

    public void send(String message) {
        mediator.sendMessage(message, this);
    }

    public void receiveMessage(String message) {
        System.out.println("ConcreteColleague2 received message: " + message);
    }
}

// 示例用法
public class Main {
    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 from colleague1");
        colleague2.send("Hello from colleague2");
    }
}

备忘录模式
什么是备忘录模式

备忘录模式用于将对象的状态保存在一个备忘录对象中,并在需要时恢复该状态。这种模式可以用于实现撤销操作或者实现状态的保存和恢复。

备忘录模式包含三个主要的角色:

  1. Originator(原发器):需要保存和恢复状态的对象。
  2. Memento(备忘录):用于保存原发器的状态。
  3. Caretaker(负责人):负责保存备忘录,并在需要时恢复原发器的状态。

使用备忘录模式时,原发器会创建一个备忘录对象,将自己的状态保存到备忘录中。然后,负责人将备忘录保存起来。在需要恢复状态时,负责人将备忘录返回给原发器,并由原发器使用备忘录中保存的状态进行恢复操作。

备忘录模式可以帮助我们实现对象状态的保存和恢复,同时也可以避免在对象中暴露状态的细节,提高了对象的封装性。

如何在Java代码中实现备忘录模式
  1. 定义原发器类(Originator),该类需要保存和恢复状态。
public class Originator {
    private String state;

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

    public String getState() {
        return state;
    }

    public Memento saveStateToMemento() {
        return new Memento(state);
    }

    public void getStateFromMemento(Memento memento) {
        state = memento.getState();
    }
}
  1. 定义备忘录类(Memento),该类用于保存原发器的状态。
public class Memento {
    private final String state;

    public Memento(String stateToSave) {
        state = stateToSave;
    }

    public String getState() {
        return state;
    }
}
  1. 定义负责人类(Caretaker),该类负责保存备忘录并在需要时返回备忘录。
public class Caretaker {
    private final List<Memento> mementoList = new ArrayList<>();

    public void add(Memento state) {
        mementoList.add(state);
    }

    public Memento get(int index) {
        return mementoList.get(index);
    }
}
  1. 在调用方代码中,创建原发器和负责人对象,使用原发器对象保存状态,并使用负责人对象保存备忘录。在需要恢复状态时,从负责人对象中获取备忘录并恢复状态。

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

    originator.setState("State1");
    originator.setState("State2");
    caretaker.add(originator.saveStateToMemento());

    originator.setState("State3");
    caretaker.add(originator.saveStateToMemento());

    originator.setState("State4");

    System.out.println("Current State: " + originator.getState());

    originator.getStateFromMemento(caretaker.get(1));
    System.out.println("Previous State: " + originator.getState());

    originator.getStateFromMemento(caretaker.get(0));
    System.out.println("First State: " + originator.getState());
}

在上述代码中,我们创建了一个 Originator 对象,并设置了一些状态,然后使用 saveStateToMemento 方法将状态保存到 Memento 对象中。接着,将 Memento 对象添加到 Caretaker 对象中。最后,在需要恢复状态时,从 Caretaker 对象中获取 Memento 对象,并使用 getStateFromMemento 方法将状态恢复到原发器中。

观察者模式
什么是观察者模式

它定义了对象之间一对多的依赖关系,使得当一个对象的状态发生变化时,它的所有依赖对象都会收到通知并自动更新。

观察者模式包含两个主要角色:

  1. Subject(主题):被观察的对象,它维护一系列观察者并且通知它们状态的变化。
  2. Observer(观察者):观察主题的对象,它们接收主题通知的变化并作出相应的更新。

使用观察者模式时,主题对象维护一个观察者列表,当主题状态发生变化时,它会通知所有的观察者。观察者会根据主题通知的变化来更新自己的状态。主题和观察者之间的依赖关系是松散耦合的,它们可以独立地变化而不会相互影响。

观察者模式可以用于许多场景,例如 GUI 程序中的事件处理、消息通知系统等等。它可以帮助我们实现对象之间的松散耦合,提高程序的可维护性和可扩展性。

如何在Java代码中实现观察者模式
  1. 定义主题接口(Subject),该接口包含注册观察者、删除观察者和通知观察者的方法。

public interface Subject {
    public void registerObserver(Observer observer);
    public void removeObserver(Observer observer);
    public void notifyObservers();
}
  1. 定义观察者接口(Observer),该接口包含更新状态的方法。

public interface Observer {
    public void update();
}
  1. 实现主题类(ConcreteSubject),该类维护一个观察者列表,并在状态变化时通知所有的观察者。

public 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 registerObserver(Observer observer) {
        observers.add(observer);
    }

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

    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update();
        }
    }
}
  1. 实现观察者类(ConcreteObserver),该类在接收到主题通知时更新自己的状态。
public class ConcreteObserver implements Observer {
    private final ConcreteSubject subject;

    public ConcreteObserver(ConcreteSubject subject) {
        this.subject = subject;
        subject.registerObserver(this);
    }

    @Override
    public void update() {
        System.out.println("State changed to " + subject.getState());
    }
}

在上述代码中,我们定义了 Subject 接口和 Observer 接口,并实现了 ConcreteSubject 和 ConcreteObserver 类。ConcreteSubject 类维护了一个观察者列表,当状态发生变化时,它会通知所有的观察者。ConcreteObserver 类在接收到主题通知时更新自己的状态。

我们可以通过以下代码来测试观察者模式的实现:

public static void main(String[] args) {
    ConcreteSubject subject = new ConcreteSubject();
    ConcreteObserver observer1 = new ConcreteObserver(subject);
    ConcreteObserver observer2 = new ConcreteObserver(subject);

    subject.setState(1);
    subject.setState(2);
    subject.setState(3);
}

在上述代码中,我们创建了一个 ConcreteSubject 对象和两个 ConcreteObserver 对象。当 ConcreteSubject 对象的状态发生变化时,它会通知所有的 ConcreteObserver 对象,并更新它们的状态。

状态模式
什么是状态模式

它允许一个对象在内部状态发生改变时改变它的行为,从而看起来像是改变了它的类。状态模式将对象的行为和状态分离开来,使得对象状态的改变不会影响到调用方的代码。

状态模式通常由以下几个角色组成:

  1. Context(上下文):它是包含状态的对象,它将行为委托给当前状态对象来处理。它可以维护一个对于状态对象的引用,并可以设置当前状态对象。
  2. State(状态):它定义了一个接口,用于封装与 Context 相关的行为。
  3. ConcreteState(具体状态):它实现了 State 接口,并定义了它自己的行为。

使用状态模式,我们可以将对象的状态作为一个对象来处理,而不是使用大量的 if-else 语句。这样可以使代码更加清晰、易于维护和扩展。

状态模式可以在许多场景中使用,例如一个对象的行为取决于它的状态、状态之间的转换需要被封装、状态改变时需要执行某些操作等等。它也可以和其他设计模式一起使用,例如策略模式、装饰模式等等。

如何在Java代码中实现状态模式
  1. 定义状态接口(State),其中包含了具体状态需要实现的方法。

public interface State {
    public void handle();
}
  1. 定义具体状态类(ConcreteState),实现 State 接口中定义的方法。
typescriptCopy code
public class ConcreteState implements State {
    @Override
    public void handle() {
        System.out.println("Handling ConcreteState");
    }
}
  1. 定义上下文类(Context),该类中包含了状态实例以及相关的操作方法,如设置状态、获取状态等。

public class Context {
    private State state;

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

    public State getState() {
        return state;
    }

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

在上述代码中,我们定义了 State 接口和 ConcreteState 类,其中 ConcreteState 类实现了 State 接口中定义的方法。我们还定义了 Context 类,它包含了当前状态以及相关的操作方法。在 Context 类中,我们定义了 handle() 方法,它会委托当前状态实例来处理具体的操作。

下面是一个简单的示例,演示了如何使用状态模式:


public static void main(String[] args) {
    Context context = new Context();

    State state1 = new ConcreteState();
    context.setState(state1);
    context.handle();

    State state2 = new AnotherConcreteState();
    context.setState(state2);
    context.handle();
}

在上述代码中,我们首先创建了一个 Context 对象,并设置了 ConcreteState 实例作为当前状态。接着,我们调用了 Context 对象的 handle() 方法,它会委托 ConcreteState 实例来处理具体的操作。

然后,我们又创建了一个 AnotherConcreteState 实例,并将它设置为当前状态。再次调用 Context 对象的 handle() 方法,它会委托 AnotherConcreteState 实例来处理具体的操作。可以看到,状态对象之间的切换是通过 Context 对象来完成的,从而实现了状态模式的功能。

策略模式
什么是策略模式

策略模式它允许在运行时动态地改变对象的行为。在策略模式中,算法是被封装在独立的类中,可以互相替换,从而改变对象的行为。这使得算法可以独立于使用它的调用方而变化。

策略模式通常包含以下几个角色:

  1. Context(上下文):它是包含一个策略对象的类。Context 将请求委托给策略对象执行具体的算法,并负责策略对象的创建和销毁。
  2. Strategy(策略):它是定义所有支持算法的通用接口,它通常由一个接口或者抽象类实现。
  3. ConcreteStrategy(具体策略):它实现了 Strategy 接口,并提供了具体的算法实现。

使用策略模式,我们可以将不同的算法进行独立的封装,并将它们交给 Context 对象进行管理。在运行时,可以根据需要动态地切换算法,从而达到改变对象行为的目的。

策略模式通常适用于具有多种算法实现的场景,例如排序算法、加密算法、数据压缩算法等等。它还可以用于解耦策略的实现和使用,使得策略可以独立地变化而不影响调用方的代码。

如何在Java代码中实现策略模式
  1. 定义策略接口(Strategy),其中包含了具体策略需要实现的方法。

public interface Strategy {
    public void execute();
}
  1. 定义具体策略类(ConcreteStrategy),实现 Strategy 接口中定义的方法。

public class ConcreteStrategy implements Strategy {
    @Override
    public void execute() {
        System.out.println("Executing ConcreteStrategy");
    }
}
  1. 定义上下文类(Context),该类中包含了策略实例以及相关的操作方法,如设置策略、执行策略等。

public class Context {
    private Strategy strategy;

    public Context(Strategy strategy) {
        this.strategy = strategy;
    }

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

    public void executeStrategy() {
        strategy.execute();
    }
}

在上述代码中,我们定义了 Strategy 接口和 ConcreteStrategy 类,其中 ConcreteStrategy 类实现了 Strategy 接口中定义的方法。我们还定义了 Context 类,它包含了当前策略以及相关的操作方法。在 Context 类中,我们定义了 executeStrategy() 方法,它会委托当前策略实例来执行具体的操作。

下面是一个简单的示例,演示了如何使用策略模式:


public static void main(String[] args) {
    Context context = new Context(new ConcreteStrategy());
    context.executeStrategy();

    context.setStrategy(new AnotherConcreteStrategy());
    context.executeStrategy();
}

在上述代码中,我们首先创建了一个 Context 对象,并设置了 ConcreteStrategy 实例作为当前策略。接着,我们调用了 Context 对象的 executeStrategy() 方法,它会委托 ConcreteStrategy 实例来执行具体的操作。

然后,我们又创建了一个 AnotherConcreteStrategy 实例,并将它设置为当前策略。再次调用 Context 对象的 executeStrategy() 方法,它会委托 AnotherConcreteStrategy 实例来执行具体的操作。可以看到,策略对象之间的切换是通过 Context 对象来完成的,从而实现了策略模式的功能。

模板方法模式
什么是模板方法模式

模板方法模式它定义了一个操作中的算法框架,将某些步骤延迟到子类中实现。模板方法模式使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

模板方法模式中通常包含以下角色:

  1. AbstractClass(抽象类):它定义了一个模板方法,该方法中包含了一系列的基本操作,这些操作可以是具体的,也可以是抽象的,子类需要实现其中的一些或全部抽象操作。抽象类还可以定义一些钩子方法,子类可以通过重写钩子方法来改变模板方法中的流程。
  2. ConcreteClass(具体类):它实现了抽象类中定义的抽象操作,并且可以重写钩子方法。

在模板方法模式中,抽象类封装了算法的基本流程,具体的实现则由子类来完成。这样做的好处是,当我们需要改变算法的某个步骤时,只需要在子类中重写该步骤的实现即可,而不需要修改算法的整个流程。

模板方法模式在实际应用中非常常见,例如在软件开发中,我们也可以使用模板方法模式来实现一些常见的操作流程,例如读取配置文件、网络通信等。

如何在Java代码中实现模板方法模式

在Java中,实现模板方法模式需要创建一个抽象类来定义算法的基本流程,以及一些抽象方法和钩子方法,具体实现则由子类来完成。下面是一个简单的Java代码示例:


// 抽象类
public abstract class AbstractClass {
    // 模板方法,定义算法的基本流程
    public void templateMethod() {
        primitiveOperation1();
        primitiveOperation2();
        concreteOperation();
        hook();
    }

    // 抽象方法,由子类实现
    protected abstract void primitiveOperation1();
    protected abstract void primitiveOperation2();

    // 具体方法,由抽象类实现,子类不需要重写
    protected void concreteOperation() {
        // 实现具体的操作,由子类共享
    }

    // 钩子方法,由子类来实现或重写
    protected void hook() {
        // 默认实现为空,子类可以根据需要重写该方法
    }
}

// 具体类,实现抽象类中定义的抽象方法和钩子方法
public class ConcreteClass extends AbstractClass {
    @Override
    protected void primitiveOperation1() {
        // 具体实现
    }

    @Override
    protected void primitiveOperation2() {
        // 具体实现
    }

    @Override
    protected void hook() {
        // 重写钩子方法,改变模板方法中的流程
    }
}

// 测试代码
public class Test {
    public static void main(String[] args) {
        AbstractClass template = new ConcreteClass();
        template.templateMethod();
    }
}

在上面的示例中,抽象类AbstractClass定义了一个模板方法templateMethod(),并且包含了一些抽象方法primitiveOperation1()primitiveOperation2(),这些方法由子类来实现。同时,抽象类中还定义了一个具体方法concreteOperation()和一个钩子方法hook(),具体类可以选择是否重写这些方法。

具体类ConcreteClass继承了抽象类,并且实现了抽象方法和钩子方法。在测试代码中,我们可以创建一个具体类的实例,并且调用它的模板方法来执行算法的基本流程。

访问者模式
什么是访问者模式

它允许你将算法从对象结构中分离出来,使其可以在不改变对象结构的前提下独立变化。该模式可以方便地增加新的操作,而无需修改现有的对象结构。

访问者模式通常用于需要对一个复杂对象结构(如一个类的继承体系)进行一系列操作的情况。该模式中包含两类对象:被访问者(Accept)和访问者(Visitor)。被访问者包含一些方法,可以接受访问者来进行操作。访问者则包含了一些方法,用于处理被访问者的不同元素。

访问者模式的优点是增加新的操作变得很容易,同时还可以让你对访问对象结构的过程进行控制。然而,访问者模式的缺点是它增加了代码的复杂度,并且如果被访问者的结构发生了变化,则需要相应地修改访问者类的代码。

以下是访问者模式的一些常见用途:

  • 在编译器中,使用访问者模式来实现代码的语法分析和语义分析。
  • 在图形界面的应用程序中,使用访问者模式来处理图形对象的布局和显示。
  • 在数据库系统中,使用访问者模式来对查询结果进行操作和转换。
如何在Java代码中实现访问者模式
  1. 定义抽象访问者(Visitor)接口,其中包含了一系列的 visit 方法,用于处理不同类型的元素对象。比如:
javaCopy code
public interface Visitor {
    void visit(ElementA element);
    void visit(ElementB element);
}
  1. 定义具体访问者(ConcreteVisitor)类,实现抽象访问者接口中的各个 visit 方法,具体实现对不同类型的元素对象的处理逻辑。比如:

public class ConcreteVisitor implements Visitor {
    @Override
    public void visit(ElementA element) {
        System.out.println("Visitor is visiting ElementA");
    }

    @Override
    public void visit(ElementB element) {
        System.out.println("Visitor is visiting ElementB");
    }
}
  1. 定义抽象元素(Element)接口,其中包含了一个接受访问者对象的方法 accept,用于将访问者对象传递给元素对象。比如:

public interface Element {
    void accept(Visitor visitor);
}
  1. 定义具体元素(ConcreteElement)类,实现抽象元素接口中的 accept 方法,将访问者对象传递给具体元素对象。比如:

public class ElementA implements Element {
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

public class ElementB implements Element {
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}
  1. 定义对象结构(ObjectStructure)类,其中包含了多个元素对象,可以提供一个接受访问者对象的方法 accept,从而让访问者对象可以访问到其中的所有元素。比如:

public class ObjectStructure {
    private List<Element> elements = new ArrayList<>();

    public void add(Element element) {
        elements.add(element);
    }

    public void remove(Element element) {
        elements.remove(element);
    }

    public void accept(Visitor visitor) {
        for (Element element : elements) {
            element.accept(visitor);
        }
    }
}
  1. 在测试中创建具体元素和具体访问者对象,并将其传递给对象结构类的 accept 方法中,从而实现对元素对象的操作。比如:

public class Test {
    public static void main(String[] args) {
        ElementA elementA = new ElementA();
        ElementB elementB = new ElementB();

        ObjectStructure objectStructure = new ObjectStructure();
        objectStructure.add(elementA);
        objectStructure.add(elementB);

        Visitor visitor = new ConcreteVisitor();
        objectStructure.accept(visitor);
    }
}

当执行 accept 方法时,访问者对象会依次访问对象结构中的每个元素,并调用相应的 visit 方法进行处理。这样,访问者模式就可以帮助我们实现对复杂对象结构的操作。