常见设计模式及其应用示例

一、创建型模式

创建型模式主要关注如何实例化对象,尤其是如何避免直接使用构造器来创建对象,而是通过某些模式来控制对象的创建。

单例模式 (Singleton)

1. 介绍

单例模式(Singleton Pattern)是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来访问该实例。单例模式的核心目的是控制实例化的过程,防止类的多个实例产生,从而节约资源、提高效率,并且保证全局使用的是同一个实例。

2. 作用

  • 全局唯一性:保证类在整个应用生命周期中只有一个实例。
  • 控制资源的共享:通过提供全局访问点,使得某些资源(例如数据库连接、线程池、配置管理等)可以被多个地方共享,避免不必要的创建。
  • 延迟加载:实例只在第一次需要的时候创建。

3. 应用场景

  • 配置管理:比如程序配置、日志管理等功能,需要保证全局只存在一个配置管理器或日志记录器实例。
  • 数据库连接池:为了避免频繁地创建和销毁数据库连接,可以使用单例模式来创建数据库连接池。
  • 线程池:在多线程应用中,线程池需要被多个线程共享,避免创建多个线程池实例。
  • 缓存管理:缓存管理系统常常需要全局唯一的实例来缓存数据。

4. Java中的应用场景

  • Spring框架中的单例Bean:Spring的默认作用域是单例,每个Spring容器只会创建一个Bean实例。
  • 日志框架:如Log4j和SLF4J通常使用单例模式来保证日志管理类的唯一性。
  • 数据库连接池:HikariCP、C3P0等连接池的管理类通常会采用单例模式。
  • 线程池管理:Java的ExecutorService也有单例的实现,确保线程池的唯一性。

5. 代码展示

**饿汉式单例模式:**类加载时即创建实例,线程安全,不需要额外的同步机制。缺点是可能造成资源浪费,因为即使没有使用该实例,类加载时也会初始化它

java 复制代码
public class Singleton {
    // 类加载时就初始化实例
    private static final Singleton instance = new Singleton();

    // 私有构造函数,防止外部实例化
    private Singleton() {}

    // 提供一个全局的静态方法来访问实例
    public static Singleton getInstance() {
        return instance;
    }
}

懒汉式单例模式(线程不安全):实例只有在第一次需要时才会被创建,但该实现方式在多线程环境下不安全,可能会导致多个实例被创建。

java 复制代码
public class Singleton {
    private static Singleton instance;

    private Singleton() {}

    // 通过方法延迟加载实例,线程不安全
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton(); // 非线程安全
        }
        return instance;
    }
}

懒汉式单例模式(线程安全):通过 synchronized保证线程安全,但每次调用getInstance()方法时都需要加锁,性能较差。

java 复制代码
public class Singleton {
    private static Singleton instance;

    private Singleton() {}

    // 通过方法延迟加载实例,线程安全
    public synchronized static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

双重检查锁(DCL)单例模式:解决了懒汉式中的性能问题,在多线程环境下进行双重检查,只在第一次创建实例时加锁,后续的调用不需要加锁,保证线程安全的同时提升了性能。

java 复制代码
public class Singleton {
    private static volatile Singleton instance;

    private Singleton() {}

    public static Singleton getInstance() {
        if (instance == null) {  // 第一重检查
            synchronized (Singleton.class) {
                if (instance == null) {  // 第二重检查
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

简单工厂模式 (Simple Factory Pattern)

1. 介绍

简单工厂模式是一种创建型设计模式,它定义一个工厂类,通过传入不同的参数来决定创建哪一种产品类的实例。简单工厂模式将实例化的工作从客户端转移到工厂类中,使得客户端不需要知道如何创建对象,只需要知道产品的类型。

2. 组成部分

  • 产品(Product):抽象产品类或接口,定义所有具体产品的公共接口。
  • 具体产品(ConcreteProduct):实现产品接口的具体类。
  • 工厂(Factory):负责创建具体产品的工厂类,客户端通过工厂获取实例。

3. 优点

  • 封装了对象的创建:客户端只关心要使用的产品接口,具体的产品类由工厂类创建,客户端不需要知道具体的实现。
  • 降低了系统的耦合度:客户端无需依赖具体的产品类,只依赖工厂接口和抽象产品接口。

4. 缺点

  • 不符合开闭原则:当需要新增产品时,必须修改工厂类,违反了开闭原则。
  • 工厂类职责过重:如果工厂需要创建的产品种类很多,工厂类可能会非常庞大,不利于维护。

5. 应用场景

  • 产品种类固定:如果你知道在系统运行过程中产品的种类是固定的,那么使用简单工厂模式是合适的。
  • 避免客户端创建产品的复杂过程:当客户端不需要知道如何创建产品对象时,使用简单工厂可以隐藏复杂的创建过程。

6. 代码实现

java 复制代码
// 抽象产品类
public interface Shape {
    void draw();
}

// 具体产品类 1
public class Circle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing Circle");
    }
}

// 具体产品类 2
public class Rectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing Rectangle");
    }
}

// 具体产品类 3
public class Triangle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing Triangle");
    }
}

// 工厂类
public class ShapeFactory {
    // 根据输入的类型创建不同的产品对象
    public static Shape getShape(String shapeType) {
        if (shapeType == null) {
            return null;
        }
        if (shapeType.equalsIgnoreCase("CIRCLE")) {
            return new Circle();
        } else if (shapeType.equalsIgnoreCase("RECTANGLE")) {
            return new Rectangle();
        } else if (shapeType.equalsIgnoreCase("TRIANGLE")) {
            return new Triangle();
        }
        return null;
    }
}

// 客户端使用工厂类
public class FactoryPatternDemo {
    public static void main(String[] args) {
        // 通过工厂获取不同的Shape对象
        Shape shape1 = ShapeFactory.getShape("CIRCLE");
        shape1.draw();

        Shape shape2 = ShapeFactory.getShape("RECTANGLE");
        shape2.draw();

        Shape shape3 = ShapeFactory.getShape("TRIANGLE");
        shape3.draw();
    }
}

工厂方法模式 (Factory Method)

1. 介绍

工厂方法模式是简单工厂模式的一个改进版,也是创建型设计模式之一。在工厂方法模式中,工厂类将实例化对象的任务交给子类,而不是由工厂类直接创建对象。具体来说,工厂类提供一个抽象的工厂方法,由具体子类来实现该方法并决定具体的产品实例。

2. 作用

  • 将对象的创建交给子类:工厂方法模式的主要目的是将对象的创建职责从类本身转移到子类。通过将对象创建的任务交给子类来实现,可以避免主程序直接依赖具体的类,增加了系统的灵活性。
  • 支持扩展:当需要增加新的产品时,不需要修改原有的代码,只需要增加新的子类实现,符合开闭原则。
  • 减少系统的耦合度:客户端代码只依赖抽象工厂类或接口,而不直接依赖具体的类,实现了高内聚低耦合。

3. 优缺点

优点

  • 符合开闭原则:在系统中添加新的产品时,无需修改现有代码,只需扩展新的工厂和产品类。
  • 提高了灵活性:由于对象的创建交给子类负责,客户端无需关心对象的具体实现,增强了系统的灵活性。
  • 降低了系统耦合度:客户端只依赖抽象的工厂类,而不直接依赖具体产品的实现类,使得产品的创建和使用解耦。

缺点

  • 类的数量增加:为了支持不同的产品,需要为每一个产品定义一个工厂类,可能导致系统类的数量激增,增加了复杂性。
  • 过度抽象:如果系统产品种类不多,使用工厂方法模式可能导致不必要的复杂性,产生过多的抽象层次

4. Java应用场景

  • 产品种类较多且需要扩展:当系统中有多个产品需要创建,并且随着时间的推移需要不断扩展新的产品时,工厂方法模式是一个理想选择。通过为每种产品提供一个具体的工厂类,可以保持代码的高可扩展性。
  • 系统中有多个变种的产品,每个产品由不同的子类工厂负责创建:例如,图形界面的控件(按钮、输入框、复选框等)可以通过不同的工厂子类来创建,便于统一管理和扩展。
  • 避免客户端直接依赖产品类:通过抽象工厂类,客户端代码不直接依赖具体产品的实现,从而降低了系统的耦合度。

5. 代码展示

java 复制代码
// 抽象产品类
public interface Shape {
    void draw();
}

// 具体产品类 1
public class Circle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing Circle");
    }
}

// 具体产品类 2
public class Rectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing Rectangle");
    }
}

// 具体产品类 3
public class Triangle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing Triangle");
    }
}

// 抽象工厂类
public abstract class ShapeFactory {
    // 抽象工厂方法
    public abstract Shape createShape();
}

// 具体工厂类 1
public class CircleFactory extends ShapeFactory {
    @Override
    public Shape createShape() {
        return new Circle();  // 返回具体的Circle对象
    }
}

// 具体工厂类 2
public class RectangleFactory extends ShapeFactory {
    @Override
    public Shape createShape() {
        return new Rectangle();  // 返回具体的Rectangle对象
    }
}

// 具体工厂类 3
public class TriangleFactory extends ShapeFactory {
    @Override
    public Shape createShape() {
        return new Triangle();  // 返回具体的Triangle对象
    }
}

// 客户端使用
public class FactoryMethodPatternDemo {
    public static void main(String[] args) {
        // 创建工厂实例
        ShapeFactory circleFactory = new CircleFactory();
        Shape circle = circleFactory.createShape();
        circle.draw();

        ShapeFactory rectangleFactory = new RectangleFactory();
        Shape rectangle = rectangleFactory.createShape();
        rectangle.draw();

        ShapeFactory triangleFactory = new TriangleFactory();
        Shape triangle = triangleFactory.createShape();
        triangle.draw();
    }
}

抽象工厂模式 (Abstract Factory)

1. 介绍

抽象工厂模式(Abstract Factory Pattern)是一种创建型设计模式,它提供一个接口,用于创建一系列相关或相互依赖的对象,而无需指定它们具体的类。抽象工厂模式通常包含多个产品族(Product Family),每个产品族中可能有多个具体产品(Concrete Product)。

在抽象工厂模式中,工厂方法的接口被抽象化,通过具体的工厂类来创建具体的产品。与工厂方法模式不同,抽象工厂模式不仅仅创建单个产品,而是创建一系列相关或相互依赖的产品。

2. 作用

  • 封装产品族的创建:抽象工厂模式通过将产品的创建交给工厂类,客户端只需要通过工厂接口获取所需的产品对象,而不关心具体的实现细节。
  • 提供一系列相关的产品:抽象工厂允许你创建一系列相关的对象,而不需要指定它们的具体类。例如,创建UI组件时,不仅需要按钮,还需要文本框,所有的UI组件会属于一个产品族(Windows UI、Mac UI等)。
  • 减少系统耦合度:通过定义抽象工厂接口,系统中的客户端不需要直接依赖具体产品类,减少了系统的耦合度。
  • 提高扩展性:新增一个产品族时,只需添加相应的工厂类和产品类,无需修改原有代码,符合开闭原则。

3. 优缺点

优点

  • 符合开闭原则:新增产品系列时,只需要增加新的具体工厂和具体产品类,不需要修改现有的代码。
  • 提供一致的产品族:确保系统中相关产品的一致性,产品之间可以协调工作,避免了不兼容的产品被创建。
  • 高内聚,低耦合:客户端通过工厂接口创建产品对象,不依赖具体的产品类,从而减少了产品之间的耦合性。
  • 易于扩展:当需要扩展新的产品系列时,只需增加对应的工厂类和产品类,而无需修改现有的客户端代码。

缺点

  • 类的数量增加:每增加一个新的产品系列,必须为该系列添加一个新的具体工厂类和一组具体产品类,可能导致类的数量迅速增加,增加系统的复杂性。
  • 不适用于产品种类不多的情况:当系统中的产品系列较少时,使用抽象工厂模式会导致过度抽象,增加了不必要的复杂度。

4. Java应用场景

  • 产品系列多且变化频繁:当系统中有多个相关的产品系列,并且这些产品的创建需要根据平台或环境进行不同的实现时,可以使用抽象工厂模式。例如,UI框架中的跨平台按钮、文本框等控件的创建。
  • 系统需要根据不同环境或平台创建一系列对象:例如,创建Windows和Mac平台的UI控件时,可以使用抽象工厂模式来提供一致的API,客户端无需关心具体平台的实现。
  • 确保产品的一致性和兼容性:当需要创建一组相关产品时(例如,产品A、产品B必须一起使用),抽象工厂可以保证它们在同一产品族内的一致性,避免了产品不兼容的情况。

5. 代码展示

java 复制代码
// 抽象产品类:按钮接口
public interface Button {
    void render();
}

// 具体产品类 1:Windows按钮
public class WindowsButton implements Button {
    @Override
    public void render() {
        System.out.println("Rendering Windows Button");
    }
}

// 具体产品类 2:Mac按钮
public class MacButton implements Button {
    @Override
    public void render() {
        System.out.println("Rendering Mac Button");
    }
}

// 抽象产品类:文本框接口
public interface TextBox {
    void render();
}

// 具体产品类 1:Windows文本框
public class WindowsTextBox implements TextBox {
    @Override
    public void render() {
        System.out.println("Rendering Windows TextBox");
    }
}

// 具体产品类 2:Mac文本框
public class MacTextBox implements TextBox {
    @Override
    public void render() {
        System.out.println("Rendering Mac TextBox");
    }
}

// 抽象工厂类:UI组件工厂接口
public interface GUIFactory {
    Button createButton();
    TextBox createTextBox();
}

// 具体工厂类 1:Windows工厂
public class WindowsFactory implements GUIFactory {
    @Override
    public Button createButton() {
        return new WindowsButton();  // 返回Windows按钮
    }

    @Override
    public TextBox createTextBox() {
        return new WindowsTextBox();  // 返回Windows文本框
    }
}

// 具体工厂类 2:Mac工厂
public class MacFactory implements GUIFactory {
    @Override
    public Button createButton() {
        return new MacButton();  // 返回Mac按钮
    }

    @Override
    public TextBox createTextBox() {
        return new MacTextBox();  // 返回Mac文本框
    }
}

// 客户端代码
public class AbstractFactoryPatternDemo {
    public static void main(String[] args) {
        // 创建Windows工厂实例
        GUIFactory windowsFactory = new WindowsFactory();
        Button windowsButton = windowsFactory.createButton();
        TextBox windowsTextBox = windowsFactory.createTextBox();
        windowsButton.render();
        windowsTextBox.render();

        // 创建Mac工厂实例
        GUIFactory macFactory = new MacFactory();
        Button macButton = macFactory.createButton();
        TextBox macTextBox = macFactory.createTextBox();
        macButton.render();
        macTextBox.render();
    }
}

建造者模式 (Builder)

1. 介绍

建造者模式(Builder Pattern)是一种创建型设计模式,旨在使用多个简单的对象一步步构建一个复杂的对象。与其他创建型设计模式(如工厂模式、抽象工厂模式)不同,建造者模式的重点是在构建过程中逐步将产品的多个组成部分进行组合,而不是一次性地创建一个对象。

2. 作用

  • 简化复杂对象的构建过程:建造者模式能够一步一步构建复杂对象,避免了创建对象时所有参数都在构造函数中的复杂情况。
  • 提高可读性和可维护性:通过逐步构建复杂对象,可以清晰地分步创建对象,便于后期维护。
  • 灵活性:能够根据不同的需求变化来动态改变产品的构建过程。

3. 优缺点

优点

  • 分离复杂对象的构建和表示:通过使用建造者模式,可以将复杂对象的构建与表示分离,客户端可以通过建造者来定制复杂对象,而不需要关注如何构建它。
  • 构建过程易于扩展:当需要扩展新的产品时,只需增加新的具体建造者类,而无需修改原有的代码。
  • 代码更清晰:在创建复杂对象时,构建过程分步进行,代码更简洁,易于理解。
  • 适用于产品包含多个部件,且部件的创建步骤复杂的场景。

缺点

  • 建造者模式实现较为复杂:由于涉及多个角色和多个类,系统会变得更复杂。
  • 不适用于简单对象的创建:对于简单对象的构建,使用建造者模式可能导致不必要的复杂性。

4. Java应用场景

  • 复杂对象的创建:当一个对象由多个部分组成,并且这些部分的构建顺序不确定时,可以使用建造者模式。例如,构建一个包含多个步骤的复杂页面,或者一辆汽车的组装。
  • 产品构建的灵活性需求:如果一个产品有多个可能的构建方式,可以使用建造者模式来定义不同的构建步骤。
  • 构建具有多个可选配置的对象:如创建一个具有多个可选属性的对象,建造者模式能够根据不同需求灵活配置。

5. 代码展示

java 复制代码
// Product:Computer(复杂对象)
public class Computer {
    private String CPU;
    private String RAM;
    private String hardDrive;
    private String GPU;

    // 构造方法,提供默认的值,防止构造过程中缺失
    public Computer(String CPU, String RAM, String hardDrive, String GPU) {
        this.CPU = CPU;
        this.RAM = RAM;
        this.hardDrive = hardDrive;
        this.GPU = GPU;
    }

    @Override
    public String toString() {
        return "Computer [CPU=" + CPU + ", RAM=" + RAM + ", HardDrive=" + hardDrive + ", GPU=" + GPU + "]";
    }

    // Getter methods
    public String getCPU() {
        return CPU;
    }

    public String getRAM() {
        return RAM;
    }

    public String getHardDrive() {
        return hardDrive;
    }

    public String getGPU() {
        return GPU;
    }
}

// Builder:ComputerBuilder(抽象建造者)
public interface ComputerBuilder {
    void buildCPU();
    void buildRAM();
    void buildHardDrive();
    void buildGPU();
    Computer getComputer();
}

// ConcreteBuilder:HighEndComputerBuilder(具体建造者)
public class HighEndComputerBuilder implements ComputerBuilder {
    private Computer computer;

    public HighEndComputerBuilder() {
        this.computer = new Computer("", "", "", "");
    }

    @Override
    public void buildCPU() {
        computer = new Computer("Intel i9", computer.getRAM(), computer.getHardDrive(), computer.getGPU());
    }

    @Override
    public void buildRAM() {
        computer = new Computer(computer.getCPU(), "32GB", computer.getHardDrive(), computer.getGPU());
    }

    @Override
    public void buildHardDrive() {
        computer = new Computer(computer.getCPU(), computer.getRAM(), "1TB SSD", computer.getGPU());
    }

    @Override
    public void buildGPU() {
        computer = new Computer(computer.getCPU(), computer.getRAM(), computer.getHardDrive(), "NVIDIA RTX 3090");
    }

    @Override
    public Computer getComputer() {
        return computer;
    }
}

// Director:ComputerDirector(指挥者)
public class ComputerDirector {
    private ComputerBuilder builder;

    public ComputerDirector(ComputerBuilder builder) {
        this.builder = builder;
    }

    public Computer constructComputer() {
        builder.buildCPU();
        builder.buildRAM();
        builder.buildHardDrive();
        builder.buildGPU();
        return builder.getComputer();
    }
}

// 客户端代码:使用建造者模式创建复杂对象
public class BuilderPatternDemo {
    public static void main(String[] args) {
        // 使用高端电脑建造者
        ComputerBuilder builder = new HighEndComputerBuilder();
        ComputerDirector director = new ComputerDirector(builder);
        
        // 构建高端电脑
        Computer computer = director.constructComputer();
        System.out.println("Constructed Computer: " + computer);
    }
}

原型模式 (Prototype)

1. 介绍

原型模式(Prototype Pattern)是创建型设计模式之一,旨在通过复制已有的实例来创建新的对象,而不是通过直接构造。该模式通常适用于需要大量相似对象的情况,可以通过复制现有对象来减少创建新对象时的时间消耗,尤其是当创建对象的代价较大或复杂时。

在原型模式中,客户端通过克隆已有的原型对象来获得一个新对象,这样可以避免反复创建相同的对象,降低系统的性能开销。

2. 组成部分
  • Prototype(原型):声明一个克隆方法,通常是 clone(),用于复制当前实例。
  • ConcretePrototype(具体原型):实现原型接口,具体实现 clone() 方法,返回当前对象的副本。
  • Client(客户端):使用 clone() 方法来获得原型的副本,而不需要直接构造新的对象。
3. 作用
  • 提高性能:通过复制现有对象来生成新的对象,而不是重新创建对象,从而提高系统的性能,特别是在需要生成大量相似对象的情况下。
  • 简化对象的创建:不需要通过复杂的构造过程,直接复制现有对象来获得新对象,简化了对象的创建过程。
  • 支持对象的深拷贝和浅拷贝:原型模式可以支持对象的浅拷贝和深拷贝,具体取决于 clone() 方法的实现。
4. 优缺点

优点

  • 性能优化:当创建一个对象的代价较大或需要频繁创建相似对象时,使用原型模式可以显著提高性能,避免重复创建相同对象。
  • 简化对象创建:可以通过简单的克隆现有对象来创建新对象,避免重复的创建逻辑,简化了代码的复杂度。
  • 灵活性:通过原型模式,可以动态地切换或替换原型对象来创建不同的对象,从而增加了系统的灵活性。

缺点

  • 实现复杂:在某些情况下,克隆一个对象可能非常复杂,特别是对象内部包含复杂的子对象时,可能需要实现深拷贝,增加了实现的复杂度。
  • 可能存在不兼容的类型:如果原型对象内部有复杂的引用类型(例如,依赖外部资源或不支持复制的对象),那么克隆过程可能会产生错误或不兼容的问题。
5. Java应用场景
  • 对象的创建非常复杂或代价昂贵:当对象的创建代价很高(例如,创建一个复杂的图形或数据库连接等),而这些对象的状态可以从现有对象中派生时,使用原型模式能够提高性能。
  • 需要大量相似对象:如果程序中需要大量创建相似的对象(例如,具有相同的结构和属性但不同值的对象),原型模式可以通过复制现有对象来减少创建成本。
  • 支持对象的动态配置:原型模式可以用来实现一个对象的多个配置,使用不同的原型实例可以动态地改变对象的特性。
6. 代码展示
java 复制代码
// Prototype:原型接口
public interface Prototype {
    Prototype clone();
}

// ConcretePrototype:具体原型类
public class Car implements Prototype {
    private String model;
    private String color;
    private int year;

    public Car(String model, String color, int year) {
        this.model = model;
        this.color = color;
        this.year = year;
    }

    // 实现clone方法
    @Override
    public Prototype clone() {
        // 简单的浅拷贝
        return new Car(this.model, this.color, this.year);
    }

    @Override
    public String toString() {
        return "Car [model=" + model + ", color=" + color + ", year=" + year + "]";
    }

    // Getter and Setter methods
    public String getModel() {
        return model;
    }

    public String getColor() {
        return color;
    }

    public int getYear() {
        return year;
    }
}

// 客户端代码:使用原型模式来复制对象
public class PrototypePatternDemo {
    public static void main(String[] args) {
        // 创建原型对象
        Car originalCar = new Car("Tesla Model S", "Red", 2021);
        
        // 克隆原型对象
        Car clonedCar = (Car) originalCar.clone();
        
        System.out.println("Original Car: " + originalCar);
        System.out.println("Cloned Car: " + clonedCar);
        
        // 修改克隆对象的属性
        clonedCar = new Car("Tesla Model X", "Black", 2022);
        
        System.out.println("After modification - Cloned Car: " + clonedCar);
    }
}

二、结构型模式 (Structural Patterns)

结构型设计模式关注如何将类和对象组合成更大的结构。其目的是帮助确保系统具有灵活性、可扩展性以及便于修改和扩展。结构型模式主要处理类和对象之间的关系,设计出合适的结构,来完成某一任务。

适配器模式 (Adapter)

1.介绍:

适配器模式是一种结构型设计模式,它允许将一个类的接口转换成客户端所期望的另一个接口。通过引入适配器类,原本因接口不兼容而无法协同工作的类可以相互合作。适配器模式通常用于使已有类的接口符合目标接口的需求,避免修改现有的类。

2作用:

  • 解决接口不兼容问题:当一个类的接口不符合另一个类的接口要求时,通过适配器模式可以使这两个类协同工作。
  • 增强系统灵活性:可以在不修改现有代码的基础上,适配新的接口或第三方库的接口。
  • 复用已有代码:通过适配器,可以在不同的场景下复用现有的类,而不必修改它们的内部实现。

3优缺点:

优点:

  • 接口兼容:通过适配器模式,能够在不改变现有系统代码的情况下,使不同接口的类能够协同工作。
  • 增强系统灵活性:适配器模式提供了一种增强系统灵活性和扩展性的方式,尤其在处理第三方库或外部接口时尤为重要。
  • 降低耦合度:将接口适配的责任转移到适配器类,避免了多个类之间直接的复杂耦合。

缺点:

  • 增加系统复杂性:每个被适配的类都需要一个适配器类,因此会增加代码的复杂性,尤其在系统中有多个适配器时,可能导致系统结构变得混乱。
  • 可能引起性能问题:如果适配器类的设计不当,可能会导致额外的性能开销,特别是在需要频繁转换的情况下。

4Java应用场景:

  • 接口不兼容的外部库:当你需要集成第三方库,并且该库的接口与系统接口不兼容时,适配器模式可以用来适配接口。
  • 系统升级:当系统中的某些类需要升级,而升级后的类接口发生了变化,通过适配器可以保证旧有的代码不受影响。
  • Java IO流:Java的输入输出流使用适配器模式,允许不同的数据流以不同的格式进行处理。例如,FileInputStream 和 BufferedReader 就是通过适配器模式来实现数据的转换。

5代码示例

java 复制代码
// 新接口:Printer
interface Printer {
    void printText(String text);
}

// 老接口:OldPrinter
class OldPrinter {
    public void printCharacter(char character) {
        System.out.println("Printing character: " + character);
    }
}

// 适配器类:将OldPrinter适配为Printer
class PrinterAdapter implements Printer {
    private OldPrinter oldPrinter;

    public PrinterAdapter(OldPrinter oldPrinter) {
        this.oldPrinter = oldPrinter;
    }

    @Override
    public void printText(String text) {
        for (char c : text.toCharArray()) {
            oldPrinter.printCharacter(c);  // 使用旧接口的打印方法
        }
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        OldPrinter oldPrinter = new OldPrinter();
        Printer printer = new PrinterAdapter(oldPrinter);
        
        printer.printText("Hello, Adapter!");  // 使用新接口打印文本
    }
}

桥接模式 (Bridge)

1介绍:

桥接模式是一种结构型设计模式,它通过将抽象部分与实现部分分离,使它们可以独立地变化。该模式的核心思想是:将抽象与实现解耦,让它们各自独立变化。桥接模式通过引入一个桥接接口,使得抽象类与实现类之间的耦合度降低,从而增加了系统的灵活性和可扩展性。

在不使用桥接模式的情况下,通常会在抽象类和实现类之间直接建立继承关系,这样一来,如果实现方式发生变化,抽象类也需要发生变化。而使用桥接模式后,抽象类和实现类通过桥接接口分开,彼此独立修改,减少了代码的依赖性。

2作用:

  • 解耦抽象和实现:通过将抽象类和实现类分开,避免了它们之间的强耦合关系。系统可以独立地扩展抽象类和实现类。
  • 增加系统的灵活性:能够在不修改客户端代码的情况下,灵活地替换或扩展抽象类和实现类。
  • 符合开闭原则:系统可以通过增加新的实现类来扩展功能,而无需修改原有的抽象类和客户端代码。

3.优缺点:

优点:

  1. 减少类的个数:通过将抽象类与实现类分开,减少了继承层次,简化了类的数量。
  2. 提高系统的可扩展性:抽象和实现分离后,抽象类和实现类可以独立变化,增加新的功能时,不会影响原有的代码结构。
  3. 遵循开闭原则:在不修改现有代码的基础上,可以通过添加新实现来扩展功能,符合开闭原则。

缺点:

  1. 增加系统复杂性:桥接模式通过引入抽象接口和实现接口,增加了系统的复杂度,尤其在结构较简单的情况下,可能显得过于繁琐。
  2. 增加了代码的层次:需要为每种功能创建抽象类和实现类,在功能非常简单时,可能造成过多的代码层次。

4.Java应用场景:

  • 图形绘制系统:例如,在绘制图形(如圆形、正方形等)时,可以将图形的颜色、填充方式等实现细节抽象成不同的实现类,而具体的图形类型(如圆形、正方形)作为抽象类来处理。这样,当需要支持新的图形类型或新的颜色方式时,只需要扩展对应的类。
  • 设备遥控器:当你需要控制不同品牌的电视、空调等设备时,每个设备的接口和操作可能不同。可以通过桥接模式将"设备操作"和"遥控器操作"分开,使得可以独立控制遥控器类型和设备类型。
  • 操作系统中的图形用户界面(GUI)框架:图形界面通常有多个平台(Windows、Linux、macOS),不同平台有不同的图形库和绘制方法。桥接模式可以使得图形抽象与平台的具体实现解耦,从而可以轻松地为不同平台添加新的图形组件。
  • 多种数据库管理系统(DBMS):如果系统需要支持多种数据库(如MySQL、Oracle、SQLServer等),可以使用桥接模式将数据库操作(如插入、查询)与数据库类型分离,增加新的数据库类型时无需修改原有的操作代码。

5代码示例:

java 复制代码
// Color接口(实现部分)
interface Color {
    void applyColor();
}

// 具体颜色实现类:Red
class Red implements Color {
    @Override
    public void applyColor() {
        System.out.println("Applying Red color");
    }
}

// 具体颜色实现类:Blue
class Blue implements Color {
    @Override
    public void applyColor() {
        System.out.println("Applying Blue color");
    }
}

// Shape接口(抽象部分)
abstract class Shape {
    protected Color color;

    public Shape(Color color) {
        this.color = color;
    }

    abstract void draw();
}

// 具体形状实现类:Circle
class Circle extends Shape {

    public Circle(Color color) {
        super(color);
    }

    @Override
    void draw() {
        System.out.print("Drawing Circle with ");
        color.applyColor();  // 使用颜色
    }
}

// 具体形状实现类:Rectangle
class Rectangle extends Shape {

    public Rectangle(Color color) {
        super(color);
    }

    @Override
    void draw() {
        System.out.print("Drawing Rectangle with ");
        color.applyColor();  // 使用颜色
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        Shape circle = new Circle(new Red());
        circle.draw();  // 

        Shape rectangle = new Rectangle(new Blue());
        rectangle.draw();  // 
    }
}

组合模式 (Composite)

1介绍:

组合模式是一种结构型设计模式,它允许你将对象组合成树形结构来表示"部分-整体"的层次结构。组合模式可以让客户端以统一的方式对待单个对象和对象集合,使得单个对象和组合对象(集合)具有一致的接口。

在组合模式中,叶子对象(即单个对象)和组合对象(即包含多个对象的容器)都实现相同的接口,客户端通过统一接口来操作这些对象,无论是操作单个对象还是操作整个组合。

2作用:

  • 表示部分-整体层次结构:组合模式可以让你创建一个树形结构,表示部分与整体之间的关系。叶子节点代表单个对象,组合节点代表包含子对象的容器。
  • 简化代码结构:客户端通过统一接口来访问单个对象或组合对象,简化了代码结构。
  • 增强灵活性:组合模式支持动态地将对象组合成不同的结构,可以在运行时改变组合的方式,增加了系统的灵活性。

3优缺点:

优点:

  1. 统一的接口:客户端通过统一的接口访问单个对象和组合对象,简化了代码。
  2. 树形结构的表示:非常适合表示部分-整体层次结构,使得树形结构更加清晰。
  3. 支持递归操作:通过递归的方式,可以轻松处理多级的组合结构。

缺点:

  1. 设计复杂:组合模式引入了多层结构,可能导致设计上的复杂性,尤其是在组合对象数量较多时。
  2. 不适合叶子节点和组合节点有显著差异的情况:如果叶子节点和组合节点的行为差异非常大,使用组合模式可能会导致不必要的复杂性。

4Java应用场景:

  1. 文件系统:文件和文件夹具有部分-整体的关系,文件可以是单个文件,而文件夹可以包含多个文件或文件夹。可以使用组合模式将文件和文件夹统一表示。
  2. 图形界面(GUI)组件:在图形界面中,一个窗口可能包含多个按钮、文本框等组件,而这些组件又可以包含更复杂的子组件。可以使用组合模式来处理这些GUI组件。
  3. 组织结构:组织结构中,员工可以是单个员工,部门可以包含多个员工或者其他部门,组合模式适合这种层次结构。
  4. 树形结构的表示:任何需要表示层次结构(如公司架构、文件目录等)的场景都可以使用组合模式。

5.代码示例:

java 复制代码
// 文件组件接口(统一接口)
interface FileSystemComponent {
    void showDetails();  // 显示详细信息
}

// 文件类(叶子节点)
class File implements FileSystemComponent {
    private String name;

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

    @Override
    public void showDetails() {
        System.out.println("File: " + name);
    }
}

// 文件夹类(组合节点)
class Folder implements FileSystemComponent {
    private String name;
    private List<FileSystemComponent> components = new ArrayList<>();  // 存储子组件(文件或文件夹)

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

    public void add(FileSystemComponent component) {
        components.add(component);
    }

    public void remove(FileSystemComponent component) {
        components.remove(component);
    }

    @Override
    public void showDetails() {
        System.out.println("Folder: " + name);
        for (FileSystemComponent component : components) {
            component.showDetails();  // 递归显示文件夹内部的内容
        }
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        // 创建文件和文件夹
        File file1 = new File("File1.txt");
        File file2 = new File("File2.txt");
        Folder folder1 = new Folder("Folder1");

        // 创建一个文件夹,文件夹中包含文件
        Folder folder2 = new Folder("Folder2");
        folder2.add(file1);
        folder2.add(file2);

        // 在文件夹1中添加文件夹2
        folder1.add(folder2);

        // 显示文件夹和文件的结构
        folder1.showDetails();
    }
}

装饰器模式 (Decorator)

1介绍:

装饰器模式是一种结构型设计模式,允许你在不改变对象结构的情况下动态地给一个对象添加额外的功能。它通过创建一个包装对象来增强目标对象的功能。装饰器模式提供了一种灵活的方式来扩展对象的功能,相比于继承方式,装饰器更加灵活,因为它不需要修改原有类的代码,并且可以在运行时动态地添加或移除功能。

装饰器模式的关键是将功能分解为多个装饰器类,每个装饰器类负责增强某一特定功能,而不需要修改原始对象的代码。

2作用:

  • 增强功能:装饰器模式可以在不修改现有代码的前提下,为对象动态增加新的行为。
  • 灵活扩展:通过装饰器链,可以组合不同的功能装饰器,灵活地构建不同的功能。
  • 遵循开闭原则:装饰器模式允许系统在不修改原有类的代码的情况下,通过扩展来添加新功能,符合开闭原则(对扩展开放,对修改关闭)。

3优缺点:

优点:

  1. 灵活性:装饰器可以在运行时动态地添加功能,组合使用不同的装饰器类,可以提供各种不同的功能组合。
  2. 不需要修改原始类:你可以通过创建新的装饰器类来增强对象的功能,而不需要修改原始类的代码。
  3. 符合开闭原则:通过装饰器模式扩展功能,不会影响原有的类,符合"对扩展开放,对修改关闭"的设计原则。

缺点:

  1. 类的数量增加:由于装饰器模式涉及到多个装饰器类,它会增加系统中的类的数量,可能导致系统的复杂性增加。
  2. 多层嵌套装饰器:如果装饰器层级过多,会导致系统难以理解和维护,可能会让对象的行为变得不透明。

4Java应用场景:

  1. GUI组件:在图形用户界面(GUI)中,装饰器模式可以用于动态地给组件添加额外的功能,比如为按钮添加边框、颜色、阴影等装饰,而不修改按钮的核心实现。
  2. 日志记录:你可以通过装饰器模式给已有的日志系统动态添加功能,比如记录日志到文件、输出到控制台等。
  3. 输入输出流:Java的I/O流(如 BufferedReader, FileReader 等)使用了装饰器模式,通过包装现有流对象,来增强其功能,如增加缓存、字符转换等。

5代码示例:

java 复制代码
// 基本接口:饮品接口
interface Beverage {
    String getDescription();  // 获取饮品描述
    double cost();  // 计算饮品价格
}

// 具体的饮品类:咖啡
class Coffee implements Beverage {
    @Override
    public String getDescription() {
        return "Coffee";
    }

    @Override
    public double cost() {
        return 5.00;  // 咖啡的基本价格
    }
}

// 装饰器基类:装饰器实现了Beverage接口
abstract class CondimentDecorator implements Beverage {
    protected Beverage beverage;  // 被装饰的饮品

    public CondimentDecorator(Beverage beverage) {
        this.beverage = beverage;
    }
}

// 具体装饰器类:添加牛奶
class MilkDecorator extends CondimentDecorator {
    public MilkDecorator(Beverage beverage) {
        super(beverage);
    }

    @Override
    public String getDescription() {
        return beverage.getDescription() + ", Milk";  // 添加牛奶的描述
    }

    @Override
    public double cost() {
        return beverage.cost() + 1.50;  // 牛奶的额外费用
    }
}

// 具体装饰器类:添加糖
class SugarDecorator extends CondimentDecorator {
    public SugarDecorator(Beverage beverage) {
        super(beverage);
    }

    @Override
    public String getDescription() {
        return beverage.getDescription() + ", Sugar";  // 添加糖的描述
    }

    @Override
    public double cost() {
        return beverage.cost() + 0.50;  // 糖的额外费用
    }
}

// 客户端代码:组合饮品
public class Client {
    public static void main(String[] args) {
        // 创建一个基本的咖啡
        Beverage coffee = new Coffee();
        System.out.println(coffee.getDescription() + " $" + coffee.cost());

        // 为咖啡添加牛奶
        Beverage milkCoffee = new MilkDecorator(coffee);
        System.out.println(milkCoffee.getDescription() + " $" + milkCoffee.cost());

        // 为咖啡添加牛奶和糖
        Beverage milkSugarCoffee = new SugarDecorator(milkCoffee);
        System.out.println(milkSugarCoffee.getDescription() + " $" + milkSugarCoffee.cost());
    }
}

外观模式 (Facade)

1介绍:

外观模式是一种结构型设计模式,它为子系统中的一组接口提供一个统一的高层接口,使得子系统更易使用。通过外观模式,可以将复杂的子系统隐藏在一个简单的接口后面,客户端只需要与外观类交互,而不需要直接处理子系统中的复杂类或多层级的功能。外观模式的核心思想是简化系统的使用,通过封装子系统的复杂性,提供一个更简洁、更易用的接口。

外观模式不仅简化了客户端的操作,还能够隔离子系统与客户端之间的耦合,使得子系统能够独立变化而不影响客户端。

2.作用:

  • 简化接口:为复杂的子系统提供一个统一的接口,减少了客户端的调用复杂度。
  • 解耦客户端和子系统:客户端不需要直接与多个子系统交互,通过外观类与子系统交互,减少了系统的耦合性。
  • 提升系统可维护性:通过减少客户端与子系统之间的直接依赖,增强了系统的模块化,便于日后的修改和维护。

3.优缺点:

优点:

  1. 简化了客户端代码:客户端只需要通过外观接口与子系统交互,而无需关心内部细节,代码更加简洁。
  2. 降低了耦合度:外观模式将客户端与多个子系统隔离,避免了客户端直接与多个类或子系统交互。
  3. 提高系统可维护性:子系统的复杂性被隐藏,系统更易于理解和修改。

缺点:

  1. 可能造成不必要的依赖:外观模式将子系统隐藏在外观类中,如果不当使用,可能会导致客户端过于依赖外观类,进而增加系统的复杂性。
  2. 可能掩盖子系统的某些细节:外观模式可能会简化某些复杂功能,但如果外观类过于简化,可能会限制子系统的某些高级功能。

4.Java应用场景:

  1. 视频播放系统:在视频播放器中,涉及多个子系统(解码、播放控制、音量控制等)。通过外观模式,我们可以提供一个简单的接口来控制播放,而不需要让用户理解和管理每个子系统。
  2. 家居自动化系统:智能家居系统可能涉及多个子系统(灯光、空调、安全监控等)。外观模式可以提供一个统一的接口,简化用户的操作。
  3. 银行系统:在银行系统中,可能会涉及多个子系统(账户管理、贷款管理、支付处理等)。通过外观模式,可以将这些子系统的复杂操作封装在一个简单的接口中,方便客户使用。

5.代码示例:

java 复制代码
// 子系统:音响
class SoundSystem {
    public void turnOn() {
        System.out.println("Sound system is on.");
    }

    public void turnOff() {
        System.out.println("Sound system is off.");
    }

    public void setVolume(int level) {
        System.out.println("Setting sound volume to " + level);
    }
}

// 子系统:投影仪
class Projector {
    public void turnOn() {
        System.out.println("Projector is on.");
    }

    public void turnOff() {
        System.out.println("Projector is off.");
    }

    public void setInput(String input) {
        System.out.println("Setting projector input to " + input);
    }
}

// 子系统:DVD播放器
class DVDPlayer {
    public void turnOn() {
        System.out.println("DVD Player is on.");
    }

    public void turnOff() {
        System.out.println("DVD Player is off.");
    }

    public void play(String movie) {
        System.out.println("Playing movie: " + movie);
    }
}

// 外观类:家庭影院
class HomeTheaterFacade {
    private SoundSystem soundSystem;
    private Projector projector;
    private DVDPlayer dvdPlayer;

    public HomeTheaterFacade(SoundSystem soundSystem, Projector projector, DVDPlayer dvdPlayer) {
        this.soundSystem = soundSystem;
        this.projector = projector;
        this.dvdPlayer = dvdPlayer;
    }

    // 启动家庭影院
    public void watchMovie(String movie) {
        System.out.println("Get ready to watch a movie...");
        projector.turnOn();
        projector.setInput("DVD");
        soundSystem.turnOn();
        soundSystem.setVolume(10);
        dvdPlayer.turnOn();
        dvdPlayer.play(movie);
    }

    // 关闭家庭影院
    public void endMovie() {
        System.out.println("Shutting down the home theater...");
        dvdPlayer.turnOff();
        soundSystem.turnOff();
        projector.turnOff();
    }
}

// 客户端代码:使用外观类
public class Client {
    public static void main(String[] args) {
        // 创建子系统对象
        SoundSystem soundSystem = new SoundSystem();
        Projector projector = new Projector();
        DVDPlayer dvdPlayer = new DVDPlayer();

        // 创建外观对象
        HomeTheaterFacade homeTheater = new HomeTheaterFacade(soundSystem, projector, dvdPlayer);

        // 使用外观类操作子系统
        homeTheater.watchMovie("Inception");
        System.out.println("\n--- Movie End ---");
        homeTheater.endMovie();
    }
}

享元模式 (Flyweight)

1.介绍:

享元模式是一种结构型设计模式,它通过共享对象来支持大量细粒度的对象,从而减少内存消耗。享元模式的核心思想是将相同的数据共享,把可以共享的部分提取出来,避免重复存储。这对于大量类似的对象尤其有用,可以有效降低内存使用,提升性能。

享元模式的关键是将对象分为两类:

  1. 共享部分(内蕴状态):这些部分是相同的,多个对象可以共享这些部分。
  2. 非共享部分(外蕴状态):这些部分是不同的,每个对象的具体实例都有自己的外部状态。

享元模式通过将共享部分提取出来,减少了系统中对象的数量,同时保持对象的行为和状态。它适用于那些状态重复并且共享的对象,尤其是内存开销较大时。

2.作用:

  • 减少内存消耗:通过共享相同的部分,减少了对象的数量,从而减少了内存的使用。
  • 提升性能:共享对象可以减少对象的创建,降低对象创建和销毁的开销,提升系统性能。
  • 提高系统可扩展性:通过共享对象,系统可以更容易地扩展。

3.优缺点:

优点:

  1. 节省内存:通过共享相同的对象,避免了重复存储,节省了内存。
  2. 提高性能:减少了对象的创建和销毁,降低了资源消耗,提升了系统性能。
  3. 支持大规模系统:适用于需要管理大量对象的系统,能够在保证效率的同时提供可扩展性。

缺点:

  1. 增加了复杂性:需要管理共享对象和非共享对象,增加了代码的复杂度。
  2. 线程安全问题:共享对象在多线程环境下可能需要考虑线程安全问题,增加了额外的同步开销。
  3. 限制了对象的灵活性:由于共享对象的状态是固定的,某些情况下可能会限制对象的灵活性和定制化。

4.Java应用场景:

  1. 图形对象管理:在一些图形系统中,可能有大量重复的图形对象(如相同的颜色、形状等),可以通过享元模式将这些重复的部分共享,以减少内存使用。
  2. 字符和字体管理:文本编辑器中可能会有大量相同的字符对象,使用享元模式可以共享相同的字符对象,避免每个字符都占用独立的内存空间。
  3. 游戏开发:在游戏开发中,可能有大量的相同敌人、道具、场景元素等对象,使用享元模式可以有效地减少内存消耗,提高游戏性能。
  4. 数据库连接池:在数据库连接池中,连接对象通常是相同的,可以通过享元模式共享这些连接,避免每次都创建新的连接对象。

5.代码示例:

java 复制代码
// 享元接口
interface Enemy {
    void display();  // 显示敌人信息
}

// 具体享元类:僵尸敌人
class Zombie implements Enemy {
    private String type;  // 敌人类型(共享部分)

    public Zombie() {
        this.type = "Zombie";
    }

    @Override
    public void display() {
        System.out.println("Enemy type: " + type);
    }
}

// 具体享元类:吸血鬼敌人
class Vampire implements Enemy {
    private String type;  // 敌人类型(共享部分)

    public Vampire() {
        this.type = "Vampire";
    }

    @Override
    public void display() {
        System.out.println("Enemy type: " + type);
    }
}

// 享元工厂:管理享元对象的创建与共享
class EnemyFactory {
    private Map<String, Enemy> enemyMap = new HashMap<>();  // 存储已创建的享元对象

    public Enemy getEnemy(String type) {
        Enemy enemy = enemyMap.get(type);
        if (enemy == null) {
            switch (type) {
                case "Zombie":
                    enemy = new Zombie();
                    break;
                case "Vampire":
                    enemy = new Vampire();
                    break;
                default:
                    throw new IllegalArgumentException("Unknown enemy type");
            }
            enemyMap.put(type, enemy);  // 缓存敌人对象
        }
        return enemy;
    }
}

// 游戏中的敌人位置和状态(外蕴状态)
class GameEnemy {
    private Enemy enemy;  // 享元对象
    private int x, y;  // 位置(外部状态)
    private int health;  // 生命值(外部状态)

    public GameEnemy(Enemy enemy, int x, int y, int health) {
        this.enemy = enemy;
        this.x = x;
        this.y = y;
        this.health = health;
    }

    public void display() {
        enemy.display();
        System.out.println("Position: (" + x + ", " + y + "), Health: " + health);
    }
}

// 客户端代码:游戏管理敌人
public class Client {
    public static void main(String[] args) {
        // 创建敌人工厂
        EnemyFactory enemyFactory = new EnemyFactory();

        // 创建并显示不同位置和状态的敌人
        GameEnemy enemy1 = new GameEnemy(enemyFactory.getEnemy("Zombie"), 10, 20, 100);
        GameEnemy enemy2 = new GameEnemy(enemyFactory.getEnemy("Zombie"), 30, 40, 150);
        GameEnemy enemy3 = new GameEnemy(enemyFactory.getEnemy("Vampire"), 50, 60, 200);

        enemy1.display();
        enemy2.display();
        enemy3.display();
    }
}

代理模式 (Proxy)

1介绍:

代理模式是一种结构型设计模式,允许通过代理对象来控制对另一个对象的访问。代理对象通常会和被代理对象实现相同的接口,并在实际操作被代理对象时进行一些额外的控制或功能增强。代理模式主要用于控制对对象的访问,它提供了一种间接访问方式,可以用来延迟初始化、权限控制、日志记录、缓存等。

代理模式的核心思想是通过代理对象对实际对象的访问进行控制,客户端并不直接与目标对象交互,而是通过代理对象间接与目标对象交互。

2.作用:

  • 控制访问:代理可以用于控制对对象的访问,例如权限验证、延迟加载等。
  • 增强功能:通过代理对象,可以在访问实际对象之前或之后执行一些额外的操作,如日志记录、性能监控等。
  • 保护代理:可以用来保护对象,限制对对象的访问,防止直接操作敏感数据或复杂对象。
  • 延迟加载:可以通过代理延迟某些资源的加载,直到真正需要使用时才加载。

3.优缺点:

优点:

  1. 增强功能:代理模式可以在不修改目标对象的前提下,通过代理对象为目标对象提供附加功能(如权限控制、缓存、日志等)。
  2. 控制访问:代理可以控制对目标对象的访问权限,限制某些操作,增强安全性。
  3. 延迟加载:代理可以帮助延迟某些资源的加载,提高系统启动速度,或者减少不必要的资源消耗。

缺点:

  1. 增加了系统复杂性:引入代理对象会增加系统中的类的数量,可能导致系统结构更加复杂。
  2. 性能开销:代理模式会增加一层间接调用,可能导致性能损失,尤其是在调用频繁的情况下。
  3. 不适合简单场景:如果代理对象的功能过于简单,可能会导致系统不必要的复杂性。

4.Java应用场景:

  1. 远程代理:用于处理远程对象的访问,通常用于分布式系统中。代理对象可以屏蔽网络通信的细节,客户端通过代理对象访问远程服务。
  2. 虚拟代理:用于控制对象的初始化,只有在真正需要时才创建对象。这常用于资源消耗较大的对象,例如图像、视频等。
  3. 保护代理:用于控制对目标对象的访问,通常在权限控制、数据保护等场景中使用。
  4. 缓存代理:通过代理来实现缓存,避免重复的资源消耗。例如,通过代理缓存数据库查询结果,避免重复查询。
  5. 智能代理:用于在访问对象时执行一些附加功能,如日志记录、性能监控等。

5.代码示例:

java 复制代码
// 目标对象接口:Subject
interface Subject {
    void request();  // 执行请求
}

// 具体目标对象:RealSubject
class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("RealSubject: Executing request.");
    }
}

// 代理对象:Proxy
class Proxy implements Subject {
    private RealSubject realSubject;

    @Override
    public void request() {
        if (realSubject == null) {
            realSubject = new RealSubject();  // 延迟加载目标对象
        }
        
        // 在实际请求之前执行额外操作(如权限验证)
        System.out.println("Proxy: Checking permissions before executing the request.");
        
        // 执行目标对象的请求
        realSubject.request();
        
        // 在实际请求之后执行额外操作(如日志记录)
        System.out.println("Proxy: Logging the request execution.");
    }
}

// 客户端代码:使用代理对象访问目标对象
public class Client {
    public static void main(String[] args) {
        Subject proxy = new Proxy();  // 使用代理对象
        proxy.request();  // 通过代理对象执行请求
    }
}

三、行为型模式 (Behavioral Patterns)

行为型模式主要关注对象之间的通信和职责分配。它们定义了对象之间的通信方式和职责分配方式,使得系统中的对象可以在不紧密耦合的情况下协作。行为型模式有助于简化复杂的控制流、避免代码重复、促进可扩展性和可维护性。

观察者模式 (Observer)

1. 介绍

观察者模式(Observer Pattern)是一种行为型设计模式,用于定义一种一对多的依赖关系,让多个观察者对象同时监听一个主题对象。当主题对象的状态发生变化时,它的所有依赖者(观察者)都会收到通知并自动更新。这种模式主要用于实现对象之间的松耦合,避免了在系统中创建大量的直接引用。

在观察者模式中,主题(Subject) 通常是状态的提供者,而 观察者(Observer) 则是依赖于这些状态变化的对象。每当主题对象发生变化时,观察者会自动得到通知,并作出相应的处理。

2. 作用

  • 松耦合:主题和观察者之间的关系是松散耦合的,观察者无需知道主题的实现细节,反之亦然。观察者通过接口与主题进行通信,不直接依赖于主题的具体实现。
  • 动态更新:观察者可以动态地注册和取消注册,允许在运行时动态增加或移除观察者。主题的状态变化会自动通知所有注册的观察者,保持同步更新。
  • 一对多依赖关系:观察者模式能够轻松实现一对多的关系,多个观察者可以监听同一个主题,保持状态的一致性。
  • 简化系统设计:通过观察者模式,可以减少代码中直接依赖关系,简化系统的设计和维护。

3. 优缺点

优点:

  1. 松散耦合:观察者和主题之间没有直接依赖关系,避免了组件间的强耦合。
  2. 灵活的对象通知机制:可以在运行时动态地增加、移除观察者,使得系统可以灵活地扩展。
  3. 多对一的通知机制:多个观察者可以同时接收主题的状态变化通知,确保系统中所有相关对象保持同步。
  4. 扩展性好:如果需要引入新的观察者,只需要实现观察者接口并注册到主题中,无需修改其他代码。

缺点:

  1. 可能导致更新操作过多:在主题状态变化频繁时,所有观察者都需要更新,可能会带来性能问题。
  2. 观察者数量多时,系统性能可能下降:当观察者非常多时,通知所有观察者可能会影响性能,尤其是在实时性要求高的场景下。
  3. 不知道观察者是否已被更新:主题无法知道观察者的处理情况,可能导致一些观察者未及时处理信息。

4. Java应用场景

  • 事件监听:图形用户界面(GUI)中的事件监听就是一个典型的观察者模式应用。例如,按钮点击、窗口状态变化等事件,都可以通过观察者模式来处理。
  • 消息推送系统:多个用户或客户端需要接收相同的消息或通知(如社交媒体平台、新闻推送等),使用观察者模式能够确保消息推送给所有关注的用户。
  • 股票价格监控系统:一个股票的价格变化需要通知多个订阅者(如投资者、股票分析师等),每当股票价格变化时,所有订阅者都能收到更新通知。
  • 日志系统:在日志管理系统中,可以将日志的变化作为一个主题,多个日志观察者(如文件、数据库、控制台等)可以实时接收并处理日志消息。

5. 代码示例

java 复制代码
// Observer 接口:定义更新的接口
public interface Observer {
    void update(String message);
}

// ConcreteObserver 类:具体的观察者
public class ConcreteObserver implements Observer {
    private String name;

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

    @Override
    public void update(String message) {
        System.out.println(name + " received message: " + message);
    }
}

// Subject 类:主题,维护观察者列表
public class Subject {
    private List<Observer> observers = new ArrayList<>();

    // 注册观察者
    public void addObserver(Observer observer) {
        observers.add(observer);
    }

    // 移除观察者
    public void removeObserver(Observer observer) {
        observers.remove(observer);
    }

    // 通知所有观察者
    public void notifyObservers(String message) {
        for (Observer observer : observers) {
            observer.update(message);
        }
    }
}

// 使用示例
public class ObserverPatternDemo {
    public static void main(String[] args) {
        // 创建主题
        Subject subject = new Subject();

        // 创建观察者
        Observer observer1 = new ConcreteObserver("Observer 1");
        Observer observer2 = new ConcreteObserver("Observer 2");

        // 注册观察者
        subject.addObserver(observer1);
        subject.addObserver(observer2);

        // 主题状态变化,通知观察者
        subject.notifyObservers("State has changed!");

        // 移除观察者
        subject.removeObserver(observer1);

        // 再次通知,只有 observer2 会收到消息
        subject.notifyObservers("Another state change!");
    }
}

策略模式 (Strategy)

1. 介绍

策略模式(Strategy Pattern)是一种行为型设计模式,用于定义一系列算法(策略),并将每个算法封装起来,使它们可以相互替换。策略模式让算法的变化独立于使用算法的客户端。换句话说,策略模式允许客户端在运行时决定使用哪种算法(策略),而不需要修改使用该算法的代码。

策略模式的核心思想是:将算法封装到独立的策略类中,让客户端可以灵活选择和切换算法,而无需修改客户端代码。这样,算法的变动与客户端的代码解耦,提高了代码的可扩展性和维护性。

2. 作用

  • 封装算法:策略模式通过封装算法,使得客户端无需关注算法的具体实现,只需要关心如何选择合适的策略。
  • 减少条件判断:如果你有多个算法或行为,使用策略模式可以避免在客户端写大量的 if-else 或 switch 语句来判断选择哪个算法。
  • 提高扩展性:策略模式使得你可以轻松地添加新策略,而不影响现有代码。只需新增一个策略类即可,而不需要修改客户端或其他策略类。
  • 动态选择策略:客户端可以在运行时根据条件选择不同的策略,而不需要修改已有代码。

3. 优缺点

优点:

  1. 避免使用多个条件语句:策略模式将算法封装成独立的策略类,避免了大量的条件语句,提高了代码的可读性。
  2. 灵活性高:客户端可以动态选择不同的策略(算法),在不同的情况下执行不同的策略,增加了系统的灵活性。
  3. 易于扩展:新增策略时只需新增策略类,而不需要修改现有的代码,符合开闭原则(对扩展开放,对修改关闭)。
  4. 清晰的分层:每个策略类负责自己的算法或行为,使得代码更加清晰易懂,遵循单一职责原则。

缺点:

  1. 策略类数量多:每个策略需要一个独立的类,可能导致系统中类的数量增加。
  2. 客户端必须了解所有策略:客户端需要了解所有可用的策略,并根据条件选择合适的策略,这可能会增加客户端的复杂度。
  3. 可能导致类的爆炸:当策略变多时,系统中可能会有很多策略类,维护成本可能增高。

4. Java应用场景

  • 支付方式:在电商平台中,支付方式可以有多种,如支付宝、微信支付、银行卡支付等。使用策略模式可以将每种支付方式封装成独立的策略,用户可以选择不同的支付方式。
  • 排序算法:在数据处理中,可能需要根据不同的场景选择不同的排序算法(如快速排序、归并排序、冒泡排序等)。策略模式可以封装不同的排序算法,动态选择合适的排序策略。
  • 文件压缩:在文件管理系统中,可以使用策略模式封装不同的压缩算法(如 ZIP、RAR、7z 等),根据用户的需求选择合适的压缩策略。
  • 推荐系统:在一些推荐系统中,可以根据不同的业务逻辑和用户需求使用不同的推荐算法。策略模式能够封装不同的推荐算法,并动态选择。
  1. 代码示例
java 复制代码
// Strategy 接口:定义所有支持的算法(策略)接口
public interface Strategy {
    void execute();  // 执行算法
}

// ConcreteStrategyA 类:具体的策略A
public class ConcreteStrategyA implements Strategy {
    @Override
    public void execute() {
        System.out.println("Executing strategy A");
    }
}

// ConcreteStrategyB 类:具体的策略B
public class ConcreteStrategyB implements Strategy {
    @Override
    public void execute() {
        System.out.println("Executing strategy B");
    }
}

// Context 类:上下文,使用某种策略
public class Context {
    private Strategy strategy;

    // 设置策略
    public void setStrategy(Strategy strategy) {
        this.strategy = strategy;
    }

    // 执行策略
    public void executeStrategy() {
        strategy.execute();
    }
}

// 使用示例
public class StrategyPatternDemo {
    public static void main(String[] args) {
        Context context = new Context();

        // 使用策略A
        context.setStrategy(new ConcreteStrategyA());
        context.executeStrategy();  // 输出:Executing strategy A

        // 使用策略B
        context.setStrategy(new ConcreteStrategyB());
        context.executeStrategy();  // 输出:Executing strategy B
    }
}

命令模式 (Command)

1. 介绍

命令模式(Command Pattern)是一种行为型设计模式,它将请求封装为一个对象,从而使用户能够通过不同的请求参数对客户进行请求。命令模式的核心思想是:将请求封装成一个命令对象,通过该对象调用请求操作,实现请求与执行操作的解耦。

在命令模式中,客户端发起的请求不直接调用方法,而是通过命令对象传递给接收者对象,接收者对象执行实际的操作。命令对象通常封装了所有的信息,包括方法的调用、参数、以及执行时的上下文信息。

2. 作用

  • 解耦请求发送者与接收者:命令模式将请求发送者与请求接收者解耦,客户端无需知道请求是如何被处理的,只需要知道发送命令给接收者即可。
  • 支持撤销操作:命令模式允许对命令对象进行撤销和重做操作。可以通过命令的历史记录来回滚到先前的状态。
  • 组合命令:命令模式可以将多个命令对象组合成一个宏命令,在需要时一次性执行一组命令。
  • 易于扩展:通过增加新的命令类来增加新功能,而不影响原有代码,符合开闭原则。

3. 优缺点

优点:

  1. 解耦请求发送者与接收者:客户端不直接调用方法,客户端仅关注命令对象的创建和调用,而不关心方法的具体实现,降低了耦合度。
  2. 支持命令的撤销/重做功能:可以很容易地实现命令的撤销(undo)与重做(redo)操作。
  3. 扩展性强:可以通过增加新的命令类来增加新功能,而无需修改现有代码。
  4. 灵活性高:可以将多个命令组合成一个宏命令进行执行,或对多个命令执行排队操作。

缺点:

  1. 可能增加类的数量:每个命令都会有一个相应的命令类,这可能导致类的数量增多。
  2. 系统的复杂性:如果系统中命令非常复杂,可能导致系统结构更加复杂,理解和维护成本增加。
  3. 命令类可能过于冗长:每个命令类都需要实现一个独立的接口或抽象类,可能会增加代码冗余。

4. Java应用场景

  • UI 操作:命令模式可用于UI框架中的按钮点击事件、菜单项操作等。每个用户操作(如点击按钮)都可以看作是一个命令,通过命令对象来封装。
  • 多线程任务管理:在多线程环境中,可以将任务封装为命令,交由线程池执行,命令模式能够有效地处理任务的排队和执行。
  • 宏命令执行:在一些系统中,可能需要执行一组命令(例如批量操作),命令模式可以将这些操作封装成一个宏命令,统一执行。
  • Undo/Redo功能:命令模式非常适合实现撤销和重做功能,特别是在图形编辑器、文本编辑器等应用中。
  1. 代码示例
java 复制代码
// Command 接口:定义执行命令的抽象方法
public interface Command {
    void execute();
}

// ConcreteCommand 类:具体的命令对象
public class LightOnCommand implements Command {
    private Light light;

    public LightOnCommand(Light light) {
        this.light = light;
    }

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

// 另一个 ConcreteCommand 类
public class LightOffCommand implements Command {
    private Light light;

    public LightOffCommand(Light light) {
        this.light = light;
    }

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

// 接收者类:Light 设备
public class Light {
    public void turnOn() {
        System.out.println("The light is ON");
    }

    public void turnOff() {
        System.out.println("The light is OFF");
    }
}

// Invoker 类:调用命令
public class RemoteControl {
    private Command command;

    // 设置命令
    public void setCommand(Command command) {
        this.command = command;
    }

    // 执行命令
    public void pressButton() {
        command.execute();
    }
}

// 使用示例
public class CommandPatternDemo {
    public static void main(String[] args) {
        // 创建接收者对象
        Light light = new Light();

        // 创建命令对象
        Command lightOn = new LightOnCommand(light);
        Command lightOff = new LightOffCommand(light);

        // 创建请求者对象
        RemoteControl remote = new RemoteControl();

        // 按钮按下,执行命令
        remote.setCommand(lightOn);
        remote.pressButton();  // 输出:The light is ON

        remote.setCommand(lightOff);
        remote.pressButton();  // 输出:The light is OFF
    }
}

责任链模式 (Chain of Responsibility)

1. 介绍

责任链模式(Chain of Responsibility Pattern)是一种行为型设计模式,用于避免请求发送者与多个接收者之间的耦合关系。通过将多个处理对象连成一条链,让每个处理对象沿着链传递请求,直到某个对象处理这个请求为止。每个处理对象只能处理自己职责范围内的请求,若不能处理,则将请求传递给链中的下一个处理对象。

责任链模式的关键思想是将请求的处理过程分解成多个处理环节,通过责任链的方式,使得请求的处理更加灵活,且可以避免强耦合的情况。

2. 作用

  • 解耦请求的发送者和接收者:请求者和处理者之间不直接关联,处理者之间通过链式结构解耦,避免了请求发送者与接收者之间的紧耦合。
  • 动态处理请求:责任链模式可以动态地改变请求的处理方式,客户端可以根据需要选择链中的处理对象。
  • 简化对象结构:请求的处理分布在责任链上的多个对象中,每个对象处理一部分工作,减少了单一对象的复杂度。

3. 优缺点

优点:

  1. 解耦发送者和接收者:客户端不需要知道请求的处理者是谁,责任链中的处理对象是相互独立的。
  2. 灵活性高:责任链的处理顺序是可配置的,可以动态添加或修改责任链中的处理对象。
  3. 降低类之间的耦合:通过责任链的方式,多个处理对象可以共享处理请求的职责,避免了强耦合关系。
  4. 增加新的处理环节方便:在责任链中添加新的处理对象无需修改原有代码,符合开闭原则。

缺点:

  1. 不一定能够确保请求被处理:如果责任链中的所有处理对象都无法处理该请求,则请求最终可能得不到处理。
  2. 链条的长度可能过长:如果责任链很长,可能会增加系统的复杂度,影响性能。
  3. 维护成本增加:在链中的每个处理节点都可能需要调整和维护,可能导致链式结构难以管理。

4. Java应用场景

  • 日志处理:在日志记录中,日志请求可能需要经过多个处理步骤,如过滤日志级别、格式化日志、输出日志等,每个步骤可以视为一个责任链的处理对象。
  • 审批流程:在公司或组织的审批流程中,审批可能会按照不同的级别(如经理、主管、总经理)依次进行,责任链模式可以用来处理不同级别的审批操作。
  • 事件处理机制:在UI框架或Web框架中,事件的处理过程可能涉及多个步骤,例如按钮点击事件可能需要执行多个事件处理器,责任链模式可以处理这种多步骤的事件处理。
  • 权限校验:在Web应用的权限验证过程中,可以有多个权限检查点(例如管理员权限、用户权限等),每个权限检查可以看作责任链的一个节点。

5. 代码示例

java 复制代码
// Handler 类:定义一个处理请求的接口
public abstract class Handler {
    protected Handler nextHandler;  // 下一个处理者

    // 设置下一个处理者
    public void setNextHandler(Handler nextHandler) {
        this.nextHandler = nextHandler;
    }

    // 处理请求的抽象方法
    public abstract void handleRequest(String request);
}

// ConcreteHandler 类:具体的处理者1
public class ConcreteHandlerA extends Handler {
    @Override
    public void handleRequest(String request) {
        if (request.equals("A")) {
            System.out.println("Handler A is processing request: " + request);
        } else if (nextHandler != null) {
            nextHandler.handleRequest(request);
        }
    }
}

// ConcreteHandler 类:具体的处理者2
public class ConcreteHandlerB extends Handler {
    @Override
    public void handleRequest(String request) {
        if (request.equals("B")) {
            System.out.println("Handler B is processing request: " + request);
        } else if (nextHandler != null) {
            nextHandler.handleRequest(request);
        }
    }
}

// ConcreteHandler 类:具体的处理者3
public class ConcreteHandlerC extends Handler {
    @Override
    public void handleRequest(String request) {
        if (request.equals("C")) {
            System.out.println("Handler C is processing request: " + request);
        } else if (nextHandler != null) {
            nextHandler.handleRequest(request);
        }
    }
}

// 使用示例
public class ChainOfResponsibilityDemo {
    public static void main(String[] args) {
        // 创建责任链的各个处理者
        Handler handlerA = new ConcreteHandlerA();
        Handler handlerB = new ConcreteHandlerB();
        Handler handlerC = new ConcreteHandlerC();

        // 设置责任链的顺序
        handlerA.setNextHandler(handlerB);
        handlerB.setNextHandler(handlerC);

        // 请求处理
        handlerA.handleRequest("A");  // 输出:Handler A is processing request: A
        handlerA.handleRequest("B");  // 输出:Handler B is processing request: B
        handlerA.handleRequest("C");  // 输出:Handler C is processing request: C
        handlerA.handleRequest("D");  // 没有输出,因为没有处理者能够处理"D"
    }
}

状态模式 (State)

1. 介绍

状态模式(State Pattern)是一种行为型设计模式,用于让一个对象在其内部状态变化时改变其行为。状态模式将每个状态封装成一个对象,将状态的相关行为与对象本身分离,使得对象的状态与行为在每个状态下是独立的,状态变化时可以动态改变对象的行为。

状态模式的关键思想是:对象的行为由其当前的状态决定,因此,每个状态可以有一组独立的行为。当对象的状态发生变化时,系统不需要去修改对象的代码,而是通过更换状态来改变对象的行为。

2. 作用

  • 让对象在不同状态下具有不同的行为:根据不同的状态,系统可以改变对象的行为,而无需在对象本身做大量条件判断。
  • 封装状态变化:状态变化的逻辑集中在状态类中,客户端不需要关心具体的状态变换过程。
  • 避免大量的条件语句:状态模式消除了在不同状态下使用大量 if-else 或 switch 的情况,使得代码更加简洁和可维护。

3. 优缺点

优点:

  1. 减少条件判断:状态模式将每个状态的行为封装到状态类中,从而避免了大量的条件判断语句。
  2. 易于扩展和维护:如果要增加新的状态,只需创建新的状态类即可,无需修改原有的代码,符合开闭原则。
  3. 清晰的结构:每个状态都是独立的,状态的变化和行为非常清晰,易于理解和调试。

缺点:

  1. 增加类的数量:每个状态都需要一个状态类,这会导致类的数量增多,可能导致系统变得较为复杂。
  2. 状态切换复杂性:在某些复杂的状态切换场景中,状态管理可能变得不易控制。
  3. 可能导致大量的状态类:如果系统的状态非常多,每个状态都需要一个独立的类,可能会造成类数目庞大。

4. Java应用场景

  • 电梯控制系统:电梯在不同状态下的行为不同,如"停止"、"运行"、"门开"等,状态模式能够通过将每个状态的行为封装成不同的状态类,使得电梯系统能够在不改变主逻辑的情况下切换不同状态。
  • 游戏角色状态管理:在游戏开发中,角色可能处于不同状态,如"跑"、"跳"、"攻击"等,每个状态下角色的行为不同,可以使用状态模式来管理不同的角色行为。
  • 订单处理系统:在电商平台中,订单有不同的处理状态,如"已支付"、"待发货"、"已发货"、"已完成"等,订单的行为根据状态不同而不同,可以通过状态模式来管理这些状态。
  • 网络请求处理:状态模式可用于模拟网络请求的不同阶段,如"等待连接"、"连接中"、"连接成功"、"请求完成"等,不同状态下的处理方式不同。

5. 代码示例

java 复制代码
// State 接口:定义所有具体状态的公共行为
public interface State {
    void handleRequest();
}

// 具体状态:正在处理状态
public class ConcreteStateA implements State {
    @Override
    public void handleRequest() {
        System.out.println("当前状态:处理状态A");
    }
}

// 具体状态:已处理状态
public class ConcreteStateB implements State {
    @Override
    public void handleRequest() {
        System.out.println("当前状态:处理状态B");
    }
}

// Context 类:维护一个当前状态
public class Context {
    private State currentState;

    // 设置当前状态
    public void setState(State state) {
        this.currentState = state;
    }

    // 执行当前状态的行为
    public void request() {
        currentState.handleRequest();
    }
}

// 使用示例
public class StateDemo {
    public static void main(String[] args) {
        Context context = new Context();

        // 设置状态为ConcreteStateA
        context.setState(new ConcreteStateA());
        context.request();  // 输出:当前状态:处理状态A

        // 切换状态为ConcreteStateB
        context.setState(new ConcreteStateB());
        context.request();  // 输出:当前状态:处理状态B
    }
}

备忘录模式 (Memento)

1. 介绍

备忘录模式(Memento Pattern)是一种行为型设计模式,旨在在不暴露对象实现细节的情况下,捕获对象的内部状态,并允许在未来的某个时刻恢复该状态。该模式的主要目的是提供一种机制,使得对象能够保存其状态,以便以后可以恢复,而不需要了解对象的内部实现。

备忘录模式通常用于实现撤销(undo)功能,或者当对象的状态非常复杂,且需要从某个特定时刻恢复时使用。

2. 作用

  • 保存和恢复对象的状态:备忘录模式通过保存对象的状态,能够在需要时恢复对象的状态。例如,用户在文本编辑器中修改了文件内容,但可以通过撤销操作恢复到修改前的状态。
  • 不暴露内部实现细节:备忘录模式通过封装状态对象,只暴露恢复接口,避免外部访问对象内部的私有状态,提高了系统的封装性。
  • 实现撤销和重做功能:在用户界面应用中,常常需要提供撤销(Undo)或重做(Redo)操作,备忘录模式是实现这一功能的理想选择。

3. 优缺点

优点:

  • 提高封装性:备忘录模式能够将对象的内部状态封装起来,外部类无法直接修改该状态,这增加了系统的封装性和灵活性。
  • 简化复杂对象的状态保存:备忘录使得对象能够存储其完整的状态,并允许在不同时间点进行恢复,这简化了复杂对象状态管理。
  • 方便实现撤销功能:适用于需要频繁保存和恢复对象状态的场景,比如撤销操作、版本控制等。

缺点:

  • 可能增加内存消耗:由于每次修改都可能保存一个备忘录,若状态变更频繁且每个备忘录体积较大,可能会导致大量内存消耗。
  • 管理备忘录的复杂性:需要维护多个备忘录对象,管理和清理过期的备忘录可能会增加代码的复杂度。
  • 不适用于大对象:如果对象的状态非常庞大或复杂,备忘录模式可能会导致内存占用过多,从而影响性能。

4. Java应用场景

  • 文本编辑器的撤销操作:文本编辑器中可以使用备忘录模式来保存文本的历史版本。每次用户输入字符时,都会保存当前状态到备忘录中,当用户点击"撤销"按钮时,系统能够恢复到上一个状态。
  • 图形编辑软件:在图形设计软件中,用户的每一步操作(如绘制线条、修改形状)都可以保存为备忘录。当用户选择撤销时,软件可以恢复到之前的图形状态。
  • 游戏存档与回档:在游戏中,玩家的每个游戏进度(如角色的血量、位置等)可以保存为备忘录,用户可以随时回到之前的某个存档点。
  • 状态机回溯:在实现状态机的系统中,可以通过备忘录模式保存当前状态,并在需要时恢复到历史状态。

5. 代码示例

java 复制代码
// Memento 类:用于存储状态
public class Memento {
    private String state;

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

    public String getState() {
        return state;
    }
}

// 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) {
        this.state = memento.getState();
    }
}

// Caretaker 类:负责管理备忘录
public class Caretaker {
    private List<Memento> mementoList = new ArrayList<>();

    // 保存备忘录
    public void add(Memento state) {
        mementoList.add(state);
    }

    // 获取指定索引的备忘录
    public Memento get(int index) {
        return mementoList.get(index);
    }
}

// 使用示例
public class MementoDemo {
    public static void main(String[] args) {
        Originator originator = new Originator();
        Caretaker caretaker = new Caretaker();

        // 改变状态并保存
        originator.setState("State1");
        caretaker.add(originator.saveStateToMemento());

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

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

        // 恢复到之前的状态
        System.out.println("Current State: " + originator.getState());
        originator.getStateFromMemento(caretaker.get(0));
        System.out.println("First saved State: " + originator.getState());
        originator.getStateFromMemento(caretaker.get(1));
        System.out.println("Second saved State: " + originator.getState());
    }
}

访问者模式 (Visitor)

1. 介绍

访问者模式的核心思想是将数据结构(例如元素对象)与操作(例如行为或功能)分开。通过访问者模式,你可以为不同类型的元素对象定义不同的操作,同时避免修改元素对象的类。访问者模式常用于元素类结构相对稳定,但需要频繁新增操作的场景。

2. 作用

  • 将数据结构与操作分离:通过引入访问者,数据结构和操作之间没有直接的耦合,可以独立扩展。
  • 增加新操作而不修改原有类:通过访问者模式,你可以方便地为现有的类增加新的操作,而不需要修改它们的源代码。
  • 高效操作复杂结构:访问者模式特别适合复杂结构(例如复合对象、树形结构、集合等)的操作。

3. 优缺点

优点:

  • 扩展性强:通过添加新的访问者来增加新的功能,而无需修改现有的对象结构。
  • 集中的操作:所有操作都集中在访问者类中,方便管理。
  • 支持复杂数据结构:适用于需要对复杂数据结构进行遍历的场景。

缺点:

  • 违背开闭原则:如果对象结构发生变化(例如添加新的元素类),那么你需要修改所有访问者类。
  • 增加了系统的复杂度:尤其在访问者的数量很大时,管理和维护会变得复杂。

4. Java应用场景

  • 编译器设计:编译器可以使用访问者模式来为不同类型的语法树节点定义不同的处理操作。
  • GUI(图形用户界面)框架:可以在GUI框架中使用访问者模式来定义不同组件的渲染或交互逻辑。
  • 数据转换:当需要对复杂的数据结构进行转换或处理时,访问者模式非常有用。

5. 代码示例

java 复制代码
// 1. 定义访问者接口
public interface ShapeVisitor {
    void visit(Rectangle rectangle);
    void visit(Circle circle);
}

// 2. 定义元素接口
public interface Shape {
    void accept(ShapeVisitor visitor);
}

// 3. 具体元素类 - 矩形
public class Rectangle implements Shape {
    @Override
    public void accept(ShapeVisitor visitor) {
        visitor.visit(this);  // 接受访问者
    }

    public void draw() {
        System.out.println("Drawing Rectangle");
    }
}

// 4. 具体元素类 - 圆形
public class Circle implements Shape {
    @Override
    public void accept(ShapeVisitor visitor) {
        visitor.visit(this);  // 接受访问者
    }

    public void draw() {
        System.out.println("Drawing Circle");
    }
}

// 5. 具体访问者类 - 打印访问者
public class ShapePrinter implements ShapeVisitor {
    @Override
    public void visit(Rectangle rectangle) {
        System.out.println("Visiting Rectangle");
        rectangle.draw();
    }

    @Override
    public void visit(Circle circle) {
        System.out.println("Visiting Circle");
        circle.draw();
    }
}

// 6. 客户端代码
public class VisitorPatternDemo {
    public static void main(String[] args) {
        Shape rectangle = new Rectangle();
        Shape circle = new Circle();

        ShapeVisitor printer = new ShapePrinter();

        // 通过访问者访问不同的元素
        rectangle.accept(printer);
        circle.accept(printer);
    }
}

中介者模式 (Mediator)

1. 介绍

在没有中介者模式的情况下,如果有多个对象需要进行交互,每个对象之间都可能需要直接通信,这种情况会导致系统变得复杂,维护困难,特别是当对象数量较多时。中介者模式通过引入一个中介者对象,将对象之间的交互转移到中介者上,减少了对象之间的依赖和耦合度。

2. 作用

  • 解耦:减少了对象之间的直接依赖和耦合,使得对象之间只与中介者通信,而不需要直接相互通信。
  • 集中管理:所有的交互都通过中介者进行,便于集中管理和控制对象之间的协调逻辑。
  • 简化代码:避免了多个对象之间的复杂交互,使得系统设计更加简洁和清晰。

3. 优缺点

优点:

  • 降低耦合度:通过中介者,将对象之间的直接依赖关系转移到中介者上,减少了对象之间的耦合。
  • 集中管理:可以集中处理对象之间的交互逻辑,修改交互方式时只需要调整中介者即可,而不需要修改各个对象。
  • 增强可维护性:减少了系统中各个对象之间的复杂相互依赖,使得系统更易于维护和扩展。

缺点:

  • 中介者过于复杂:所有对象的交互都通过中介者进行,可能导致中介者对象变得过于复杂,难以维护。
  • 违背单一职责原则:中介者模式可能会违反单一职责原则,因为中介者负责了多个对象之间的交互,而这些交互本应由这些对象各自处理。

4. Java应用场景

  • GUI(图形用户界面):多个组件(如按钮、文本框、标签等)需要相互通信时,可以通过中介者模式来集中管理它们之间的交互。
  • 聊天系统:在一个聊天室系统中,中介者模式可以用于管理不同用户之间的消息交换,避免直接将每个用户与其他用户连接。
  • 分布式系统中的协调:在分布式系统中,可以使用中介者模式来协调不同服务之间的交互,避免服务之间直接的依赖。

5. 代码示例

java 复制代码
// 1. 定义中介者接口
public interface Mediator {
    void send(String message, User user);
}

// 2. 定义用户类
public class User {
    private String name;
    private Mediator mediator;

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

    public String getName() {
        return name;
    }

    public void send(String message) {
        System.out.println(this.name + " sends message: " + message);
        mediator.send(message, this);  // 通过中介者发送消息
    }

    public void receive(String message) {
        System.out.println(this.name + " receives message: " + message);
    }
}

// 3. 定义具体中介者类
public class ConcreteMediator implements Mediator {
    private User user1;
    private User user2;

    public void setUser1(User user1) {
        this.user1 = user1;
    }

    public void setUser2(User user2) {
        this.user2 = user2;
    }

    @Override
    public void send(String message, User user) {
        if (user == user1) {
            user2.receive(message);  // user1发送消息,user2接收
        } else {
            user1.receive(message);  // user2发送消息,user1接收
        }
    }
}

// 4. 使用中介者模式
public class MediatorPatternDemo {
    public static void main(String[] args) {
        // 创建中介者
        ConcreteMediator mediator = new ConcreteMediator();

        // 创建用户并与中介者关联
        User user1 = new User("User1", mediator);
        User user2 = new User("User2", mediator);

        // 将用户添加到中介者中
        mediator.setUser1(user1);
        mediator.setUser2(user2);

        // 用户之间通过中介者发送消息
        user1.send("Hello User2!");
        user2.send("Hi User1!");
    }
}

迭代器模式 (Iterator)

1. 介绍

迭代器模式提供了一种标准方法来访问集合对象中的元素。它定义了一个迭代器接口,提供对集合中的元素进行遍历的功能。这样,我们可以不关心集合的具体实现(例如,列表、数组、集合等),只需要关心如何获取每一个元素。

通过使用迭代器模式,集合类不再需要向外部暴露其具体实现细节,例如内部的存储方式、数据结构等,从而提高了灵活性和可维护性。

2. 作用

  • 解耦:使得集合的遍历逻辑与集合的实现解耦,增强了代码的灵活性。
  • 顺序访问:提供对集合的顺序访问,避免了直接暴露集合的内部结构。
  • 统一接口:提供了统一的接口来访问不同类型的集合(如数组、链表、集合等),从而提高了代码的可复用性。
  • 支持多种遍历方式:通过迭代器可以灵活地支持多种遍历方式(如顺序遍历、逆序遍历等)。

3. 优缺点

优点:

  • 封装性强:迭代器模式将遍历逻辑封装在一个单独的迭代器对象中,外部代码无需了解集合的实现方式。
  • 减少耦合:访问集合的代码与集合类解耦,提高了代码的灵活性,尤其在需要更换集合实现时,不需要修改遍历代码。
  • 支持多种遍历方式:可以方便地实现多种遍历模式(如前向遍历、后向遍历、跳跃式遍历等)。

缺点:

  • 增加类的数量:每个集合都需要定义一个对应的迭代器类,导致系统中类的数量增加。
  • 性能问题:在某些场景下,迭代器的使用可能导致性能下降,尤其在不需要遍历整个集合时。

4. Java应用场景

  • 集合遍历:Java中的Iterator接口就是典型的迭代器模式实现,用于遍历集合如List、Set等。
  • 多种集合类型的统一遍历:不同类型的集合类可以实现同一个Iterator接口,允许使用相同的代码遍历不同的集合。
  • 增量遍历:当你需要顺序地访问集合中的每一个元素,或者在集合中进行某种处理时,迭代器模式非常有用。

5. 代码示例

java 复制代码
// 1. 定义迭代器接口
public interface Iterator {
    boolean hasNext();  // 判断是否还有下一个元素
    Object next();      // 返回下一个元素
}

// 2. 定义集合接口
public interface Collection {
    Iterator createIterator();  // 创建迭代器的方法
}

// 3. 实现具体的集合类
public class ConcreteCollection implements Collection {
    private String[] items = {"Item1", "Item2", "Item3", "Item4"};

    @Override
    public Iterator createIterator() {
        return new ConcreteIterator();  // 返回具体的迭代器
    }

    // 内部类:具体迭代器
    private class ConcreteIterator implements Iterator {
        private int index = 0;  // 当前索引

        @Override
        public boolean hasNext() {
            return index < items.length;  // 判断是否还有下一个元素
        }

        @Override
        public Object next() {
            if (hasNext()) {
                return items[index++];  // 返回当前元素并递增索引
            }
            return null;
        }
    }
}

// 4. 使用迭代器模式遍历集合
public class IteratorPatternDemo {
    public static void main(String[] args) {
        ConcreteCollection collection = new ConcreteCollection();
        Iterator iterator = collection.createIterator();
        
        while (iterator.hasNext()) {
            System.out.println(iterator.next());  // 输出集合中的每一个元素
        }
    }
}

解释器模式 (Interpreter)

1. 介绍

解释器模式定义了一个用于解释语言句子的类结构。它包含一个解释器接口,该接口负责解析和执行某种语言或表达式。每种表达式都有自己的解释器类,该类实现了解释器接口,并负责解释和计算某一类表达式。

解释器模式通常适用于那些需要处理、计算或转换语言结构的场景,特别是当该结构被频繁扩展或变化时。

2. 作用

  • 语法解析:可以用于处理一种语言的语法规则,如数学表达式、命令模式等。
  • 提高可扩展性:通过定义语法和语义,解释器模式可以方便地扩展新的语言或表达式规则,而无需大规模修改现有代码。
  • 表达式求值:常用于需要求值的表达式解析,像计算器、表达式树等都可以使用解释器模式。

3. 优缺点

优点:

  • 易于扩展:新增加的语法规则或表达式可以通过增加新类来实现,不需要修改现有的代码。
  • 灵活性:解释器模式可以轻松地处理多种类型的输入数据,因为它通过一种明确的结构来进行处理。
  • 表达清晰:代码结构清晰,通过将每种表达式封装成独立的类,可以将复杂的解析逻辑分解成多个小的模块。

缺点:

  • 类数量庞大:每种表达式都需要一个新的解释器类,随着语法规则的增加,类的数量会非常多,代码复杂度也会增加。
  • 性能问题:每次对表达式进行解释时都需要重新解析,可能会导致性能瓶颈,尤其是在对大量数据进行求值时。
  • 不适合简单语法:对于简单的表达式,使用解释器模式可能会过于复杂,导致代码维护困难。

4. Java应用场景

  • 编译器和解释器:当需要设计一个编译器或解释器时,解释器模式常用于实现语法解析和代码解释。
  • 表达式求值:计算器、查询语言等场景中,可以使用解释器模式将输入的表达式转换为抽象语法树 (AST),并求得结果。
  • DSL(领域特定语言):当需要设计一个自定义语言时,如图形化语言、查询语言、规则引擎等,可以通过解释器模式来解析和执行这些语言。

5. 代码示例

java 复制代码
// 1. 定义表达式接口
interface Expression {
    int interpret();
}

// 2. 创建具体的表达式类

// 数字类
class Number implements Expression {
    private int number;

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

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

// 加法类
class Add implements Expression {
    private Expression leftExpression;
    private Expression rightExpression;

    public Add(Expression left, Expression right) {
        this.leftExpression = left;
        this.rightExpression = right;
    }

    @Override
    public int interpret() {
        return leftExpression.interpret() + rightExpression.interpret();
    }
}

// 减法类
class Subtract implements Expression {
    private Expression leftExpression;
    private Expression rightExpression;

    public Subtract(Expression left, Expression right) {
        this.leftExpression = left;
        this.rightExpression = right;
    }

    @Override
    public int interpret() {
        return leftExpression.interpret() - rightExpression.interpret();
    }
}

// 乘法类
class Multiply implements Expression {
    private Expression leftExpression;
    private Expression rightExpression;

    public Multiply(Expression left, Expression right) {
        this.leftExpression = left;
        this.rightExpression = right;
    }

    @Override
    public int interpret() {
        return leftExpression.interpret() * rightExpression.interpret();
    }
}

// 3. 客户端代码(用来计算表达式)
public class InterpreterPatternDemo {
    public static void main(String[] args) {
        // (3 + 5) * (10 - 4)
        Expression number1 = new Number(3);
        Expression number2 = new Number(5);
        Expression number3 = new Number(10);
        Expression number4 = new Number(4);

        Expression add = new Add(number1, number2);  // 3 + 5
        Expression subtract = new Subtract(number3, number4);  // 10 - 4

        Expression multiply = new Multiply(add, subtract);  // (3 + 5) * (10 - 4)

        System.out.println("Result: " + multiply.interpret());  // Output: 48
    }
}
相关推荐
REI-2 小时前
黑马点评项目启动
java·后端
AlunYegeer2 小时前
【JAVA】网关的管理原理和微服务的Interceptor区分
java·服务器·前端
xieliyu.2 小时前
Java、抽象类
java·开发语言
我真会写代码2 小时前
SpringBoot自动装配原理:告别繁琐配置,读懂底层逻辑
java·spring boot·mybatis
happymaker06262 小时前
servlet、jsp、请求转发、重定向的一些个人理解
java·开发语言·servlet
于先生吖2 小时前
国际版答题系统 JAVA 源码实战指南
java·开发语言
gelald2 小时前
JVM - 垃圾回收
java·jvm·后端
东离与糖宝3 小时前
模式匹配支持原生类型!JDK26 switch语法极简实战
java·人工智能
workflower3 小时前
如何使用设计模式-误区
java·开发语言·设计模式·集成测试·软件工程·需求分析·软件需求