经典设计模式
# 经典设计模式
设计模式通常分为三大类:创建型模式、结构型模式和行为型模式。下面是这三大类的简要介绍及其常见模式:
# 创建型模式
创建型模式主要关注对象的创建过程,通过使用这些模式,可以更灵活和高效地创建对象。这类模式提供了从类的实例化中分离出对象创建的细节。
常见的创建型模式包括:
工厂方法模式 (Factory Method):定义一个用于创建对象的接口,但让子类决定要实例化的类。
抽象工厂模式 (Abstract Factory):提供一个接口,用于创建一系列相关或相互依赖的对象,而无需指定它们具体的类。
建造者模式 (Builder):使用多个简单的对象一步一步构建一个复杂的对象。
原型模式 (Prototype):通过复制现有对象来创建新对象,而不是通过构造函数来创建。
单例模式 (Singleton):确保一个类只有一个实例,并提供一个全局访问点。
# 结构型模式
结构型模式主要关注如何将类或对象组合在一起以形成更大的结构。它们帮助确保如果一个部分发生变化,整体结构不会受到太大影响。
常见的结构型模式包括:
- 适配器模式 (Adapter):允许将一个接口转换成客户端期望的另一种接口。
- 桥接模式 (Bridge):将抽象部分与实现部分分离,以便它们可以独立地变化。
- 组合模式 (Composite):允许将对象组合成树形结构以表示部分-整体层次结构。
- 装饰模式 (Decorator):通过将新功能添加到对象来扩展其行为,而不改变其结构。
- 外观模式 (Facade):提供一个简单的接口来访问复杂的子系统。
- 享元模式 (Flyweight):通过共享对象来支持大量细粒度的对象。
- 代理模式(Proxy ):一个类代表另一个类的功能。
# 行为型模式
行为型模式主要关注对象之间的交互和责任。它们定义了对象之间的通信方式,并提高了程序的灵活性和可扩展性。
常见的行为型模式包括:
- 责任链模式(Chain of Responsibility):请求沿着一条处理者链传递,直到某个处理者处理它。如果第一个处理者处理不了请求,它会把请求传递给下一个处理者,依此类推。
- 命令模式(Command):将请求封装成对象,以便用不同请求、队列或日志来参数化对象,并支持撤销操作。
- 解释器模式(Interpreter):为语言创建解释器来解释语言中的句子或表达式,适合对某些规则进行解析。
- 迭代器模式(Iterator):提供一种方法来顺序访问一个聚合对象中的各个元素,而又不暴露对象的内部表示。
- 中介者模式(Mediator):通过一个中介对象来集中控制多个对象之间的通信,以减少对象之间的直接依赖。
- 备忘录模式(Memento):保存对象的状态,以便在未来某个时间恢复到原来的状态,适用于提供撤销功能。
- 观察者模式(Observer):定义了对象间的一对多依赖关系,当一个对象改变状态时,所有依赖于它的对象都能得到通知并自动更新。
- 状态模式(State):允许对象在内部状态发生改变时改变其行为,看起来像是改变了对象的类。
- 空对象模式(Null Object):用一个空对象来代替
null,以避免检查null引起的错误。 - 策略模式(Strategy):定义一系列算法,将它们分别封装起来,并使它们可以互相替换,适用于根据情况选择不同算法的场景。
- 模板模式(Template):定义一个算法的框架,让子类可以在不改变算法结构的情况下重定义算法的某些步骤。
- 访问者模式(Visitor):为一个对象结构中的每个元素创建一个新操作,让你可以在不改变对象结构的前提下定义新的操作。
# 创建型模式
# 工厂方法模式 (Factory Method)
工厂模式是一种创建对象的设计模式,它可以在不明确指定对象具体类型的情况下创建对象,常用于降低代码的耦合度、提高灵活性和可维护性。
# 代码实现案例
- 接口或抽象类:首先定义一个抽象的产品接口(
Product),所有具体的产品类都要实现这个接口(如ConcreteProductA和ConcreteProductB)。 - 具体实现类:不同的产品(例如
ConcreteProductA和ConcreteProductB)实现了同一个接口,提供不同的功能。 - 工厂类:工厂类(
ProductFactory)提供一个静态方法createProduct(String type),根据传入的参数创建不同的产品对象。 - 客户端代码:客户端只需要调用工厂方法,而不需要知道如何创建具体的产品对象,从而解耦了对象创建的过程。
interface Product {
void create();
}
class ConcreteProductA implements Product {
@Override
public void create() {
System.out.println("ConcreteProductA is created");
}
}
class ConcreteProductB implements Product {
@Override
public void create() {
System.out.println("ConcreteProductB is created");
}
}
// 工厂类
class ProductFactory {
public static Product createProduct(String type) {
if (type.equalsIgnoreCase("A")) {
return new ConcreteProductA();
} else if (type.equalsIgnoreCase("B")) {
return new ConcreteProductB();
} else {
throw new IllegalArgumentException("Unknown product type");
}
}
}
// 客户端代码
public class FactoryPatternDemo {
public static void main(String[] args) {
Product productA = ProductFactory.createProduct("A");
productA.create();
Product productB = ProductFactory.createProduct("B");
productB.create();
}
}
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
# 常用具体使用场景
- 日志记录器:根据配置文件决定记录到文件、控制台还是数据库,可以通过工厂创建对应的日志记录器。
- 数据库连接:根据不同的数据库(如 MySQL、PostgreSQL)创建不同的数据库连接。
- 数据解析器:不同的数据格式(如 XML、JSON)使用不同的解析器,通过工厂模式可以根据文件类型创建相应的解析器对象。
- 图形控件:GUI 程序中,通过工厂模式来创建按钮、文本框等不同的图形控件,可以根据需求创建不同风格的控件对象。
# Java核心库应用场景
核心 Java 程序库中有该模式的应用:
java.util.Calendar#getInstance()(opens new window)java.util.ResourceBundle#getBundle()(opens new window)java.text.NumberFormat#getInstance()(opens new window)java.nio.charset.Charset#forName()(opens new window)java.net.URLStreamHandlerFactory#createURLStreamHandler(String)(opens new window) (根据协议返回不同的单例对象)java.util.EnumSet#of()(opens new window)javax.xml.bind.JAXBContext#createMarshaller()(opens new window) 及其他类似的方法。
# 抽象工厂方法模式 (Abstract Factory)
抽象工厂模式适用于需要创建一组相关或者互相依赖的对象的场景,同时希望通过工厂接口来隐藏对象创建的具体实现,以确保系统灵活且符合开闭原则。这种模式特别适合用于构建多个产品族,并确保同一产品族的产品可以一起使用,而不会产生兼容性问题。
# 代码实现案例
- 抽象产品接口:定义多个抽象产品接口(例如
Chair和Table),这些接口代表同一产品族的不同产品。 - 具体产品类:为每个抽象产品定义多个具体实现类(例如
VictorianChair、ModernChair等),它们是不同产品族的实现。 - 抽象工厂接口:定义一个抽象工厂接口(
FurnitureFactory),声明用于创建一组相关产品的方法。 - 具体工厂类:每个具体工厂类实现抽象工厂接口,负责创建特定产品族中的产品(例如
VictorianFurnitureFactory、ModernFurnitureFactory)。 - 客户端代码:客户端通过使用抽象工厂接口创建相关的产品对象,而不直接依赖于具体实现类。
interface Chair {
void create();
}
class VictorianChair implements Chair {
@Override
public void create() {
System.out.println("Victorian Chair is created");
}
}
class ModernChair implements Chair {
@Override
public void create() {
System.out.println("Modern Chair is created");
}
}
interface Table {
void create();
}
class VictorianTable implements Table {
@Override
public void create() {
System.out.println("Victorian Table is created");
}
}
class ModernTable implements Table {
@Override
public void create() {
System.out.println("Modern Table is created");
}
}
// 抽象工厂接口
interface FurnitureFactory {
Chair createChair();
Table createTable();
}
// 具体工厂类
class VictorianFurnitureFactory implements FurnitureFactory {
@Override
public Chair createChair() {
return new VictorianChair();
}
@Override
public Table createTable() {
return new VictorianTable();
}
}
class ModernFurnitureFactory implements FurnitureFactory {
@Override
public Chair createChair() {
return new ModernChair();
}
@Override
public Table createTable() {
return new ModernTable();
}
}
// 客户端代码
public class AbstractFactoryPatternDemo {
public static void main(String[] args) {
FurnitureFactory victorianFactory = new VictorianFurnitureFactory();
Chair victorianChair = victorianFactory.createChair();
Table victorianTable = victorianFactory.createTable();
victorianChair.create();
victorianTable.create();
FurnitureFactory modernFactory = new ModernFurnitureFactory();
Chair modernChair = modernFactory.createChair();
Table modernTable = modernFactory.createTable();
modernChair.create();
modernTable.create();
}
}
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
# 常用具体使用场景
- 跨平台 UI 工具包:当你需要为不同的平台(如 Windows、macOS、Linux)提供不同风格的用户界面组件时,可以使用抽象工厂来创建特定平台的 UI 控件工厂。
- 产品族:当产品之间存在依赖关系,并且要求使用相同产品族中的配件时,比如在游戏中创建不同风格的武器和防具(例如“精灵”风格和“矮人”风格),可以使用抽象工厂创建相关的产品族。
- 数据库访问层:当系统需要支持多种数据库(如 MySQL、PostgreSQL、Oracle)时,可以使用抽象工厂来创建不同数据库类型的连接和相关操作对象。
- 测试和生产环境:如果你需要为应用提供测试和生产环境中不同的实现(如不同的数据源、配置等),可以使用抽象工厂模式为测试和生产环境生成相关的实例。
# Java核心库应用场景
javax.xml.parsers.DocumentBuilderFactory#newInstance()(opens new window)javax.xml.transform.TransformerFactory#newInstance()(opens new window)javax.xml.xpath.XPathFactory#newInstance()(opens new window)
# 建造者模式 (Builder)
建造者模式的核心思想是将对象的构建过程和表示分离,允许你创建复杂对象的不同表示。它适用于构建复杂对象、需要多种配置、以及有多步骤创建过程的场景,能够简化代码结构,提高可读性和灵活性。
# 代码实现案例
- 产品类(Product):首先定义一个复杂对象(
House),它有多个组成部分。House提供了设置各部分的方法(setFoundation、setStructure等)。 - 抽象建造者(Builder):定义一个
HouseBuilder接口,包含构造产品各部分的方法,以及返回最终产品的方法。 - 具体建造者(Concrete Builder):实现
HouseBuilder接口的ConcreteHouseBuilder类负责构建各个部分的具体实现,并最终返回完整的产品。 - 指挥者(Director):
ConstructionEngineer类负责控制建造的过程,调用建造者的各个方法来完成产品的建造。 - 客户端代码:客户端通过指挥者类来调用具体建造者完成对象的构建。
// 产品类
class House {
private String foundation;
private String structure;
private String roof;
private String interior;
public void setFoundation(String foundation) {
this.foundation = foundation;
}
public void setStructure(String structure) {
this.structure = structure;
}
public void setRoof(String roof) {
this.roof = roof;
}
public void setInterior(String interior) {
this.interior = interior;
}
@Override
public String toString() {
return "House [foundation=" + foundation + ", structure=" + structure + ", roof=" + roof + ", interior=" + interior + "]";
}
}
// 抽象建造者
interface HouseBuilder {
void buildFoundation();
void buildStructure();
void buildRoof();
void buildInterior();
House getHouse();
}
// 具体建造者类
class ConcreteHouseBuilder implements HouseBuilder {
private House house;
public ConcreteHouseBuilder() {
this.house = new House();
}
@Override
public void buildFoundation() {
house.setFoundation("Concrete, iron rods, and sand");
System.out.println("ConcreteHouseBuilder: Foundation complete.");
}
@Override
public void buildStructure() {
house.setStructure("Concrete and bricks");
System.out.println("ConcreteHouseBuilder: Structure complete.");
}
@Override
public void buildRoof() {
house.setRoof("Concrete roof");
System.out.println("ConcreteHouseBuilder: Roof complete.");
}
@Override
public void buildInterior() {
house.setInterior("Paint, tiles");
System.out.println("ConcreteHouseBuilder: Interior complete.");
}
@Override
public House getHouse() {
return this.house;
}
}
// 指挥者类
class ConstructionEngineer {
private HouseBuilder houseBuilder;
public ConstructionEngineer(HouseBuilder houseBuilder) {
this.houseBuilder = houseBuilder;
}
public House constructHouse() {
this.houseBuilder.buildFoundation();
this.houseBuilder.buildStructure();
this.houseBuilder.buildRoof();
this.houseBuilder.buildInterior();
return this.houseBuilder.getHouse();
}
}
// 客户端代码
public class BuilderPatternDemo {
public static void main(String[] args) {
HouseBuilder builder = new ConcreteHouseBuilder();
ConstructionEngineer engineer = new ConstructionEngineer(builder);
House house = engineer.constructHouse();
System.out.println("House is: " + house);
}
}
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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# 常用具体使用场景
- 复杂对象的创建:当创建一个对象需要经过多步骤的复杂过程,或者有很多字段和参数时,使用建造者模式可以一步一步地构建对象,避免构造函数参数太多的问题。
- 配置对象:当一个对象有多种不同的配置方式(例如家具有不同的风格和材质),可以使用建造者模式进行不同的配置,以便更灵活地创建对象。
- 不可变对象:当构造不可变对象时(如 Java 的
StringBuilder),可以使用建造者模式按需设置对象的各个部分,最后生成对象。 - 流畅接口(Fluent Interface):链式调用时,如 Java 的
StringBuilder,可以用建造者模式实现流畅的 API,使代码更具可读性和易于使用。
# Java核心库应用场景
java.lang.StringBuilder#append()(opens new window) (非同步)java.lang.StringBuffer#append()(opens new window) (同步)java.nio.ByteBuffer#put()(opens new window) (还有CharBuffer(opens new window)、ShortBuffer(opens new window)、IntBuffer(opens new window)、LongBuffer(opens new window)、FloatBuffer(opens new window) 和DoubleBuffer(opens new window))javax.swing.GroupLayout.Group#addComponent()(opens new window)java.lang.Appendable(opens new window)的所有实现
# 原型模式 (Prototype)
原型模式的核心思想是通过克隆已有对象来创建新的对象,从而减少创建复杂对象的成本。它适用于需要创建结构复杂或成本高昂的对象、隐藏对象创建过程、频繁创建相似对象等场景。使用原型模式可以提高对象创建的灵活性和效率,避免类层次结构的过度扩展。
# 代码实现案例
- 实现 Cloneable 接口:在 Java 中实现原型模式,类必须实现
Cloneable接口,并提供自己的clone方法。Cloneable接口只是一个标记接口,它告诉 JVM 该对象可以被安全地克隆。 - 重写
clone方法:在重写clone方法时,要处理可能的CloneNotSupportedException异常,并返回对象的深复制或浅复制。 - 原型注册管理:使用一个原型管理器(如
PrototypeRegistry),它保存了所有可供克隆的对象,以便通过原型创建对象时更加灵活。 - 简化复杂对象的创建:通过原型模式可以简化复杂对象的创建过程,因为直接克隆对象比手动创建新的对象要高效得多。
import java.util.HashMap;
import java.util.Map;
// 抽象原型接口
interface Prototype extends Cloneable {
Prototype clone();
}
// 具体实现类A
class ConcretePrototypeA implements Prototype {
private String name;
public ConcretePrototypeA(String name) {
this.name = name;
}
@Override
public Prototype clone() {
try {
return (ConcretePrototypeA) super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException("Clone not supported", e);
}
}
@Override
public String toString() {
return "ConcretePrototypeA{name='" + name + "'}";
}
}
// 具体实现类B
class ConcretePrototypeB implements Prototype {
private int value;
public ConcretePrototypeB(int value) {
this.value = value;
}
@Override
public Prototype clone() {
try {
return (ConcretePrototypeB) super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException("Clone not supported", e);
}
}
@Override
public String toString() {
return "ConcretePrototypeB{value=" + value + "}";
}
}
// 原型管理器
class PrototypeRegistry {
private Map<String, Prototype> prototypes = new HashMap<>();
public void addPrototype(String key, Prototype prototype) {
prototypes.put(key, prototype);
}
public Prototype getPrototype(String key) {
Prototype prototype = prototypes.get(key);
if (prototype != null) {
return prototype.clone();
}
throw new IllegalArgumentException("Prototype not found for key: " + key);
}
}
// 客户端代码
public class PrototypePatternDemo {
public static void main(String[] args) {
PrototypeRegistry registry = new PrototypeRegistry();
registry.addPrototype("A", new ConcretePrototypeA("Prototype A"));
registry.addPrototype("B", new ConcretePrototypeB(42));
Prototype cloneA = registry.getPrototype("A");
Prototype cloneB = registry.getPrototype("B");
System.out.println(cloneA);
System.out.println(cloneB);
}
}
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
# 常用具体使用场景
- 创建成本高昂的对象:当对象的创建成本非常高(包括计算复杂性、资源消耗等),可以通过克隆来复制已有对象,减少创建时间。
- 对象结构复杂:如果对象结构复杂,具有大量相互关联的子对象,通过克隆可以避免逐一创建和配置每个子对象。
- 希望隐藏对象的创建过程:如果对象的创建过程非常复杂或者需要隐私保护,通过原型模式可以将对象的创建隐藏在克隆中,而不暴露具体的创建细节。
- 需要频繁地创建相似对象:当系统需要频繁地创建相似但不完全相同的对象时,使用原型模式可以有效避免重复代码并提高效率。
- 避免类层次结构的膨胀:原型模式可以避免为不同配置创建子类,减少系统中的类数量,通过克隆现有对象来生成不同配置的实例。
# Java核心库应用场景
# 单例模式 (Singleton)
单例模式确保一个类只有一个实例,并提供一个全局的访问点。它适用于需要唯一对象的场景,例如全局配置管理、日志记录器、数据库连接池等。它有助于减少资源的消耗,并且在需要协调某些共享资源时非常有效,但需要注意线程安全问题的处理。
# 代码实现案例
- 私有构造函数:通过将构造函数设为私有,防止其他类通过
new关键字来创建该类的对象,确保只有一个实例。 - 私有静态实例变量:保存单例类的唯一实例,通过
volatile关键字保证变量的可见性和线程安全性。 - 公有静态方法:提供一个全局访问点来获取单例实例。使用双重检查锁定(Double-Checked Locking)机制来确保线程安全并减少同步开销。
- 线程安全性:使用
synchronized和双重检查机制保证在多线程环境下只创建一个实例。
单例模式的其他几种实现方式可参考:菜鸟设计模式单例模式 (opens new window)
class Singleton {
// 私有静态变量保存单例实例
private static volatile Singleton instance;
// 私有构造函数,防止外部实例化
private Singleton() {
if (instance != null) {
throw new RuntimeException("Use getInstance() method to get the single instance of this class.");
}
}
// 公共静态方法返回单例实例
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
// 示例方法
public void showMessage() {
System.out.println("Singleton instance is working");
}
}
public class SingletonPatternDemo {
public static void main(String[] args) {
Singleton singleton = Singleton.getInstance();
singleton.showMessage();
}
}
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
# 常用具体使用场景
- 配置管理:例如读取配置信息的类,只需要一个实例来共享读取的配置信息,避免多次读取。
- 日志记录器:在整个应用中保持一个全局的日志实例,确保日志记录的集中管理和线程安全。
- 数据库连接池:创建和管理数据库连接的对象通常使用单例模式,因为数据库连接的创建和销毁开销较大,使用单例可以确保全局使用同一个连接池。
- 线程池:在多线程环境中,线程池类可以用单例来维护一个共享的线程池,方便任务管理和分配。
- 控制硬件资源访问:例如打印机、文件系统等硬件资源,使用单例来控制访问,防止同时进行多个不兼容的操作。
# Java核心库应用场景
java.lang.Runtime#getRuntime()(opens new window)java.awt.Desktop#getDesktop()(opens new window)java.lang.System#getSecurityManager()(opens new window)
# 结构型模式
# 适配器模式 (Adapter)
适配器模式是一种结构型设计模式,用于将一个类的接口转换为客户端所期望的另一种接口,确保不兼容的接口能够协同工作。适配器模式常用于旧系统和新系统集成、第三方库的适配、不兼容数据格式的转换、不同协议之间的对接等场景。通过适配器模式,可以在不修改现有代码的情况下,使系统具有更高的灵活性和复用性。
# 代码实现案例
- 目标接口 (
Target):定义客户所期待的接口,所有想要对接的系统都需要实现这个接口。 - 不兼容的类 (
Adaptee):这是已经存在且不符合目标接口的类,它具有需要适配的方法(例如specificRequest)。 - 适配器类 (
Adapter):实现目标接口并通过组合的方式持有Adaptee类的引用,将目标接口的方法调用转发到Adaptee的方法,完成接口的适配。 - 客户端代码:客户端代码通过目标接口调用适配器类,从而实现对不兼容接口的访问。
// 目标接口
interface Target {
void request();
}
// 已存在的不兼容的类
class Adaptee {
public void specificRequest() {
System.out.println("Adaptee specific request");
}
}
// 适配器类
class Adapter implements Target {
private Adaptee adaptee;
public Adapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
// 将 Target 的 request 方法适配为 Adaptee 的 specificRequest 方法
adaptee.specificRequest();
}
}
// 客户端代码
public class AdapterPatternDemo {
public static void main(String[] args) {
Adaptee adaptee = new Adaptee();
Target adapter = new Adapter(adaptee);
adapter.request(); // 输出: Adaptee specific request
}
}
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
# 常用的使用场景
- 旧系统与新系统的集成:当需要将一个旧系统集成到新系统中,而它们的接口不兼容时,可以使用适配器模式来桥接它们,使旧系统的功能可以被新系统调用。
- 第三方库的适配:当你想使用一个第三方库,但其接口与现有系统不兼容时,可以通过适配器模式将第三方库的接口转换为系统可以理解的形式。
- 兼容不同的数据格式:当需要对接不同的数据来源,并且这些数据来源的格式不一致时,可以使用适配器模式将它们统一为应用可以理解的格式。
- 不同协议之间的转换:在网络编程中,不同协议之间的转换可以通过适配器模式实现,以确保各组件之间能够顺畅通信。
# Java核心库应用场景
java.util.Arrays#asList()(opens new window)java.util.Collections#list()(opens new window)java.util.Collections#enumeration()(opens new window)java.io.InputStreamReader(InputStream)(opens new window) (返回Reader对象)java.io.OutputStreamWriter(OutputStream)(opens new window) (返回Writer对象)javax.xml.bind.annotation.adapters.XmlAdapter#marshal()(opens new window) 和#unmarshal()
# 桥接模式 (Bridge)
桥接模式的核心思想是将抽象部分与实现部分分离,使它们可以独立地变化。这种模式特别适用于希望抽象和实现可以自由组合而且独立变化的场景,符合“开闭原则”。它可以减少代码之间的耦合,增强灵活性和可扩展性。桥接模式通过组合而不是继承来实现功能的扩展,从而使得代码更加灵活和可维护。
# 代码实现案例
- 实现部分接口(Implementor):定义实现部分的接口,负责实现抽象部分的基础操作。
ConcreteImplementorA和ConcreteImplementorB是具体的实现部分。 - 抽象部分(Abstraction):定义抽象类
Abstraction,它包含一个对实现部分接口的引用 (Implementor),并声明抽象操作方法operation()。 - 扩展抽象类(RefinedAbstraction):扩展抽象部分,可以为其增加额外的行为或者修改现有的行为。
- 客户端代码:客户端可以独立地选择抽象部分和实现部分的不同组合,桥接模式的核心是通过组合来解耦抽象部分和实现部分,从而让它们独立变化。
// 实现部分接口
interface Implementor {
void operationImpl();
}
class ConcreteImplementorA implements Implementor {
@Override
public void operationImpl() {
System.out.println("ConcreteImplementorA operation implemented");
}
}
class ConcreteImplementorB implements Implementor {
@Override
public void operationImpl() {
System.out.println("ConcreteImplementorB operation implemented");
}
}
// 抽象部分
abstract class Abstraction {
protected Implementor implementor;
protected Abstraction(Implementor implementor) {
this.implementor = implementor;
}
public abstract void operation();
}
class RefinedAbstraction extends Abstraction {
public RefinedAbstraction(Implementor implementor) {
super(implementor);
}
@Override
public void operation() {
System.out.print("RefinedAbstraction operation with: ");
implementor.operationImpl();
}
}
// 客户端代码
public class BridgePatternDemo {
public static void main(String[] args) {
Implementor implementorA = new ConcreteImplementorA();
Abstraction abstractionA = new RefinedAbstraction(implementorA);
abstractionA.operation();
Implementor implementorB = new ConcreteImplementorB();
Abstraction abstractionB = new RefinedAbstraction(implementorB);
abstractionB.operation();
}
}
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
# 常用的使用场景
- 图形绘制系统:可以将图形(如形状)和绘制(如显示在屏幕或打印机上)分开,使形状和绘制实现各自独立变化。
- 设备控制系统:遥控器和不同类型的设备之间的桥接。遥控器(抽象部分)可以控制电视、音响等设备(实现部分),抽象部分和实现部分可以独立扩展。
- 跨平台应用开发:可以将业务逻辑和平台细节分开。例如,UI 层次结构可以使用抽象部分,而具体的渲染逻辑(针对 Windows、Linux、macOS 等)使用实现部分。
- 数据库访问层:可以将数据操作与底层数据库(如 MySQL、Oracle)实现分开。业务操作可以作为抽象部分,具体数据库的连接和执行实现可以作为实现部分。
# Java核心库应用场景
# 组合模式 (Composite)
组合模式通过将对象组织成树形结构来表示整体和部分之间的层次关系,适合用于需要以相同方式处理单个对象和组合对象的场景。它的核心思想是通过组合对象和叶子对象实现递归结构,使得客户端可以一致地操作单个对象和组合对象,从而提高系统的灵活性和可扩展性。
# 代码实现案例
- 组件接口(
Component):定义一个接口,用于声明叶子节点和组合节点的通用方法,比如showDetails()。 - 叶子节点(
Leaf):实现组件接口,代表没有子节点的对象。在showDetails()方法中仅显示叶子节点的名称。 - 组合节点(
Composite):实现组件接口,代表可以有子节点的对象。组合节点可以包含叶子节点和其他组合节点,通过add()和remove()方法来管理子节点。 - 客户端代码:客户端使用组件接口来处理叶子节点和组合节点,从而可以忽略它们之间的差异。
import java.util.ArrayList;
import java.util.List;
// 组件接口
interface Component {
void showDetails();
}
// 叶子节点
class Leaf implements Component {
private String name;
public Leaf(String name) {
this.name = name;
}
@Override
public void showDetails() {
System.out.println("Leaf: " + name);
}
}
// 组合节点
class Composite implements Component {
private String name;
private List<Component> children = new ArrayList<>();
public Composite(String name) {
this.name = name;
}
public void add(Component component) {
children.add(component);
}
public void remove(Component component) {
children.remove(component);
}
@Override
public void showDetails() {
System.out.println("Composite: " + name);
for (Component child : children) {
child.showDetails();
}
}
}
// 客户端代码
public class CompositePatternDemo {
public static void main(String[] args) {
// 创建叶子节点
Leaf leaf1 = new Leaf("Leaf 1");
Leaf leaf2 = new Leaf("Leaf 2");
Leaf leaf3 = new Leaf("Leaf 3");
// 创建组合节点
Composite composite1 = new Composite("Composite 1");
Composite composite2 = new Composite("Composite 2");
// 将叶子节点添加到组合节点中
composite1.add(leaf1);
composite1.add(leaf2);
composite2.add(leaf3);
composite2.add(composite1);
// 显示组合结构的详细信息
composite2.showDetails();
}
}
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
# 常用的使用场景
- 文件系统:在文件系统中,文件和文件夹有着类似的行为。文件夹可以包含文件和其他子文件夹,而文件则是叶子节点。组合模式可以用于实现文件和文件夹的管理。
- 图形处理:在图形应用中,图形元素可以是简单的几何图形(如圆、方形等),也可以是由多个图形组合而成的复杂图形。使用组合模式可以方便地管理图形结构。
- 菜单系统:在 GUI 应用中,菜单和子菜单之间是包含关系,可以使用组合模式表示菜单和子菜单的层次结构,方便递归遍历和操作。
- 组织结构:在组织结构中,公司包含部门,部门包含员工,可以使用组合模式来表示公司、部门和员工之间的包含关系。
- 树形结构数据表示:组合模式特别适用于表示树形结构的数据,例如 DOM 树、公司管理层次结构等。
# Java核心库应用场景
java.awt.Container#add(Component)(opens new window) (几乎广泛存在于 Swing 组件中)javax.faces.component.UIComponent#getChildren()(opens new window) (几乎广泛存在于 JSF UI 组件中)
# 装饰模式 (Decorator)
装饰模式的核心思想是通过组合而非继承来为对象添加行为,这种方式特别适合需要动态地、灵活地扩展对象功能的场景。它可以避免类爆炸问题,也符合面向对象设计中的开闭原则,使得系统更具扩展性和灵活性。
# 代码实现案例
- 抽象组件接口:定义一个基础组件接口(例如
Beverage),声明一些基本的方法,这些方法可以被具体组件实现,也可以被装饰器扩展。 - 具体组件类:具体组件类(如
Espresso和HouseBlend)实现了基础组件接口,定义了对象的核心功能。 - 抽象装饰器类:定义一个抽象装饰器类(如
BeverageDecorator),它实现了基础组件接口,并持有一个Beverage对象的引用,方便动态添加功能。 - 具体装饰器类:具体装饰器类(如
Mocha和Whip)继承抽象装饰器类,并扩展其功能,添加额外的行为和状态。 - 客户端代码:客户端通过组合具体组件类和装饰器类来构建具有额外功能的对象,且可以灵活地添加或去除装饰器。
// 抽象组件接口
interface Beverage {
String getDescription();
double cost();
}
// 具体组件类
class Espresso implements Beverage {
@Override
public String getDescription() {
return "Espresso";
}
@Override
public double cost() {
return 1.99;
}
}
class HouseBlend implements Beverage {
@Override
public String getDescription() {
return "House Blend Coffee";
}
@Override
public double cost() {
return 0.89;
}
}
// 抽象装饰器类
abstract class BeverageDecorator implements Beverage {
protected Beverage beverage;
public BeverageDecorator(Beverage beverage) {
this.beverage = beverage;
}
@Override
public String getDescription() {
return beverage.getDescription();
}
@Override
public double cost() {
return beverage.cost();
}
}
// 具体装饰器类
class Mocha extends BeverageDecorator {
public Mocha(Beverage beverage) {
super(beverage);
}
@Override
public String getDescription() {
return beverage.getDescription() + ", Mocha";
}
@Override
public double cost() {
return beverage.cost() + 0.20;
}
}
class Whip extends BeverageDecorator {
public Whip(Beverage beverage) {
super(beverage);
}
@Override
public String getDescription() {
return beverage.getDescription() + ", Whip";
}
@Override
public double cost() {
return beverage.cost() + 0.30;
}
}
// 客户端代码
public class DecoratorPatternDemo {
public static void main(String[] args) {
Beverage beverage = new Espresso();
System.out.println(beverage.getDescription() + " $" + beverage.cost());
Beverage beverage2 = new HouseBlend();
beverage2 = new Mocha(beverage2);
beverage2 = new Whip(beverage2);
System.out.println(beverage2.getDescription() + " $" + beverage2.cost());
}
}
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
89
90
91
92
93
94
95
# 常用的使用场景
- 动态增加功能:当需要为对象动态添加一些额外的行为或责任时,可以使用装饰模式,而不是通过子类扩展来增加功能。
- 遵循开闭原则:装饰模式允许你在不修改现有代码的情况下,为对象添加新功能,从而符合开闭原则。
- 复杂对象的灵活组合:当对象有很多可选功能,并且这些功能可以任意组合时,通过装饰模式可以避免创建大量的子类。
- 替代继承:当有多个功能需要添加而不希望通过多层次的继承来实现时,装饰模式提供了一种更灵活的方式。
# Java核心库应用场景
java.io.InputStream(opens new window)、OutputStream(opens new window)、Reader(opens new window) 和Writer(opens new window) 的所有代码都有以自身类型的对象作为参数的构造函数。java.util.Collections(opens new window);checkedXXX()(opens new window)、synchronizedXXX()(opens new window) 和unmodifiableXXX()(opens new window) 方法。javax.servlet.http.HttpServletRequestWrapper(opens new window) 和HttpServletResponseWrapper(opens new window)
# 外观模式 (Facade)
外观模式是一种结构型设计模式,用于为复杂的子系统提供一个简单的接口。它隐藏了子系统的复杂性,降低了客户端与子系统之间的耦合性,提高了代码的可维护性和可扩展性。常见应用场景包括简化系统操作、模块化解耦、提供统一接口以及系统启动初始化等。
# 代码实现案例
- 子系统类:系统中存在多个子系统类,每个子系统类提供一些特定的功能(例如
SubsystemA、SubsystemB、SubsystemC)。 - 外观类:外观类(
Facade)是对多个子系统的封装,它提供了一个简单的接口用于客户端调用。外观类内部持有对各个子系统的引用,并在外观方法中协调子系统的操作。 - 客户端代码:客户端通过调用外观类提供的方法来执行操作,隐藏了内部子系统的复杂性。
// 子系统 A
class SubsystemA {
public void operationA() {
System.out.println("SubsystemA: Operation A executed");
}
}
// 子系统 B
class SubsystemB {
public void operationB() {
System.out.println("SubsystemB: Operation B executed");
}
}
// 子系统 C
class SubsystemC {
public void operationC() {
System.out.println("SubsystemC: Operation C executed");
}
}
// 外观类
class Facade {
private SubsystemA subsystemA;
private SubsystemB subsystemB;
private SubsystemC subsystemC;
public Facade() {
this.subsystemA = new SubsystemA();
this.subsystemB = new SubsystemB();
this.subsystemC = new SubsystemC();
}
public void performOperations() {
subsystemA.operationA();
subsystemB.operationB();
subsystemC.operationC();
}
}
// 客户端代码
public class FacadePatternDemo {
public static void main(String[] args) {
Facade facade = new Facade();
facade.performOperations();
}
}
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
# 常用的使用场景
- 简化复杂系统的使用:当系统内部有多个模块需要交互,外观模式可以简化客户端的调用,隐藏系统的复杂性,使系统使用起来更加简单。
- 统一接口:外观模式可以为子系统提供一个统一的接口,方便客户端调用而不用直接和多个复杂子系统交互。
- 模块化解耦:通过使用外观模式,客户端和子系统之间可以有效解耦,客户端代码不会直接依赖于子系统的细节,这样系统的维护和扩展性更好。
- 系统初始化和启动:在应用程序启动时,如果有多个步骤或初始化模块需要处理,外观模式可以提供一个单一的初始化接口来执行所有的启动任务。
# Java核心库应用场景
javax.faces.context.FacesContext(opens new window) 在底层使用了LifeCycle(opens new window)、ViewHandler(opens new window) 和NavigationHandler(opens new window) 这几个类, 但绝大多数客户端不知道。javax.faces.context.ExternalContext(opens new window) 在内部使用了ServletContext(opens new window)、HttpSession(opens new window)、HttpServletRequest(opens new window)、HttpServletResponse(opens new window) 和其他一些类。
# 享元模式 (Flyweight)
享元模式通过共享对象来减少内存消耗,适用于系统中存在大量相似对象并需要节省内存的场景。通过将对象的状态划分为内部和外部状态,可以减少对象的数量,优化内存使用。在高并发场景或者有大量重复对象的应用中,享元模式非常有效。
# 代码实现案例
- 抽象享元接口:定义一个抽象接口(
Shape),为所有具体享元类提供方法,确保所有具体享元对象具备相同的行为。 - 具体享元类:实现抽象享元接口(如
Circle),并将外部状态通过方法参数进行传递,内部状态则保持不变,以便共享。 - 享元工厂类:
ShapeFactory负责维护享元对象的池,确保共享对象的复用。如果对象不存在,工厂会创建新的对象;如果存在,就直接返回已有对象。 - 内部和外部状态:享元模式将对象的状态分为内部和外部状态,内部状态是可共享的、不变的,外部状态是变化的,由客户端通过方法参数传入。
- 客户端代码:客户端通过享元工厂获取对象,并设置外部状态,通过享元对象来执行操作。
import java.util.HashMap;
import java.util.Map;
// 抽象享元接口
interface Shape {
void draw();
}
// 具体享元类
class Circle implements Shape {
private String color;
private int x;
private int y;
private int radius;
public Circle(String color) {
this.color = color;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public void setRadius(int radius) {
this.radius = radius;
}
@Override
public void draw() {
System.out.println("Drawing Circle [Color: " + color + ", x: " + x + ", y: " + y + ", radius: " + radius + "]");
}
}
// 享元工厂类
class ShapeFactory {
private static final Map<String, Shape> circleMap = new HashMap<>();
public static Shape getCircle(String color) {
Circle circle = (Circle) circleMap.get(color);
if (circle == null) {
circle = new Circle(color);
circleMap.put(color, circle);
System.out.println("Creating circle of color: " + color);
}
return circle;
}
}
// 客户端代码
public class FlyweightPatternDemo {
private static final String[] colors = {"Red", "Green", "Blue", "White", "Black"};
public static void main(String[] args) {
for (int i = 0; i < 20; ++i) {
Circle circle = (Circle) ShapeFactory.getCircle(getRandomColor());
circle.setX(getRandomX());
circle.setY(getRandomY());
circle.setRadius(100);
circle.draw();
}
}
private static String getRandomColor() {
return colors[(int) (Math.random() * colors.length)];
}
private static int getRandomX() {
return (int) (Math.random() * 100);
}
private static int getRandomY() {
return (int) (Math.random() * 100);
}
}
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
# 常用的使用场景
- GUI 系统中的组件:在图形用户界面中,多个相同样式的按钮、窗口可以使用享元模式来节省内存。比如多个按钮的样式相同,但位置不同,可以共享按钮样式对象。
- 文字处理器:在文字处理系统中,每个字符的字体和样式可能相同,享元模式可以用来复用相同的字符对象,只改变其位置。
- 游戏开发:游戏中存在大量相同类型的对象,例如树木、石头、子弹等。这些对象的数量非常多,但样式相同,可以使用享元模式来共享这些对象以减少内存占用。
- 网络连接池:共享网络连接以减少系统资源的消耗,可以使用享元模式管理连接对象。
- 缓存系统:如果应用程序中存在大量重复的数据,可以将这些数据缓存起来并使用享元模式实现,减少内存的重复占用。
# Java核心库应用场景
java.lang.Integer#valueOf(int)(opens new window) (以及Boolean(opens new window)、Byte(opens new window)、Character(opens new window)、Short(opens new window)、Long(opens new window) 和BigDecimal(opens new window))
# 代理模式(Proxy Pattern)
# 代码实现案例
- 抽象主题接口:定义一个通用接口(例如
Service),供代理类和真实类实现。 - 真实主题类:实现抽象接口,包含具体的业务逻辑(例如
RealService)。 - 代理类:实现抽象接口,持有一个对真实主题类的引用,控制对真实对象的访问。代理类可以对真实对象进行增强或添加额外逻辑,例如延迟加载、日志记录、权限控制等(例如
ProxyService)。 - 客户端代码:客户端通过代理类调用方法,代理类负责控制对真实对象的访问。客户端无需直接接触真实主题,从而实现对具体实现的隔离。
// 抽象主题接口
interface Service {
void request();
}
// 真实主题类
class RealService implements Service {
@Override
public void request() {
System.out.println("RealService: Handling request");
}
}
// 代理类
class ProxyService implements Service {
private RealService realService;
@Override
public void request() {
if (realService == null) {
realService = new RealService();
}
System.out.println("ProxyService: Delegating request to RealService");
realService.request();
}
}
// 客户端代码
public class ProxyPatternDemo {
public static void main(String[] args) {
Service service = new ProxyService();
service.request();
}
}
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
# 常用的使用场景
- 访问控制:通过代理来控制对某些对象的访问,例如添加权限检查,确保只有合适的客户端能够访问。
- 延迟加载:在对象创建开销较大时,代理可以在需要时才实例化真实对象。例如虚拟代理,只有在调用时才创建真实对象,节省资源。
- 远程代理:在分布式系统中,代理可以用来代表远程对象,封装与远程服务的通信细节,使客户端可以像调用本地对象一样操作远程对象。
- 日志记录和审计:代理类可以在调用真实主题之前或之后添加日志记录、性能监控等额外功能。
- 智能引用:代理可以在访问真实对象时执行一些附加操作,例如引用计数、对象的生命周期管理等。
# Java核心库应用场景
java.lang.reflect.Proxy(opens new window)java.rmi.*(opens new window)javax.ejb.EJB(opens new window) (查看评论 (opens new window))javax.inject.Inject(opens new window) (查看评论 (opens new window))javax.persistence.PersistenceContext(opens new window)
# 行为型模式
# 责任链模式 (Chain of Responsibility)
责任链模式适用于需要将请求沿着一条链传递,直到有对象处理它为止的场景。这种模式可以使得请求发送者和处理者解耦,每个处理者只关注自己能处理的请求,符合开闭原则。它特别适合用于构建请求处理链、权限控制、事件处理等动态传递的场景。
# 代码实现案例
- 抽象处理者类:定义一个抽象的处理者类(
Handler),包含一个指向下一个处理者的引用(nextHandler)和一个抽象的请求处理方法(handleRequest)。 - 具体处理者类:每个具体处理者(如
ConcreteHandlerA、ConcreteHandlerB、ConcreteHandlerC)实现handleRequest方法,并根据条件处理请求或将请求传递给下一个处理者。 - 链的连接:通过调用
setNextHandler()方法,将各个处理者连接成链状。 - 请求传递:客户端将请求发送给链中的第一个处理者,之后处理者依次将请求传递,直到有处理者能够处理它或链结束。
abstract class Handler {
protected Handler nextHandler;
public void setNextHandler(Handler nextHandler) {
this.nextHandler = nextHandler;
}
public abstract void handleRequest(String request);
}
class ConcreteHandlerA extends Handler {
@Override
public void handleRequest(String request) {
if (request.equals("A")) {
System.out.println("ConcreteHandlerA handled the request.");
} else if (nextHandler != null) {
nextHandler.handleRequest(request);
} else {
System.out.println("No handler available for the request.");
}
}
}
class ConcreteHandlerB extends Handler {
@Override
public void handleRequest(String request) {
if (request.equals("B")) {
System.out.println("ConcreteHandlerB handled the request.");
} else if (nextHandler != null) {
nextHandler.handleRequest(request);
} else {
System.out.println("No handler available for the request.");
}
}
}
class ConcreteHandlerC extends Handler {
@Override
public void handleRequest(String request) {
if (request.equals("C")) {
System.out.println("ConcreteHandlerC handled the request.");
} else if (nextHandler != null) {
nextHandler.handleRequest(request);
} else {
System.out.println("No handler available for the request.");
}
}
}
public class ChainOfResponsibilityDemo {
public static void main(String[] args) {
Handler handlerA = new ConcreteHandlerA();
Handler handlerB = new ConcreteHandlerB();
Handler handlerC = new ConcreteHandlerC();
handlerA.setNextHandler(handlerB);
handlerB.setNextHandler(handlerC);
// 发出请求
handlerA.handleRequest("B");
handlerA.handleRequest("C");
handlerA.handleRequest("D");
}
}
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
# 常用的使用场景
- 请求处理链:当需要对一个请求进行多步处理,并且这些处理步骤可能需要动态组合时,责任链模式非常适用,例如日志系统可以根据不同的级别(INFO、DEBUG、ERROR)选择不同的处理者。
- 权限控制:在权限系统中,责任链模式可以用于不同级别的权限验证,每个处理者负责检查一部分权限,验证失败则交给下一个处理者。
- 事件处理机制:在 UI 框架中,事件可以沿着一条链路向上传递,直到某个处理者处理了该事件。
- 命令处理:当多个对象都可以处理同一个命令时,可以使用责任链模式来依次尝试处理,直到找到合适的对象。
# Java核心库应用场景
javax.servlet.Filter#doFilter()(opens new window)java.util.logging.Logger#log()(opens new window)
# 命令模式 (Command)
命令模式通过将请求封装为对象,将调用者和接收者解耦,使请求和执行请求的对象独立存在。它适用于需要参数化请求、记录请求日志、实现撤销/重做、任务调度等场景,可以极大地提高系统的可扩展性和灵活性。
# 代码实现案例
- 命令接口 (
Command):定义一个通用接口,包含一个执行方法 (execute),所有命令都必须实现该接口。 - 具体命令类:每个具体命令类实现命令接口,封装了接收者对象的一个或多个动作(例如
LightOnCommand和LightOffCommand)。 - 接收者 (
Receiver):具体执行请求的类(如Light),它包含执行动作的具体逻辑。 - 调用者 (
Invoker):持有命令对象,调用命令对象的执行方法(例如RemoteControl)。 - 客户端代码:客户端通过调用者设置命令并执行,无需关心具体命令如何实现。
// 命令接口
interface Command {
void execute();
}
// 具体命令类 - 开灯命令
class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.turnOn();
}
}
// 具体命令类 - 关灯命令
class LightOffCommand implements Command {
private Light light;
public LightOffCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.turnOff();
}
}
// 接收者类
class Light {
public void turnOn() {
System.out.println("The light is on");
}
public void turnOff() {
System.out.println("The light is off");
}
}
// 调用者类
class RemoteControl {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void pressButton() {
command.execute();
}
}
// 客户端代码
public class CommandPatternDemo {
public static void main(String[] args) {
Light light = new Light();
Command lightOn = new LightOnCommand(light);
Command lightOff = new LightOffCommand(light);
RemoteControl remote = new RemoteControl();
// 打开灯
remote.setCommand(lightOn);
remote.pressButton();
// 关闭灯
remote.setCommand(lightOff);
remote.pressButton();
}
}
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
# 常用的使用场景
- 请求封装为对象:适用于需要将请求封装为对象的场景,如需要对请求进行参数化、记录日志、撤销操作等。
- 宏命令:可以将多个命令组合在一起,以实现宏操作。例如,家居自动化系统中,按一个按钮可以执行一系列操作(如开灯、拉窗帘、播放音乐)。
- 任务调度:可以将命令对象排入队列,并按照某种逻辑调度执行,例如工作队列的任务调度。
- 撤销/重做操作:适用于需要支持撤销或重做操作的场景,命令模式可以记录命令的执行状态,并实现对操作的回滚。
# Java核心库应用场景
# 解释器模式 (Interpreter)
解释器模式适用于需要解释执行特定语言或表达式的场景,特别是当需要对输入的语言进行解释和处理时。它提供了一种灵活的方式来定义语法规则和操作,使得新的规则可以方便地扩展。这种模式适合用于需要构建并解释复杂表达式的场景,如规则引擎、脚本解释器、正则表达式引擎等。
# 代码实现案例
- 表达式接口(Expression):定义一个
interpret方法,所有具体的表达式类都要实现这个接口,以提供不同的解释逻辑。 - 终结符表达式和非终结符表达式:
- 终结符表达式:像
VariableExpression和ConstantExpression,它们代表具体的值或变量,是表达式中的最小单元。 - 非终结符表达式:像
AddExpression和SubtractExpression,它们组合终结符表达式或其他非终结符表达式,实现复杂的表达式解释。
- 终结符表达式:像
- 上下文(Context):用于存储表达式中变量的值,例如在代码中传递的
Map<String, Integer>。 - 组合表达式:通过组合不同的表达式,构建复杂的语法树,然后调用
interpret方法来计算或解释结果。
import java.util.Map;
// 表达式接口
interface Expression {
int interpret(Map<String, Integer> context);
}
// 变量表达式
class VariableExpression implements Expression {
private String name;
public VariableExpression(String name) {
this.name = name;
}
@Override
public int interpret(Map<String, Integer> context) {
return context.getOrDefault(name, 0);
}
}
// 常量表达式
class ConstantExpression implements Expression {
private int value;
public ConstantExpression(int value) {
this.value = value;
}
@Override
public int interpret(Map<String, Integer> context) {
return value;
}
}
// 加法表达式
class AddExpression implements Expression {
private Expression left;
private Expression right;
public AddExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}
@Override
public int interpret(Map<String, Integer> context) {
return left.interpret(context) + right.interpret(context);
}
}
// 减法表达式
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(Map<String, Integer> context) {
return left.interpret(context) - right.interpret(context);
}
}
// 客户端代码
public class InterpreterPatternDemo {
public static void main(String[] args) {
// 创建表达式 a + b - c
Expression a = new VariableExpression("a");
Expression b = new VariableExpression("b");
Expression c = new VariableExpression("c");
Expression add = new AddExpression(a, b);
Expression subtract = new SubtractExpression(add, c);
// 上下文(变量的值)
Map<String, Integer> context = Map.of("a", 5, "b", 10, "c", 3);
// 解释表达式
int result = subtract.interpret(context);
System.out.println("Result: " + result); // 输出结果:12
}
}
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
# 常用的使用场景
- 编译器:解释器模式可以用于实现编译器中的解释器,例如计算简单的数学表达式或者解析特定的脚本语言。
- 正则表达式:解释器模式非常适合用于实现类似正则表达式解析的工具,通过解析语法树对字符串进行匹配和操作。
- 规则引擎:在业务系统中,可以使用解释器模式来实现简单的业务规则引擎,通过定义规则表达式来决定系统的行为。
- 命令执行器:解释器模式可以用于解释命令脚本并执行相应的操作,例如处理机器人指令,或者命令行解析器。
# Java核心库应用场景
# 迭代器模式 (Iterator)
迭代器模式的主要目的是在不暴露集合内部结构的情况下,提供遍历集合元素的方法。这种模式特别适合用于集合对象的遍历,使代码更加简洁、统一,并符合单一职责原则。在需要灵活、简洁遍历不同类型集合的场景中,迭代器模式是非常实用的设计模式。
# 代码实现案例
- 迭代器接口 (
Iterator):定义hasNext()和next()方法,用于遍历集合元素。 - 聚合接口 (
Container):定义getIterator()方法,用于获取该容器的迭代器。 - 具体聚合类 (
NameRepository):实现Container接口,内部存储一个集合对象,并返回对应的迭代器。 - 具体迭代器类 (
NameIterator):内部类实现Iterator接口,用于遍历集合中的元素。 - 客户端代码:客户端通过调用
getIterator()方法获取迭代器,然后使用迭代器进行遍历。
import java.util.ArrayList;
import java.util.List;
// 迭代器接口
interface Iterator<T> {
boolean hasNext();
T next();
}
// 聚合接口
interface Container<T> {
Iterator<T> getIterator();
}
// 具体聚合类
class NameRepository implements Container<String> {
private List<String> names = new ArrayList<>();
public NameRepository() {
names.add("Alice");
names.add("Bob");
names.add("Charlie");
names.add("David");
}
@Override
public Iterator<String> getIterator() {
return new NameIterator();
}
// 具体迭代器类
private class NameIterator implements Iterator<String> {
int index = 0;
@Override
public boolean hasNext() {
return index < names.size();
}
@Override
public String next() {
if (this.hasNext()) {
return names.get(index++);
}
return null;
}
}
}
// 客户端代码
public class IteratorPatternDemo {
public static void main(String[] args) {
NameRepository nameRepository = new NameRepository();
Iterator<String> iterator = nameRepository.getIterator();
while (iterator.hasNext()) {
String name = iterator.next();
System.out.println("Name: " + name);
}
}
}
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
# 常用的使用场景
- 遍历集合对象:迭代器模式非常适用于需要遍历集合对象的场景,包括列表、数组、集合等。
- 统一访问方式:当需要提供一种统一的方式来遍历不同的数据结构时,迭代器模式能够隐藏集合的内部实现,使得遍历逻辑一致。
- 自定义数据结构:当实现自定义的数据结构并需要提供遍历功能时,可以使用迭代器模式为客户端提供遍历操作的接口,而不暴露内部实现细节。
- 简化复杂数据结构的遍历:对于一些复杂的数据结构(如树形结构、图结构等),可以使用迭代器模式来封装遍历逻辑,简化客户端代码。
# Java核心库应用场景
java.util.Iterator(opens new window)的所有实现 (还有java.util.Scanner(opens new window))。java.util.Enumeration(opens new window)的所有实现。
# 中介者模式 (Mediator)
中介者模式的主要目的是通过中介者对象来管理对象间的复杂交互,从而简化系统结构,避免对象之间的直接相互引用,降低系统耦合性。这种模式特别适用于对象之间相互通信复杂且数量较多的场景,比如 GUI 组件、聊天应用、飞机塔台管理等。
# 代码实现案例
- 抽象中介者接口 (
Mediator):定义了用于通信的接口方法。中介者的作用是负责协调同事对象之间的通信,而不让它们相互直接引用。 - 具体中介者实现 (
ConcreteMediator):实现了中介者接口,并且维护了同事对象的列表或引用。它负责将来自一个同事对象的消息发送给其他同事对象。 - 抽象同事类 (
Colleague):定义了同事的基本结构,通常包括对中介者的引用。所有同事对象通过中介者进行通信。 - 具体同事类 (
ConcreteColleagueA、ConcreteColleagueB):实现了同事的行为,在需要通信时通过中介者发送消息,不直接与其他同事对象进行交互。
import java.util.ArrayList;
import java.util.List;
// 抽象中介者接口
interface Mediator {
void sendMessage(String message, Colleague sender);
}
// 抽象同事类
abstract class Colleague {
protected Mediator mediator;
public Colleague(Mediator mediator) {
this.mediator = mediator;
}
public abstract void receiveMessage(String message);
}
// 具体中介者实现
class ConcreteMediator implements Mediator {
private List<Colleague> colleagues = new ArrayList<>();
public void addColleague(Colleague colleague) {
colleagues.add(colleague);
}
@Override
public void sendMessage(String message, Colleague sender) {
for (Colleague colleague : colleagues) {
if (colleague != sender) {
colleague.receiveMessage(message);
}
}
}
}
// 具体同事类
class ConcreteColleagueA extends Colleague {
public ConcreteColleagueA(Mediator mediator) {
super(mediator);
}
@Override
public void receiveMessage(String message) {
System.out.println("Colleague A received: " + message);
}
public void send(String message) {
System.out.println("Colleague A sends: " + message);
mediator.sendMessage(message, this);
}
}
class ConcreteColleagueB extends Colleague {
public ConcreteColleagueB(Mediator mediator) {
super(mediator);
}
@Override
public void receiveMessage(String message) {
System.out.println("Colleague B received: " + message);
}
public void send(String message) {
System.out.println("Colleague B sends: " + message);
mediator.sendMessage(message, this);
}
}
// 客户端代码
public class MediatorPatternDemo {
public static void main(String[] args) {
ConcreteMediator mediator = new ConcreteMediator();
ConcreteColleagueA colleagueA = new ConcreteColleagueA(mediator);
ConcreteColleagueB colleagueB = new ConcreteColleagueB(mediator);
mediator.addColleague(colleagueA);
mediator.addColleague(colleagueB);
colleagueA.send("Hello, B!");
colleagueB.send("Hi, A!");
}
}
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
# 常用的使用场景
- GUI 界面开发:中介者模式常用于图形用户界面中多个组件的协调工作。比如,按钮、文本框、下拉菜单等控件相互之间可能会触发变化,通过中介者可以减少组件之间的直接依赖。
- 聊天系统:中介者模式可以用于实现聊天室,所有用户都通过中介者(服务器)来传递消息,而不是直接相互通信。
- 复杂对象交互:在有许多对象相互交互的场景下,可以使用中介者模式来简化对象之间的耦合关系。例如,飞机场的塔台用来协调不同飞机的飞行,飞机不会直接彼此通信。
- 工作流引擎:在企业应用程序中,可以使用中介者模式来管理不同业务组件之间的交互,使得工作流的各个节点只与中介者进行交互,降低了节点之间的耦合。
# Java核心库应用场景
java.util.Timer(opens new window) (所有scheduleXXX()方法)java.util.concurrent.Executor#execute()(opens new window)java.util.concurrent.ExecutorService(opens new window) (invokeXXX()和submit()方法)java.util.concurrent.ScheduledExecutorService(opens new window) (所有scheduleXXX()方法)java.lang.reflect.Method#invoke()(opens new window)
# 备忘录模式 (Memento)
备忘录模式用于在不破坏对象封装性的前提下,捕获并保存对象的内部状态,以便日后对象恢复到之前的状态。它适用于需要撤销操作、保存恢复对象状态等场景。通过使用备忘录模式,可以有效地管理对象的历史状态,从而使系统具备良好的状态恢复功能。
# 代码实现案例
- Originator(发起人):保存当前的状态,可以创建备忘录对象以存储自己的状态,也可以从备忘录中恢复之前的状态。它包含
saveStateToMemento()方法用于创建Memento,以及getStateFromMemento()方法用于恢复状态。 - Memento(备忘录):用于存储
Originator的内部状态,其内部状态只能通过Originator访问,不提供给其他对象。 - Caretaker(管理者):负责保存和恢复
Memento,但不对备忘录的内容进行操作。它用于保存Memento的列表,从而实现状态的回溯。
// Originator 类
class Originator {
private String state;
public void setState(String state) {
System.out.println("Setting state to: " + state);
this.state = state;
}
public String getState() {
return state;
}
public Memento saveStateToMemento() {
System.out.println("Saving state to Memento");
return new Memento(state);
}
public void getStateFromMemento(Memento memento) {
state = memento.getState();
System.out.println("Restoring state from Memento: " + state);
}
// Memento 类
static class Memento {
private final String state;
public Memento(String state) {
this.state = state;
}
public String getState() {
return state;
}
}
}
// Caretaker 类
import java.util.ArrayList;
import java.util.List;
class Caretaker {
private final List<Originator.Memento> mementoList = new ArrayList<>();
public void addMemento(Originator.Memento memento) {
mementoList.add(memento);
}
public Originator.Memento getMemento(int index) {
return mementoList.get(index);
}
}
// 客户端代码
public class MementoPatternDemo {
public static void main(String[] args) {
Originator originator = new Originator();
Caretaker caretaker = new Caretaker();
originator.setState("State #1");
caretaker.addMemento(originator.saveStateToMemento());
originator.setState("State #2");
caretaker.addMemento(originator.saveStateToMemento());
originator.setState("State #3");
originator.getStateFromMemento(caretaker.getMemento(0));
originator.getStateFromMemento(caretaker.getMemento(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
# 常用的使用场景
- 撤销/恢复操作:在文本编辑器、图像编辑软件中实现撤销(Undo)和恢复(Redo)功能。
- 保存游戏进度:在游戏中保存玩家的进度(如角色属性、关卡状态等),以便在需要时恢复到之前的状态。
- 事务管理:在处理涉及到多个步骤的操作时(如数据库事务),如果某一步失败,能够回滚到之前的状态。
- 数据快照:当需要周期性地保存对象的状态,例如对象的历史记录以进行分析或审计时。
# Java核心库应用场景
- 所有
java.io.Serializable(opens new window) 的实现都可以模拟备忘录。 - 所有
javax.faces.component.StateHolder(opens new window) 的实现。
# 观察者模式 (Observer)
观察者模式适用于希望在对象状态变化时通知多个依赖对象,而不需要对象之间存在紧密耦合的场景。它为对象间的广播通信提供了一种设计方式,并且遵循开闭原则,使得可以方便地添加新的观察者,特别适合用于事件驱动、消息通知、数据同步和分布式系统等场景。
# 代码实现案例
- 观察者接口 (
Observer):定义了一个update方法,用于接收来自主题的通知。 - 具体观察者 (
ConcreteObserver):实现了Observer接口,并定义了接收到通知时的具体行为。 - 主题接口 (
Subject):包含添加、移除观察者以及通知观察者的方法。 - 具体主题 (
ConcreteSubject):实现了Subject接口,内部维护一个观察者列表,提供添加、移除观察者的方法,并在状态变化时通知所有观察者。 - 客户端代码:通过创建主题和观察者对象来实现观察者模式中的核心逻辑。主题对象变化时会通知所有注册的观察者。
import java.util.ArrayList;
import java.util.List;
// 观察者接口
interface Observer {
void update(String message);
}
// 具体观察者类
class ConcreteObserver implements Observer {
private String name;
public ConcreteObserver(String name) {
this.name = name;
}
@Override
public void update(String message) {
System.out.println(name + " received update: " + message);
}
}
// 主题接口
interface Subject {
void addObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers(String message);
}
// 具体主题类
class ConcreteSubject implements Subject {
private List<Observer> observers = new ArrayList<>();
@Override
public void addObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers(String message) {
for (Observer observer : observers) {
observer.update(message);
}
}
}
// 客户端代码
public class ObserverPatternDemo {
public static void main(String[] args) {
ConcreteSubject subject = new ConcreteSubject();
Observer observer1 = new ConcreteObserver("Observer 1");
Observer observer2 = new ConcreteObserver("Observer 2");
subject.addObserver(observer1);
subject.addObserver(observer2);
subject.notifyObservers("Hello, Observers!");
subject.removeObserver(observer1);
subject.notifyObservers("Second update!");
}
}
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
# 常用的使用场景
- 事件驱动的系统:在 GUI 应用程序中,按钮、文本框等组件的事件监听机制就是典型的观察者模式。多个监听器(观察者)监听某个事件源(主题),事件触发时,所有监听器都会收到通知。
- 消息通知系统:例如,在一个社交网络平台中,当用户发布新消息时,所有订阅该用户的人都会收到通知。
- 数据变化的同步:在 MVC 模式中,模型(Model)是主题,视图(View)是观察者。当模型的状态发生改变时,所有视图都会收到通知并进行相应更新。
- 分布式系统:在分布式系统中,可以使用观察者模式来实现服务发现和负载均衡。当某个节点发生状态变化(例如下线或超载),系统会通知其他节点以便进行相应的调整。
- 日志记录和监控:在应用系统中,可以使用观察者模式实现日志记录器或监控系统,当系统中出现事件或变化时,通知所有日志记录器或监控观察者以记录状态变化。
# Java核心库应用场景
java.util.Observer(opens new window)/java.util.Observable(opens new window) (极少在真实世界中使用)java.util.EventListener(opens new window)的所有实现 (几乎广泛存在于 Swing 组件中)javax.servlet.http.HttpSessionBindingListener(opens new window)javax.servlet.http.HttpSessionAttributeListener(opens new window)javax.faces.event.PhaseListener(opens new window)
# 状态模式 (State)
状态模式主要用于对象的行为随着状态变化而改变的场景。它通过将不同状态的行为封装到具体状态类中,使得状态的切换更加灵活,符合开闭原则。这种模式适用于具有复杂状态逻辑或者状态随时间改变的场景,例如游戏开发、工作流管理、设备状态管理等。
# 代码实现案例
- 状态接口:定义一个状态接口(
State),其中声明了特定状态下的行为。 - 具体状态类:为每个具体状态实现状态接口(如
StartState、StopState),提供不同状态下的具体行为。 - 上下文类:
Context类持有当前状态的引用,并提供设置和获取状态的方法。它会将请求委托给当前状态对象,从而实现行为随状态变化而改变。 - 状态转换:通过在
Context类中调用setState方法来切换状态,从而实现状态的转换。
// 状态接口
interface State {
void handle(Context context);
}
// 具体状态类
class StartState implements State {
@Override
public void handle(Context context) {
System.out.println("System is in Start State");
context.setState(this);
}
@Override
public String toString() {
return "Start State";
}
}
class StopState implements State {
@Override
public void handle(Context context) {
System.out.println("System is in Stop State");
context.setState(this);
}
@Override
public String toString() {
return "Stop State";
}
}
// 上下文类
class Context {
private State state;
public Context() {
state = null;
}
public void setState(State state) {
this.state = state;
}
public State getState() {
return state;
}
}
// 客户端代码
public class StatePatternDemo {
public static void main(String[] args) {
Context context = new Context();
StartState startState = new StartState();
startState.handle(context);
System.out.println("Current state: " + context.getState().toString());
StopState stopState = new StopState();
stopState.handle(context);
System.out.println("Current state: " + context.getState().toString());
}
}
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
# 常用的使用场景
- 有限状态机:例如在游戏开发中,角色可能有多个状态(如站立、行走、跑步、攻击),这些状态的行为在切换时可能会有所不同,可以使用状态模式来实现。
- 文档流转系统:例如一个文档的审批流程可以包含不同的状态(如草稿、提交、审批中、已批准),在不同状态下文档的行为不同,可以使用状态模式来管理文档的流转。
- 设备状态管理:例如电器设备(如空调、洗衣机等)的开机、关机、待机等状态可以通过状态模式实现,以便管理和控制不同状态下的行为。
- 任务处理系统:在任务处理系统中,任务可能有多种状态(如待处理、处理中、已完成、失败),可以使用状态模式来定义每个状态下的行为。
#
# Java核心库应用场景
javax.faces.lifecycle.LifeCycle#execute()(opens new window) (由FacesServlet(opens new window)控制: 行为依赖于当前 JSF 生命周期的阶段 (状态))
# 空对象模式 (Null Object)
空对象模式的核心思想是用一个实现了相同接口的“空对象”来表示没有的情况,而不是使用 null,从而简化客户端代码逻辑,减少空指针异常风险。它适用于需要频繁进行 null 检查的场景、希望通过统一接口实现而避免空值判断的场景。
# 代码实现案例
- 抽象接口:定义一个抽象对象接口(例如
Animal),声明所需的行为。 - 具体实现类:定义正常情况下的对象类(例如
Dog和Cat),实现抽象接口。 - 空对象类:实现抽象接口,并定义空的行为(例如
NullAnimal),在某些情况下不做任何事情。 - 工厂类:通过工厂类(例如
AnimalFactory)来创建对象,如果找不到匹配的对象类型,则返回一个空对象(如NullAnimal)。 - 客户端代码:客户端通过工厂获取对象,无需显式处理
null检查,所有的行为都统一通过接口调用。
// 抽象对象接口
interface Animal {
void makeSound();
}
// 具体实现类
class Dog implements Animal {
@Override
public void makeSound() {
System.out.println("Dog barks");
}
}
class Cat implements Animal {
@Override
public void makeSound() {
System.out.println("Cat meows");
}
}
// 空对象类
class NullAnimal implements Animal {
@Override
public void makeSound() {
// 什么都不做
System.out.println("No animal to make a sound");
}
}
// 工厂类
class AnimalFactory {
public static Animal getAnimal(String type) {
if (type.equalsIgnoreCase("Dog")) {
return new Dog();
} else if (type.equalsIgnoreCase("Cat")) {
return new Cat();
} else {
return new NullAnimal();
}
}
}
// 客户端代码
public class NullObjectPatternDemo {
public static void main(String[] args) {
Animal dog = AnimalFactory.getAnimal("Dog");
dog.makeSound();
Animal cat = AnimalFactory.getAnimal("Cat");
cat.makeSound();
Animal unknown = AnimalFactory.getAnimal("Elephant");
unknown.makeSound(); // 输出 "No animal to make a sound"
}
}
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
# 常用的使用场景
- 消除空检查:在代码中,如果多次需要判断对象是否为
null,可以使用空对象模式来避免显式的null检查,减少代码复杂性和潜在的空指针异常。 - 策略模式结合:当使用策略模式时,可以定义一个空策略类作为默认实现,当某些策略不可用时,使用空策略替代,避免策略为
null的情况。 - 数据处理:在数据处理时,如果某些记录不存在,可以返回一个空对象而不是
null,避免客户端对数据的存在性进行显式判断。 - 接口实现的默认行为:对于某些接口,如果部分实现类的行为为空,可以定义一个空对象类作为默认实现,避免在具体实现类中编写重复的空逻辑。
# Java核心库应用场景
# 策略模式 (Strategy)
策略模式通过定义一系列可互换的算法或行为类,并让客户端动态选择使用哪种策略,实现了行为的动态替换和扩展。它适用于算法/行为需要灵活切换、避免大量条件语句、以及需要将复杂算法解耦为独立类的场景。通过策略模式,代码更符合开闭原则,便于扩展和维护。
# 代码实现案例
- 策略接口:定义一个策略接口(
PaymentStrategy),其中包含一个执行操作的方法(pay(int amount))。 - 具体策略类:不同的策略实现类实现策略接口,提供具体的策略行为(例如
CreditCardPayment和PayPalPayment)。 - 环境类:持有策略接口的引用,允许在运行时动态地设置和更改策略对象(例如
ShoppingCart)。 - 客户端代码:客户端代码通过设置不同的策略对象,调用环境类的方法来执行不同的策略行为。
// 策略接口
interface PaymentStrategy {
void pay(int amount);
}
// 具体策略类:信用卡支付
class CreditCardPayment implements PaymentStrategy {
private String cardNumber;
public CreditCardPayment(String cardNumber) {
this.cardNumber = cardNumber;
}
@Override
public void pay(int amount) {
System.out.println("Paid " + amount + " using Credit Card: " + cardNumber);
}
}
// 具体策略类:PayPal支付
class PayPalPayment implements PaymentStrategy {
private String email;
public PayPalPayment(String email) {
this.email = email;
}
@Override
public void pay(int amount) {
System.out.println("Paid " + amount + " using PayPal: " + email);
}
}
// 环境类:使用策略
class ShoppingCart {
private PaymentStrategy paymentStrategy;
public void setPaymentStrategy(PaymentStrategy paymentStrategy) {
this.paymentStrategy = paymentStrategy;
}
public void checkout(int amount) {
if (paymentStrategy == null) {
throw new IllegalStateException("Payment strategy not set");
}
paymentStrategy.pay(amount);
}
}
// 客户端代码
public class StrategyPatternDemo {
public static void main(String[] args) {
ShoppingCart cart = new ShoppingCart();
// 使用信用卡支付
cart.setPaymentStrategy(new CreditCardPayment("1234-5678-9876-5432"));
cart.checkout(100);
// 使用PayPal支付
cart.setPaymentStrategy(new PayPalPayment("user@example.com"));
cart.checkout(200);
}
}
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
# 常用的使用场景
- 支付方式:在电子商务系统中,不同的支付方式(如信用卡、PayPal、Apple Pay)可以通过策略模式实现,用户可以在结账时选择支付方式。
- 排序算法:有多个排序算法实现(如快速排序、归并排序、冒泡排序),可以通过策略模式来选择在不同场景下使用合适的排序策略。
- 压缩算法:在文件压缩工具中,可以选择不同的压缩算法(如 ZIP、RAR、7z)来压缩文件。
- 日志记录:在系统中使用策略模式来选择不同的日志记录方式(如记录到文件、控制台、数据库),可以在不同场景下使用不同的策略。
- 促销策略:在电商应用中,根据不同的活动情况可以有不同的促销策略(如满减、打折、返现),通过策略模式可以实现动态选择促销策略。
# Java核心库应用场景
- 对
java.util.Comparator#compare()(opens new window) 的调用来自Collections#sort(). javax.servlet.http.HttpServlet(opens new window):service()方法, 还有所有接受HttpServletRequest和HttpServletResponse对象作为参数的doXXX()方法。javax.servlet.Filter#doFilter()(opens new window)
# 模板模式 (Template)
模板模式的核心思想是定义算法或操作的一般结构,将其中的一些步骤留给子类去实现,从而实现代码复用和行为扩展。它适用于具有相同流程但部分步骤可能需要定制的场景,使得不同的实现共享相同的逻辑框架。这样可以有效地减少代码重复,增强代码的可维护性和扩展性。
# 代码实现案例
- 模板方法:在基类中定义一个模板方法(
process()),它定义了一系列步骤的执行顺序。模板方法通常是final的,以防止子类改变其执行顺序。 - 抽象方法:在基类中定义一些抽象方法(如
readData()和processData()),这些方法由子类来实现,以提供具体的行为。 - 具体方法:在基类中实现一些具体的方法(如
saveData()),这些方法在模板方法中被调用,并且通常对所有子类保持一致。 - 扩展子类:每个子类负责实现基类中的抽象方法,以提供特定的数据处理逻辑。
abstract class DataProcessor {
// 模板方法
public final void process() {
readData();
processData();
saveData();
}
// 基本操作步骤(由子类实现)
abstract void readData();
abstract void processData();
// 具体步骤(在基类中实现)
void saveData() {
System.out.println("Saving data to database");
}
}
class CSVDataProcessor extends DataProcessor {
@Override
void readData() {
System.out.println("Reading data from CSV file");
}
@Override
void processData() {
System.out.println("Processing CSV data");
}
}
class JSONDataProcessor extends DataProcessor {
@Override
void readData() {
System.out.println("Reading data from JSON file");
}
@Override
void processData() {
System.out.println("Processing JSON data");
}
}
// 客户端代码
public class TemplatePatternDemo {
public static void main(String[] args) {
DataProcessor csvProcessor = new CSVDataProcessor();
csvProcessor.process();
System.out.println("-----------------");
DataProcessor jsonProcessor = new JSONDataProcessor();
jsonProcessor.process();
}
}
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
# 常用的使用场景
- 数据处理框架:在需要处理不同来源的数据(如 CSV 文件、JSON 文件、数据库)时,数据读取和处理的具体实现可能不同,但整个流程的框架是一致的,可以使用模板模式来实现。
- 工作流系统:对于一些特定的业务流程,如审批流程或订单处理流程,不同的部门或订单类型可能有不同的具体实现,但整个流程是相似的,可以使用模板模式定义标准工作流。
- 游戏开发中的 AI 行为:在游戏开发中,敌人的 AI 行为可能分为多个阶段,例如发现玩家、追击、攻击等。不同的敌人类型可以有不同的实现,但整体流程是相同的,可以用模板模式定义这些行为的顺序。
- 算法框架:当你有一个算法的框架,其中某些步骤可以改变时,模板模式可以帮助你定义算法的通用部分,而让子类提供具体步骤的实现。例如,排序算法可能有相同的比较逻辑,但交换元素的方式可以由子类定义。
# Java核心库应用场景
java.io.InputStream(opens new window)、java.io.OutputStream(opens new window)、java.io.Reader(opens new window) 和java.io.Writer(opens new window) 的所有非抽象方法。java.util.AbstractList(opens new window)、java.util.AbstractSet(opens new window) 和java.util.AbstractMap(opens new window) 的所有非抽象方法。javax.servlet.http.HttpServlet(opens new window), 所有默认发送 HTTP 405 “方法不允许” 错误响应的doXXX()方法。 你可随时对其进行重写。
# 访问者模式 (Visitor)
# 代码实现案例