设计模式

分类

分类 设计模式 简单说明
创建型模式
Creational Patterns
单例模式(Singleton) 确保类只有一个实例,并提供全局访问点。
工厂方法(Factory Method) 定义创建对象的接口,由子类决定实例化。
抽象工厂(Abstract Factory) 创建一系列相关对象的工厂。
建造者(Builder) 分步构造复杂对象。
原型模式(Prototype) 通过克隆创建新对象。
结构型模式
Structural Patterns
适配器(Adapter) 将不兼容的接口转换为兼容接口。
桥接(Bridge) 将抽象与实现分离。
组合(Composite) 将对象组织成树形结构。
装饰者(Decorator) 动态扩展对象功能。
外观(Facade) 为复杂子系统提供简单接口。
享元(Flyweight) 共享细粒度对象以节省资源。
代理(Proxy) 控制对对象的访问。
行为型模式
Behavioral Patterns
责任链(Chain of Responsibility) 将请求沿处理链传递。
命令(Command) 将请求封装为对象。
解释器(Interpreter) 定义语言的解释规则。
迭代器(Iterator) 顺序访问集合元素。
中介者(Mediator) 通过中介协调对象交互。
备忘录(Memento) 保存和恢复对象状态。
观察者(Observer) 对象状态变化时通知依赖者。
状态(State) 根据状态改变对象行为。
策略(Strategy) 定义可互换的算法家族。
模板方法(Template Method) 定义算法骨架,子类实现细节。
访问者(Visitor) 在不修改类的情况下增加新操作。
扩展模式
Extended Patterns
依赖注入(Dependency Injection) 通过外部注入依赖,解耦组件。
发布-订阅(Publish-Subscribe) 事件驱动的观察者变种。
模块模式(Module Pattern) 封装代码,管理私有/公有成员。
MVC(Model-View-Controller) 分离数据(Model)、界面(View)和逻辑(Controller)。
MVP(Model-View-Presenter) View 和 Presenter 交互,Model 隔离数据。
MVVM(Model-View-ViewModel) 通过 ViewModel 绑定 Model 和 View。
仓储模式(Repository Pattern) 封装数据访问逻辑。
服务定位器(Service Locator) 集中管理服务实例的获取。
事件溯源(Event Sourcing) 通过事件记录对象状态。

图示

graph LR
    A[设计模式<br>Design Patterns]
    
    A --> B[创建型模式<br>Creational Patterns]
    B --> B1[单例模式<br>Singleton]
    B --> B2[工厂方法<br>Factory Method]
    B --> B3[抽象工厂<br>Abstract Factory]
    B --> B4[建造者<br>Builder]
    B --> B5[原型模式<br>Prototype]
    
    A --> C[结构型模式<br>Structural Patterns]
    C --> C1[适配器<br>Adapter]
    C --> C2[桥接<br>Bridge]
    C --> C3[组合<br>Composite]
    C --> C4[装饰者<br>Decorator]
    C --> C5[外观<br>Facade]
    C --> C6[享元<br>Flyweight]
    C --> C7[代理<br>Proxy]
    
    A --> D[行为型模式<br>Behavioral Patterns]
    D --> D1[责任链<br>Chain of Responsibility]
    D --> D2[命令<br>Command]
    D --> D3[解释器<br>Interpreter]
    D --> D4[迭代器<br>Iterator]
    D --> D5[中介者<br>Mediator]
    D --> D6[备忘录<br>Memento]
    D --> D7[观察者<br>Observer]
    D --> D8[状态<br>State]
    D --> D9[策略<br>Strategy]
    D --> D10[模板方法<br>Template Method]
    D --> D11[访问者<br>Visitor]
    
    A --> E[扩展模式<br>Extended Patterns]
    E --> E1[依赖注入<br>Dependency Injection]
    E --> E2[发布-订阅<br>Publish-Subscribe]
    E --> E3[模块模式<br>Module Pattern]
    E --> E4[MVC<br>Model-View-Controller]
    E --> E5[MVP<br>Model-View-Presenter]
    E --> E6[MVVM<br>Model-View-ViewModel]
    E --> E7[仓储模式<br>Repository Pattern]
    E --> E8[服务定位器<br>Service Locator]
    E --> E9[事件溯源<br>Event Sourcing]

详解

创建型模式(Creational Patterns)

单例模式(Singleton)

单例模式(Singleton Pattern)是一种创建型设计模式,确保一个类只有一个实例,并提供全局访问点。以下是单例模式的各种实现方式,包括线程安全的版本,用 Java 编写。每种实现都会标注其特点、优缺点及适用场景。


1. 饿汉式(Eager Initialization)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class SingletonEager {
// 类加载时就创建实例
private static final SingletonEager INSTANCE = new SingletonEager();

// 私有构造方法,防止外部实例化
private SingletonEager() {
// 防止反射创建实例
if (INSTANCE != null) {
throw new RuntimeException("Singleton instance already exists");
}
}

// 全局访问点
public static SingletonEager getInstance() {
return INSTANCE;
}
}
  • 特点:类加载时即创建实例,线程安全。
  • 优点:简单,无需同步,天然线程安全。
  • 缺点:无论是否使用,都会创建实例,可能浪费资源。
  • 适用场景:实例开销小,且肯定会被使用。

2. 懒汉式(Lazy Initialization,非线程安全)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class SingletonLazy {
private static SingletonLazy instance;

private SingletonLazy() {
// 防止反射创建实例,这并不能真正防止反射,用饿汉式、枚举或者使用标志位
if (instance != null) {
throw new RuntimeException("Singleton instance already exists");
}
}

public static SingletonLazy getInstance() {
if (instance == null) {
instance = new SingletonLazy();
}
return instance;
}
}
  • 特点:延迟加载,第一次调用时创建实例。
  • 优点:节省资源,懒加载。
  • 缺点:非线程安全,多线程下可能创建多个实例。
  • 适用场景:单线程环境,或不在意线程安全。

添加标志位:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class SingletonLazyWithFlag {
private static SingletonLazyWithFlag instance;
private static volatile boolean initialized = false; // 标志位

private SingletonLazyWithFlag() {
synchronized (SingletonLazyWithFlag.class) {
if (initialized) {
throw new RuntimeException("Singleton instance already exists");
}
initialized = true;
}
}

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

3. 线程安全的懒汉式(Synchronized 方法)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class SingletonSyncMethod {
private static SingletonSyncMethod instance;

private SingletonSyncMethod() {
if (instance != null) {
throw new RuntimeException("Singleton instance already exists");
}
}

// 使用 synchronized 同步方法
public static synchronized SingletonSyncMethod getInstance() {
if (instance == null) {
instance = new SingletonSyncMethod();
}
return instance;
}
}
  • 特点:通过同步方法实现线程安全。
  • 优点:简单,保证线程安全。
  • 缺点:每次调用 getInstance 都要加锁,性能开销大。
  • 适用场景:多线程环境,但调用频率不高。

4. 双重检查锁(Double-Checked Locking, DCL)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class SingletonDCL {
// 使用 volatile 防止指令重排
private static volatile SingletonDCL instance;

private SingletonDCL() {
if (instance != null) {
throw new RuntimeException("Singleton instance already exists");
}
}

public static SingletonDCL getInstance() {
if (instance == null) { // 第一次检查(无锁)
synchronized (SingletonDCL.class) {
if (instance == null) { // 第二次检查(加锁)
instance = new SingletonDCL();
}
}
}
return instance;
}
}
  • 特点:结合懒加载和线程安全,双重检查减少同步开销。
  • 优点:高效,只有在实例未创建时加锁。
  • 缺点:实现复杂,需使用 volatile(Java 5+)防止指令重排。
  • 适用场景:多线程环境,追求性能优化。

5. 静态内部类(Static Inner Class)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class SingletonStaticInner {
private SingletonStaticInner() {
if (SingletonHolder.INSTANCE != null) {
throw new RuntimeException("Singleton instance already exists");
}
}

// 静态内部类持有实例, 静态内部类只有在第一次被使用的时候才会加载,所以是懒加载。
private static class SingletonHolder {
private static final SingletonStaticInner INSTANCE = new SingletonStaticInner();
}

public static SingletonStaticInner getInstance() {
return SingletonHolder.INSTANCE;
}
}
  • 特点:利用类加载机制实现懒加载和线程安全。
  • 优点:简单、高效,JVM 保证线程安全,无需显式同步。
  • 缺点:无法传递构造参数。
  • 适用场景:多线程环境,推荐的懒加载方式。

6. 枚举单例(Enum Singleton)

1
2
3
4
5
6
7
8
public enum SingletonEnum {
INSTANCE; // 唯一实例

// 可以添加方法
public void doSomething() {
System.out.println("SingletonEnum is working");
}
}
  • 用法
    1
    2
    SingletonEnum singleton = SingletonEnum.INSTANCE;
    singleton.doSomething();
  • 特点:使用枚举实现单例,JVM 保证唯一性。
  • 优点:最简单,天然线程安全,防止反射和序列化破坏。
  • 缺点:无法懒加载,枚举类加载时即创建。
  • 适用场景:需要绝对安全(如防止反射攻击)的场景。

7. ThreadLocal 单例(线程局部单例)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class SingletonThreadLocal {
private static final ThreadLocal<SingletonThreadLocal> THREAD_LOCAL =
ThreadLocal.withInitial(SingletonThreadLocal::new);

private SingletonThreadLocal() {
}

public static SingletonThreadLocal getInstance() {
return THREAD_LOCAL.get();
}

// 清理 ThreadLocal,防止内存泄漏
public static void remove() {
THREAD_LOCAL.remove();
}
}
  • 特点:每个线程拥有独立的单例实例。
  • 优点:线程隔离,适合线程特定的单例需求。
  • 缺点:不是全局单例,需注意 ThreadLocal 的内存管理。
  • 适用场景:线程私有单例,如线程上下文管理。

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class Main {
public static void main(String[] args) {
// 测试饿汉式
SingletonEager s1 = SingletonEager.getInstance();
SingletonEager s2 = SingletonEager.getInstance();
System.out.println("Eager: " + (s1 == s2)); // true

// 测试双重检查锁(多线程)
Runnable task = () -> System.out.println("DCL: " + SingletonDCL.getInstance());
Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start();
t2.start();

// 测试枚举
SingletonEnum e1 = SingletonEnum.INSTANCE;
SingletonEnum e2 = SingletonEnum.INSTANCE;
System.out.println("Enum: " + (e1 == e2)); // true

// 测试 ThreadLocal
Thread thread1 = new Thread(() -> {
SingletonThreadLocal stl1 = SingletonThreadLocal.getInstance();
System.out.println("ThreadLocal Thread1: " + stl1);
});
Thread thread2 = new Thread(() -> {
SingletonThreadLocal stl2 = SingletonThreadLocal.getInstance();
System.out.println("ThreadLocal Thread2: " + stl2);
});
thread1.start();
thread2.start();
}
}

说明

  • 线程安全
    • 饿汉式、静态内部类、枚举天然线程安全。
    • 懒汉式需加锁(同步方法或 DCL)实现线程安全。
    • ThreadLocal 为每个线程提供独立实例。
  • 反射和序列化防护
    • 枚举单例天然防止反射和序列化破坏。
    • 其他实现通过检查实例是否存在(构造方法抛异常)防御反射。
    • 序列化需实现 readResolve 方法:
      1
      2
      3
      private Object readResolve() {
      return getInstance();
      }
  • 性能
    • 饿汉式和枚举无延迟,静态内部类和 DCL 高效懒加载。
    • 同步方法性能较低。
  • 复杂度
    • 枚举最简单,DCL 最复杂。

总结表

实现方式 懒加载 线程安全 复杂度 防止反射 防止序列化 适用场景
饿汉式 需额外实现 简单实例
懒汉式 需额外实现 单线程
同步方法 需额外实现 调用不频繁
双重检查锁 需额外实现 高性能多线程
静态内部类 需额外实现 推荐的多线程懒加载
枚举 最高安全性
ThreadLocal 线程内 需额外实现 线程私有单例

如何防止序列化破坏

为什么会发生序列化破坏:如果一个单例类实现了 Serializable 接口,反序列化时会通过反射创建一个新实例,而不是返回现有的单例实例。这会导致系统中存在多个实例,破坏单例的唯一性。

在单例类中添加 readResolve 方法,反序列化时 JVM 会调用此方法,返回指定的对象,而不是新建的实例。或者使用枚举。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import java.io.*;

public class Singleton implements Serializable {
private static final Singleton INSTANCE = new Singleton();

private Singleton() {}

public static Singleton getInstance() {
return INSTANCE;
}

// 添加 readResolve 方法
private Object readResolve() {
return INSTANCE; // 返回单例实例
}
}

class Test {
public static void main(String[] args) throws Exception {
Singleton s1 = Singleton.getInstance();

ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton.ser"));
oos.writeObject(s1);
oos.close();

ObjectInputStream ois = new ObjectInputStream(new FileInputStream("singleton.ser"));
Singleton s2 = (Singleton) ois.readObject();
ois.close();

System.out.println("s1 == s2: " + (s1 == s2)); // 输出: true
}
}

工厂方法(Factory Method)

工厂方法模式(Factory Method Pattern)是一种创建型设计模式,它定义了一个用于创建对象的接口,但将具体的对象实例化推迟到子类中实现。相比简单工厂模式,工厂方法模式通过将对象的创建交给具体的工厂类,提升了灵活性和扩展性,遵循了“开闭原则”(对扩展开放,对修改关闭)。

以下是用 Java 实现工厂方法模式的详细讲解,包括定义、结构、代码示例及其优缺点。


1. 工厂方法模式的定义

  • 核心思想:定义一个创建对象的接口(工厂方法),由子类决定具体实例化哪种产品。
  • 目的
    • 解耦客户端与具体产品类。
    • 让子类决定创建哪种对象,增加系统的可扩展性。
  • 适用场景
    • 需要生成的产品种类较多,且可能新增类型。
    • 客户端不关心对象的具体创建过程,只关心接口。

2. 工厂方法模式的结构

  • 抽象产品(Product):定义产品的接口,所有具体产品实现此接口。
  • 具体产品(Concrete Product):实现抽象产品接口的具体类。
  • 抽象工厂(Creator):声明工厂方法,返回抽象产品类型。
  • 具体工厂(Concrete Creator):实现工厂方法,创建具体产品实例。
UML 图
classDiagram
    class Product {
        <<interface>>
        +operation() void
    }
    
    class ConcreteProduct {
        +operation() void
    }
    
    class Creator {
        <<abstract>>
        +factoryMethod() Product
    }
    
    class ConcreteCreator {
        +factoryMethod() Product
    }
    
    ConcreteProduct ..|> Product : implements
    ConcreteCreator --|> Creator : extends
    ConcreteCreator o--> "1" ConcreteProduct : creates

3. Java 实现示例

以下是一个生产不同类型车辆的工厂方法模式示例:

3.1 抽象产品接口
1
2
3
4
// 抽象产品:车辆
public interface Vehicle {
void drive(); // 定义车辆的通用行为
}
3.2 具体产品类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 具体产品:汽车
public class Car implements Vehicle {
@Override
public void drive() {
System.out.println("Driving a car");
}
}

// 具体产品:摩托车
public class Motorcycle implements Vehicle {
@Override
public void drive() {
System.out.println("Riding a motorcycle");
}
}
3.3 抽象工厂类
1
2
3
4
5
6
7
8
9
10
11
12
// 抽象工厂:车辆工厂
public abstract class VehicleFactory {
// 工厂方法,子类实现
public abstract Vehicle createVehicle();

// 可选:提供默认行为
public Vehicle getVehicle() {
Vehicle vehicle = createVehicle();
vehicle.drive();
return vehicle;
}
}
3.4 具体工厂类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 具体工厂:汽车工厂
public class CarFactory extends VehicleFactory {
@Override
public Vehicle createVehicle() {
return new Car();
}
}

// 具体工厂:摩托车工厂
public class MotorcycleFactory extends VehicleFactory {
@Override
public Vehicle createVehicle() {
return new Motorcycle();
}
}
3.5 客户端代码
1
2
3
4
5
6
7
8
9
10
11
public class Main {
public static void main(String[] args) {
// 使用汽车工厂
VehicleFactory carFactory = new CarFactory();
Vehicle car = carFactory.getVehicle(); // 输出: Driving a car

// 使用摩托车工厂
VehicleFactory motorcycleFactory = new MotorcycleFactory();
Vehicle motorcycle = motorcycleFactory.getVehicle(); // 输出: Riding a motorcycle
}
}

4. 工作原理

  1. 客户端选择具体的工厂类(CarFactoryMotorcycleFactory)。
  2. 调用工厂的 createVehicle() 方法,创建具体产品(CarMotorcycle)。
  3. 返回的 Vehicle 对象由客户端使用,客户端无需知道具体产品类。

5. 优点

  • 灵活性:新增产品只需添加新产品类和对应的工厂类,无需修改现有代码。
  • 解耦:客户端只依赖抽象接口(VehicleVehicleFactory),不关心具体实现。
  • 单一职责:每个工厂负责创建一种产品,职责清晰。

6. 缺点

  • 类数量增加:每增加一种产品,就需要新增一个具体产品类和一个具体工厂类,导致类数量增多。
  • 复杂度提升:相比简单工厂模式,代码结构更复杂。

7. 与简单工厂的对比

简单工厂(非工厂方法模式)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class SimpleVehicleFactory {
public static Vehicle createVehicle(String type) {
if ("car".equals(type)) {
return new Car();
} else if ("motorcycle".equals(type)) {
return new Motorcycle();
}
return null;
}
}

class Main {
public static void main(String[] args) {
Vehicle car = SimpleVehicleFactory.createVehicle("car");
car.drive(); // 输出: Driving a car
}
}
  • 区别
    • 简单工厂将创建逻辑集中在一个类中,违反“开闭原则”(新增类型需修改工厂类)。
    • 工厂方法将创建逻辑分散到子类中,符合“开闭原则”。

8. 扩展示例:带参数的工厂方法

如果需要根据参数创建不同配置的产品,可以这样扩展:

修改抽象工厂
1
2
3
public abstract class VehicleFactory {
public abstract Vehicle createVehicle(String color);
}
修改具体工厂
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class CarFactory extends VehicleFactory {
@Override
public Vehicle createVehicle(String color) {
return new Car(color);
}
}

public class Car implements Vehicle {
private String color;

public Car(String color) {
this.color = color;
}

@Override
public void drive() {
System.out.println("Driving a " + color + " car");
}
}
客户端
1
2
3
4
5
6
7
public class Main {
public static void main(String[] args) {
VehicleFactory carFactory = new CarFactory();
Vehicle redCar = carFactory.createVehicle("red");
redCar.drive(); // 输出: Driving a red car
}
}

9. 实际应用场景

  • 日志框架:如 SLF4J 的 LoggerFactory,根据配置创建不同类型的日志记录器。
  • 数据库连接:根据驱动类型创建不同的 Connection 对象。
  • UI 组件:根据主题创建不同样式的按钮或窗口。

10. 源码中的例子

Java 中的 java.util.Calendar 使用了类似工厂方法模式:

1
Calendar calendar = Calendar.getInstance();  // getInstance 是工厂方法
- Calendar 是抽象类,getInstance() 根据区域和时区返回具体子类(如 GregorianCalendar)。


总结

  • 工厂方法模式通过抽象工厂接口和具体工厂类实现对象的创建,提供了扩展性和解耦性。
  • Java 实现的关键是定义抽象产品和工厂接口,然后由具体子类实现。
  • 核心优势是支持新增产品而无需修改现有代码。

抽象工厂(Abstract Factory)

抽象工厂模式(Abstract Factory Pattern)是一种创建型设计模式,它提供了一个接口,用于创建一系列相关或相互依赖的对象,而无需指定具体类。与工厂方法模式不同,抽象工厂模式关注于创建一组相关的产品(产品族),适用于需要统一风格或兼容性对象的场景。

以下是用 Java 实现抽象工厂模式的详细讲解,包括定义、结构、代码示例及其优缺点。


1. 抽象工厂模式的定义

  • 核心思想:定义一个抽象工厂接口,创建一组相关产品的对象,具体工厂类实现该接口,生成具体产品。
  • 目的
    • 将一组相关产品的创建封装起来,确保产品之间的兼容性。
    • 解耦客户端与具体产品类,客户端只依赖抽象接口。
  • 适用场景
    • 需要创建多个相关对象(如 UI 组件、数据库访问对象)。
    • 系统需要支持多种产品族(如不同风格的主题或配置)。

2. 抽象工厂模式的结构

  • 抽象产品接口(Abstract Product):定义每个产品类型的接口。
  • 具体产品(Concrete Product):实现抽象产品接口的具体类。
  • 抽象工厂(Abstract Factory):声明一组创建产品的方法。
  • 具体工厂(Concrete Factory):实现抽象工厂接口,创建一组具体产品。
UML 图
classDiagram
    class AbstractProductA {
        +operationA()
    }
    class ConcreteProductA {
        +operationA()
    }
    class AbstractProductB {
        +operationB()
    }
    class ConcreteProductB {
        +operationB()
    }
    class AbstractFactory {
        +createProductA() AbstractProductA
        +createProductB() AbstractProductB
    }
    class ConcreteFactory {
        +createProductA() AbstractProductA
        +createProductB() AbstractProductB
    }
    
    ConcreteProductA --|> AbstractProductA
    ConcreteProductB --|> AbstractProductB
    ConcreteFactory --|> AbstractFactory
    ConcreteFactory --> ConcreteProductA
    ConcreteFactory --> ConcreteProductB

3. Java 实现示例

以下是一个生产 UI 组件(按钮和文本框)的抽象工厂模式示例,支持不同风格(如 Windows 和 Mac)。

3.1 抽象产品接口
1
2
3
4
5
6
7
8
9
// 抽象产品:按钮
public interface Button {
void render();
}

// 抽象产品:文本框
public interface TextField {
void input();
}
3.2 具体产品类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 具体产品:Windows 按钮
public class WindowsButton implements Button {
@Override
public void render() {
System.out.println("Rendering a Windows button");
}
}

// 具体产品:Windows 文本框
public class WindowsTextField implements TextField {
@Override
public void input() {
System.out.println("Input in a Windows text field");
}
}

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

// 具体产品:Mac 文本框
public class MacTextField implements TextField {
@Override
public void input() {
System.out.println("Input in a Mac text field");
}
}
3.3 抽象工厂接口
1
2
3
4
5
// 抽象工厂:UI 工厂
public interface UIFactory {
Button createButton();
TextField createTextField();
}
3.4 具体工厂类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 具体工厂:Windows UI 工厂
public class WindowsUIFactory implements UIFactory {
@Override
public Button createButton() {
return new WindowsButton();
}

@Override
public TextField createTextField() {
return new WindowsTextField();
}
}

// 具体工厂:Mac UI 工厂
public class MacUIFactory implements UIFactory {
@Override
public Button createButton() {
return new MacButton();
}

@Override
public TextField createTextField() {
return new MacTextField();
}
}
3.5 客户端代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Main {
public static void main(String[] args) {
// 使用 Windows UI 工厂
UIFactory windowsFactory = new WindowsUIFactory();
Button windowsButton = windowsFactory.createButton();
TextField windowsText = windowsFactory.createTextField();
windowsButton.render(); // 输出: Rendering a Windows button
windowsText.input(); // 输出: Input in a Windows text field

// 使用 Mac UI 工厂
UIFactory macFactory = new MacUIFactory();
Button macButton = macFactory.createButton();
TextField macText = macFactory.createTextField();
macButton.render(); // 输出: Rendering a Mac button
macText.input(); // 输出: Input in a Mac text field
}
}

4. 工作原理

  1. 客户端选择一个具体工厂(如 WindowsUIFactoryMacUIFactory)。
  2. 调用工厂的创建方法(createButton()createTextField()),生成一组相关产品。
  3. 客户端使用这些产品,保持风格一致性(如 Windows 风格的按钮和文本框)。

5. 优点

  • 一致性:保证创建的产品属于同一产品族,兼容性强。
  • 扩展性:新增产品族只需增加新的具体工厂和产品类,符合“开闭原则”。
  • 解耦:客户端只依赖抽象工厂和产品接口,不关心具体实现。

6. 缺点

  • 复杂度高:需要定义多个抽象产品和具体工厂,类数量较多。
  • 不易扩展产品:如果产品族中的产品种类增加(如新增 Checkbox),需要修改抽象工厂接口及其所有实现类,违反“开闭原则”。

7. 与工厂方法的对比

特性 工厂方法模式 抽象工厂模式
创建对象 单个产品 一组相关产品(产品族)
扩展性 易扩展新产品种类 易扩展新产品族
复杂度 较低 较高
抽象层级 一个工厂方法 多个创建方法
  • 工厂方法:关注单一产品的创建(如只创建按钮)。
  • 抽象工厂:关注一组相关产品的创建(如按钮和文本框的组合)。

8. 扩展示例:带参数

如果需要根据参数创建产品,可以这样扩展:

修改抽象工厂
1
2
3
4
public interface UIFactory {
Button createButton(String style);
TextField createTextField(String style);
}
修改具体工厂
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class WindowsUIFactory implements UIFactory {
@Override
public Button createButton(String style) {
return new WindowsButton(style);
}

@Override
public TextField createTextField(String style) {
return new WindowsTextField(style);
}
}

public class WindowsButton implements Button {
private String style;

public WindowsButton(String style) {
this.style = style;
}

@Override
public void render() {
System.out.println("Rendering a Windows button with style: " + style);
}
}
客户端
1
2
3
4
5
6
7
public class Main {
public static void main(String[] args) {
UIFactory factory = new WindowsUIFactory();
Button button = factory.createButton("modern");
button.render(); // 输出: Rendering a Windows button with style: modern
}
}

9. 实际应用场景

  • GUI 框架:如 Swing 或 JavaFX,创建不同平台的组件(如 Windows 和 Mac 风格的按钮、窗口)。
  • 数据库访问:创建一组数据库操作对象(如连接、语句、结果集),支持 MySQL、Oracle 等。
  • 游戏开发:创建不同主题的资源(如森林主题的树木和动物)。

10. 源码中的例子

Java 中的 java.sql.DriverManager 和 JDBC 驱动有一些抽象工厂的影子:

1
Connection conn = DriverManager.getConnection(url);  // 工厂方法创建连接
- 根据驱动类型,创建对应的 ConnectionStatement 等对象。


总结

  • 抽象工厂模式通过抽象工厂接口创建一组相关产品,适用于产品族场景。
  • Java 实现的关键是定义抽象产品和工厂接口,由具体工厂实现产品创建。
  • 核心优势是保证产品一致性和支持产品族的扩展。

如果你需要更复杂的例子(如多产品族)或 UML 图,请告诉我!

建造者(Builder)

建造者模式(Builder Pattern)是一种创建型设计模式,用于分步构建复杂对象。它将对象的构造过程与其表示分离,使得同样的构建过程可以创建不同的表示。建造者模式特别适合需要多个步骤配置的对象,例如对象有多个可选属性或复杂的初始化逻辑。

以下是用 Java 实现建造者模式的详细讲解,并附上用 Mermaid 绘制的 UML 类图。


1. 建造者模式的定义

  • 核心思想:通过一个建造者(Builder)逐步构造复杂对象,最终由指挥者(Director)或直接调用建造者生成产品。
  • 目的
    • 分离对象的构造和表示,便于灵活配置。
    • 避免构造函数参数过多(“望远镜构造函数”问题)。
  • 适用场景
    • 对象有多个可选属性或复杂初始化步骤。
    • 需要创建不可变对象。
    • 构造过程需要复用。

2. 建造者模式的结构

  • 产品(Product):最终要构建的复杂对象。
  • 抽象建造者(Builder):定义构建产品各部分的抽象方法。
  • 具体建造者(Concrete Builder):实现抽象建造者,逐步构造产品并提供获取结果的方法。
  • 指挥者(Director):可选,负责控制建造过程,调用建造者的方法。
UML 图
classDiagram
    class Product {
        -field1
        -field2
    }
    class Builder {
        +buildPart1()
        +buildPart2()
        +getResult() Product
    }
    class ConcreteBuilder {
        -product: Product
        +buildPart1()
        +buildPart2()
        +getResult() Product
    }
    class Director {
        -builder: Builder
        +construct()
    }
    
    ConcreteBuilder --> Product : builds
    ConcreteBuilder ..|> Builder : implements
    Director --> Builder : uses

3. Java 实现示例

以下是一个构建房屋(House)的建造者模式示例。

3.1 产品类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 产品:房屋
public class House {
private String foundation; // 地基
private String structure; // 结构
private String roof; // 屋顶

public void setFoundation(String foundation) { this.foundation = foundation; }
public void setStructure(String structure) { this.structure = structure; }
public void setRoof(String roof) { this.roof = roof; }

@Override
public String toString() {
return "House [foundation=" + foundation + ", structure=" + structure + ", roof=" + roof + "]";
}
}
3.2 抽象建造者接口
1
2
3
4
5
6
7
// 抽象建造者
public interface HouseBuilder {
void buildFoundation();
void buildStructure();
void buildRoof();
House getResult();
}
3.3 具体建造者类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 具体建造者:木屋建造者
public class WoodenHouseBuilder implements HouseBuilder {
private House house;

public WoodenHouseBuilder() {
this.house = new House();
}

@Override
public void buildFoundation() {
house.setFoundation("Wooden foundation");
}

@Override
public void buildStructure() {
house.setStructure("Wooden walls");
}

@Override
public void buildRoof() {
house.setRoof("Wooden roof");
}

@Override
public House getResult() {
return house;
}
}
3.4 指挥者类(可选)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 指挥者
public class HouseDirector {
private HouseBuilder builder;

public HouseDirector(HouseBuilder builder) {
this.builder = builder;
}

public House construct() {
builder.buildFoundation();
builder.buildStructure();
builder.buildRoof();
return builder.getResult();
}
}
3.5 客户端代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Main {
public static void main(String[] args) {
// 使用指挥者
HouseBuilder woodenBuilder = new WoodenHouseBuilder();
HouseDirector director = new HouseDirector(woodenBuilder);
House house = director.construct();
System.out.println(house); // 输出: House [foundation=Wooden foundation, structure=Wooden walls, roof=Wooden roof]

// 直接使用建造者
HouseBuilder builder = new WoodenHouseBuilder();
builder.buildFoundation();
builder.buildStructure();
builder.buildRoof();
House house2 = builder.getResult();
System.out.println(house2); // 同上
}
}

4. 工作原理

  1. 客户端选择一个具体建造者(如 WoodenHouseBuilder)。
  2. 通过指挥者(可选)或直接调用建造者的方法,逐步构建产品(设置地基、结构、屋顶)。
  3. 调用 getResult() 获取最终产品。

5. 优点

  • 分步构造:将复杂对象的创建分解为多个步骤,清晰可控。
  • 灵活性:支持不同的构造过程和产品表示。
  • 封装性:隐藏产品的内部构造细节。

6. 缺点

  • 类数量增加:需要为每种产品定义建造者,增加代码量。
  • 适用范围有限:适合复杂对象,不适合简单对象。

7. 与工厂模式的对比

特性 工厂模式 建造者模式
关注点 创建单一对象 分步构建复杂对象
过程 一步完成 多步配置
结果 直接返回产品 构建完成后返回产品

9. 实际应用场景

  • 字符串构建StringBuilder 是建造者模式的典型例子。
  • 对象配置:如 OkHttpClient.BuilderAlertDialog.Builder
  • 文档生成:如生成复杂的 PDF 或 HTML 文档。

10. 源码中的例子

Java 中的 StringBuilder

1
2
3
StringBuilder builder = new StringBuilder();
builder.append("Hello").append(" ").append("World");
String result = builder.toString(); // 输出: Hello World


总结

  • 建造者模式通过分步构建复杂对象,提供灵活性和封装性。
  • Java 实现的关键是定义建造者接口和产品类,指挥者可选。
  • Mermaid UML展示了核心结构,清晰表达模式关系。

如果需要更复杂的例子(如多个建造者)或调整 UML 图,请告诉我!

原型模式(Prototype)

原型模式(Prototype Pattern)是一种创建型设计模式,通过复制现有对象(原型)来创建新对象,而不是通过构造函数从头构建。它适用于创建对象成本较高(如需要大量计算或资源)或需要基于现有对象快速生成类似对象的场景。

以下是用 Java 实现原型模式的详细讲解,并附上用 Mermaid 绘制的 UML 类图。


1. 原型模式的定义

  • 核心思想:通过克隆已有对象(原型)创建新对象,而不是通过 new 实例化。
  • 目的
    • 提高创建效率,避免重复的初始化工作。
    • 支持动态生成对象副本。
  • 适用场景
    • 对象创建复杂或耗时。
    • 需要创建大量相似对象。
    • 需要对象深拷贝或浅拷贝。

2. 原型模式的结构

  • 抽象原型(Prototype):定义克隆方法的接口。
  • 具体原型(Concrete Prototype):实现克隆方法,复制自身。
  • 客户端(Client):使用原型创建新对象。
UML 图
classDiagram
    class Prototype {
        +clone() Prototype
    }
    class ConcretePrototype {
        -field1
        -field2
        +clone() Prototype
    }
    class Client {
        -prototype: Prototype
    }
    
    ConcretePrototype ..|> Prototype : implements
	Client --> Prototype : uses
	ConcretePrototype --> ConcretePrototype : clones

3. Java 实现示例

以下是一个形状(Shape)的原型模式示例,支持克隆圆形对象。

3.1 抽象原型接口
1
2
3
4
5
// 抽象原型:实现 Cloneable 接口
public interface Shape extends Cloneable {
Shape clone(); // 克隆方法
void draw(); // 示例方法
}
3.2 具体原型类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 具体原型:圆形
public class Circle implements Shape {
private int radius;
private String color;

public Circle(int radius, String color) {
this.radius = radius;
this.color = color;
}

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

@Override
public Shape clone() {
try {
return (Shape) super.clone(); // 浅拷贝
} catch (CloneNotSupportedException e) {
return null; // 异常处理
}
}

@Override
public void draw() {
System.out.println("Drawing a " + color + " circle with radius " + radius);
}
}
3.3 客户端代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Main {
public static void main(String[] args) {
// 创建原型
Circle original = new Circle(10, "red");
original.draw(); // 输出: Drawing a red circle with radius 10

// 克隆对象
Circle cloned = (Circle) original.clone();
cloned.setColor("blue");
cloned.draw(); // 输出: Drawing a blue circle with radius 10

// 原对象保持不变
original.draw(); // 输出: Drawing a red circle with radius 10
}
}

4. 工作原理

  1. 客户端创建初始原型对象(如 Circle)。
  2. 调用 clone() 方法生成副本。
  3. 修改副本的属性,保持原对象不变。

5. 深拷贝 vs 浅拷贝

  • 浅拷贝(如上例):
    • 使用 Object.clone(),只复制对象本身,引用类型字段仍指向原对象。
    • 如果原型包含复杂对象(如列表),副本修改会影响原对象。
  • 深拷贝
    • 手动实现 clone(),递归复制所有引用字段。
深拷贝示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class Circle implements Shape {
private int radius;
private String color;
private List<String> tags; // 复杂字段

public Circle(int radius, String color, List<String> tags) {
this.radius = radius;
this.color = color;
this.tags = tags;
}

@Override
public Shape clone() {
try {
Circle cloned = (Circle) super.clone();
cloned.tags = new ArrayList<>(this.tags); // 深拷贝列表
return cloned;
} catch (CloneNotSupportedException e) {
return null;
}
}

@Override
public void draw() {
System.out.println("Drawing a " + color + " circle with radius " + radius + ", tags: " + tags);
}

public void setTags(List<String> tags) { this.tags = tags; }
}

6. 优点

  • 性能提升:避免重复初始化,适合复杂对象。
  • 灵活性:动态创建对象副本,可修改副本而不影响原型。
  • 简化创建:无需复杂构造函数。

7. 缺点

  • 实现复杂性:深拷贝需要手动实现,浅拷贝可能导致意外副作用。
  • 依赖 Cloneable:Java 的克隆机制不够灵活,异常处理麻烦。

8. 与工厂模式的对比

特性 工厂模式 原型模式
创建方式 通过类实例化 通过复制现有对象
初始化 从头构建 基于原型克隆
适用性 创建新对象 复制已有对象

10. 实际应用场景

  • 对象缓存:缓存复杂对象,通过克隆提供副本。
  • 图形编辑器:复制图形元素(如矩形、圆形)。
  • 游戏开发:克隆敌人或道具对象。

11. 源码中的例子

Java 中的 Object.clone()

1
2
ArrayList<String> list = new ArrayList<>(Arrays.asList("a", "b"));
ArrayList<String> cloned = (ArrayList<String>) list.clone();


总结

  • 原型模式通过克隆原型创建对象,提高效率和灵活性。
  • Java 实现依赖 Cloneableclone(),可实现浅拷贝或深拷贝。
  • Mermaid UML展示了原型模式的核心结构。

结构型模式(Structural Patterns)

适配器(Adapter)

适配器模式(Adapter Pattern)是一种结构型设计模式,用于将一个类的接口转换为客户端期望的另一个接口。它解决接口不兼容的问题,使原本不匹配的类能够协同工作。适配器模式有两种形式:类适配器(通过继承)和对象适配器(通过组合)。

以下是用 Java 实现适配器模式的讲解,并直接用 Mermaid 绘制 UML 类图。


1. 适配器模式的定义

  • 核心思想:通过一个适配器,将不兼容的接口转换为目标接口。
  • 目的
    • 让现有类适配新接口,无需修改原有代码。
    • 提高代码复用性。
  • 适用场景
    • 集成遗留系统或第三方库。
    • 接口不匹配但功能类似的情况。

2. 适配器模式的结构

  • 目标接口(Target):客户端期望的接口。
  • 适配者(Adaptee):需要适配的现有类,接口不兼容。
  • 适配器(Adapter):实现目标接口,调用适配者的方法。
UML 图

对象适配器版本:

classDiagram
    class Target {
        +request()
    }
    class Adaptee {
        +specificRequest()
    }
    class Adapter {
        -adaptee: Adaptee
        +request()
    }
    
    Adapter ..|> Target : implements
    Adapter --> Adaptee : uses

类适配器版本:

1
2
3
4
5
6
7
8
9
10
11
12
13
classDiagram
class Target {
+request()
}
class Adaptee {
+specificRequest()
}
class Adapter {
+request()
}

Target <|.. Adapter : implements
Adaptee <|-- Adapter : extends

说明

  • Target:目标接口,定义客户端期望的方法。
  • Adaptee:适配者,提供不兼容的功能。
  • Adapter
    • 对象适配器:通过组合(-->)持有 Adaptee
    • 类适配器:通过继承(<|--)扩展 Adaptee
  • 关系
    • Target <|.. Adapter:适配器实现目标接口。
    • 对象适配器:Adapter --> Adaptee(组合)。
    • 类适配器:Adaptee <|-- Adapter(继承)。

3. Java 实现示例

以下是一个将旧电源接口适配为新接口的例子。

3.1 目标接口
1
2
3
4
// 目标接口:新电源
public interface NewPower {
void providePower();
}
3.2 适配者类
1
2
3
4
5
6
// 适配者:旧电源
public class OldPower {
public void outputPower() {
System.out.println("Outputting power from old system");
}
}
3.3 对象适配器
1
2
3
4
5
6
7
8
9
10
11
12
13
// 对象适配器
public class PowerAdapter implements NewPower {
private OldPower oldPower;

public PowerAdapter(OldPower oldPower) {
this.oldPower = oldPower;
}

@Override
public void providePower() {
oldPower.outputPower(); // 调用适配者的方法
}
}
3.4 类适配器(替代实现)
1
2
3
4
5
6
7
// 类适配器(通过继承)
public class PowerAdapterClass extends OldPower implements NewPower {
@Override
public void providePower() {
outputPower(); // 继承并调用父类方法
}
}
3.5 客户端代码
1
2
3
4
5
6
7
8
9
10
11
12
public class Main {
public static void main(String[] args) {
// 对象适配器
OldPower oldPower = new OldPower();
NewPower adapter = new PowerAdapter(oldPower);
adapter.providePower(); // 输出: Outputting power from old system

// 类适配器
NewPower adapterClass = new PowerAdapterClass();
adapterClass.providePower(); // 输出: Outputting power from old system
}
}

4. 工作原理

  • 对象适配器:通过组合持有适配者对象,转换调用。
  • 类适配器:通过继承适配者并实现目标接口,转换调用。

5. 优点

  • 复用性:无需修改现有代码即可复用适配者。
  • 灵活性:对象适配器通过组合更灵活,类适配器适合简单场景。
  • 解耦:客户端只依赖目标接口。

6. 缺点

  • 复杂度:增加一个适配器类,可能提高系统复杂性。
  • 类适配器局限:Java 单继承限制了类适配器的使用。

7. 与其他模式的对比

特性 适配器模式 装饰者模式
目的 接口转换 增强功能
结构 适配已有类 包装同类对象
关系 转换调用 递归调用

8. 实际应用场景

  • IO 流InputStreamReader 适配 InputStreamReader
  • 事件监听MouseAdapter 适配 MouseListener 接口。
  • 第三方库:适配旧 API 到新系统。

9. 源码中的例子

Java 中的 InputStreamReader

1
2
InputStream input = new FileInputStream("file.txt");
Reader reader = new InputStreamReader(input); // 适配器


总结

  • 适配器模式通过转换接口实现兼容性,支持对象适配器(组合)和类适配器(继承)。
  • Java 实现依赖接口和组合/继承机制。
  • Mermaid UML展示了两种形式的结构。

如果需要深究某种适配器或调整 UML 图,请告诉我!

桥接(Bridge)

桥接模式(Bridge Pattern)是一种结构型设计模式,用于将抽象部分与实现部分分离,使它们可以独立变化。它通过组合而非继承,将两个维度的变化解耦,增强系统的灵活性和扩展性。

以下是用 Java 实现桥接模式的详细讲解,并在“模式结构”部分提供 Mermaid UML 图。


1. 桥接模式的定义

  • 核心思想:将抽象(Abstraction)与实现(Implementation)分离,通过组合关系连接两者。
  • 目的
    • 解耦抽象和实现,避免多层继承导致的复杂性。
    • 支持独立扩展抽象和实现。
  • 适用场景
    • 一个类存在两个独立变化的维度(如形状和颜色)。
    • 需要在运行时动态切换实现。
    • 避免继承带来的紧耦合。

2. 桥接模式的结构

  • 抽象部分(Abstraction):定义高层接口,包含对实现的引用。
  • 修正抽象(Refined Abstraction):扩展抽象部分,提供具体功能。
  • 实现接口(Implementor):定义实现部分的接口。
  • 具体实现(Concrete Implementor):实现接口的具体类。
Mermaid UML 图
classDiagram
    class Abstraction {
        -implementor: Implementor
        +operation()
    }
    class RefinedAbstraction {
        +operation()
    }
    class Implementor {
        +operationImpl()
    }
    class ConcreteImplementorA {
        +operationImpl()
    }
    class ConcreteImplementorB {
        +operationImpl()
    }
    
	RefinedAbstraction --|> Abstraction : extends
	ConcreteImplementorA ..|> Implementor : implements
	ConcreteImplementorB ..|> Implementor : implements
	Abstraction --> Implementor : uses

3. Java 实现示例

以下是一个绘制形状(矩形)并支持不同绘制方式(颜色)的桥接模式示例。

3.1 实现接口
1
2
3
4
// 实现接口:绘制方式
public interface DrawAPI {
void drawRectangle(int width, int height);
}
3.2 具体实现类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 具体实现:红色绘制
public class RedDraw implements DrawAPI {
@Override
public void drawRectangle(int width, int height) {
System.out.println("Drawing a red rectangle: " + width + "x" + height);
}
}

// 具体实现:蓝色绘制
public class BlueDraw implements DrawAPI {
@Override
public void drawRectangle(int width, int height) {
System.out.println("Drawing a blue rectangle: " + width + "x" + height);
}
}
3.3 抽象部分
1
2
3
4
5
6
7
8
9
10
// 抽象部分:形状
public abstract class Shape {
protected DrawAPI drawAPI;

protected Shape(DrawAPI drawAPI) {
this.drawAPI = drawAPI;
}

public abstract void draw();
}
3.4 修正抽象类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 修正抽象:矩形
public class Rectangle extends Shape {
private int width;
private int height;

public Rectangle(int width, int height, DrawAPI drawAPI) {
super(drawAPI);
this.width = width;
this.height = height;
}

@Override
public void draw() {
drawAPI.drawRectangle(width, height);
}
}
3.5 客户端代码
1
2
3
4
5
6
7
8
9
10
11
public class Main {
public static void main(String[] args) {
// 使用红色绘制矩形
Shape redRectangle = new Rectangle(10, 5, new RedDraw());
redRectangle.draw(); // 输出: Drawing a red rectangle: 10x5

// 使用蓝色绘制矩形
Shape blueRectangle = new Rectangle(20, 10, new BlueDraw());
blueRectangle.draw(); // 输出: Drawing a blue rectangle: 20x10
}
}

4. 工作原理

  1. 客户端创建具体实现(RedDrawBlueDraw)。
  2. 将实现注入抽象类(Rectangle),通过组合连接。
  3. 调用抽象类的操作(draw()),间接调用实现的方法。

5. 优点

  • 解耦:抽象和实现独立变化,互不影响。
  • 扩展性:可独立增加新的抽象或实现类。
  • 运行时切换:通过组合动态改变实现。

6. 缺点

  • 复杂度增加:引入额外接口和类,设计更复杂。
  • 适用性有限:仅适合有两个独立变化维度的场景。

7. 与适配器模式的对比

特性 桥接模式 适配器模式
目的 分离抽象和实现 接口转换
时机 设计时规划 集成已有代码
关系 组合为主 组合或继承

8. 实际应用场景

  • JDBCDriverConnection 分离,驱动实现独立变化。
  • GUI 框架:组件(如按钮)与绘制方式(如 Windows/Mac 风格)分离。
  • 日志系统:日志级别与输出方式(如文件、控制台)解耦。

9. 源码中的例子

Java 中的 java.sql.DriverManager

1
Connection conn = DriverManager.getConnection(url);  // 桥接驱动实现


10. 总结

  • 桥接模式通过组合将抽象与实现分离,支持独立扩展。
  • Java 实现依赖接口和组合,灵活性高。
  • UML 图清晰展示了两部分的解耦关系。

如果需要更复杂示例(如多实现)或调整 UML,请告诉我!

组合(Composite)

组合模式(Composite Pattern)是一种结构型设计模式,用于将对象组织成树形结构,以表示“部分-整体”的层次关系。它允许客户端统一对待单个对象(叶子)和对象集合(容器),简化操作。

以下是用 Java 实现组合模式的详细讲解,包括定义、结构、代码示例及其优缺点。


1. 组合模式的定义

  • 核心思想:将对象组合成树形结构,使单个对象和组合对象具有一致的接口。
  • 目的
    • 统一处理叶子节点和容器节点。
    • 隐藏内部结构差异,简化客户端代码。
  • 适用场景
    • 表示树形结构(如文件系统、组织架构)。
    • 需要统一操作单个对象和对象集合。

2. 组合模式的结构

  • 抽象组件(Component):定义叶子和容器的公共接口。
  • 叶子(Leaf):实现组件接口,表示树中的单个对象。
  • 容器(Composite):实现组件接口,包含子组件集合,可递归操作。
Mermaid UML 图
classDiagram
    class Component {
        +operation()
        +add(Component)
        +remove(Component)
        +getChild(int) Component
    }
    class Leaf {
        +operation()
    }
    class Composite {
        -children: List<Component>
        +operation()
        +add(Component)
        +remove(Component)
        +getChild(int) Component
    }
    
    Leaf ..|> Component : implements
	Composite ..|> Component : implements
    Composite o--> "many" Component : contains

3. Java 实现示例

以下是一个文件系统(文件和文件夹)的组合模式示例。

3.1 抽象组件
1
2
3
4
// 抽象组件:文件系统项
public interface FileSystemItem {
void display(); // 显示信息
}
3.2 叶子类
1
2
3
4
5
6
7
8
9
10
11
12
13
// 叶子:文件
public class File implements FileSystemItem {
private String name;

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

@Override
public void display() {
System.out.println("File: " + name);
}
}
3.3 容器类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 容器:文件夹
public class Folder implements FileSystemItem {
private String name;
private List<FileSystemItem> children;

public Folder(String name) {
this.name = name;
this.children = new ArrayList<>();
}

public void add(FileSystemItem item) {
children.add(item);
}

public void remove(FileSystemItem item) {
children.remove(item);
}

@Override
public void display() {
System.out.println("Folder: " + name);
for (FileSystemItem item : children) {
item.display(); // 递归显示子项
}
}
}
3.4 客户端代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class Main {
public static void main(String[] args) {
// 创建文件
FileSystemItem file1 = new File("doc1.txt");
FileSystemItem file2 = new File("doc2.txt");

// 创建文件夹
Folder folder1 = new Folder("Documents");
folder1.add(file1);
folder1.add(file2);

Folder root = new Folder("Root");
root.add(folder1);
root.add(new File("readme.txt"));

// 显示树形结构
root.display();
/*
输出:
Folder: Root
Folder: Documents
File: doc1.txt
File: doc2.txt
File: readme.txt
*/
}
}

4. 工作原理

  1. 客户端创建叶子(File)和容器(Folder)。
  2. 将叶子和子容器添加到容器中,形成树形结构。
  3. 调用统一接口(display()),递归处理整个结构。

5. 优点

  • 统一性:客户端无需区分叶子和容器,使用一致接口。
  • 扩展性:易于添加新组件(叶子或容器)。
  • 简化操作:递归处理树形结构。

6. 缺点

  • 限制性:叶子和容器的接口必须一致,可能导致叶子实现无意义的方法(如 add)。
  • 复杂度:管理树形结构可能增加系统复杂性。

7. 与装饰者模式的对比

特性 组合模式 装饰者模式
目的 表示部分-整体关系 动态增强功能
结构 树形层次 线性包装
操作 统一处理 递归调用

8. 实际应用场景

  • 文件系统:文件和文件夹的树形结构。
  • GUI 组件:如 Swing 中的 ContainerComponent
  • 组织架构:公司部门和员工的层次关系。

9. 源码中的例子

Java 中的 java.awt.ComponentContainer

1
2
Container panel = new JPanel();
panel.add(new JButton("Click")); // 组合结构


10. 总结

  • 组合模式通过统一接口处理树形结构,适合部分-整体场景。
  • Java 实现依赖接口和递归,容器管理子组件。
  • UML 图展示了组件、叶子和容器的关系。

装饰者(Decorator)

装饰者模式是一种结构型设计模式,用于动态地为对象添加功能,而无需修改其原有代码。它通过包装对象实现功能的扩展,遵循“开闭原则”(对扩展开放,对修改关闭)。

以下是用 Java 实现装饰者模式的详细讲解,包括定义、结构、代码示例及其优缺点。


1. 装饰者模式的定义

  • 核心思想:通过组合动态地为对象添加职责,替代继承。
  • 目的
    • 增强对象功能,同时保持接口一致。
    • 提供灵活的替代继承的扩展方式。
  • 适用场景
    • 需要动态添加功能(如日志、权限)。
    • 无法通过子类扩展现有类。
    • 需要多种功能组合。

2. 装饰者模式的结构

  • 抽象组件(Component):定义被装饰对象的接口。
  • 具体组件(Concrete Component):实现组件接口,是被装饰的核心对象。
  • 抽象装饰者(Decorator):实现组件接口,持有组件引用,定义装饰框架。
  • 具体装饰者(Concrete Decorator):扩展装饰者,添加具体功能。
Mermaid UML 图
classDiagram
    class Component {
        +operation()
    }
    class ConcreteComponent {
        +operation()
    }
    class Decorator {
        -component: Component
        +operation()
    }
    class ConcreteDecorator {
        +operation()
        +addedBehavior()
    }
    
    ConcreteComponent ..|> Component : implements
	Decorator ..|> Component : implements
	ConcreteDecorator --|> Decorator : extends
    Decorator o--> Component : contains

3. Java 实现示例

以下是一个咖啡定价的装饰者模式示例,支持添加配料(如牛奶、糖)。

3.1 抽象组件
1
2
3
4
5
// 抽象组件:咖啡
public interface Coffee {
double cost(); // 计算价格
String description(); // 获取描述
}
3.2 具体组件
1
2
3
4
5
6
7
8
9
10
11
12
// 具体组件:简单咖啡
public class SimpleCoffee implements Coffee {
@Override
public double cost() {
return 5.0;
}

@Override
public String description() {
return "Simple Coffee";
}
}
3.3 抽象装饰者
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 抽象装饰者
public abstract class CoffeeDecorator implements Coffee {
protected Coffee coffee;

public CoffeeDecorator(Coffee coffee) {
this.coffee = coffee;
}

@Override
public double cost() {
return coffee.cost();
}

@Override
public String description() {
return coffee.description();
}
}
3.4 具体装饰者
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 具体装饰者:加牛奶
public class MilkDecorator extends CoffeeDecorator {
public MilkDecorator(Coffee coffee) {
super(coffee);
}

@Override
public double cost() {
return super.cost() + 2.0; // 牛奶加价
}

@Override
public String description() {
return super.description() + ", Milk";
}
}

// 具体装饰者:加糖
public class SugarDecorator extends CoffeeDecorator {
public SugarDecorator(Coffee coffee) {
super(coffee);
}

@Override
public double cost() {
return super.cost() + 1.0; // 糖加价
}

@Override
public String description() {
return super.description() + ", Sugar";
}
}
3.5 客户端代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Main {
public static void main(String[] args) {
// 基础咖啡
Coffee coffee = new SimpleCoffee();
System.out.println(coffee.description() + " $" + coffee.cost());
// 输出: Simple Coffee $5.0

// 加牛奶
coffee = new MilkDecorator(coffee);
System.out.println(coffee.description() + " $" + coffee.cost());
// 输出: Simple Coffee, Milk $7.0

// 加糖
coffee = new SugarDecorator(coffee);
System.out.println(coffee.description() + " $" + coffee.cost());
// 输出: Simple Coffee, Milk, Sugar $8.0
}
}

4. 工作原理

  1. 客户端创建具体组件(如 SimpleCoffee)。
  2. 使用装饰者(如 MilkDecoratorSugarDecorator)包装组件,动态添加功能。
  3. 调用统一接口(cost()description()),递归执行装饰逻辑。

5. 优点

  • 灵活性:动态添加或移除功能,无需修改原有类。
  • 复用性:装饰者可组合使用,生成多种组合。
  • 符合开闭原则:扩展功能不改动源代码。

6. 缺点

  • 复杂度增加:多层装饰导致对象层次复杂,调试困难。
  • 类数量增多:每个新功能需定义新装饰者。

7. 与适配器模式的对比

特性 装饰者模式 适配器模式
目的 增强功能 接口转换
结构 包装同类对象 适配不同类
调用 递归调用 转换调用

8. 实际应用场景

  • IO 流BufferedInputStream 装饰 InputStream
  • GUI 组件:添加边框、滚动条等装饰。
  • 日志系统:为日志添加时间戳、格式化。

9. 源码中的例子

Java 中的 java.io 包:

1
2
InputStream input = new FileInputStream("file.txt");
InputStream buffered = new BufferedInputStream(input); // 装饰


10. 总结

  • 装饰者模式通过包装动态增强对象功能,保持接口一致。
  • Java 实现依赖接口和组合,支持多层装饰。
  • UML 图展示了组件和装饰者的递归关系。

外观(Facade)

外观模式是一种结构型设计模式,它为复杂的子系统提供一个简化的统一接口,隐藏子系统的复杂性,使客户端更容易使用。

以下是用 Java 实现外观模式的详细讲解,包括定义、结构、代码示例及其优缺点。


1. 外观模式的定义

  • 核心思想:通过一个外观类封装子系统的复杂操作,提供简单的高层接口。
  • 目的
    • 简化客户端与子系统的交互。
    • 解耦客户端与子系统,降低依赖性。
  • 适用场景
    • 子系统复杂且难以直接使用。
    • 需要为子系统提供统一入口。
    • 分层设计中隔离层间耦合。

2. 外观模式的结构

  • 子系统(Subsystem):一组独立的类,完成特定功能。
  • 外观(Facade):封装子系统,提供简化的接口。
  • 客户端(Client):通过外观访问子系统。
Mermaid UML 图
classDiagram
    class Facade {
        -subsystemA: SubsystemA
        -subsystemB: SubsystemB
        +operation()
    }
    class SubsystemA {
        +operationA()
    }
    class SubsystemB {
        +operationB()
    }
    class Client
    
    Client --> Facade : uses
    Facade --> SubsystemA : delegates
    Facade --> SubsystemB : delegates

3. Java 实现示例

以下是一个家庭影院系统的外观模式示例,简化音响、投影仪和灯光的操作。

3.1 子系统类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 子系统:音响
public class AudioSystem {
public void turnOn() {
System.out.println("Audio system is on");
}
public void setVolume(int level) {
System.out.println("Audio volume set to " + level);
}
public void turnOff() {
System.out.println("Audio system is off");
}
}

// 子系统:投影仪
public class Projector {
public void turnOn() {
System.out.println("Projector is on");
}
public void setInput(String input) {
System.out.println("Projector input set to " + input);
}
public void turnOff() {
System.out.println("Projector is off");
}
}

// 子系统:灯光
public class Lights {
public void dim(int level) {
System.out.println("Lights dimmed to " + level + "%");
}
public void turnOff() {
System.out.println("Lights are off");
}
}
3.2 外观类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 外观:家庭影院
public class HomeTheaterFacade {
private AudioSystem audio;
private Projector projector;
private Lights lights;

public HomeTheaterFacade(AudioSystem audio, Projector projector, Lights lights) {
this.audio = audio;
this.projector = projector;
this.lights = lights;
}

public void watchMovie(String input) {
System.out.println("Preparing to watch a movie...");
audio.turnOn();
audio.setVolume(50);
projector.turnOn();
projector.setInput(input);
lights.dim(20);
System.out.println("Movie is ready!");
}

public void endMovie() {
System.out.println("Shutting down the movie...");
audio.turnOff();
projector.turnOff();
lights.turnOff();
}
}
3.3 客户端代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class Main {
public static void main(String[] args) {
AudioSystem audio = new AudioSystem();
Projector projector = new Projector();
Lights lights = new Lights();

HomeTheaterFacade theater = new HomeTheaterFacade(audio, projector, lights);

theater.watchMovie("HDMI"); // 启动电影
/*
输出:
Preparing to watch a movie...
Audio system is on
Audio volume set to 50
Projector is on
Projector input set to HDMI
Lights dimmed to 20%
Movie is ready!
*/

theater.endMovie(); // 关闭电影
/*
输出:
Shutting down the movie...
Audio system is off
Projector is off
Lights are off
*/
}
}

4. 工作原理

  1. 客户端创建子系统实例并注入外观类。
  2. 调用外观的简化方法(如 watchMovie),外观协调子系统完成任务。
  3. 子系统独立运行,客户端无需直接操作。

5. 优点

  • 简化接口:降低客户端使用子系统的复杂度。
  • 解耦:客户端与子系统隔离,降低耦合度。
  • 封装性:隐藏子系统细节。

6. 缺点

  • 功能有限:外观可能无法满足所有需求,客户端可能仍需直接访问子系统。
  • 新增开销:增加一个外观类,可能提高维护成本。

7. 与适配器模式的对比

特性 外观模式 适配器模式
目的 简化接口 接口转换
作用对象 整个子系统 单个类
复杂度 降低使用复杂度 增加适配层复杂度

8. 实际应用场景

  • 数据库访问:封装 JDBC 操作,提供简单 API。
  • 多媒体系统:简化设备控制(如电视、音响)。
  • 工具库:为复杂底层功能提供高层接口。

9. 源码中的例子

Java 中的 java.util.logging.Logger

1
2
Logger logger = Logger.getLogger("myLogger");  // 外观简化日志操作
logger.info("Message");


10. 总结

  • 外观模式通过封装子系统提供简化的统一接口,降低使用难度。
  • Java 实现依赖组合,协调多个子系统。
  • UML 图展示了外观与子系统的委托关系。

享元(Flyweight)

享元模式(Flyweight Pattern)是一种结构型设计模式,旨在通过共享对象来减少内存使用和提高性能。它适用于系统中存在大量相似对象的情况,通过复用已有的对象而不是创建新实例来优化资源消耗。

以下是用 Java 实现享元模式的详细讲解,包括定义、结构、代码示例及其优缺点。


1. 享元模式的定义

  • 核心思想:共享相同的对象实例,减少重复创建,降低内存开销。
  • 目的
    • 提高系统效率,特别是在对象数量庞大时。
    • 分离对象的内在状态(共享)和外在状态(不共享)。
  • 适用场景
    • 系统有大量相似对象。
    • 对象创建成本高且可共享。
    • 需要缓存不可变对象。

2. 享元模式的结构

  • 抽象享元(Flyweight):定义共享对象的接口。
  • 具体享元(Concrete Flyweight):实现享元接口,存储内在状态。
  • 享元工厂(Flyweight Factory):管理享元对象的创建和共享。
  • 客户端(Client):使用享元对象,维护外在状态。
Mermaid UML 图
classDiagram
    class Flyweight {
        +operation(extrinsicState)
    }
    class ConcreteFlyweight {
        -intrinsicState
        +operation(extrinsicState)
    }
    class FlyweightFactory {
        -flyweights: Map
        +getFlyweight(key) Flyweight
    }
    class Client
    
    ConcreteFlyweight ..|> Flyweight : implements
    FlyweightFactory --> Flyweight : creates
    Client --> FlyweightFactory : uses
    Client --> Flyweight : uses

3. Java 实现示例

以下是一个绘制圆形的享元模式示例,共享颜色相同的圆形对象。

3.1 抽象享元接口
1
2
3
4
// 抽象享元:圆形
public interface Shape {
void draw(int x, int y); // 外在状态由客户端传入
}
3.2 具体享元类
1
2
3
4
5
6
7
8
9
10
11
12
13
// 具体享元:圆形(共享颜色)
public class Circle implements Shape {
private String color; // 内在状态(共享)

public Circle(String color) {
this.color = color;
}

@Override
public void draw(int x, int y) {
System.out.println("Drawing a " + color + " circle at (" + x + ", " + y + ")");
}
}
3.3 享元工厂
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 享元工厂:管理圆形对象
public class ShapeFactory {
private static final Map<String, Shape> circleMap = new HashMap<>();

public static Shape getCircle(String color) {
Shape circle = circleMap.get(color);
if (circle == null) {
circle = new Circle(color);
circleMap.put(color, circle);
System.out.println("Created a new " + color + " circle");
}
return circle;
}
}
3.4 客户端代码
1
2
3
4
5
6
7
8
9
10
11
public class Main {
private static final String[] colors = {"Red", "Blue", "Green"};

public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
String color = colors[(int) (Math.random() * colors.length)];
Shape circle = ShapeFactory.getCircle(color);
circle.draw(i, i * 2); // 外在状态:位置
}
}
}

输出示例

1
2
3
4
5
6
7
8
9
Created a new Red circle
Drawing a Red circle at (0, 0)
Created a new Blue circle
Drawing a Blue circle at (1, 2)
Drawing a Red circle at (2, 4)
Created a new Green circle
Drawing a Green circle at (3, 6)
Drawing a Blue circle at (4, 8)
...
- 说明:相同颜色的圆形只创建一次,之后共享。


4. 工作原理

  1. 客户端通过工厂请求享元对象(如 getCircle)。
  2. 工厂检查是否已有对应对象,若无则创建并缓存,若有则返回共享实例。
  3. 客户端传入外在状态(如位置),调用享元方法。

5. 内在状态 vs 外在状态

  • 内在状态:存储在享元对象中,不可变且可共享(如 color)。
  • 外在状态:由客户端维护,不共享(如 xy 坐标)。

6. 优点

  • 节省内存:共享对象减少实例数量。
  • 性能提升:避免重复创建耗时对象。
  • 集中管理:工厂统一管理共享对象。

7. 缺点

  • 复杂度增加:需要分离内外状态,设计更复杂。
  • 线程安全:共享对象需考虑并发访问(可用同步或不可变设计)。
  • 适用性有限:仅适合大量相似对象的场景。

8. 与单例模式的对比

特性 享元模式 单例模式
实例数量 多实例(按键共享) 单实例
目的 共享减少内存 确保唯一性
管理 工厂管理 静态方法控制

9. 实际应用场景

  • 字符缓存:文本编辑器中共享字体或字符对象。
  • 游戏开发:复用纹理或模型实例。
  • 连接池:数据库连接或线程池的共享管理。

10. 源码中的例子

Java 中的 Integer.valueOf()

1
2
3
Integer a = Integer.valueOf(100);  // 缓存 -128 到 127 的整数
Integer b = Integer.valueOf(100);
System.out.println(a == b); // true,共享同一对象


11. 总结

  • 享元模式通过共享对象优化资源使用,分离内外状态。
  • Java 实现依赖工厂和缓存(如 Map),支持高效复用。
  • UML 图展示了享元、工厂和客户端的关系。

代理(Proxy)

代理模式(Proxy Pattern)是一种结构型设计模式,通过引入一个代理对象来控制对目标对象的访问。它可以在不修改目标对象的情况下,增强功能(如权限检查、延迟加载、日志记录等)。代理模式的核心是为目标对象提供一个替代者,客户端通过代理间接访问目标。

以下是用 Java 实现代理模式的详细讲解,包括定义、结构、代码示例及其优缺点。


1. 代理模式的定义

  • 核心思想:通过代理对象控制对目标对象的访问,增加额外的功能或限制。
  • 目的
    • 增强目标对象功能。
    • 保护或隐藏目标对象。
  • 适用场景
    • 延迟加载(虚拟代理)。
    • 访问控制(保护代理)。
    • 日志或缓存(装饰代理)。

2. 代理模式的结构

  • 抽象主题(Subject):定义目标和代理的公共接口。
  • 真实主题(Real Subject):实现主题接口,是被代理的目标对象。
  • 代理(Proxy):实现主题接口,持有真实主题引用,控制访问并添加功能。
Mermaid UML 图
classDiagram
    class Subject {
        +request()
    }
    class RealSubject {
        +request()
    }
    class Proxy {
        -realSubject: RealSubject
        +request()
    }
    
	RealSubject ..|> Subject : implements
	Proxy ..|> Subject : implements
	Proxy --> RealSubject : uses

3. 代理模式的类型

  1. 静态代理:代理类在编译时手动创建。
  2. 动态代理:运行时通过反射动态生成(如 Java 的 Proxy 类)。
  3. 虚拟代理:延迟加载目标对象。
  4. 保护代理:控制访问权限。

4. Java 实现示例

以下是一个文件访问的代理模式示例,展示静态代理和动态代理。

4.1 抽象主题
1
2
3
4
// 抽象主题:文件操作
public interface FileService {
void readFile(String fileName);
}
4.2 真实主题
1
2
3
4
5
6
7
// 真实主题:文件服务实现
public class FileServiceImpl implements FileService {
@Override
public void readFile(String fileName) {
System.out.println("Reading file: " + fileName);
}
}
4.3 静态代理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 静态代理:添加日志功能
public class FileServiceProxy implements FileService {
private FileService realService;

public FileServiceProxy(FileService realService) {
this.realService = realService;
}

@Override
public void readFile(String fileName) {
System.out.println("Logging: Attempting to read file " + fileName);
realService.readFile(fileName);
System.out.println("Logging: File read completed");
}
}
4.4 动态代理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 动态代理:通过反射实现
public class DynamicProxyHandler implements InvocationHandler {
private Object target;

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

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Dynamic Logging: Before " + method.getName());
Object result = method.invoke(target, args);
System.out.println("Dynamic Logging: After " + method.getName());
return result;
}

public static FileService createProxy(FileService target) {
return (FileService) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new DynamicProxyHandler(target)
);
}
}
4.5 客户端代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Main {
public static void main(String[] args) {
// 静态代理
FileService realService = new FileServiceImpl();
FileService staticProxy = new FileServiceProxy(realService);
staticProxy.readFile("data.txt");
/*
输出:
Logging: Attempting to read file data.txt
Reading file: data.txt
Logging: File read completed
*/

// 动态代理
FileService dynamicProxy = DynamicProxyHandler.createProxy(realService);
dynamicProxy.readFile("config.txt");
/*
输出:
Dynamic Logging: Before readFile
Reading file: config.txt
Dynamic Logging: After readFile
*/
}
}

5. 工作原理

  1. 客户端通过代理对象调用方法。
  2. 代理执行额外逻辑(如日志、权限检查),然后委托给真实主题。
  3. 真实主题完成核心操作,返回结果。

6. 优点

  • 功能增强:无需修改目标代码即可添加功能。
  • 控制访问:代理可限制或延迟对目标的访问。
  • 灵活性:动态代理支持运行时调整。

7. 缺点

  • 性能开销:代理层增加调用 overhead,尤其是动态代理。
  • 复杂度:实现动态代理需熟悉反射。
  • 静态局限:静态代理需为每个目标手动编写代理类。

8. 与装饰者模式的对比

特性 代理模式 装饰者模式
目的 控制访问 增强功能
关注点 代理行为 对象功能
结构 单层代理 多层包装

9. 实际应用场景

  • 延迟加载:如 ORM 框架中的懒加载(Hibernate)。
  • 权限控制:限制对敏感资源的访问。
  • AOP:Spring 中通过代理实现切面(如日志、事务)。

10. 源码中的例子

Java 中的 java.lang.reflect.Proxy

1
2
3
4
5
6
7
8
9
Runnable proxy = (Runnable) Proxy.newProxyInstance(
Runnable.class.getClassLoader(),
new Class<?>[]{Runnable.class},
(p, method, args) -> {
System.out.println("Before run");
return null;
}
);
proxy.run();


11. 总结

  • 代理模式通过代理对象控制目标访问,支持静态和动态实现。
  • Java 实现静态代理用组合,动态代理用反射。
  • UML 图展示了代理与真实主题的关系。

行为型模式(Behavioral Patterns)

责任链(Chain of Responsibility)

我假设你指的是“责任链模式”(Chain of Responsibility Pattern),可能是“责任链”中的简写或输入问题。责任链模式是一种行为型设计模式,它将请求的处理者组织成一个链,请求沿着链传递,直到某个处理者处理它或链结束。这种模式解耦了请求的发送者和接收者,增强了灵活性。

以下是用 Java 实现责任链模式的详细讲解,包括定义、结构、代码示例及其优缺点。


1. 责任链模式的定义

  • 核心思想:将请求的处理者组成一个链,请求沿链传递,每个处理者决定处理或转发。
  • 目的
    • 解耦请求发送者和处理者。
    • 动态调整处理顺序或责任分配。
  • 适用场景
    • 请求有多个处理者,但处理者不确定。
    • 需要动态指定处理流程。
    • 处理逻辑分层或条件化。

2. 责任链模式的结构

  • 抽象处理者(Handler):定义处理请求的接口和链的链接方法。
  • 具体处理者(Concrete Handler):实现处理逻辑,决定处理或传递请求。
  • 客户端(Client):发起请求并交给链的起点。
Mermaid UML 图
classDiagram
    class Handler {
        -next: Handler
        +setNext(Handler)
        +handleRequest(request)
    }
    class ConcreteHandlerA {
        +handleRequest(request)
    }
    class ConcreteHandlerB {
        +handleRequest(request)
    }
    class Client
    
    ConcreteHandlerA ..|> Handler : implements
    ConcreteHandlerB ..|> Handler : implements
    Handler o--> Handler : next
    Client --> Handler : sends to

3. Java 实现示例

以下是一个日志处理的责任链模式示例,根据日志级别分发处理。

3.1 抽象处理者
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 抽象处理者:日志处理器
public abstract class Logger {
protected Logger nextLogger; // 下一个处理者
protected int level; // 处理级别

public void setNext(Logger nextLogger) {
this.nextLogger = nextLogger;
}

public void logMessage(int level, String message) {
if (this.level <= level) {
write(message); // 当前处理者处理
}
if (nextLogger != null) {
nextLogger.logMessage(level, message); // 传递给下一个
}
}

protected abstract void write(String message);
}
3.2 具体处理者
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 具体处理者:控制台日志
public class ConsoleLogger extends Logger {
public ConsoleLogger(int level) {
this.level = level;
}

@Override
protected void write(String message) {
System.out.println("Console Logger: " + message);
}
}

// 具体处理者:文件日志
public class FileLogger extends Logger {
public FileLogger(int level) {
this.level = level;
}

@Override
protected void write(String message) {
System.out.println("File Logger: " + message);
}
}

// 具体处理者:错误日志
public class ErrorLogger extends Logger {
public ErrorLogger(int level) {
this.level = level;
}

@Override
protected void write(String message) {
System.out.println("Error Logger: " + message);
}
}
3.3 客户端代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Main {
public static final int INFO = 1;
public static final int DEBUG = 2;
public static final int ERROR = 3;

public static void main(String[] args) {
// 创建处理者
Logger consoleLogger = new ConsoleLogger(INFO);
Logger fileLogger = new FileLogger(DEBUG);
Logger errorLogger = new ErrorLogger(ERROR);

// 构建责任链
consoleLogger.setNext(fileLogger);
fileLogger.setNext(errorLogger);

// 发送请求
consoleLogger.logMessage(INFO, "This is an info message");
consoleLogger.logMessage(DEBUG, "This is a debug message");
consoleLogger.logMessage(ERROR, "This is an error message");
}
}

输出

1
2
3
4
5
6
Console Logger: This is an info message
Console Logger: This is a debug message
File Logger: This is a debug message
Console Logger: This is an error message
File Logger: This is an error message
Error Logger: This is an error message
- 说明:消息按级别沿链传递,符合条件的处理者输出日志。


4. 工作原理

  1. 客户端构建责任链,设置处理者顺序。
  2. 请求从链头开始传递,每个处理者检查是否处理。
  3. 若当前处理者不处理,则传递给下一个,直到链结束。

5. 优点

  • 解耦:请求发送者无需知道具体处理者。
  • 灵活性:可动态调整链的结构和顺序。
  • 扩展性:易于添加新处理者。

6. 缺点

  • 不确定性:请求可能未被处理(链尾无匹配)。
  • 性能开销:链过长可能影响效率。
  • 调试复杂:多处理者时难以跟踪流程。

7. 与装饰者模式的对比

特性 责任链模式 装饰者模式
目的 分担责任 增强功能
传递 条件传递 递归调用
结果 单处理或无处理 层层增强

8. 实际应用场景

  • Servlet 过滤器:请求沿 Filter 链处理。
  • 日志框架:如 SLF4J 的日志级别处理。
  • 事件处理:GUI 事件传递。

9. 源码中的例子

Java 中的 javax.servlet.FilterChain

1
2
3
4
5
6
7
8
public class MyFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
System.out.println("Before filter");
chain.doFilter(request, response); // 传递给下一个
System.out.println("After filter");
}
}


10. 总结

  • 责任链模式通过链式结构分担请求处理,解耦发送者和接收者。
  • Java 实现依赖抽象类和链式引用,支持动态调整。
  • UML 图展示了处理者的链接关系。

命令(Command)

命令模式(Command Pattern)是一种行为型设计模式,它将请求封装成一个对象,从而允许参数化客户端、记录请求日志、支持撤销操作以及将请求排队或延迟执行。命令模式通过解耦请求的发送者和接收者,增强了系统的灵活性。

以下是用 Java 实现命令模式的详细讲解,包括定义、结构、代码示例及其优缺点。


1. 命令模式的定义

  • 核心思想:将请求封装为命令对象,分离请求的发起者(Invoker)和执行者(Receiver)。
  • 目的
    • 解耦调用者和执行逻辑。
    • 支持请求的存储、撤销、重做等操作。
  • 适用场景
    • 需要参数化操作(如菜单、按钮)。
    • 支持事务或回滚。
    • 请求需要排队或延迟执行。

2. 命令模式的结构

  • 抽象命令(Command):定义命令的接口,通常包含执行方法。
  • 具体命令(Concrete Command):实现命令接口,关联接收者并调用其操作。
  • 接收者(Receiver):执行具体操作的类。
  • 调用者(Invoker):持有命令对象并触发执行。
  • 客户端(Client):创建命令并配置调用者。
Mermaid UML 图
classDiagram
    class Command {
        +execute()
    }
    class ConcreteCommand {
        -receiver: Receiver
        +execute()
    }
    class Receiver {
        +action()
    }
    class Invoker {
        -command: Command
        +setCommand(Command)
        +executeCommand()
    }
    class Client
    
    ConcreteCommand ..|> Command : implements
    ConcreteCommand --> Receiver : uses
    Invoker --> Command : holds
    Client --> Invoker : configures
    Client --> ConcreteCommand : creates

3. Java 实现示例

以下是一个控制灯光开关的命令模式示例。

3.1 接收者
1
2
3
4
5
6
7
8
9
10
// 接收者:灯
public class Light {
public void turnOn() {
System.out.println("Light is ON");
}

public void turnOff() {
System.out.println("Light is OFF");
}
}
3.2 抽象命令
1
2
3
4
// 抽象命令
public interface Command {
void execute();
}
3.3 具体命令
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 具体命令:开灯
public class LightOnCommand implements Command {
private Light light;

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

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

// 具体命令:关灯
public class LightOffCommand implements Command {
private Light light;

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

@Override
public void execute() {
light.turnOff();
}
}
3.4 调用者
1
2
3
4
5
6
7
8
9
10
11
12
// 调用者:遥控器
public class RemoteControl {
private Command command;

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

public void pressButton() {
command.execute();
}
}
3.5 客户端代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Main {
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(); // 输出: Light is ON

// 执行关灯
remote.setCommand(lightOff);
remote.pressButton(); // 输出: Light is OFF
}
}

4. 工作原理

  1. 客户端创建接收者和命令,将命令与接收者关联。
  2. 将命令注入调用者(RemoteControl)。
  3. 调用者触发命令执行,命令调用接收者的方法。

5. 优点

  • 解耦:请求发起者和执行者分离。
  • 扩展性:易于添加新命令。
  • 支持撤销:可通过添加 undo() 方法实现回滚。
  • 可记录:命令对象可存储用于日志或重做。

6. 缺点

  • 类数量增加:每个操作需定义新命令类。
  • 复杂度:简单场景可能显得过于繁琐。

7. 支持撤销的扩展

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public interface Command {
void execute();
void undo();
}

public class LightOnCommand implements Command {
private Light light;

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

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

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

public class Main {
public static void main(String[] args) {
Light light = new Light();
Command lightOn = new LightOnCommand(light);
RemoteControl remote = new RemoteControl();

remote.setCommand(lightOn);
remote.pressButton(); // 输出: Light is ON
((LightOnCommand) lightOn).undo(); // 输出: Light is OFF
}
}

8. 与策略模式的对比

特性 命令模式 策略模式
目的 封装请求 封装算法
关注点 操作执行 行为选择
结构 命令+接收者 上下文+策略

9. 实际应用场景

  • GUI 按钮:按钮点击触发命令。
  • 事务管理:支持回滚的操作队列。
  • 宏命令:组合多个命令执行。

10. 源码中的例子

Java 中的 Runnable

1
2
Thread thread = new Thread(() -> System.out.println("Running"));
thread.start(); // 封装并执行命令


11. 总结

  • 命令模式将请求封装为对象,解耦发起者和执行者。
  • Java 实现依赖接口和组合,支持扩展功能如撤销。
  • UML 图展示了命令、接收者和调用者的关系。

如果需要宏命令示例(组合多个命令)或调整 UML,请告诉我!

解释器(Interpreter)

解释器模式(Interpreter Pattern)是一种行为型设计模式,用于定义一种语言的语法,并通过解释器对象来解析和执行这种语言的语句。它特别适合处理简单的语法规则或领域特定语言(DSL),将复杂的表达式分解为可递归解释的对象。

以下是对解释器模式的详细讲解,包括定义、结构、Java 实现示例及其优缺点。


1. 解释器模式的定义

  • 核心思想:为特定语言定义语法表示,并通过递归解释器解析语句。
  • 目的
    • 将语法规则封装为对象,便于处理复杂的表达式。
    • 支持动态解释和执行语言规则。
  • 适用场景
    • 解析简单的脚本或表达式(如数学表达式、查询语言)。
    • 处理规则引擎或自定义语言。

2. 解释器模式的结构

  • 抽象表达式(Abstract Expression):定义解释方法的接口。
  • 终结符表达式(Terminal Expression):表示语法中的基本元素,直接实现解释逻辑。
  • 非终结符表达式(Non-Terminal Expression):表示复合规则,递归调用子表达式解释。
  • 上下文(Context):存储解释过程中需要的全局信息(如变量值)。
  • 客户端(Client):构建表达式树并调用解释器。
Mermaid UML 图
classDiagram
    class AbstractExpression {
        +interpret(context) int
    }
    class TerminalExpression {
        -value: int
        +interpret(context) int
    }
    class NonTerminalExpression {
        -left: AbstractExpression
        -right: AbstractExpression
        +interpret(context) int
    }
    class Context {
        -data: Map
        +getValue(key) int
        +setValue(key, value)
    }
    class Client
    
    TerminalExpression ..|> AbstractExpression : implements
	NonTerminalExpression ..|> AbstractExpression : implements
    NonTerminalExpression o--> "2" AbstractExpression : contains
    Client --> AbstractExpression : uses
    Client --> Context : uses

3. Java 实现示例

以下是一个解析简单加减法的解释器模式示例。

3.1 上下文类
1
2
3
4
5
6
7
8
9
10
11
12
// 上下文:存储变量值
public class Context {
private Map<String, Integer> variables = new HashMap<>();

public void setVariable(String name, int value) {
variables.put(name, value);
}

public int getVariable(String name) {
return variables.getOrDefault(name, 0);
}
}
3.2 抽象表达式
1
2
3
4
// 抽象表达式
public interface Expression {
int interpret(Context context);
}
3.3 终结符表达式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 终结符表达式:数字或变量
public class NumberExpression implements Expression {
private String value; // 可以是数字或变量名

public NumberExpression(String value) {
this.value = value;
}

@Override
public int interpret(Context context) {
try {
return Integer.parseInt(value); // 直接数字
} catch (NumberFormatException e) {
return context.getVariable(value); // 变量名
}
}
}
3.4 非终结符表达式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 非终结符表达式:加法
public class AddExpression implements Expression {
private Expression left;
private Expression right;

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

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

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

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

@Override
public int interpret(Context context) {
return left.interpret(context) - right.interpret(context);
}
}
3.5 客户端代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Main {
public static void main(String[] args) {
// 创建上下文并设置变量
Context context = new Context();
context.setVariable("x", 10);
context.setVariable("y", 5);

// 构建表达式:x + y - 3
Expression expression = new SubtractExpression(
new AddExpression(
new NumberExpression("x"),
new NumberExpression("y")
),
new NumberExpression("3")
);

// 解释执行
int result = expression.interpret(context);
System.out.println("Result: " + result); // 输出: Result: 12 (10 + 5 - 3)
}
}

4. 工作原理

  1. 客户端构建表达式树(如 x + y - 3)。
  2. 将上下文(变量值)传递给表达式。
  3. 表达式递归调用 interpret() 方法,计算结果:
    • 终结符直接返回值。
    • 非终结符组合子表达式的结果。

5. 优点

  • 灵活性:易于扩展新语法规则(新增表达式类)。
  • 模块化:语法规则封装为独立对象。
  • 可解释性:支持动态解析语言。

6. 缺点

  • 复杂度高:大量表达式类增加系统复杂性。
  • 性能开销:递归解析复杂表达式可能效率低。
  • 适用性有限:仅适合简单语言,复杂语法(如完整编程语言)不实用。

7. 与其他模式的对比

特性 解释器模式 命令模式
目的 解析语法 封装请求
结构 表达式树 命令对象
执行 递归解释 单次调用

8. 实际应用场景

  • 数学表达式:计算器程序解析公式。
  • 规则引擎:解释业务规则。
  • 脚本语言:简单 DSL 的解析。

9. 源码中的例子

Java 中的正则表达式(java.util.regex):

1
2
3
4
5
Pattern pattern = Pattern.compile("\\d+");  // 定义语法
Matcher matcher = pattern.matcher("123");
if (matcher.matches()) {
System.out.println("Valid number"); // 解释执行
}


10. 总结

  • 解释器模式通过对象表示语法规则,递归解析表达式。
  • Java 实现依赖接口和表达式树,支持动态解释。
  • UML 图展示了表达式和上下文的关系。

迭代器(Iterator)

迭代器模式(Iterator Pattern)是一种行为型设计模式,它提供了一种方法来顺序访问聚合对象(如集合、列表或数组)的元素,而无需暴露其内部结构。迭代器模式将遍历逻辑从集合中分离出来,增强了封装性和灵活性。

以下是用 Java 实现迭代器模式的详细讲解,包括定义、结构、代码示例及其优缺点。


1. 迭代器模式的定义

  • 核心思想:通过迭代器对象提供对集合元素的顺序访问,隐藏集合的实现细节。
  • 目的
    • 统一访问不同类型的集合。
    • 分离集合的遍历逻辑和数据存储。
  • 适用场景
    • 需要遍历复杂数据结构(如树、图)。
    • 集合类型多样但需一致访问。
    • 增强集合的封装性。

2. 迭代器模式的结构

  • 抽象迭代器(Iterator):定义遍历接口,如 hasNext()next()
  • 具体迭代器(Concrete Iterator):实现迭代器接口,跟踪遍历位置。
  • 抽象聚合(Aggregate):定义创建迭代器的方法。
  • 具体聚合(Concrete Aggregate):实现聚合接口,存储数据并返回迭代器。
  • 客户端(Client):使用迭代器遍历集合。
Mermaid UML 图
classDiagram
    class Iterator {
        +hasNext() boolean
        +next() Object
    }
    class ConcreteIterator {
        -aggregate: ConcreteAggregate
        -index: int
        +hasNext() boolean
        +next() Object
    }
    class Aggregate {
        +createIterator() Iterator
    }
    class ConcreteAggregate {
        -items: List
        +createIterator() Iterator
    }
    class Client
    
	ConcreteIterator ..|> Iterator : implements
	ConcreteAggregate ..|> Aggregate : implements
	ConcreteAggregate --> ConcreteIterator : creates
	ConcreteIterator --> ConcreteAggregate : references
	Client --> Iterator : uses
	Client --> Aggregate : uses

3. Java 实现示例

以下是一个简单列表的迭代器模式示例。

3.1 抽象迭代器
1
2
3
4
5
// 抽象迭代器
public interface Iterator {
boolean hasNext();
Object next();
}
3.2 抽象聚合
1
2
3
4
// 抽象聚合
public interface Collection {
Iterator createIterator();
}
3.3 具体聚合
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 具体聚合:自定义列表
public class MyList implements Collection {
private String[] items;

public MyList(String[] items) {
this.items = items;
}

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

public String get(int index) {
return items[index];
}

public int size() {
return items.length;
}
}
3.4 具体迭代器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 具体迭代器
public class ListIterator implements Iterator {
private MyList list;
private int index;

public ListIterator(MyList list) {
this.list = list;
this.index = 0;
}

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

@Override
public Object next() {
if (hasNext()) {
return list.get(index++);
}
return null;
}
}
3.5 客户端代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Main {
public static void main(String[] args) {
String[] data = {"Apple", "Banana", "Cherry"};
MyList list = new MyList(data);
Iterator iterator = list.createIterator();

while (iterator.hasNext()) {
System.out.println(iterator.next());
}
/*
输出:
Apple
Banana
Cherry
*/
}
}

4. 工作原理

  1. 客户端创建具体聚合(如 MyList)并获取迭代器。
  2. 使用迭代器的 hasNext()next() 方法顺序访问元素。
  3. 迭代器内部维护遍历状态,隐藏聚合的实现细节。

5. 优点

  • 封装性:隐藏集合内部结构,保护数据。
  • 统一性:为不同集合提供一致的遍历接口。
  • 解耦:分离遍历逻辑和集合实现。

6. 缺点

  • 复杂度增加:为简单集合引入迭代器可能显得多余。
  • 功能有限:标准迭代器不支持修改集合或双向遍历。
  • 性能开销:动态创建迭代器可能增加少量开销。

7. 与其他模式的对比

特性 迭代器模式 访问者模式
目的 顺序访问 操作复杂结构
关注点 遍历集合 扩展功能
控制权 客户端控制遍历 访问者控制操作

8. 实际应用场景

  • 集合遍历:Java 集合框架中的 Iterator
  • 树结构:遍历树节点。
  • 数据库查询:结果集迭代。

9. 源码中的例子

Java 中的 java.util.Iterator

1
2
3
4
5
List<String> list = Arrays.asList("A", "B", "C");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}


10. 总结

  • 迭代器模式通过分离遍历逻辑提供集合的顺序访问。
  • Java 实现依赖接口和具体迭代器,支持灵活遍历。
  • UML 图展示了迭代器与聚合的关系。

如果需要增强版(如支持 remove() 或双向迭代器)或调整 UML,请告诉我!

中介者(Mediator)

中介者模式(Mediator Pattern)是一种行为型设计模式,它通过引入一个中介者对象来封装一组对象之间的交互,从而减少对象间的直接耦合。中介者模式将多对多的通信转换为一对多的通信,提升系统的灵活性和可维护性。

以下是用 Java 实现中介者模式的详细讲解,包括定义、结构、代码示例及其优缺点。


1. 中介者模式的定义

  • 核心思想:通过中介者协调对象间的通信,降低耦合。
  • 目的
    • 解耦复杂对象间的直接依赖。
    • 集中管理交互逻辑。
  • 适用场景
    • 对象间存在复杂的多对多关系。
    • 需要协调多个组件的行为。
    • 简化对象间的通信。

2. 中介者模式的结构

  • 抽象中介者(Mediator):定义中介者接口,声明通信方法。
  • 具体中介者(Concrete Mediator):实现中介逻辑,协调同事对象。
  • 抽象同事(Colleague):定义同事类的接口,与中介者交互。
  • 具体同事(Concrete Colleague):实现同事接口,通过中介者通信。
  • 客户端(Client):创建同事和中介者,发起交互。
Mermaid UML 图
classDiagram
    class Mediator {
        +notify(sender: Colleague, event)
    }
    class ConcreteMediator {
        -colleagueA: ColleagueA
        -colleagueB: ColleagueB
        +notify(sender: Colleague, event)
    }
    class Colleague {
        +send(event)
        +receive(event)
    }
    class ColleagueA {
        -mediator: Mediator
        +send(event)
        +receive(event)
    }
    class ColleagueB {
        -mediator: Mediator
        +send(event)
        +receive(event)
    }
    
    ConcreteMediator ..|> Mediator : implements
    ColleagueA ..|> Colleague : implements
    ColleagueB ..|> Colleague : implements
    ConcreteMediator --> ColleagueA : coordinates
    ConcreteMediator --> ColleagueB : coordinates
    ColleagueA --> Mediator : uses
    ColleagueB --> Mediator : uses

3. Java 实现示例

以下是一个聊天室的中介者模式示例,用户通过中介者发送和接收消息。

3.1 抽象中介者
1
2
3
4
// 抽象中介者
public interface ChatMediator {
void sendMessage(String message, User sender);
}
3.2 具体中介者
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 具体中介者:聊天室
public class ChatRoom implements ChatMediator {
private List<User> users = new ArrayList<>();

public void addUser(User user) {
users.add(user);
}

@Override
public void sendMessage(String message, User sender) {
for (User user : users) {
if (user != sender) { // 不发给自己
user.receive(message);
}
}
}
}
3.3 抽象同事
1
2
3
4
5
6
7
8
9
10
11
12
13
// 抽象同事:用户
public abstract class User {
protected ChatMediator mediator;
protected String name;

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

public abstract void send(String message);
public abstract void receive(String message);
}
3.4 具体同事
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 具体同事:普通用户
public class ChatUser extends User {
public ChatUser(ChatMediator mediator, String name) {
super(mediator, name);
}

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

@Override
public void receive(String message) {
System.out.println(name + " receives: " + message);
}
}
3.5 客户端代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class Main {
public static void main(String[] args) {
ChatRoom chatRoom = new ChatRoom();

User alice = new ChatUser(chatRoom, "Alice");
User bob = new ChatUser(chatRoom, "Bob");
User charlie = new ChatUser(chatRoom, "Charlie");

chatRoom.addUser(alice);
chatRoom.addUser(bob);
chatRoom.addUser(charlie);

alice.send("Hello everyone!");
/*
输出:
Alice sends: Hello everyone!
Bob receives: Hello everyone!
Charlie receives: Hello everyone!
*/

bob.send("Hi Alice!");
/*
输出:
Bob sends: Hi Alice!
Alice receives: Hi Alice!
Charlie receives: Hi Alice!
*/
}
}

4. 工作原理

  1. 客户端创建中介者和同事对象,注册同事到中介者。
  2. 同事通过中介者发送消息(send),中介者协调分发(notify)。
  3. 其他同事接收消息(receive),完成通信。

5. 优点

  • 解耦:减少同事间的直接依赖,降低耦合。
  • 集中控制:交互逻辑集中在中介者,便于管理。
  • 灵活性:易于修改通信规则。

6. 缺点

  • 中介者复杂性:中介者可能变得臃肿,难以维护。
  • 性能开销:大量对象交互时,中介者可能成为瓶颈。
  • 单点故障:中介者失败影响整个系统。

7. 与观察者模式的对比

特性 中介者模式 观察者模式
通信方式 通过中介者协调 发布-订阅
耦合 同事依赖中介者 观察者依赖主题
控制权 中介者集中控制 主题广播

8. 实际应用场景

  • GUI 框架:控件间通过对话框协调。
  • 聊天系统:用户通过服务器通信。
  • 航空管制:飞机通过塔台协调。

9. 源码中的例子

Java 中的 java.util.Timer(简化中介者):

1
2
Timer timer = new Timer();
timer.schedule(new TimerTask() { public void run() { System.out.println("Task"); } }, 1000);


10. 总结

  • 中介者模式通过中介者封装对象交互,降低耦合。
  • Java 实现依赖接口和组合,支持集中管理通信。
  • UML 图展示了中介者与同事的关系。

如果需要更复杂示例(如支持事件类型)或调整 UML,请告诉我!

备忘录(Memento)

备忘录模式(Memento Pattern)是一种行为型设计模式,用于捕获并保存对象的内部状态,以便在需要时恢复到该状态,而不破坏对象的封装性。它常用于实现撤销(Undo)功能或状态快照。

以下是用 Java 实现备忘录模式的详细讲解,包括定义、结构、代码示例及其优缺点。


1. 备忘录模式的定义

  • 核心思想:通过备忘录对象保存对象状态,并在需要时恢复。
  • 目的
    • 保存和恢复对象的状态。
    • 保持封装性,避免暴露内部细节。
  • 适用场景
    • 需要撤销或回滚操作。
    • 保存历史状态(如游戏存档)。
    • 状态快照管理。

2. 备忘录模式的结构

  • 原发器(Originator):需要保存和恢复状态的对象,创建和使用备忘录。
  • 备忘录(Memento):存储原发器状态的对象,通常只提供有限访问。
  • 管理者(Caretaker):负责保存备忘录,但不修改其内容。
  • 客户端(Client):触发状态保存和恢复。
Mermaid UML 图
classDiagram
    class Originator {
        -state: State
        +createMemento() Memento
        +setMemento(Memento)
        +setState(state)
        +getState() State
    }
    class Memento {
        -state: State
        +getState() State
    }
    class Caretaker {
        -memento: Memento
        +setMemento(Memento)
        +getMemento() Memento
    }
    class Client
    
    Originator --> Memento : creates/uses
    Caretaker --> Memento : holds
    Client --> Originator : uses
    Client --> Caretaker : uses

3. Java 实现示例

以下是一个文本编辑器的备忘录模式示例,支持保存和恢复文本状态。

3.1 备忘录类
1
2
3
4
5
6
7
8
9
10
11
12
// 备忘录:保存状态
public class Memento {
private String state;

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

public String getState() {
return state;
}
}
3.2 原发器类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 原发器:文本编辑器
public class TextEditor {
private String state;

public void setState(String state) {
this.state = state;
System.out.println("Current state: " + state);
}

public String getState() {
return state;
}

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

public void setMemento(Memento memento) {
this.state = memento.getState();
System.out.println("Restored state: " + state);
}
}
3.3 管理者类
1
2
3
4
5
6
7
8
9
10
11
12
// 管理者:保存备忘录
public class History {
private Memento memento;

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

public Memento getMemento() {
return memento;
}
}
3.4 客户端代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Main {
public static void main(String[] args) {
TextEditor editor = new TextEditor();
History history = new History();

// 设置初始状态
editor.setState("Hello"); // 输出: Current state: Hello

// 保存状态
history.setMemento(editor.createMemento());

// 修改状态
editor.setState("Hello World"); // 输出: Current state: Hello World

// 恢复状态
editor.setMemento(history.getMemento()); // 输出: Restored state: Hello
}
}

4. 工作原理

  1. 客户端通过原发器(TextEditor)设置状态。
  2. 调用 createMemento() 保存状态到备忘录,交给管理者(History)存储。
  3. 修改状态后,通过 setMemento() 从备忘录恢复之前的状态。

5. 优点

  • 封装性:备忘录隐藏原发器的内部状态。
  • 支持撤销:方便实现回滚功能。
  • 状态隔离:管理者不修改备忘录内容。

6. 缺点

  • 内存开销:保存大量状态可能消耗内存。
  • 复杂度:增加备忘录和管理类,提高系统复杂性。
  • 状态管理:多状态时需额外设计存储结构。

7. 与命令模式的对比

特性 备忘录模式 命令模式
目的 保存/恢复状态 封装请求
关注点 状态管理 操作执行
撤销 通过状态恢复 通过命令回滚

8. 实际应用场景

  • 文本编辑器:撤销和重做功能。
  • 游戏:保存和加载游戏进度。
  • 数据库:事务回滚。

9. 源码中的例子

Java 中的 java.util.Date(简化备忘录思想):

1
2
3
Date date = new Date();
long time = date.getTime(); // 保存状态
date.setTime(time); // 恢复状态


10. 总结

  • 备忘录模式通过备忘录保存和恢复对象状态,支持撤销。
  • Java 实现依赖原发器创建和使用备忘录,管理者存储。
  • UML 图展示了原发器、备忘录和管理者的关系。

如果需要多状态管理示例(如历史记录列表)或调整 UML,请告诉我!

观察者(Observer)

观察者模式(Observer Pattern)是一种行为型设计模式,它定义了对象之间的一对多依赖关系,使得当一个对象的状态发生变化时,所有依赖它的对象都会自动收到通知并更新。观察者模式也被称为“发布-订阅模式”,广泛用于事件处理和状态监控。

以下是用 Java 实现观察者模式的详细讲解,包括定义、结构、代码示例及其优缺点。


1. 观察者模式的定义

  • 核心思想:通过主题(Subject)维护观察者(Observer)列表,当主题状态变化时通知所有观察者。
  • 目的
    • 解耦主体和依赖对象。
    • 实现状态变化的自动广播。
  • 适用场景
    • 事件监听(如 GUI 按钮点击)。
    • 数据变化通知(如股票价格)。
    • 发布-订阅系统。

2. 观察者模式的结构

  • 抽象主题(Subject):维护观察者列表,提供添加、移除和通知方法。
  • 具体主题(Concrete Subject):实现主题接口,存储状态并触发通知。
  • 抽象观察者(Observer):定义更新接口。
  • 具体观察者(Concrete Observer):实现更新逻辑,响应主题变化。
  • 客户端(Client):创建主题和观察者,发起交互。
Mermaid UML 图
classDiagram
    class Subject {
        +attach(Observer)
        +detach(Observer)
        +notify()
    }
    class ConcreteSubject {
        -state: State
        -observers: List<Observer>
        +attach(Observer)
        +detach(Observer)
        +notify()
        +setState(state)
        +getState() State
    }
    class Observer {
        +update()
    }
    class ConcreteObserver {
        -subject: Subject
        +update()
    }
    
    ConcreteSubject ..|> Subject : implements
    ConcreteObserver ..|> Observer : implements
    ConcreteSubject o--> "many" Observer : notifies
    ConcreteObserver --> Subject : observes

3. Java 实现示例

以下是一个天气站的观察者模式示例,天气变化时通知显示器。

3.1 抽象观察者
1
2
3
4
// 抽象观察者
public interface Observer {
void update(float temperature);
}
3.2 抽象主题
1
2
3
4
5
6
// 抽象主题
public interface WeatherStation {
void addObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}
3.3 具体主题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 具体主题:天气站
public class ConcreteWeatherStation implements WeatherStation {
private List<Observer> observers = new ArrayList<>();
private float temperature;

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

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

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

public void setTemperature(float temperature) {
this.temperature = temperature;
System.out.println("Temperature changed to: " + temperature);
notifyObservers();
}
}
3.4 具体观察者
1
2
3
4
5
6
7
8
9
10
11
12
13
// 具体观察者:温度显示器
public class TemperatureDisplay implements Observer {
private String name;

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

@Override
public void update(float temperature) {
System.out.println(name + " displays temperature: " + temperature);
}
}
3.5 客户端代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class Main {
public static void main(String[] args) {
ConcreteWeatherStation station = new ConcreteWeatherStation();

Observer display1 = new TemperatureDisplay("Display 1");
Observer display2 = new TemperatureDisplay("Display 2");

station.addObserver(display1);
station.addObserver(display2);

station.setTemperature(25.0f);
/*
输出:
Temperature changed to: 25.0
Display 1 displays temperature: 25.0
Display 2 displays temperature: 25.0
*/

station.setTemperature(30.0f);
/*
输出:
Temperature changed to: 30.0
Display 1 displays temperature: 30.0
Display 2 displays temperature: 30.0
*/
}
}

4. 工作原理

  1. 客户端创建主题和观察者,将观察者注册到主题。
  2. 主题状态变化(setTemperature)时,调用 notifyObservers
  3. 所有观察者收到通知,执行 update 更新状态。

5. 优点

  • 解耦:主题和观察者松耦合,仅通过接口交互。
  • 动态性:支持运行时添加或移除观察者。
  • 广播:自动通知所有依赖对象。

6. 缺点

  • 性能开销:大量观察者时通知可能变慢。
  • 内存泄漏:未移除的观察者可能导致引用残留。
  • 复杂性:多观察者时调试困难。

7. 与中介者模式的对比

特性 观察者模式 中介者模式
通信方式 广播 协调
耦合 观察者依赖主题 同事依赖中介者
控制权 主题主动通知 中介者集中控制

8. 实际应用场景

  • GUI 事件:按钮点击通知监听器。
  • 数据绑定:视图随模型变化更新。
  • 消息系统:发布-订阅模型。

9. 源码中的例子

Java 中的 java.util.ObservableObserver(已废弃,但为经典示例):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import java.util.Observable;

class Weather extends Observable {
private float temp;
public void setTemp(float temp) {
this.temp = temp;
setChanged();
notifyObservers(temp);
}
}

class Display implements java.util.Observer {
@Override
public void update(Observable o, Object arg) {
System.out.println("Temperature: " + arg);
}
}

public class Main {
public static void main(String[] args) {
Weather weather = new Weather();
weather.addObserver(new Display());
weather.setTemp(25.0f); // 输出: Temperature: 25.0
}
}

现代替代:java.beans.PropertyChangeSupport


10. 总结

  • 观察者模式通过主题广播状态变化,解耦依赖关系。
  • Java 实现依赖接口和列表管理,支持动态通知。
  • UML 图展示了主题与观察者的一对多关系。

如果需要推模式(推送数据)与拉模式(观察者拉取)的对比或调整 UML,请告诉我!

状态(State)

状态模式(State Pattern)是一种行为型设计模式,它允许对象在内部状态发生变化时改变其行为,使对象看起来像是改变了其类。状态模式通过将状态封装为独立的对象,简化复杂条件逻辑,提高代码的可维护性和扩展性。

以下是用 Java 实现状态模式的详细讲解,包括定义、结构、代码示例及其优缺点。


1. 状态模式的定义

  • 核心思想:将状态抽象为对象,对象根据状态切换行为。
  • 目的
    • 消除大量条件语句(如 if-else)。
    • 使状态转换逻辑清晰且可扩展。
  • 适用场景
    • 对象行为随状态变化。
    • 状态转换规则复杂。
    • 需要状态机(如订单流程)。

2. 状态模式的结构

  • 上下文(Context):持有当前状态,委托行为给状态对象。
  • 抽象状态(State):定义状态接口,声明行为方法。
  • 具体状态(Concrete State):实现状态接口,定义具体行为并处理状态转换。
  • 客户端(Client):创建上下文并触发操作。
Mermaid UML 图
classDiagram
    class Context {
        -state: State
        +setState(State)
        +request()
    }
    class State {
        +handle(Context)
    }
    class ConcreteStateA {
        +handle(Context)
    }
    class ConcreteStateB {
        +handle(Context)
    }
    
    ConcreteStateA ..|> State : implements
    ConcreteStateB ..|> State : implements
    Context --> State : uses
    ConcreteStateA --> Context : changes state
    ConcreteStateB --> Context : changes state

3. Java 实现示例

以下是一个订单状态机的状态模式示例,订单状态在待支付、已支付和已发货之间切换。

3.1 抽象状态
1
2
3
4
// 抽象状态
public interface OrderState {
void handle(Order order); // 处理订单
}
3.2 具体状态
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 具体状态:待支付
public class PendingState implements OrderState {
@Override
public void handle(Order order) {
System.out.println("Order is pending payment");
order.setState(new PaidState()); // 转换为已支付
}
}

// 具体状态:已支付
public class PaidState implements OrderState {
@Override
public void handle(Order order) {
System.out.println("Order has been paid");
order.setState(new ShippedState()); // 转换为已发货
}
}

// 具体状态:已发货
public class ShippedState implements OrderState {
@Override
public void handle(Order order) {
System.out.println("Order has been shipped");
// 不再转换状态
}
}
3.3 上下文
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 上下文:订单
public class Order {
private OrderState state;

public Order() {
this.state = new PendingState(); // 初始状态
}

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

public void process() {
state.handle(this); // 委托给当前状态
}
}
3.4 客户端代码
1
2
3
4
5
6
7
8
9
public class Main {
public static void main(String[] args) {
Order order = new Order();

order.process(); // 输出: Order is pending payment
order.process(); // 输出: Order has been paid
order.process(); // 输出: Order has been shipped
}
}

4. 工作原理

  1. 客户端创建上下文(Order),初始状态为 PendingState
  2. 调用 process(),上下文委托给当前状态对象执行。
  3. 状态对象处理请求并可切换上下文的状态(如从 PendingPaid)。

5. 优点

  • 清晰性:状态逻辑封装在独立类中,减少条件分支。
  • 扩展性:新增状态只需添加新类。
  • 符合开闭原则:修改行为不需更改上下文。

6. 缺点

  • 类数量增加:每个状态需定义新类。
  • 复杂度:小型系统使用状态模式可能显得繁琐。
  • 状态切换管理:转换逻辑分散在状态类中,可能难跟踪。

7. 与策略模式的对比

特性 状态模式 策略模式
目的 状态驱动行为 算法选择
切换 状态内部控制 客户端控制
关注点 状态转换 行为替换

8. 实际应用场景

  • 订单系统:状态流转(如待支付 → 已支付)。
  • 游戏角色:角色状态(如站立、行走、跳跃)。
  • 有限状态机:网络协议状态管理。

9. 源码中的例子

Java 中的 java.nio.channels.SelectionKey(状态相关):

1
2
SelectionKey key = channel.register(selector, SelectionKey.OP_READ);
// 根据状态执行不同操作


10. 总结

  • 状态模式通过状态对象改变行为,简化状态管理。
  • Java 实现依赖接口和状态切换,支持动态行为。
  • UML 图展示了上下文与状态的关系。

如果需要复杂状态机(如带条件转换)或调整 UML,请告诉我!

策略(Strategy)

策略模式(Strategy Pattern)是一种行为型设计模式,它定义了一系列算法或策略,将每个策略封装成独立的对象,使得这些策略可以在运行时动态切换。策略模式通过解耦上下文和具体策略,增强了代码的灵活性和可扩展性。

以下是用 Java 实现策略模式的详细讲解,包括定义、结构、代码示例及其优缺点。


1. 策略模式的定义

  • 核心思想:将算法或行为封装为策略对象,上下文根据需要选择策略。
  • 目的
    • 消除条件语句(如 if-else)。
    • 支持运行时动态替换行为。
  • 适用场景
    • 多个类似行为需动态选择。
    • 算法或策略可能频繁变化。
    • 需要隔离具体实现。

2. 策略模式的结构

  • 抽象策略(Strategy):定义策略接口,声明行为方法。
  • 具体策略(Concrete Strategy):实现策略接口,提供具体算法。
  • 上下文(Context):持有策略引用,调用策略执行行为。
  • 客户端(Client):创建上下文并配置策略。

# Mermaid UML 图

classDiagram
    class Strategy {
        +execute()
    }
    class ConcreteStrategyA {
        +execute()
    }
    class ConcreteStrategyB {
        +execute()
    }
    class Context {
        -strategy: Strategy
        +setStrategy(Strategy)
        +executeStrategy()
    }
    class Client
    
    ConcreteStrategyA ..|> Strategy : implements
    ConcreteStrategyB ..|> Strategy : implements
    Context --> Strategy : uses
    Client --> Context : uses
    Client --> Strategy : configures

3. Java 实现示例

以下是一个支付系统的策略模式示例,支持不同支付方式。

# 3.1 抽象策略

1
2
3
4
// 抽象策略:支付方式
public interface PaymentStrategy {
void pay(double amount);
}

# 3.2 具体策略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 具体策略:信用卡支付
public class CreditCardPayment implements PaymentStrategy {
private String cardNumber;

public CreditCardPayment(String cardNumber) {
this.cardNumber = cardNumber;
}

@Override
public void pay(double amount) {
System.out.println("Paid " + amount + " with credit card " + cardNumber);
}
}

// 具体策略:PayPal 支付
public class PayPalPayment implements PaymentStrategy {
private String email;

public PayPalPayment(String email) {
this.email = email;
}

@Override
public void pay(double amount) {
System.out.println("Paid " + amount + " with PayPal " + email);
}
}

# 3.3 上下文

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 上下文:购物车
public class ShoppingCart {
private PaymentStrategy paymentStrategy;

public void setPaymentStrategy(PaymentStrategy paymentStrategy) {
this.paymentStrategy = paymentStrategy;
}

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

# 3.4 客户端代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Main {
public static void main(String[] args) {
ShoppingCart cart = new ShoppingCart();

// 使用信用卡支付
PaymentStrategy creditCard = new CreditCardPayment("1234-5678-9012-3456");
cart.setPaymentStrategy(creditCard);
cart.checkout(100.0); // 输出: Paid 100.0 with credit card 1234-5678-9012-3456

// 切换到 PayPal 支付
PaymentStrategy payPal = new PayPalPayment("user@example.com");
cart.setPaymentStrategy(payPal);
cart.checkout(50.0); // 输出: Paid 50.0 with PayPal user@example.com
}
}

4. 工作原理

  1. 客户端创建上下文(ShoppingCart)和策略(如 CreditCardPayment)。
  2. 将策略注入上下文(setPaymentStrategy)。
  3. 调用上下文方法(checkout),执行当前策略的行为。

5. 优点

  • 灵活性:运行时动态切换策略。
  • 扩展性:新增策略只需添加新类。
  • 消除条件:避免大量 if-else 语句。

6. 缺点

  • 类数量增加:每个策略需定义新类。
  • 客户端复杂性:客户端需了解并选择策略。
  • 适用性有限:适合行为差异明显的情况。

7. 与状态模式的对比

特性 策略模式 状态模式
目的 算法选择 状态驱动行为
切换控制 客户端控制 状态内部控制
关注点 行为替换 状态转换

8. 实际应用场景

  • 排序算法:选择不同排序策略(如快排、冒泡)。
  • 支付系统:动态选择支付方式。
  • 游戏 AI:切换敌人行为策略。

9. 源码中的例子

Java 中的 Comparator

1
2
3
List<String> list = Arrays.asList("b", "a", "c");
Collections.sort(list, Comparator.naturalOrder()); // 自然顺序策略
Collections.sort(list, Comparator.reverseOrder()); // 逆序策略


10. 总结

  • 策略模式通过封装策略对象实现行为动态切换。
  • Java 实现依赖接口和组合,支持灵活替换。
  • UML 图展示了上下文与策略的关系。

如果需要更复杂示例(如结合工厂模式)或调整 UML,请告诉我!

模板方法(Template Method)

模板方法模式(Template Method Pattern)是一种行为型设计模式,它在抽象类中定义了一个操作的算法骨架,将某些步骤的具体实现推迟到子类中。模板方法模式通过固定的流程控制和灵活的实现分离,实现了代码复用和行为定制。

以下是用 Java 实现模板方法模式的详细讲解,包括定义、结构、代码示例及其优缺点。


1. 模板方法模式的定义

  • 核心思想:在抽象类中定义算法框架,子类实现具体步骤。
  • 目的
    • 复用通用算法结构。
    • 允许子类定制部分行为。
  • 适用场景
    • 多个类共享固定流程但细节不同。
    • 需要控制算法执行顺序。
    • 提取公共代码。

2. 模板方法模式的结构

  • 抽象类(Abstract Class):定义模板方法(算法骨架)和抽象步骤。
  • 具体类(Concrete Class):实现抽象步骤,完成具体逻辑。
  • 客户端(Client):调用模板方法执行算法。
Mermaid UML 图
classDiagram
    class AbstractClass {
        +templateMethod()
        #abstractStep1()
        #abstractStep2()
        +concreteStep()
    }
    class ConcreteClass {
        #abstractStep1()
        #abstractStep2()
    }
    class Client
    
    ConcreteClass --|> AbstractClass : extends
    Client --> AbstractClass : uses

3. Java 实现示例

以下是一个制作饮料的模板方法模式示例,流程固定但配方不同。

3.1 抽象类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 抽象类:饮料制作
public abstract class Beverage {
// 模板方法:定义制作流程
public final void prepareBeverage() {
boilWater(); // 固定步骤
brew(); // 抽象步骤,由子类实现
pourInCup(); // 固定步骤
if (customerWantsCondiments()) { // 钩子方法
addCondiments(); // 抽象步骤
}
}

// 固定步骤
private void boilWater() {
System.out.println("Boiling water");
}

private void pourInCup() {
System.out.println("Pouring into cup");
}

// 抽象步骤
protected abstract void brew();
protected abstract void addCondiments();

// 钩子方法(可选步骤)
protected boolean customerWantsCondiments() {
return true; // 默认添加调料
}
}
3.2 具体类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 具体类:咖啡
public class Coffee extends Beverage {
@Override
protected void brew() {
System.out.println("Brewing coffee grounds");
}

@Override
protected void addCondiments() {
System.out.println("Adding sugar and milk");
}
}

// 具体类:茶
public class Tea extends Beverage {
@Override
protected void brew() {
System.out.println("Steeping tea leaves");
}

@Override
protected void addCondiments() {
System.out.println("Adding lemon");
}

@Override
protected boolean customerWantsCondiments() {
return false; // 茶默认不加调料
}
}
3.3 客户端代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class Main {
public static void main(String[] args) {
Beverage coffee = new Coffee();
System.out.println("Making coffee:");
coffee.prepareBeverage();
/*
输出:
Making coffee:
Boiling water
Brewing coffee grounds
Pouring into cup
Adding sugar and milk
*/

Beverage tea = new Tea();
System.out.println("\nMaking tea:");
tea.prepareBeverage();
/*
输出:
Making tea:
Boiling water
Steeping tea leaves
Pouring into cup
*/
}
}

4. 工作原理

  1. 客户端创建具体类实例(如 CoffeeTea)。
  2. 调用模板方法(prepareBeverage),执行固定流程。
  3. 子类实现抽象步骤(brewaddCondiments),定制行为。

5. 优点

  • 代码复用:通用流程在抽象类中定义,避免重复。
  • 控制性:模板方法控制算法顺序,子类只实现细节。
  • 扩展性:新增行为只需添加子类。

6. 缺点

  • 类数量增加:每个新行为需定义新子类。
  • 继承依赖:子类必须继承抽象类,灵活性受限。
  • 复杂性:大型系统可能导致继承层次深。

7. 与策略模式的对比

特性 模板方法模式 策略模式
实现方式 继承 组合
控制权 抽象类控制流程 客户端选择策略
灵活性 固定流程 动态切换

8. 实际应用场景

  • ServletHttpServletservice 方法。
  • 游戏流程:固定游戏循环,子类实现细节。
  • 数据处理:通用处理框架,子类定制步骤。

9. 源码中的例子

Java 中的 AbstractList

1
2
3
4
5
public abstract class AbstractList<E> extends AbstractCollection<E> {
public void add(int index, E element) {
throw new UnsupportedOperationException(); // 子类实现
}
}
- 子类如 ArrayList 实现具体逻辑。


10. 总结

  • 模板方法模式通过抽象类定义算法框架,子类实现细节。
  • Java 实现结合继承和抽象方法,支持行为定制。
  • UML 图展示了抽象类与具体类的关系。

如果需要更复杂示例(如带钩子方法的多步骤流程)或调整 UML,请告诉我!

访问者(Visitor)

访问者模式(Visitor Pattern)是一种行为型设计模式,它允许在不修改对象结构的情况下,为对象添加新的操作。访问者模式通过将操作逻辑分离到访问者对象中,实现了数据结构与操作的解耦,特别适用于结构稳定但操作频繁变化的场景。

以下是用 Java 实现访问者模式的详细讲解,包括定义、结构、代码示例及其优缺点。


1. 访问者模式的定义

  • 核心思想:将操作封装到访问者对象中,对象结构接受访问者并调用其操作。
  • 目的
    • 分离数据结构与操作逻辑。
    • 支持动态添加新功能。
  • 适用场景
    • 对象结构稳定但操作多变。
    • 需要对复杂对象执行多种操作。
    • 数据结构与操作独立演化。

2. 访问者模式的结构

  • 抽象访问者(Visitor):定义对每个元素类的访问方法。
  • 具体访问者(Concrete Visitor):实现访问逻辑。
  • 抽象元素(Element):定义接受访问者的方法。
  • 具体元素(Concrete Element):实现接受方法,调用访问者操作。
  • 对象结构(Object Structure):包含元素集合,允许访问者遍历。
  • 客户端(Client):创建结构和访问者,发起访问。
Mermaid UML 图
classDiagram
    class Visitor {
        +visitElementA(ElementA)
        +visitElementB(ElementB)
    }
    class ConcreteVisitor {
        +visitElementA(ElementA)
        +visitElementB(ElementB)
    }
    class Element {
        +accept(Visitor)
    }
    class ElementA {
        +accept(Visitor)
    }
    class ElementB {
        +accept(Visitor)
    }
    class ObjectStructure {
        -elements: List<Element>
        +accept(Visitor)
    }
    class Client
    
    ConcreteVisitor ..|> Visitor : implements
    ElementA ..|> Element : implements
    ElementB ..|> Element : implements
    ObjectStructure o--> "many" Element : contains
    Element --> Visitor : accepts
    Client --> ObjectStructure : uses
    Client --> Visitor : uses

3. Java 实现示例

以下是一个计算文件大小的访问者模式示例,访问文件和文件夹。

3.1 抽象元素
1
2
3
4
// 抽象元素
public interface FileSystemElement {
void accept(FileSystemVisitor visitor);
}
3.2 具体元素
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// 具体元素:文件
public class File implements FileSystemElement {
private String name;
private int size;

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

public int getSize() {
return size;
}

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

// 具体元素:文件夹
public class Folder implements FileSystemElement {
private String name;
private List<FileSystemElement> children = new ArrayList<>();

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

public void add(FileSystemElement element) {
children.add(element);
}

public List<FileSystemElement> getChildren() {
return children;
}

@Override
public void accept(FileSystemVisitor visitor) {
visitor.visit(this);
}
}
3.3 抽象访问者
1
2
3
4
5
// 抽象访问者
public interface FileSystemVisitor {
void visit(File file);
void visit(Folder folder);
}
3.4 具体访问者
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 具体访问者:计算大小
public class SizeCalculatorVisitor implements FileSystemVisitor {
private int totalSize = 0;

@Override
public void visit(File file) {
totalSize += file.getSize();
}

@Override
public void visit(Folder folder) {
for (FileSystemElement element : folder.getChildren()) {
element.accept(this); // 递归访问子元素
}
}

public int getTotalSize() {
return totalSize;
}
}
3.5 对象结构(可选)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 对象结构
public class FileSystem {
private List<FileSystemElement> elements = new ArrayList<>();

public void addElement(FileSystemElement element) {
elements.add(element);
}

public void accept(FileSystemVisitor visitor) {
for (FileSystemElement element : elements) {
element.accept(visitor);
}
}
}
3.6 客户端代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Main {
public static void main(String[] args) {
File file1 = new File("doc1.txt", 100);
File file2 = new File("doc2.txt", 200);
Folder folder = new Folder("Documents");
folder.add(file1);
folder.add(file2);

FileSystem fileSystem = new FileSystem();
fileSystem.addElement(folder);

SizeCalculatorVisitor sizeVisitor = new SizeCalculatorVisitor();
fileSystem.accept(sizeVisitor);
System.out.println("Total size: " + sizeVisitor.getTotalSize()); // 输出: Total size: 300
}
}

4. 工作原理

  1. 客户端创建对象结构(FileSystem)和具体元素(FileFolder)。
  2. 创建访问者(SizeCalculatorVisitor)并传递给结构。
  3. 元素通过 accept 调用访问者的对应方法,执行操作。

5. 优点

  • 扩展性:新增操作只需添加新访问者。
  • 解耦:数据结构与操作分离。
  • 集中逻辑:操作集中在访问者中。

6. 缺点

  • 结构稳定要求:元素类变化会影响所有访问者。
  • 复杂度:增加访问者类,提高系统复杂性。
  • 访问限制:需暴露元素接口给访问者。

7. 与迭代器模式的对比

特性 访问者模式 迭代器模式
目的 扩展操作 顺序访问
关注点 操作逻辑 遍历逻辑
控制权 访问者控制 客户端控制

8. 实际应用场景

  • 编译器:语法树遍历和操作(如类型检查)。
  • 文档处理:对文档元素执行统计或格式化。
  • 游戏:对不同对象执行渲染或碰撞检测。

9. 源码中的例子

Java 中的 javax.swing.tree.TreeVisitor(概念类似):

1
2
3
4
// 伪代码示例
interface TreeVisitor {
void visit(Node node);
}


10. 总结

  • 访问者模式通过访问者分离操作和结构,支持动态功能扩展。
  • Java 实现依赖双重分派(acceptvisit),实现灵活操作。
  • UML 图展示了访问者与元素的关系。

如果需要更复杂示例(如多访问者)或调整 UML,请告诉我!

扩展模式(Extended Patterns)

依赖注入(Dependency Injection)

依赖注入模式(Dependency Injection Pattern,简称 DI)是一种设计模式,用于实现控制反转(Inversion of Control,IoC)。它通过将对象的依赖关系从对象内部创建转移到外部注入,从而解耦对象的创建和使用,提高代码的灵活性、可测试性和可维护性。依赖注入是 IoC 的一种具体实现方式。

以下是用 Java 实现依赖注入模式的详细讲解,包括定义、结构、代码示例及其优缺点。


1. 依赖注入模式的定义

  • 核心思想:将对象的依赖(即所需的服务或组件)从外部注入,而不是由对象自己创建。
  • 目的
    • 解耦对象的创建和使用。
    • 提高代码的可测试性(易于 mock 依赖)。
    • 支持动态配置和替换依赖。
  • 适用场景
    • 对象依赖复杂或需频繁更换。
    • 单元测试需要隔离依赖。
    • 系统需要集中管理组件。

2. 依赖注入模式的结构

  • 服务(Service):被注入的依赖,提供具体功能。
  • 客户端(Client):依赖服务的对象,使用注入的依赖。
  • 注入器(Injector):负责创建和注入依赖,可以是手动实现或框架(如 Spring)。
  • 接口(Interface):定义服务契约,解耦客户端和具体实现。
Mermaid UML 图
classDiagram
    class Service {
        <<interface>>
        +doSomething()
    }
    class ConcreteService {
        +doSomething()
    }
    class Client {
        -service: Service
        +setService(Service)
        +useService()
    }
    class Injector {
        +injectDependencies(Client)
    }
    
    ConcreteService ..|> Service : implements
    Client --> Service : uses
    Injector --> Client : injects
    Injector --> ConcreteService : creates

3. 依赖注入的类型

  1. 构造函数注入:通过构造函数传递依赖。
  2. Setter 注入:通过 setter 方法设置依赖。
  3. 接口注入:通过接口定义注入方法(较少使用)。

4. Java 实现示例

以下是一个消息服务的依赖注入示例,展示构造函数和 Setter 注入。

4.1 服务接口
1
2
3
4
// 服务接口
public interface MessageService {
void sendMessage(String message);
}
4.2 具体服务
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 具体服务:邮件服务
public class EmailService implements MessageService {
@Override
public void sendMessage(String message) {
System.out.println("Sending email: " + message);
}
}

// 具体服务:短信服务
public class SMSService implements MessageService {
@Override
public void sendMessage(String message) {
System.out.println("Sending SMS: " + message);
}
}
4.3 客户端(构造函数注入)
1
2
3
4
5
6
7
8
9
10
11
12
// 客户端:消息发送器(构造函数注入)
public class MessageSenderConstructor {
private final MessageService service;

public MessageSenderConstructor(MessageService service) {
this.service = service;
}

public void send(String message) {
service.sendMessage(message);
}
}
4.4 客户端(Setter 注入)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 客户端:消息发送器(Setter 注入)
public class MessageSenderSetter {
private MessageService service;

public void setService(MessageService service) {
this.service = service;
}

public void send(String message) {
if (service != null) {
service.sendMessage(message);
} else {
System.out.println("Service not set");
}
}
}
4.5 注入器(手动实现)
1
2
3
4
5
6
7
8
9
10
11
12
// 注入器
public class DependencyInjector {
public static MessageSenderConstructor createConstructorSender() {
return new MessageSenderConstructor(new EmailService());
}

public static MessageSenderSetter createSetterSender() {
MessageSenderSetter sender = new MessageSenderSetter();
sender.setService(new SMSService());
return sender;
}
}
4.6 客户端代码
1
2
3
4
5
6
7
8
9
10
11
public class Main {
public static void main(String[] args) {
// 构造函数注入
MessageSenderConstructor constructorSender = DependencyInjector.createConstructorSender();
constructorSender.send("Hello via email"); // 输出: Sending email: Hello via email

// Setter 注入
MessageSenderSetter setterSender = DependencyInjector.createSetterSender();
setterSender.send("Hello via SMS"); // 输出: Sending SMS: Hello via SMS
}
}

5. 工作原理

  1. 客户端定义依赖接口(MessageService),不直接创建实例。
  2. 注入器创建具体服务(如 EmailService)并注入客户端。
  3. 客户端通过接口调用服务,完成功能。

6. 优点

  • 解耦:客户端不依赖具体实现,只依赖接口。
  • 可测试性:易于注入 mock 对象进行测试。
  • 灵活性:运行时可切换依赖实现。

7. 缺点

  • 复杂度:手动注入增加代码量,需依赖框架简化。
  • 运行时错误:依赖未注入可能导致空指针异常。
  • 学习曲线:理解 IoC 和 DI 需一定经验。

8. 与工厂模式的对比

特性 依赖注入模式 工厂模式
创建方式 外部注入 工厂创建
控制权 注入器控制 客户端控制
耦合 更低(接口依赖) 较高(工厂依赖)

9. 实际应用场景

  • Spring 框架:通过 @Autowired 注入依赖。
  • 单元测试:注入 mock 对象(如 Mockito)。
  • 模块化系统:动态配置服务。

10. 源码中的例子(Spring 示例)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
class MessageSender {
private final MessageService service;

@Autowired
public MessageSender(MessageService service) {
this.service = service;
}

public void send(String message) {
service.sendMessage(message);
}
}

@Component
class EmailService implements MessageService {
@Override
public void sendMessage(String message) {
System.out.println("Email: " + message);
}
}

11. 总结

  • 依赖注入模式通过外部注入解耦依赖关系。
  • Java 实现支持构造函数和 Setter 注入,手动或框架实现。
  • UML 图展示了服务、客户端和注入器的关系。

如果需要框架(如 Spring)示例或调整 UML,请告诉我!

发布-订阅(Publish-Subscribe)

发布订阅模式(Publish-Subscribe Pattern,简称 Pub/Sub)是一种消息传递模式,虽然它与观察者模式(Observer Pattern)有相似之处,但它更强调松耦合的异步通信。发布订阅模式通过一个中间消息代理(Broker)实现发布者(Publisher)和订阅者(Subscriber)之间的解耦,订阅者无需直接依赖发布者。

以下是用 Java 实现发布订阅模式的详细讲解,包括定义、结构、代码示例及其优缺点。


1. 发布订阅模式的定义

  • 核心思想:发布者发送消息到中间代理,订阅者通过订阅主题或频道接收消息。
  • 目的
    • 解耦消息发送者和接收者。
    • 支持异步、分布式通信。
  • 适用场景
    • 事件驱动系统(如 GUI 事件)。
    • 消息队列(如 Kafka、RabbitMQ)。
    • 实时通知(如股票价格更新)。

2. 发布订阅模式的结构

  • 发布者(Publisher):生成并发送消息。
  • 订阅者(Subscriber):接收感兴趣的消息。
  • 消息代理(Broker):管理订阅关系,分发消息。
  • 主题(Topic):消息的分类,订阅者订阅特定主题。
  • 客户端(Client):创建发布者和订阅者,发起交互。
Mermaid UML 图
classDiagram
    class Publisher {
        +publish(topic, message)
    }
    class Subscriber {
        +receive(topic, message)
    }
    class MessageBroker {
        -subscriptions: Map<Topic, List<Subscriber>>
        +subscribe(topic, Subscriber)
        +unsubscribe(topic, Subscriber)
        +publish(topic, message)
    }
    class Client
    
    MessageBroker --> Publisher : receives from
    MessageBroker --> Subscriber : notifies
    Client --> Publisher : uses
    Client --> Subscriber : uses
    Client --> MessageBroker : configures

3. Java 实现示例

以下是一个简单的事件通知系统的发布订阅模式示例。

3.1 消息代理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 消息代理
public class MessageBroker {
private Map<String, List<Subscriber>> subscriptions = new HashMap<>();

public void subscribe(String topic, Subscriber subscriber) {
subscriptions.computeIfAbsent(topic, k -> new ArrayList<>()).add(subscriber);
}

public void unsubscribe(String topic, Subscriber subscriber) {
List<Subscriber> subscribers = subscriptions.get(topic);
if (subscribers != null) {
subscribers.remove(subscriber);
}
}

public void publish(String topic, String message) {
List<Subscriber> subscribers = subscriptions.get(topic);
if (subscribers != null) {
for (Subscriber subscriber : subscribers) {
subscriber.receive(topic, message);
}
}
}
}
3.2 订阅者接口
1
2
3
4
// 订阅者接口
public interface Subscriber {
void receive(String topic, String message);
}
3.3 具体订阅者
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 具体订阅者:邮件通知
public class EmailSubscriber implements Subscriber {
private String name;

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

@Override
public void receive(String topic, String message) {
System.out.println(name + " received email on " + topic + ": " + message);
}
}

// 具体订阅者:短信通知
public class SMSSubscriber implements Subscriber {
private String name;

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

@Override
public void receive(String topic, String message) {
System.out.println(name + " received SMS on " + topic + ": " + message);
}
}
3.4 发布者
1
2
3
4
5
6
7
8
9
10
11
12
// 发布者
public class NewsPublisher {
private MessageBroker broker;

public NewsPublisher(MessageBroker broker) {
this.broker = broker;
}

public void publish(String topic, String message) {
broker.publish(topic, message);
}
}
3.5 客户端代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class Main {
public static void main(String[] args) {
MessageBroker broker = new MessageBroker();

// 创建订阅者
Subscriber emailSub = new EmailSubscriber("Alice");
Subscriber smsSub = new SMSSubscriber("Bob");

// 订阅主题
broker.subscribe("news", emailSub);
broker.subscribe("news", smsSub);

// 创建发布者
NewsPublisher publisher = new NewsPublisher(broker);

// 发布消息
publisher.publish("news", "Breaking news!");
/*
输出:
Alice received email on news: Breaking news!
Bob received SMS on news: Breaking news!
*/

// 取消订阅
broker.unsubscribe("news", smsSub);
publisher.publish("news", "Update!");
/*
输出:
Alice received email on news: Update!
*/
}
}

4. 工作原理

  1. 客户端创建消息代理、订阅者和发布者。
  2. 订阅者通过代理订阅主题(subscribe)。
  3. 发布者发送消息到代理(publish),代理分发给订阅者(receive)。

5. 优点

  • 解耦:发布者和订阅者无直接依赖,仅通过代理交互。
  • 灵活性:支持动态订阅和取消。
  • 异步性:可结合队列实现异步通信。

6. 缺点

  • 复杂度:代理增加系统复杂性。
  • 性能:大量订阅者或消息可能影响分发效率。
  • 依赖代理:代理故障影响整个系统。

7. 与观察者模式的对比

特性 发布订阅模式 观察者模式
耦合 松耦合(通过代理) 紧耦合(直接依赖主题)
中介 有独立代理 无(主题直接通知)
通信 异步支持强 通常同步

8. 实际应用场景

  • 消息队列:如 Kafka、RabbitMQ。
  • 事件总线:GUI 框架中的事件分发。
  • 实时系统:股票价格通知。

9. 源码中的例子

Java 中的 java.util.concurrent.Flow(Java 9+):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import java.util.concurrent.Flow.*;

class MyPublisher implements Publisher<String> {
private List<Subscriber<? super String>> subscribers = new ArrayList<>();
@Override
public void subscribe(Subscriber<? super String> subscriber) {
subscribers.add(subscriber);
subscriber.onSubscribe(new Subscription() {
@Override public void request(long n) {}
@Override public void cancel() {}
});
}
public void publish(String message) {
subscribers.forEach(s -> s.onNext(message));
}
}


10. 总结

  • 发布订阅模式通过代理实现松耦合消息传递。
  • Java 实现依赖代理分发,支持动态订阅。
  • UML 图展示了发布者、订阅者和代理的关系。

如果需要异步示例(如线程池)或调整 UML,请告诉我!

模块模式(Module Pattern)

我假设你指的是“模块模式”(Module Pattern),可能是“模块”中的拼写或表述差异。模块模式不是经典的 GoF 设计模式,而是一种在 JavaScript 中广泛使用的设计模式,用于封装代码、隐藏实现细节并提供公共接口。它通过闭包和立即执行函数(IIFE)实现私有作用域和模块化。

由于你的问题要求用 Java,而 Java 的模块化机制与 JavaScript 不同(如 Java 使用类和包),我将讲解模块模式的核心思想,并将其适配到 Java 环境中,提供类似的实现方式(基于类的封装或 Java 9+ 的模块系统)。以下是详细讲解。


1. 模块模式的定义

  • 核心思想:通过封装隐藏私有成员,只暴露公共接口,实现数据隐私和模块化。
  • 目的
    • 防止全局命名空间污染。
    • 提供清晰的 API,隐藏实现细节。
  • 适用场景
    • 需要封装逻辑和状态。
    • 避免变量冲突。
    • 分离关注点。

2. 模块模式的结构(JavaScript 视角)

  • 私有成员:通过闭包定义的变量和方法,仅模块内部可访问。
  • 公共接口:通过返回的对象暴露给外部。
  • 立即执行函数(IIFE):创建模块作用域。
JavaScript 示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var CounterModule = (function() {
// 私有成员
var count = 0;
function incrementInternal() { count++; }

// 公共接口
return {
increment: function() {
incrementInternal();
console.log("Count: " + count);
},
getCount: function() { return count; }
};
})();

CounterModule.increment(); // 输出: Count: 1
CounterModule.increment(); // 输出: Count: 2
console.log(CounterModule.getCount()); // 输出: 2

3. Java 中的模块模式适配

Java 没有闭包和 IIFE,但可以通过类的封装(私有字段和公共方法)实现类似效果。Java 9 引入的模块系统(JPMS)进一步支持模块化。以下是两种方式的实现。

3.1 使用类封装(类似 JavaScript 模块模式)
模块类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 模块类:计数器模块
public class CounterModule {
// 私有成员
private int count = 0;

// 私有方法
private void incrementInternal() {
count++;
}

// 公共接口
public void increment() {
incrementInternal();
System.out.println("Count: " + count);
}

public int getCount() {
return count;
}

// 单例模式确保唯一实例(可选)
private static final CounterModule INSTANCE = new CounterModule();
private CounterModule() {} // 私有构造防止外部实例化
public static CounterModule getInstance() {
return INSTANCE;
}
}
客户端代码
1
2
3
4
5
6
7
8
public class Main {
public static void main(String[] args) {
CounterModule counter = CounterModule.getInstance();
counter.increment(); // 输出: Count: 1
counter.increment(); // 输出: Count: 2
System.out.println("Current count: " + counter.getCount()); // 输出: Current count: 2
}
}
  • 说明
    • 私有字段 count 和方法 incrementInternal 隐藏实现。
    • 公共方法 incrementgetCount 暴露接口。
    • 单例模式模拟 JavaScript 的单一模块实例。
3.2 使用 Java 9+ 模块系统(JPMS)

Java 9 引入的模块系统提供了更高级的模块化支持,通过 module-info.java 定义模块边界。

模块定义(module-info.java)
1
2
3
module counterModule {
exports com.example.counter; // 暴露公共接口包
}
模块实现(com.example.counter.CounterModule)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.example.counter;

public class CounterModule {
private int count = 0;

private void incrementInternal() {
count++;
}

public void increment() {
incrementInternal();
System.out.println("Count: " + count);
}

public int getCount() {
return count;
}
}
客户端模块(module-info.java)
1
2
3
module client {
requires counterModule; // 依赖 counterModule
}
客户端代码(com.example.client.Main)
1
2
3
4
5
6
7
8
9
10
11
package com.example.client;

import com.example.counter.CounterModule;

public class Main {
public static void main(String[] args) {
CounterModule counter = new CounterModule();
counter.increment(); // 输出: Count: 1
counter.increment(); // 输出: Count: 2
}
}
  • 说明
    • module-info.java 定义模块边界,控制包的可见性。
    • 类似 JavaScript 的模块作用域,但更静态。

4. 工作原理

  1. 封装:私有成员(如 count)隐藏在类或模块内部。
  2. 暴露接口:通过公共方法(如 increment)提供访问。
  3. 客户端:仅通过接口操作模块,无需了解实现。

5. 优点

  • 封装性:隐藏实现细节,保护数据。
  • 模块化:逻辑清晰,便于维护。
  • 复用性:单一模块可多次使用。

6. 缺点

  • 灵活性低:Java 类封装静态,动态性不如 JavaScript。
  • 单例限制:单例模式可能不适合所有场景。
  • JPMS 复杂性:模块系统配置较繁琐。

7. 与工厂模式的对比

特性 模块模式 工厂模式
目的 封装和模块化 创建对象
结构 单一实例或模块 工厂类
动态性 较静态 动态创建

8. 实际应用场景

  • 工具类:封装工具函数(如 Math 类)。
  • 配置管理:隐藏配置细节,提供接口。
  • 服务模块:如日志或缓存服务。

9. 源码中的例子

Java 中的 java.util.Collections

1
2
3
4
import java.util.Collections;

List<String> list = new ArrayList<>();
Collections.sort(list); // 模块化接口
- 说明Collections 封装排序逻辑,隐藏实现。


10. 总结

  • 模块模式通过封装实现私有性和公共接口。
  • Java 实现可用类封装或 JPMS,模拟 JavaScript 模块化。
  • UML 图(若需要)可展示单一模块与客户端关系。

如果需要更复杂示例(如带配置的模块)或 UML 图,请告诉我!

MVC(Model-View-Controller)

MVC 模式(Model-View-Controller Pattern)是一种广泛使用的架构模式,用于组织代码,分分离用户界面、数据和控制逻辑。它通过将应用程序分为三个相互连接的部分,提高了代码的可维护性、可扩展性和模块化。

以下是用 Java 实现 MVC 模式的详细讲解,包括定义、结构、代码示例及其优缺点。


1. MVC 模式的定义

  • 核心思想:将应用程序分为模型(Model)、视图(View)和控制器(Controller),实现职责分离。
  • 目的
    • 解耦数据、界面和逻辑。
    • 支持独立开发和测试。
  • 适用场景
    • GUI 应用程序(如 Swing、JavaFX)。
    • Web 应用(如 Spring MVC)。
    • 复杂交互系统。

2. MVC 模式的结构

  • 模型(Model):管理数据和业务逻辑,独立于 UI。
  • 视图(View):显示模型数据,负责用户界面。
  • 控制器(Controller):处理用户输入,协调模型和视图。
  • 交互关系
    • 控制器接收用户输入,更新模型。
    • 模型通知视图更新显示。
    • 视图从模型获取数据。
Mermaid UML 图
classDiagram
    class Model {
        -data: Data
        +setData(data)
        +getData() Data
        +notifyViews()
    }
    class View {
        -model: Model
        +update()
        +display()
    }
    class Controller {
        -model: Model
        -view: View
        +handleInput(input)
    }
    
    Model --> View : notifies
    View --> Model : queries
    Controller --> Model : updates
    Controller --> View : controls

3. Java 实现示例

以下是一个简单的计数器应用的 MVC 示例。

3.1 模型(Model)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 模型:计数器数据
public class CounterModel {
private int count;
private List<View> views = new ArrayList<>();

public void increment() {
count++;
notifyViews();
}

public void decrement() {
count--;
notifyViews();
}

public int getCount() {
return count;
}

public void addView(View view) {
views.add(view);
view.update(); // 初始化视图
}

private void notifyViews() {
for (View view : views) {
view.update();
}
}
}
3.2 视图(View)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 视图:显示计数器
public class CounterView implements View {
private CounterModel model;

public CounterView(CounterModel model) {
this.model = model;
model.addView(this);
}

@Override
public void update() {
System.out.println("Current count: " + model.getCount());
}
}

// 视图接口
interface View {
void update();
}
3.3 控制器(Controller)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 控制器:处理用户输入
public class CounterController {
private CounterModel model;
private CounterView view;

public CounterController(CounterModel model, CounterView view) {
this.model = model;
this.view = view;
}

public void increment() {
model.increment();
}

public void decrement() {
model.decrement();
}
}
3.4 客户端代码
1
2
3
4
5
6
7
8
9
10
11
12
public class Main {
public static void main(String[] args) {
CounterModel model = new CounterModel();
CounterView view = new CounterView(model);
CounterController controller = new CounterController(model, view);

// 模拟用户操作
controller.increment(); // 输出: Current count: 1
controller.increment(); // 输出: Current count: 2
controller.decrement(); // 输出: Current count: 1
}
}

4. 工作原理

  1. 客户端创建模型、视图和控制器,关联三者。
  2. 用户通过控制器发起操作(如 increment)。
  3. 控制器更新模型,模型通知视图刷新显示。

5. 优点

  • 解耦:模型、视图和控制器独立,互不干扰。
  • 可扩展:易于添加新视图或控制器。
  • 可测试:模型独立于 UI,便于单元测试。

6. 缺点

  • 复杂度:小型应用可能显得过于繁琐。
  • 耦合风险:视图和控制器可能间接耦合。
  • 维护成本:多组件增加协调难度。

7. 与 MVP/MVVM 的对比

特性 MVC MVP MVVM
控制器角色 协调视图和模型 主动更新视图 数据绑定
视图依赖 依赖模型 依赖 Presenter 通过 ViewModel 绑定
耦合 中等 较低 最低

8. 实际应用场景

  • Swing/JavaFX:GUI 组件分离。
  • Spring MVC:Web 应用框架。
  • 游戏开发:游戏逻辑与渲染分离。

9. 源码中的例子

Java Swing 中的 MVC:

1
2
JButton button = new JButton("Click");
button.addActionListener(e -> System.out.println("Clicked")); // 控制器监听
- Model:按钮状态。 - View:按钮 UI。 - ControllerActionListener


10. 总结

  • MVC 模式通过分离模型、视图和控制器实现职责分工。
  • Java 实现依赖观察者机制协调视图更新。
  • UML 图展示了三者之间的关系。

如果需要更复杂示例(如多视图支持)或调整 UML,请告诉我!

MVP(Model-View-Presenter)

MVP 模式(Model-View-Presenter Pattern)是一种架构模式,演变自 MVC 模式,旨在进一步解耦视图和逻辑,增强可测试性。MVP 通过将视图(View)和模型(Model)之间的交互完全交给展示者(Presenter)处理,避免了视图直接依赖模型,提高了代码的模块化和清晰度。

以下是用 Java 实现 MVP 模式的详细讲解,包括定义、结构、代码示例及其优缺点。


1. MVP 模式的定义

  • 核心思想:通过展示者(Presenter)协调视图和模型,视图只负责显示,Presenter 处理逻辑。
  • 目的
    • 解耦视图和模型。
    • 提高代码可测试性。
  • 适用场景
    • GUI 应用程序需要清晰分离。
    • 需要测试业务逻辑而不依赖 UI。
    • 视图逻辑复杂。

2. MVP 模式的结构

  • 模型(Model):管理数据和业务逻辑,与 MVC 类似。
  • 视图(View):显示数据和用户界面,定义与 Presenter 交互的接口。
  • 展示者(Presenter):处理用户输入,更新模型并控制视图。
  • 客户端(Client):创建并连接组件。
Mermaid UML 图
classDiagram
    class Model {
        -data: Data
        +setData(data)
        +getData() Data
    }
    class View {
        <<interface>>
        +showData(data)
        +getInput() Input
    }
    class ConcreteView {
        +showData(data)
        +getInput() Input
    }
    class Presenter {
        -model: Model
        -view: View
        +handleInput(input)
    }
    class Client
    
    ConcreteView ..|> View : implements
    Presenter --> Model : updates
    Presenter --> View : controls
    ConcreteView --> Presenter : notifies
    Client --> Presenter : uses
    Client --> ConcreteView : uses

3. Java 实现示例

以下是一个计数器的 MVP 示例,Presenter 处理增减逻辑。

3.1 模型(Model)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 模型:计数器数据
public class CounterModel {
private int count;

public void increment() {
count++;
}

public void decrement() {
count--;
}

public int getCount() {
return count;
}
}
3.2 视图接口(View)
1
2
3
4
5
6
// 视图接口
public interface CounterView {
void showCount(int count); // 显示计数
void incrementClicked(); // 通知增操作
void decrementClicked(); // 通知减操作
}
3.3 具体视图(Concrete View)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 具体视图
public class ConsoleCounterView implements CounterView {
private CounterPresenter presenter;

public ConsoleCounterView(CounterPresenter presenter) {
this.presenter = presenter;
}

@Override
public void showCount(int count) {
System.out.println("Current count: " + count);
}

@Override
public void incrementClicked() {
presenter.increment();
}

@Override
public void decrementClicked() {
presenter.decrement();
}

// 模拟用户操作
public void simulateUserInteraction() {
incrementClicked();
decrementClicked();
}
}
3.4 展示者(Presenter)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 展示者
public class CounterPresenter {
private CounterModel model;
private CounterView view;

public CounterPresenter(CounterModel model, CounterView view) {
this.model = model;
this.view = view;
}

public void increment() {
model.increment();
view.showCount(model.getCount());
}

public void decrement() {
model.decrement();
view.showCount(model.getCount());
}
}
3.5 客户端代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Main {
public static void main(String[] args) {
CounterModel model = new CounterModel();
ConsoleCounterView view = new ConsoleCounterView(null); // 先创建视图
CounterPresenter presenter = new CounterPresenter(model, view);
view.presenter = presenter; // 注入 Presenter

view.simulateUserInteraction();
/*
输出:
Current count: 1
Current count: 0
*/
}
}

4. 工作原理

  1. 客户端创建模型、视图和展示者,关联三者。
  2. 视图捕获用户输入(如 incrementClicked),通知 Presenter。
  3. Presenter 更新模型并调用视图方法刷新显示。

5. 优点

  • 解耦:视图不直接访问模型,依赖 Presenter。
  • 可测试性:Presenter 无 UI 依赖,易于单元测试。
  • 清晰性:职责明确,逻辑集中。

6. 缺点

  • 复杂度:小型应用可能显得繁琐。
  • 类数量:增加接口和实现类。
  • 双向依赖:视图和 Presenter 可能形成循环引用。

7. 与 MVC 的对比

特性 MVP MVC
视图依赖 不依赖模型 依赖模型
控制角色 Presenter 主动更新视图 Controller 协调
通信 双向(View ↔︎ Presenter) 三方(View ↔︎ Controller ↔︎ Model)

8. 实际应用场景

  • Android 开发:Activity/View 作为视图,Presenter 处理逻辑。
  • 桌面应用:Swing/JavaFX 的逻辑分离。
  • 测试驱动开发:隔离 UI 进行测试。

9. 源码中的例子

Android 中的 MVP(伪代码):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public interface LoginView {
void showLoginSuccess();
}

public class LoginPresenter {
private LoginView view;
private LoginModel model;

public LoginPresenter(LoginView view, LoginModel model) {
this.view = view;
this.model = model;
}

public void login(String username, String password) {
if (model.validate(username, password)) {
view.showLoginSuccess();
}
}
}


10. 总结

  • MVP 模式通过 Presenter 分离视图和模型,增强解耦。
  • Java 实现依赖接口和双向通信,支持灵活控制。
  • UML 图展示了视图、Presenter 和模型的关系。

如果需要更复杂示例(如多视图)或调整 UML,请告诉我!

MVVM(Model-View-ViewModel)

MVVM 模式(Model-View-ViewModel Pattern)是一种架构模式,起源于微软的 WPF 和 Silverlight,广泛应用于现代前端框架(如 Angular、Vue.js)和桌面应用开发。它通过引入 ViewModel 层,将视图(View)和模型(Model)解耦,并利用数据绑定简化视图更新。MVVM 模式特别适合双向数据绑定的场景。

以下是用 Java 实现 MVVM 模式的详细讲解,包括定义、结构、代码示例及其优缺点。由于 Java 不原生支持双向数据绑定,我会通过观察者机制模拟绑定效果。


1. MVVM 模式的定义

  • 核心思想:通过 ViewModel 连接视图和模型,利用数据绑定实现视图与数据的自动同步。
  • 目的
    • 解耦视图和业务逻辑。
    • 简化视图更新,提高开发效率。
  • 适用场景
    • 视图频繁更新(如表单、实时数据)。
    • 支持数据绑定的 UI 框架。
    • 需要清晰分离 UI 和逻辑。

2. MVVM 模式的结构

  • 模型(Model):管理数据和业务逻辑。
  • 视图(View):显示数据,响应用户输入。
  • 视图模型(ViewModel):持有视图所需数据,处理逻辑,绑定模型和视图。
  • 绑定机制(Binding):同步 View 和 ViewModel(Java 中需手动模拟)。
  • 客户端(Client):创建组件并启动应用。
Mermaid UML 图
classDiagram
    class Model {
        -data: Data
        +setData(data)
        +getData() Data
    }
    class View {
        +display(data)
        +notifyInput(input)
    }
    class ViewModel {
        -model: Model
        -observers: List<View>
        +setValue(value)
        +getValue() Value
        +notifyViews()
    }
    class Client
    
    ViewModel --> Model : uses
    View --> ViewModel : binds to
    ViewModel --> View : notifies
    Client --> View : uses
    Client --> ViewModel : uses

3. Java 实现示例

以下是一个计数器的 MVVM 示例,ViewModel 管理计数并通过观察者通知视图。

3.1 模型(Model)
1
2
3
4
5
6
7
8
9
10
11
12
// 模型:计数器数据
public class CounterModel {
private int count;

public void setCount(int count) {
this.count = count;
}

public int getCount() {
return count;
}
}
3.2 视图(View)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 视图:计数器显示
public class CounterView {
private CounterViewModel viewModel;

public CounterView(CounterViewModel viewModel) {
this.viewModel = viewModel;
viewModel.addObserver(this::update); // 绑定 ViewModel
update(viewModel.getValue()); // 初始化显示
}

public void update(int count) {
System.out.println("View updated - Count: " + count);
}

// 模拟用户输入
public void increment() {
viewModel.setValue(viewModel.getValue() + 1);
}

public void decrement() {
viewModel.setValue(viewModel.getValue() - 1);
}
}
3.3 视图模型(ViewModel)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 视图模型
public class CounterViewModel {
private CounterModel model;
private List<Consumer<Integer>> observers = new ArrayList<>();

public CounterViewModel(CounterModel model) {
this.model = model;
}

public void setValue(int value) {
model.setCount(value);
notifyViews();
}

public int getValue() {
return model.getCount();
}

public void addObserver(Consumer<Integer> observer) {
observers.add(observer);
}

private void notifyViews() {
observers.forEach(observer -> observer.accept(model.getCount()));
}
}
3.4 客户端代码
1
2
3
4
5
6
7
8
9
10
11
12
public class Main {
public static void main(String[] args) {
CounterModel model = new CounterModel();
CounterViewModel viewModel = new CounterViewModel(model);
CounterView view = new CounterView(viewModel);

// 模拟用户操作
view.increment(); // 输出: View updated - Count: 1
view.increment(); // 输出: View updated - Count: 2
view.decrement(); // 输出: View updated - Count: 1
}
}

4. 工作原理

  1. 客户端创建模型、ViewModel 和视图,关联三者。
  2. 视图绑定到 ViewModel,监听数据变化。
  3. 用户通过视图发起操作(如 increment),ViewModel 更新模型并通知视图刷新。

5. 优点

  • 解耦:视图不直接访问模型,依赖 ViewModel。
  • 自动化:数据绑定减少手动更新代码。
  • 可测试性:ViewModel 独立于 UI,易于测试。

6. 缺点

  • 复杂度:绑定机制增加实现难度(Java 中需手动)。
  • 性能:大量绑定可能影响效率。
  • 适用性:需框架支持(如 JavaFX)才充分发挥优势。

7. 与 MVC/MVP 的对比

特性 MVVM MVC MVP
视图更新 数据绑定 Controller 控制 Presenter 控制
视图依赖 ViewModel Model Presenter
耦合 最低 中等 较低

8. 实际应用场景

  • JavaFX:绑定属性到 UI。
  • 前端框架:Vue.js、WPF 的 MVVM。
  • 表单应用:实时同步输入和数据。

9. 源码中的例子(JavaFX)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;

public class CounterViewModel {
private IntegerProperty count = new SimpleIntegerProperty(0);

public void increment() { count.set(count.get() + 1); }
public IntegerProperty countProperty() { return count; }
}

class Main {
public static void main(String[] args) {
CounterViewModel vm = new CounterViewModel();
vm.countProperty().addListener((obs, old, newVal) ->
System.out.println("Count: " + newVal));
vm.increment(); // 输出: Count: 1
}
}

10. 总结

  • MVVM 模式通过 ViewModel 和绑定解耦视图和模型。
  • Java 实现需手动模拟绑定(如观察者),框架支持更佳。
  • UML 图展示了 ViewModel 的中介作用。

如果需要更复杂示例(如双向绑定)或调整 UML,请告诉我!

MVC, MVP, MVVM 比较

以下是 MVC(Model-View-Controller)、MVP(Model-View-Presenter)和 MVVM(Model-View-ViewModel)三种架构模式的详细比较表格,涵盖定义、结构、职责、耦合性、数据流、测试性等关键方面。

特性 MVC (Model-View-Controller) MVP (Model-View-Presenter) MVVM (Model-View-ViewModel)
定义 通过控制器协调模型和视图,分离数据、UI 和逻辑。 通过展示者处理视图和模型交互,视图不直接访问模型。 通过 ViewModel 和数据绑定解耦视图和模型,强调自动化。
核心思想 视图显示模型,控制器处理输入,模型管理数据。 Presenter 控制视图和模型,视图被动接收更新。 ViewModel 暴露数据和命令,视图通过绑定自动更新。
结构 - Model: 数据和逻辑
- View: UI
- Controller: 协调
- Model: 数据和逻辑
- View: UI 接口
- Presenter: 逻辑
- Model: 数据和逻辑
- View: UI
- ViewModel: 中介
职责分配 - Model: 数据存储和业务逻辑
- View: 显示和输入
- Controller: 更新模型、控制视图
- Model: 数据存储和逻辑
- View: 显示和通知
- Presenter: 处理输入、更新视图
- Model: 数据和逻辑
- View: 显示
- ViewModel: 数据绑定和逻辑
耦合性 中等:View 直接依赖 Model,Controller 依赖两者 较低:View 只依赖 Presenter,Presenter 依赖 Model 最低:View 通过绑定依赖 ViewModel,ViewModel 依赖 Model
数据流 - 用户 → Controller → Model → View - 用户 → View → Presenter → Model → View - 用户 → View → ViewModel → Model(绑定双向同步)
视图更新 Controller 主动更新 View Presenter 主动更新 View 数据绑定自动更新 View
控制器/Presenter/ViewModel 角色 Controller 协调模型和视图更新 Presenter 处理逻辑,主动控制视图 ViewModel 暴露数据和命令,视图绑定更新
通信方式 - View → Controller (输入)
- Model → View (通知)
- View → Presenter (通知)
- Presenter → View (更新)
- View ↔︎ ViewModel (绑定)
- ViewModel → Model (更新)
测试性 Model 可独立测试,Controller 依赖 UI 难测 Presenter 可独立测试,View 可 mock ViewModel 可独立测试,View 可 mock
复杂度 中等:简单应用可能繁琐 中等:需定义接口和实现 较高:绑定机制增加复杂性(Java 需手动实现)
优点 - 解耦 UI 和逻辑
- 易于理解
- 视图与模型解耦
- 高测试性
- 视图自动更新
- 最低耦合
缺点 - View 依赖 Model
- 控制器可能臃肿
- 类数量增加
- View-Presenter 双向依赖
- 绑定复杂
- 性能开销(大量绑定)
适用场景 - 传统 GUI
- Web 应用(如 Spring MVC)
- 测试驱动开发
- 复杂视图逻辑
- 数据驱动 UI
- 支持绑定的框架(如 JavaFX)
Java 示例 Controller 更新 Model,Model 通知 View Presenter 处理输入,调用 View 更新 ViewModel 绑定数据,View 自动更新
典型框架 Spring MVC、Struts Android (早期)、GWT JavaFX、WPF、Vue.js

详细说明

数据流
  • MVC:用户输入通过控制器传递到模型,模型更新后通知视图显示(单向为主,视图可直接查询模型)。
  • MVP:用户输入通过视图通知 Presenter,Presenter 更新模型并主动刷新视图(双向通信)。
  • MVVM:用户输入直接影响 ViewModel(通过绑定),ViewModel 更新模型,视图自动同步(双向绑定)。
视图与模型关系
  • MVC:视图直接访问模型获取数据,可能导致耦合。
  • MVP:视图通过 Presenter 间接访问模型,解耦更彻底。
  • MVVM:视图通过 ViewModel 绑定访问数据,完全隔离模型。
测试性
  • MVC:控制器依赖视图,测试需模拟 UI。
  • MVP:Presenter 不依赖具体视图实现,可轻松 mock View 接口。
  • MVVM:ViewModel 独立于视图,测试只需验证数据逻辑。
Java 示例代码片段
  1. MVC

    1
    2
    3
    4
    5
    6
    class Model { int count; void setCount(int c) { count = c; } }
    class View { void show(int count) { System.out.println(count); } }
    class Controller {
    Model m; View v;
    void increment() { m.setCount(m.count + 1); v.show(m.count); }
    }

  2. MVP

    1
    2
    3
    interface View { void show(int count); }
    class Model { int count; void setCount(int c) { count = c; } }
    class Presenter { Model m; View v; void increment() { m.setCount(m.count + 1); v.show(m.count); } }

  3. MVVM

    1
    2
    3
    4
    5
    6
    class Model { int count; void setCount(int c) { count = c; } }
    class ViewModel {
    Model m; Consumer<Integer> observer;
    void setValue(int v) { m.setCount(v); observer.accept(v); }
    }
    class View { ViewModel vm; void bind() { vm.observer = this::show; } void show(int v) { System.out.println(v); } }


总结

  • MVC:适合传统应用,视图与模型直接交互。
  • MVP:增强解耦,Presenter 主动控制,利于测试。
  • MVVM:依赖数据绑定,视图最被动,适合现代 UI 框架。

如果需要更具体示例或调整表格内容,请告诉我!

仓储模式(Repository Pattern)

仓储模式(Repository Pattern)是一种设计模式,广泛用于数据访问层,旨在将数据访问逻辑从业务逻辑中分离出来。它通过提供一个类似集合的接口来操作数据,隐藏底层数据存储的实现细节(如数据库、文件系统),从而解耦应用程序与数据源。

以下是用 Java 实现仓储模式的详细讲解,包括定义、结构、代码示例及其优缺点。


1. 仓储模式的定义

  • 核心思想:通过仓储对象提供对数据的统一访问接口,封装数据操作逻辑。
  • 目的
    • 解耦业务逻辑与数据访问。
    • 提供一致的数据操作方式。
    • 便于测试和替换数据源。
  • 适用场景
    • 需要隔离数据库操作。
    • 数据源可能切换(如从 SQL 到 NoSQL)。
    • 单元测试需要 mock 数据访问。

2. 仓储模式的结构

  • 领域模型(Domain Model):表示业务实体的数据结构。
  • 仓储接口(Repository Interface):定义数据操作的抽象方法。
  • 具体仓储(Concrete Repository):实现仓储接口,封装数据访问逻辑。
  • 数据存储(Data Store):底层数据源(如数据库、文件)。
  • 客户端(Client):使用仓储访问数据。
Mermaid UML 图
classDiagram
    class DomainModel {
        -id: int
        -name: String
    }
    class Repository {
        <<interface>>
        +add(entity: DomainModel)
        +findById(id: int) DomainModel
        +remove(id: int)
    }
    class ConcreteRepository {
        -dataStore: DataStore
        +add(entity: DomainModel)
        +findById(id: int) DomainModel
        +remove(id: int)
    }
    class DataStore {
        +save(entity)
        +load(id)
        +delete(id)
    }
    class Client
    
    ConcreteRepository ..|> Repository : implements
    ConcreteRepository --> DataStore : uses
    Client --> Repository : uses

3. Java 实现示例

以下是一个简单的用户管理的仓储模式示例,使用内存存储模拟数据源。

3.1 领域模型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 领域模型:用户
public class User {
private int id;
private String name;

public User(int id, String name) {
this.id = id;
this.name = name;
}

public int getId() { return id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }

@Override
public String toString() { return "User{id=" + id + ", name='" + name + "'}"; }
}
3.2 仓储接口
1
2
3
4
5
6
// 仓储接口
public interface UserRepository {
void add(User user);
User findById(int id);
void remove(int id);
}
3.3 具体仓储
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 具体仓储:内存实现的仓储
public class InMemoryUserRepository implements UserRepository {
private Map<Integer, User> store = new HashMap<>();

@Override
public void add(User user) {
store.put(user.getId(), user);
}

@Override
public User findById(int id) {
return store.get(id);
}

@Override
public void remove(int id) {
store.remove(id);
}
}
3.4 客户端代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Main {
public static void main(String[] args) {
UserRepository repository = new InMemoryUserRepository();

// 添加用户
User user1 = new User(1, "Alice");
User user2 = new User(2, "Bob");
repository.add(user1);
repository.add(user2);

// 查询用户
User found = repository.findById(1);
System.out.println("Found: " + found); // 输出: Found: User{id=1, name='Alice'}

// 删除用户
repository.remove(2);
System.out.println("After removal, find Bob: " + repository.findById(2)); // 输出: After removal, find Bob: null
}
}
3.5 扩展:数据库仓储
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 具体仓储:数据库实现的仓储(伪代码)
public class DatabaseUserRepository implements UserRepository {
private DataStore database; // 模拟数据库连接

public DatabaseUserRepository(DataStore database) {
this.database = database;
}

@Override
public void add(User user) {
database.save(user); // 假设 save 是数据库操作
}

@Override
public User findById(int id) {
return (User) database.load(id); // 假设 load 从数据库读取
}

@Override
public void remove(int id) {
database.delete(id); // 假设 delete 是数据库删除
}
}

4. 工作原理

  1. 客户端通过仓储接口(UserRepository)操作数据。
  2. 具体仓储(如 InMemoryUserRepository)实现接口,封装数据访问逻辑。
  3. 底层数据存储(如内存或数据库)执行实际操作。

5. 优点

  • 解耦:业务逻辑不依赖具体数据源。
  • 可测试性:易于 mock 仓储进行测试。
  • 一致性:提供类似集合的统一接口。

6. 缺点

  • 额外层:增加抽象层,可能提高复杂度。
  • 实现成本:每个实体需定义仓储接口和实现。
  • 性能:抽象可能掩盖底层优化。

7. 与 DAO 模式的对比

特性 仓储模式 DAO 模式
目的 封装数据访问,提供集合接口 数据访问对象,直接操作数据
抽象级别 更高(业务导向) 较低(数据导向)
接口风格 类似集合(如 add、find) 数据库操作(如 CRUD)

8. 实际应用场景

  • Spring Data:JPA 仓储接口。
  • DDD(领域驱动设计):仓储管理聚合根。
  • 测试:mock 数据访问层。

9. 源码中的例子(Spring Data)

1
2
3
4
5
import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository<User, Integer> {
// 自动实现 CRUD 方法
}
  • 说明:Spring Data 提供仓储接口,自动生成实现。

10. 总结

  • 仓储模式通过接口封装数据访问,解耦业务与存储。
  • Java 实现支持内存、数据库等多种存储方式。
  • UML 图展示了仓储与数据源的关系。

如果需要更复杂示例(如带查询方法)或调整 UML,请告诉我!

服务定位器(Service Locator)

服务定位器模式(Service Locator Pattern)是一种设计模式,用于通过一个中央服务定位器(Service Locator)来查找和获取服务或依赖,而不是直接在客户端中硬编码或手动创建。它是一种替代依赖注入(Dependency Injection)的服务获取方式,旨在解耦客户端与具体服务的实现。

以下是用 Java 实现服务定位器模式的详细讲解,包括定义、结构、代码示例及其优缺点。


1. 服务定位器模式的定义

  • 核心思想:通过服务定位器集中管理和提供服务实例,客户端只需知道服务接口和定位器。
  • 目的
    • 解耦客户端与服务实现。
    • 提供统一的服务查找方式。
  • 适用场景
    • 服务实例创建复杂或需要缓存。
    • 客户端需动态获取服务。
    • 不想使用依赖注入框架。

2. 服务定位器模式的结构

  • 服务接口(Service Interface):定义服务的契约。
  • 具体服务(Concrete Service):实现服务接口。
  • 服务定位器(Service Locator):管理服务实例,提供查找方法。
  • 客户端(Client):通过定位器获取服务并使用。
Mermaid UML 图
classDiagram
    class Service {
        <<interface>>
        +doSomething()
    }
    class ConcreteServiceA {
        +doSomething()
    }
    class ConcreteServiceB {
        +doSomething()
    }
    class ServiceLocator {
        -services: Map<String, Service>
        +getService(name: String) Service
    }
    class Client
    
    ConcreteServiceA ..|> Service : implements
    ConcreteServiceB ..|> Service : implements
    ServiceLocator --> Service : provides
    Client --> ServiceLocator : uses
    Client --> Service : uses

3. Java 实现示例

以下是一个日志服务的服务定位器示例,客户端通过定位器获取不同日志服务。

3.1 服务接口
1
2
3
4
// 服务接口
public interface Logger {
void log(String message);
}
3.2 具体服务
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 具体服务:控制台日志
public class ConsoleLogger implements Logger {
@Override
public void log(String message) {
System.out.println("Console: " + message);
}
}

// 具体服务:文件日志
public class FileLogger implements Logger {
@Override
public void log(String message) {
System.out.println("File: " + message);
}
}
3.3 服务定位器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 服务定位器
public class ServiceLocator {
private static final Map<String, Logger> services = new HashMap<>();

static {
services.put("console", new ConsoleLogger());
services.put("file", new FileLogger());
}

public static Logger getLogger(String name) {
Logger logger = services.get(name);
if (logger == null) {
throw new IllegalArgumentException("No logger found for: " + name);
}
return logger;
}
}
3.4 客户端代码
1
2
3
4
5
6
7
8
9
10
11
public class Main {
public static void main(String[] args) {
// 获取控制台日志服务
Logger consoleLogger = ServiceLocator.getLogger("console");
consoleLogger.log("Hello from console"); // 输出: Console: Hello from console

// 获取文件日志服务
Logger fileLogger = ServiceLocator.getLogger("file");
fileLogger.log("Hello from file"); // 输出: File: Hello from file
}
}

4. 工作原理

  1. 服务定位器初始化时注册服务实例(静态或动态)。
  2. 客户端通过定位器请求服务(getLogger),只需提供服务标识。
  3. 定位器返回服务实例,客户端调用服务方法。

5. 优点

  • 解耦:客户端不直接依赖服务实现。
  • 集中管理:服务实例在定位器中统一配置。
  • 简单性:无需复杂框架即可实现。

6. 缺点

  • 隐式依赖:客户端依赖定位器,依赖关系不显式。
  • 全局状态:静态定位器可能导致单例问题。
  • 测试性差:相比依赖注入,难以 mock 服务。

7. 与依赖注入的对比

特性 服务定位器模式 依赖注入模式
依赖获取 客户端主动请求 外部注入
控制权 客户端控制 注入器控制
耦合 依赖定位器 依赖接口
测试性 较差(需 mock 定位器) 较高(易 mock 依赖)

8. 实际应用场景

  • JNDI:Java 的命名和目录接口。
  • 日志框架:如 SLF4J 的 LoggerFactory
  • 遗留系统:集中管理服务实例。

9. 源码中的例子

Java 中的 javax.naming.InitialContext(JNDI):

1
2
3
4
5
6
7
8
import javax.naming.*;

public class Example {
public static void main(String[] args) throws NamingException {
InitialContext context = new InitialContext();
Object service = context.lookup("java:comp/env/myService"); // 服务定位
}
}


10. 总结

  • 服务定位器模式通过集中定位器提供服务访问,解耦客户端与实现。
  • Java 实现依赖静态或动态注册,简单但隐式。
  • UML 图展示了定位器与服务的关系。

如果需要动态注册示例或调整 UML,请告诉我!

事件溯源(Event Sourcing)

事件溯源(Event Sourcing)是一种架构模式,主要用于数据存储和状态管理。它不同于传统的 CRUD(创建、读取、更新、删除)方式,而是通过记录系统中发生的所有事件(Event)来构建和追踪应用程序状态。事件溯源的核心思想是将状态变化作为事件序列存储,而不是直接存储当前状态。

以下是用 Java 实现事件溯源模式的详细讲解,包括定义、结构、代码示例及其优缺点。


1. 事件溯源的定义

  • 核心思想:将应用程序的状态变化记录为一系列不可变的事件,通过回放事件重建当前状态。
  • 目的
    • 提供完整的历史记录。
    • 支持状态重建和审计。
    • 增强系统的可扩展性和一致性。
  • 适用场景
    • 需要审计或回溯(如金融系统)。
    • 事件驱动架构(EDA)。
    • 分布式系统(如微服务)。

2. 事件溯源的结构

  • 事件(Event):表示状态变化的不可变记录。
  • 事件存储(Event Store):持久化事件序列的存储。
  • 聚合(Aggregate):业务实体,应用事件重建状态。
  • 事件处理器(Event Handler):处理事件并更新聚合状态。
  • 客户端(Client):触发事件并查询状态。
Mermaid UML 图
classDiagram
    class Event {
        -id: String
        -type: String
        -data: Object
        +getId() String
        +getType() String
        +getData() Object
    }
    class EventStore {
        +append(event: Event)
        +getEvents(aggregateId: String) List<Event>
    }
    class Aggregate {
        -id: String
        -state: State
        +apply(Event)
        +getState() State
    }
    class EventHandler {
        +handle(Event, Aggregate)
    }
    class Client
    
    EventStore --> Event : stores
    Aggregate --> Event : applies
    EventHandler --> Event : processes
    EventHandler --> Aggregate : updates
    Client --> EventStore : saves to
    Client --> Aggregate : queries

3. Java 实现示例

以下是一个银行账户的事件溯源示例,记录存款和取款事件。

3.1 事件类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// 事件基类
abstract class AccountEvent {
private final String accountId;
private final long timestamp;

public AccountEvent(String accountId) {
this.accountId = accountId;
this.timestamp = System.currentTimeMillis();
}

public String getAccountId() { return accountId; }
public long getTimestamp() { return timestamp; }
}

// 具体事件:存款
class DepositEvent extends AccountEvent {
private final double amount;

public DepositEvent(String accountId, double amount) {
super(accountId);
this.amount = amount;
}

public double getAmount() { return amount; }
}

// 具体事件:取款
class WithdrawalEvent extends AccountEvent {
private final double amount;

public WithdrawalEvent(String accountId, double amount) {
super(accountId);
this.amount = amount;
}

public double getAmount() { return amount; }
}
3.2 事件存储
1
2
3
4
5
6
7
8
9
10
11
12
// 事件存储(内存实现)
public class EventStore {
private Map<String, List<AccountEvent>> events = new HashMap<>();

public void append(AccountEvent event) {
events.computeIfAbsent(event.getAccountId(), k -> new ArrayList<>()).add(event);
}

public List<AccountEvent> getEvents(String accountId) {
return events.getOrDefault(accountId, new ArrayList<>());
}
}
3.3 聚合
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 聚合:银行账户
public class Account {
private String id;
private double balance;

public Account(String id) {
this.id = id;
this.balance = 0;
}

public void apply(AccountEvent event) {
if (event instanceof DepositEvent) {
balance += ((DepositEvent) event).getAmount();
} else if (event instanceof WithdrawalEvent) {
balance -= ((WithdrawalEvent) event).getAmount();
}
}

public double getBalance() { return balance; }
public String getId() { return id; }

// 重建状态
public static Account rebuild(String id, List<AccountEvent> events) {
Account account = new Account(id);
for (AccountEvent event : events) {
account.apply(event);
}
return account;
}
}
3.4 客户端代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Main {
public static void main(String[] args) {
EventStore store = new EventStore();
String accountId = "acc1";

// 记录事件
store.append(new DepositEvent(accountId, 100.0));
store.append(new DepositEvent(accountId, 50.0));
store.append(new WithdrawalEvent(accountId, 30.0));

// 重建账户状态
Account account = Account.rebuild(accountId, store.getEvents(accountId));
System.out.println("Account balance: " + account.getBalance()); // 输出: Account balance: 120.0
}
}

4. 工作原理

  1. 客户端触发事件(如存款),存储到事件存储。
  2. 事件存储保存不可变的事件序列。
  3. 需要状态时,聚合通过回放事件(apply)重建当前状态。

5. 优点

  • 审计性:完整事件历史,便于追踪和调试。
  • 灵活性:可重建任意历史状态。
  • 一致性:事件序列保证数据完整性。

6. 缺点

  • 存储开销:事件量大时占用空间多。
  • 性能:状态重建需回放所有事件,效率低。
  • 复杂性:实现和维护较复杂。

7. 与传统状态存储对比

特性 事件溯源 传统状态存储
存储内容 事件序列 当前状态
状态获取 回放事件重建 直接读取
历史记录 完整 需额外实现

8. 实际应用场景

  • 银行系统:记录交易历史。
  • 电商:订单状态追踪。
  • CQRS:与命令查询职责分离结合。

9. 源码中的例子

Java 中无直接内置实现,但类似概念见于日志框架:

1
2
3
// 类似事件日志的记录
Logger logger = Logger.getLogger("app");
logger.info("Deposit: 100"); // 事件记录


10. 总结

  • 事件溯源通过事件序列管理状态,支持审计和重建。
  • Java 实现依赖事件类和存储,回放重建聚合。
  • UML 图展示了事件与聚合的关系。

如果需要更复杂示例(如快照优化)或调整 UML,请告诉我!