Java 常用设计模式详解
设计模式 (Design Patterns) 是在软件工程中,针对特定问题场景提出的一套经过验证的、可复用的解决方案。它们是从实践中总结出来的,是软件开发过程中的最佳实践。学习和应用设计模式,可以帮助开发者构建出结构清晰、可维护、可扩展、复用性强的软件系统,同时也能促进团队成员之间的沟通。
核心思想:设计模式的目标是提升软件的灵活性 (Flexibility)、可重用性 (Reusability) 和可扩展性 (Extensibility),同时降低维护成本 (Maintainability)。它们不是代码,而是解决特定问题的思想和方法。
一、设计模式的分类
根据 GoF (Gang of Four,《设计模式:可复用面向对象软件的基础》的四位作者) 的经典分类,设计模式主要分为三类:
- 创建型模式 (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)。
本文将重点介绍 Java 开发中最常用的一些设计模式。
二、常用创建型模式
2.1 单例模式 (Singleton Pattern)
- 定义:确保一个类只有一个实例,并提供一个全局访问点。
- 应用场景:需要严格控制资源使用、或全局唯一服务(如日志记录器、配置管理器、线程池、缓存)。
- Java 实现方式:
- 饿汉式 (Eager Initialization):在类加载时就创建实例。
- 优点:线程安全,实现简单。
- 缺点:无论是否使用都会创建实例,可能造成资源浪费。
1
2
3
4
5
6
7public class EagerSingleton {
private static final EagerSingleton INSTANCE = new EagerSingleton(); // 类加载时创建
private EagerSingleton() {}
public static EagerSingleton getInstance() {
return INSTANCE;
}
} - 懒汉式 (Lazy Initialization) - 线程不安全:在第一次调用
getInstance()时才创建。- 优点:按需创建,节省资源。
- 缺点:多线程环境下不安全(可能创建多个实例)。
1
2
3
4
5
6
7
8
9
10public class LazySingletonUnsafe {
private static LazySingletonUnsafe instance;
private LazySingletonUnsafe() {}
public static LazySingletonUnsafe getInstance() {
if (instance == null) { // 线程不安全点
instance = new LazySingletonUnsafe();
}
return instance;
}
} - 懒汉式 - 线程安全 (双重检查锁定 DCL):在懒汉式基础上,通过
synchronized和双重检查优化。- 优点:线程安全,延迟加载,性能较高。
- 注意:
instance必须使用volatile关键字,防止指令重排。
1
2
3
4
5
6
7
8
9
10
11
12
13
14public class DCLSingleton {
private volatile static DCLSingleton instance; // volatile 保证可见性和有序性
private DCLSingleton() {}
public static DCLSingleton getInstance() {
if (instance == null) { // 第一次检查:避免不需要同步的请求进入同步块
synchronized (DCLSingleton.class) {
if (instance == null) { // 第二次检查:如果实例仍为空,则创建
instance = new DCLSingleton();
}
}
}
return instance;
}
} - 静态内部类 (Static Inner Class):巧妙利用类加载机制来保证线程安全。
- 优点:线程安全,延迟加载,实现简单,推荐使用。
1
2
3
4
5
6
7
8
9public class InnerClassSingleton {
private InnerClassSingleton() {}
private static class SingletonHolder { // 静态内部类
private static final InnerClassSingleton INSTANCE = new InnerClassSingleton();
}
public static InnerClassSingleton getInstance() {
return SingletonHolder.INSTANCE; // 只有在调用时才会加载内部类
}
} - 枚举 (Enum):最简洁、最安全的单例实现方式,能有效避免反射和序列化漏洞。
- 优点:线程安全,延迟加载,防止反射攻击和序列化问题,推荐使用。
1
2
3
4
5
6public enum EnumSingleton {
INSTANCE; // 唯一的实例
public void doSomething() {
System.out.println("Enum Singleton is doing something.");
}
}
- 饿汉式 (Eager Initialization):在类加载时就创建实例。
2.2 工厂方法模式 (Factory Method Pattern)
- 定义:定义一个创建对象的接口,但由子类决定实例化哪一个类。工厂方法让类的实例化延迟到子类。
- 应用场景:当一个类不知道它所需要的对象的类时;当一个类希望由其子类来指定它所创建的对象时。
- 核心角色:
Product:抽象产品接口或抽象类。ConcreteProduct:具体产品实现。Factory:抽象工厂接口或抽象类,声明工厂方法。ConcreteFactory:具体工厂类,实现工厂方法,负责创建具体产品。
- 示例:
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
43
44
45
46
47
48
49
50// 1. 抽象产品
interface Product {
void use();
}
// 2. 具体产品 A
class ConcreteProductA implements Product {
public void use() {
System.out.println("Using ConcreteProductA");
}
}
// 3. 具体产品 B
class ConcreteProductB implements Product {
public void use() {
System.out.println("Using ConcreteProductB");
}
}
// 4. 抽象工厂
interface Factory {
Product createProduct();
}
// 5. 具体工厂 A
class ConcreteFactoryA implements Factory {
public Product createProduct() {
return new ConcreteProductA();
}
}
// 6. 具体工厂 B
class ConcreteFactoryB implements Factory {
public Product createProduct() {
return new ConcreteProductB();
}
}
// 客户端使用
public class Client {
public static void main(String[] args) {
Factory factoryA = new ConcreteFactoryA();
Product productA = factoryA.createProduct();
productA.use(); // Output: Using ConcreteProductA
Factory factoryB = new ConcreteFactoryB();
Product productB = factoryB.createProduct();
productB.use(); // Output: Using ConcreteProductB
}
}
2.3 抽象工厂模式 (Abstract Factory Pattern)
- 定义:提供一个接口,用于创建一系列相关或相互依赖的对象的家族,而无需指定它们具体的类。
- 应用场景:当系统需要独立于产品的创建、组合和表示时;当系统要由多个产品系列中的一个来配置时。
- 与工厂方法区别:工厂方法模式是一个工厂只生产一个产品,而抽象工厂模式是一个工厂可以生产一系列(一族)产品。
- 核心角色:
AbstractFactory:抽象工厂接口,声明一组用于创建抽象产品的方法。ConcreteFactory:具体工厂实现,负责创建具体产品族中的具体产品。AbstractProduct:抽象产品接口。ConcreteProduct:具体产品实现。
- 示例:
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// 1. 抽象产品 A
interface ProductA { void showA(); }
class ConcreteProductA1 implements ProductA { public void showA() { System.out.println("Product A1"); } }
class ConcreteProductA2 implements ProductA { public void showA() { System.out.println("Product A2"); } }
// 2. 抽象产品 B
interface ProductB { void showB(); }
class ConcreteProductB1 implements ProductB { public void showB() { System.out.println("Product B1"); } }
class ConcreteProductB2 implements ProductB { public void showB() { System.out.println("Product B2"); } }
// 3. 抽象工厂
interface AbstractFactory {
ProductA createProductA();
ProductB createProductB();
}
// 4. 具体工厂 1 (生产产品族 1)
class ConcreteFactory1 implements AbstractFactory {
public ProductA createProductA() { return new ConcreteProductA1(); }
public ProductB createProductB() { return new ConcreteProductB1(); }
}
// 5. 具体工厂 2 (生产产品族 2)
class ConcreteFactory2 implements AbstractFactory {
public ProductA createProductA() { return new ConcreteProductA2(); }
public ProductB createProductB() { return new ConcreteProductB2(); }
}
// 客户端使用
public class Client {
public static void main(String[] args) {
AbstractFactory factory1 = new ConcreteFactory1();
factory1.createProductA().showA(); // Output: Product A1
factory1.createProductB().showB(); // Output: Product B1
AbstractFactory factory2 = new ConcreteFactory2();
factory2.createProductA().showA(); // Output: Product A2
factory2.createProductB().showB(); // Output: Product B2
}
}
2.4 建造者模式 (Builder Pattern)
- 定义:将一个复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。
- 应用场景:当创建对象的参数很多,或者创建过程复杂且需要分步进行时。
- 核心角色:
Product:被构建的复杂对象。Builder:抽象建造者接口,定义构建各个部件的抽象方法和返回产品的方法。ConcreteBuilder:具体建造者,实现 Builder 接口,负责构建和装配复杂对象的各个部件。Director:导演者,负责安排构建过程,但它不直接创建产品,而是知道如何使用 Builder。
- 示例 (简化版,链式调用):
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72// Product
class Computer {
private String cpu;
private String ram;
private String storage;
private String graphicsCard;
public Computer(String cpu, String ram, String storage, String graphicsCard) {
this.cpu = cpu;
this.ram = ram;
this.storage = storage;
this.graphicsCard = graphicsCard;
}
public String toString() {
return "Computer [CPU=" + cpu + ", RAM=" + ram + ", Storage=" + storage + ", GraphicsCard=" + graphicsCard + "]";
}
}
// Builder
class ComputerBuilder {
private String cpu;
private String ram;
private String storage;
private String graphicsCard;
public ComputerBuilder buildCpu(String cpu) {
this.cpu = cpu;
return this; // 返回 Builder 自身,实现链式调用
}
public ComputerBuilder buildRam(String ram) {
this.ram = ram;
return this;
}
public ComputerBuilder buildStorage(String storage) {
this.storage = storage;
return this;
}
public ComputerBuilder buildGraphicsCard(String graphicsCard) {
this.graphicsCard = graphicsCard;
return this;
}
public Computer build() {
return new Computer(cpu, ram, storage, graphicsCard);
}
}
// 客户端使用
public class Client {
public static void main(String[] args) {
// 使用建造者模式构建 Computer 对象
Computer gamingComputer = new ComputerBuilder()
.buildCpu("Intel i9")
.buildRam("32GB")
.buildStorage("1TB SSD")
.buildGraphicsCard("NVIDIA RTX 4090")
.build();
System.out.println(gamingComputer);
Computer officeComputer = new ComputerBuilder()
.buildCpu("Intel i5")
.buildRam("16GB")
.buildStorage("512GB SSD")
.build(); // 没有显卡
System.out.println(officeComputer);
}
}
三、常用结构型模式
3.1 适配器模式 (Adapter Pattern)
- 定义:将一个类的接口转换成客户希望的另一个接口。适配器模式使原本由于接口不兼容而不能一起工作的那些类可以一起工作。
- 应用场景:当你需要使用一个已经存在的类,而它的接口不符合你的期望时;系统需要和第三方组件集成时。
- 核心角色:
Target:目标接口,客户期望使用的接口。Adaptee:被适配者,需要被适配的类。Adapter:适配器类,实现 Target 接口,并持有 Adaptee 实例或继承 Adaptee。
- 实现方式:
- 类适配器 (Class Adapter):通过继承实现,Java 中不常用,因为 Java 不支持多重继承。
- 对象适配器 (Object Adapter):通过组合实现,推荐使用。
- 示例 (对象适配器):将一个
UsbCable适配成LightningPort。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// 1. 目标接口 (客户期望使用的接口)
interface LightningPort {
void chargeWithLightning();
}
// 2. 被适配者 (已存在的类,接口不兼容)
class UsbCable {
public void chargeWithUsb() {
System.out.println("Charging with USB cable.");
}
}
// 3. 适配器 (将 UsbCable 适配成 LightningPort)
class UsbToLightningAdapter implements LightningPort {
private UsbCable usbCable; // 持有被适配者的实例
public UsbToLightningAdapter(UsbCable usbCable) {
this.usbCable = usbCable;
}
public void chargeWithLightning() {
System.out.println("Adapter: Converting USB charge to Lightning charge...");
usbCable.chargeWithUsb(); // 调用被适配者的方法
}
}
// 客户端
public class Client {
public static void main(String[] args) {
UsbCable oldUsbCable = new UsbCable();
// oldUsbCable.chargeWithUsb(); // 充电
// 现在需要一个 Lightning port
LightningPort lightningPort = new UsbToLightningAdapter(oldUsbCable);
lightningPort.chargeWithLightning(); // 输出:Adapter: Converting USB charge to Lightning charge... \n Charging with USB cable.
}
}
3.2 装饰器模式 (Decorator Pattern)
- 定义:动态地给一个对象添加一些额外的职责。相较于继承,装饰器提供了更灵活的方式扩展功能。
- 应用场景:需要透明地、灵活地扩展对象的功能,而不影响其他对象。
- 核心角色:
Component:抽象组件,定义了被装饰对象和装饰器都应该实现的方法。ConcreteComponent:具体组件,被装饰的原始对象。Decorator:抽象装饰器,实现了 Component 接口,并持有一个 Component 对象的引用。ConcreteDecorator:具体装饰器,为具体组件添加新功能。
- 示例:给咖啡添加牛奶、糖等配料。
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78// 1. 抽象组件 (饮料接口)
interface Beverage {
String getDescription();
double getCost();
}
// 2. 具体组件 (意式浓缩咖啡)
class Espresso implements Beverage {
public String getDescription() {
return "Espresso";
}
public double getCost() {
return 1.99;
}
}
// 3. 抽象装饰器 (配料抽象类)
abstract class CondimentDecorator implements Beverage {
protected Beverage beverage; // 持有被装饰对象的引用
public CondimentDecorator(Beverage beverage) {
this.beverage = beverage;
}
// 装饰器需要实现 Component 的方法
public abstract String getDescription();
public abstract double getCost();
}
// 4. 具体装饰器 A (牛奶)
class Milk extends CondimentDecorator {
public Milk(Beverage beverage) {
super(beverage);
}
public String getDescription() {
return beverage.getDescription() + ", Milk";
}
public double getCost() {
return beverage.getCost() + 0.50; // 添加牛奶的成本
}
}
// 5. 具体装饰器 B (糖)
class Sugar extends CondimentDecorator {
public Sugar(Beverage beverage) {
super(beverage);
}
public String getDescription() {
return beverage.getDescription() + ", Sugar";
}
public double getCost() {
return beverage.getCost() + 0.20; // 添加糖的成本
}
}
// 客户端
public class Client {
public static void main(String[] args) {
Beverage espresso = new Espresso();
System.out.println(espresso.getDescription() + " $" + espresso.getCost()); // Espresso $1.99
// 加牛奶
Beverage espressoWithMilk = new Milk(espresso);
System.out.println(espressoWithMilk.getDescription() + " $" + espressoWithMilk.getCost()); // Espresso, Milk $2.49
// 加牛奶再加糖
Beverage espressoWithMilkAndSugar = new Sugar(espressoWithMilk);
System.out.println(espressoWithMilkAndSugar.getDescription() + " $" + espressoWithMilkAndSugar.getCost()); // Espresso, Milk, Sugar $2.69
}
}
3.3 代理模式 (Proxy Pattern)
- 定义:为其他对象提供一个替身或占位符,以控制对这个对象的访问。
- 应用场景:远程代理 (RPC)、虚拟代理 (按需加载大对象)、安全代理 (权限控制)、智能引用。
- 核心角色:
Subject:抽象主题接口,定义真实对象和代理对象共有的接口。RealSubject:真实主题,是代理对象所代表的真实对象。Proxy:代理对象,持有对真实主题的引用,并在访问真实主题时执行额外的操作。
- 示例:图片加载代理。
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66// 1. 抽象主题接口
interface Image {
void display();
}
// 2. 真实主题 (大图片,加载耗时)
class RealImage implements Image {
private String filename;
public RealImage(String filename) {
this.filename = filename;
loadFromDisk(filename); // 模拟耗时加载
}
private void loadFromDisk(String filename) {
System.out.println("Loading " + filename + " from disk...");
try {
Thread.sleep(2000); // 模拟加载时间
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
public void display() {
System.out.println("Displaying " + filename);
}
}
// 3. 代理主题
class ProxyImage implements Image {
private String filename;
private RealImage realImage; // 只有在需要时才创建 RealImage
public ProxyImage(String filename) {
this.filename = filename;
}
public void display() {
if (realImage == null) {
realImage = new RealImage(filename); // 首次调用时才加载真实对象
}
realImage.display(); // 调用真实对象的方法
}
}
// 客户端
public class Client {
public static void main(String[] args) {
Image image1 = new ProxyImage("photo1.jpg"); // 此时并不会真正加载图片
System.out.println("Image object created, but not loaded yet.");
// 第一次显示图片,会进行加载
System.out.println("Trying to display photo1.jpg for the first time:");
image1.display(); // Output: Loading photo1.jpg from disk... \n Displaying photo1.jpg
// 第二次显示图片,直接显示,不再加载
System.out.println("Trying to display photo1.jpg for the second time:");
image1.display(); // Output: Displaying photo1.jpg
Image image2 = new ProxyImage("photo2.jpg");
System.out.println("Trying to display photo2.jpg:");
image2.display(); // Output: Loading photo2.jpg from disk... \n Displaying photo2.jpg
}
}
3.4 外观模式 (Facade Pattern)
- 定义:为子系统中的一组接口提供一个统一的接口。外观模式定义了一个高层接口,使子系统更容易使用。
- 应用场景:当需要为一个复杂子系统提供一个简单接口时,或将客户端与子系统解耦时。
- 核心角色:
Facade:外观类,提供一个简单统一的接口,内部将客户端的请求转发给子系统中的相应对象。Subsystem Classes:子系统中的各个类,它们实现子系统的复杂功能。
- 示例:家庭影院系统。
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64// 1. 子系统类 A
class DVDPlayer {
public void on() { System.out.println("DVD Player On"); }
public void play(String movie) { System.out.println("Playing movie: " + movie); }
public void off() { System.out.println("DVD Player Off"); }
}
// 2. 子系统类 B
class Projector {
public void on() { System.out.println("Projector On"); }
public void wideScreenMode() { System.out.println("Projector in widescreen mode"); }
public void off() { System.out.println("Projector Off"); }
}
// 3. 子系统类 C
class Lights {
public void dim(int level) { System.out.println("Lights dim to " + level + "%"); }
public void on() { System.out.println("Lights On"); }
}
// 4. 外观类 (统一接口)
class HomeTheaterFacade {
private DVDPlayer dvdPlayer;
private Projector projector;
private Lights lights;
public HomeTheaterFacade(DVDPlayer dvdPlayer, Projector projector, Lights lights) {
this.dvdPlayer = dvdPlayer;
this.projector = projector;
this.lights = lights;
}
// 观看电影的简单接口
public void watchMovie(String movie) {
System.out.println("\nGet ready to watch a movie...");
lights.dim(10);
projector.on();
projector.wideScreenMode();
dvdPlayer.on();
dvdPlayer.play(movie);
}
// 结束观看的简单接口
public void endMovie() {
System.out.println("\nShutting down home theater...");
dvdPlayer.off();
projector.off();
lights.on();
}
}
// 客户端
public class Client {
public static void main(String[] args) {
DVDPlayer dvd = new DVDPlayer();
Projector projector = new Projector();
Lights lights = new Lights();
HomeTheaterFacade homeTheater = new HomeTheaterFacade(dvd, projector, lights);
homeTheater.watchMovie("The Matrix");
homeTheater.endMovie();
}
}
四、常用行为型模式
4.1 策略模式 (Strategy Pattern)
- 定义:定义一系列算法,将它们封装起来,并且使它们可以相互替换。策略模式可以使算法的变化独立于使用它的客户端。
- 应用场景:当一个对象具有多个行为,并且这些行为可以在运行时改变时;当一个算法有多种实现方式,需要在运行时动态切换时。
- 核心角色:
Strategy:抽象策略接口,定义所有具体策略的公共接口。ConcreteStrategy:具体策略类,实现 Strategy 接口,提供具体的算法实现。Context:上下文类,持有对 Strategy 对象的引用,并委托 Strategy 对象执行算法。
- 示例:不同支付方式。
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67// 1. 抽象策略接口 (支付策略)
interface PaymentStrategy {
void pay(double amount);
}
// 2. 具体策略 A (信用卡支付)
class CreditCardPayment implements PaymentStrategy {
private String cardNumber;
private String name;
public CreditCardPayment(String cardNumber, String name) {
this.cardNumber = cardNumber;
this.name = name;
}
public void pay(double amount) {
System.out.println("Paying " + amount + " using Credit Card (" + cardNumber + ")");
}
}
// 3. 具体策略 B (PayPal 支付)
class PayPalPayment implements PaymentStrategy {
private String email;
public PayPalPayment(String email) {
this.email = email;
}
public void pay(double amount) {
System.out.println("Paying " + amount + " using PayPal (" + email + ")");
}
}
// 4. 上下文类 (购物订单)
class ShoppingCart {
private PaymentStrategy paymentStrategy;
public void setPaymentStrategy(PaymentStrategy paymentStrategy) {
this.paymentStrategy = paymentStrategy;
}
public void checkout(double amount) {
if (paymentStrategy == null) {
System.out.println("No payment strategy selected.");
return;
}
paymentStrategy.pay(amount);
}
}
// 客户端
public class Client {
public static void main(String[] args) {
ShoppingCart cart = new ShoppingCart();
double itemPrice = 100.0;
// 使用信用卡支付
cart.setPaymentStrategy(new CreditCardPayment("1234-5678-9012-3456", "John Doe"));
cart.checkout(itemPrice); // Output: Paying 100.0 using Credit Card (1234-5678-9012-3456)
// 使用 PayPal 支付
cart.setPaymentStrategy(new PayPalPayment("john.doe@example.com"));
cart.checkout(itemPrice); // Output: Paying 100.0 using PayPal (john.doe@example.com)
}
}
4.2 观察者模式 (Observer Pattern)
- 定义:定义对象之间一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。
- 应用场景:当一个对象的改变需要同时改变其他对象,而不知道具体有多少对象需要改变时;事件处理系统。
- 核心角色:
Subject(或Observable):主题/被观察者,维护一系列观察者,提供注册和删除观察者的方法,并在状态改变时通知观察者。Observer:抽象观察者,定义一个更新接口,以便在主题状态改变时得到通知。ConcreteSubject:具体主题,存储有关它感兴趣的状态,并在其状态改变时向观察者发送通知。ConcreteObserver:具体观察者,实现 Observer 接口,维护一个指向ConcreteSubject对象的引用,存储它自己的状态,以便与主题的状态保持一致。
- 示例:新闻发布订阅。
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89import java.util.ArrayList;
import java.util.List;
// 1. 抽象主题 (被观察者)
interface Subject {
void attach(Observer observer); // 注册观察者
void detach(Observer observer); // 移除观察者
void notifyObservers(); // 通知观察者
}
// 2. 抽象观察者
interface Observer {
void update(String message); // 接收更新的方法
}
// 3. 具体主题 (新闻发布者)
class NewsPublisher implements Subject {
private List<Observer> observers = new ArrayList<>();
private String latestNews;
public void setLatestNews(String news) {
this.latestNews = news;
notifyObservers(); // 状态改变时通知所有观察者
}
public void attach(Observer observer) {
observers.add(observer);
System.out.println("Observer attached: " + observer.getClass().getSimpleName());
}
public void detach(Observer observer) {
observers.remove(observer);
System.out.println("Observer detached: " + observer.getClass().getSimpleName());
}
public void notifyObservers() {
System.out.println("\nNotifying subscribers about: " + latestNews);
for (Observer observer : observers) {
observer.update(latestNews);
}
}
}
// 4. 具体观察者 A (用户 A)
class UserA implements Observer {
private String name = "UserA";
public void update(String message) {
System.out.println(name + " received news: " + message);
}
}
// 5. 具体观察者 B (用户 B)
class UserB implements Observer {
private String name = "UserB";
public void update(String message) {
System.out.println(name + " received news: " + message);
}
}
// 客户端
public class Client {
public static void main(String[] args) {
NewsPublisher publisher = new NewsPublisher();
UserA userA = new UserA();
UserB userB = new UserB();
publisher.attach(userA);
publisher.attach(userB);
publisher.setLatestNews("Breaking News: New Feature Released!");
// Output:
// Notifying subscribers about: Breaking News: New Feature Released!
// UserA received news: Breaking News: New Feature Released!
// UserB received news: Breaking News: New Feature Released!
publisher.detach(userA);
publisher.setLatestNews("New Stock Market Report Available.");
// Output:
// Notifying subscribers about: New Stock Market Report Available.
// UserB received news: New Stock Market Report Available. (UserA 不再接收)
}
}
4.3 模板方法模式 (Template Method Pattern)
- 定义:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些特定步骤。
- 应用场景:当多个类中存在公共行为,但它们在不同地方行为又有所不同时;控制子类对特定操作的扩展。
- 核心角色:
AbstractClass:抽象类,定义了模板方法(包含算法的骨架)和抽象操作(子类实现)。ConcreteClass:具体类,实现AbstractClass中定义的抽象操作,以完成算法中由子类决定的步骤。
- 示例:制作饮料。
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60// 1. 抽象类 (制作饮料的模板)
abstract class BeverageTemplate {
// 模板方法,定义了制作饮料的通用算法骨架
public final void prepareBeverage() {
boilWater();
brew(); // 抽象方法,子类实现
pourInCup();
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();
}
// 2. 具体类 A (制作咖啡)
class Coffee extends BeverageTemplate {
protected void brew() {
System.out.println("Dripping Coffee through filter");
}
protected void addCondiments() {
System.out.println("Adding Sugar and Milk");
}
}
// 3. 具体类 B (制作茶)
class Tea extends BeverageTemplate {
protected void brew() {
System.out.println("Steeping the tea bag");
}
protected void addCondiments() {
System.out.println("Adding Lemon");
}
}
// 客户端
public class Client {
public static void main(String[] args) {
System.out.println("Preparing Coffee:");
BeverageTemplate coffeeMaker = new Coffee();
coffeeMaker.prepareBeverage(); // Output: Boiling water -> Dripping Coffee... -> Pouring into cup -> Adding Sugar and Milk
System.out.println("\nPreparing Tea:");
BeverageTemplate teaMaker = new Tea();
teaMaker.prepareBeverage(); // Output: Boiling water -> Steeping the tea bag -> Pouring into cup -> Adding Lemon
}
}
五、设计模式的原则
设计模式的背后是六大设计原则,它们是实现高内聚、低耦合、可维护、可扩展软件的基础:
- 单一职责原则 (Single Responsibility Principle - SRP):一个类只负责一个职责。
- 开闭原则 (Open/Closed Principle - OCP):对扩展开放,对修改关闭。
- 里氏替换原则 (Liskov Substitution Principle - LSP):子类型必须能够替换掉它们的基类型。
- 依赖倒置原则 (Dependency Inversion Principle - DIP):高层模块不应该依赖低层模块,它们都应该依赖抽象。抽象不应该依赖于细节,细节应该依赖于抽象。
- 接口隔离原则 (Interface Segregation Principle - ISP):客户端不应该被迫依赖于它不使用的方法;一个类对另一个类的依赖应该建立在最小的接口上。
- 迪米特法则 (Law of Demeter - LoD) / 最少知识原则:一个对象应该对其他对象有最少的了解。
六、总结
设计模式是软件开发的宝贵经验总结,它们为解决常见问题提供了经过验证的解决方案。理解并掌握设计模式,不仅能够帮助我们写出更优雅、更健壮、更易于维护的代码,还能提升我们分析问题和设计系统的能力。然而,设计模式并非银弹,不应为了使用模式而使用模式,而应在理解问题本质后,选择最合适的模式。最佳实践是,从简单入手,当问题复杂到需要模式来解决时,再逐步引入。
通过学习这些常用设计模式,开发者可以更好地理解面向对象设计思想,并将其应用到实际的项目中,从而创造出高质量的软件产品。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 1024 维度!
