设计模式
# 一、设计模式
设计模式的核心在于提供了相关问题的解决方案,使得人们可以更加简单方便地复用成功的设计和体系结构。
类型 | 创建型 | 结构型 | 行为型 |
---|---|---|---|
类 | 工厂方法 | 适配器(类) | 解释器 模板方法 |
对象 | 抽象工厂 建造者 原型 单例 | 适配器(对象) 桥接 组合 装饰者 外观 享元 代理 | 职责链 命令 迭代器 中介者 备忘录 观察者 状态 策略 访问者 |
创建型 | 结构型 | 行为型 | |
---|---|---|---|
类 | Factory Method | Adapter (类) | Interpreter Template Method |
对象 | Abstract Factory Builder Prototype Singleton | Adapter (对象) Bridge Composite Decorator Facade Flyweight Proxy | Chain of Responsibility Command Iterator Mediator Memento Observer State Strategy Visitor |
# 二、简单工厂模式
简单工厂模式(Simple Factory Pattern)是一种创建型设计模式,它提供了一个统一的工厂类来创建不同类型的对象,根据客户端的参数或条件来实例化对象。
简单工厂模式的组成部分包括:
- 工厂类(Factory):负责根据客户端的需求创建对象。
- 抽象产品类(Product):定义产品的共同接口或抽象类,具体产品类将实现这些接口或继承这些抽象类。
- 具体产品类(Concrete Product):实现抽象产品类定义的接口或继承抽象产品类,完成具体产品的构造和业务逻辑。
下面用 Java 代码来示例简单工厂模式:
首先,定义抽象产品类 Product
,其中包含一个抽象方法 operate()
:
public abstract class Product {
public abstract void operate();
}
2
3
然后,创建具体产品类 ProductA
和 ProductB
,它们分别实现了抽象产品类 Product
的抽象方法:
public class ProductA extends Product {
@Override
public void operate() {
System.out.println("Product A is operating.");
}
}
2
3
4
5
6
public class ProductB extends Product {
@Override
public void operate() {
System.out.println("Product B is operating.");
}
}
2
3
4
5
6
接下来,创建工厂类 Factory
,根据客户端的参数或条件来实例化具体产品对象:
public class Factory {
public static Product createProduct(String type) {
if (type.equals("A")) {
return new ProductA();
} else if (type.equals("B")) {
return new ProductB();
} else {
throw new IllegalArgumentException("Invalid product type.");
}
}
}
2
3
4
5
6
7
8
9
10
11
最后,在客户端代码中通过工厂类创建具体产品对象,并调用其操作方法:
public class Client {
public static void main(String[] args) {
Product productA = Factory.createProduct("A");
productA.operate();
Product productB = Factory.createProduct("B");
productB.operate();
}
}
2
3
4
5
6
7
8
9
当客户端代码需要创建产品对象时,只需通过工厂类 Factory
调用 createProduct()
方法,并传入相应的参数(例如 "A" 或 "B"),工厂类将根据参数实例化具体产品对象并返回。
简单工厂模式将对象的创建过程封装在工厂类中,客户端只需要关注和使用工厂类,而不需要直接与具体产品类进行交互,避免了耦合性的增加。同时,如果需要新增或修改产品类,只需要修改工厂类,不会对客户端代码产生影响,提高了可维护性和灵活性。
# 三、工厂方法模式
工厂方法模式(Factory Method Pattern)是一种创建型设计模式,它定义了一个创建对象的接口,但将具体对象的实例化延迟到子类来完成。工厂方法模式关注于一个具体产品的创建
首先,我们先定义抽象产品类 Product
:
public abstract class Product {
public abstract void operate();
}
2
3
对于工厂方法模式,我们需要定义一个抽象工厂类 Factory
,其中包含一个创建产品的抽象方法:
public abstract class Factory {
public abstract Product createProduct();
}
2
3
在具体的工厂类中实现抽象工厂类的抽象方法,完成具体产品的创建:
public class ConcreteFactory extends Factory {
@Override
public Product createProduct() {
return new ConcreteProduct();
}
}
2
3
4
5
6
public class ConcreteProduct extends Product {
@Override
public void operate() {
System.out.println("Concrete product is operating.");
}
}
2
3
4
5
6
使用工厂方法模式时,客户端代码可以通过具体的工厂类来创建相应的产品对象:
public class Client {
public static void main(String[] args) {
Factory factory = new ConcreteFactory();
Product product = factory.createProduct();
product.operate();
}
}
2
3
4
5
6
7
# 四、抽象工厂模式
抽象工厂模式(Abstract Factory Pattern)是一种创建型设计模式,它提供了一个接口来创建一系列相关或依赖对象的家族,而不需要指定具体的类。抽象工厂模式关注于一系列产品的创建。
首先,我们先定义抽象产品类 Product
:
public abstract class Product {
public abstract void operate();
}
2
3
对于抽象工厂模式,我们需要定义一个抽象工厂接口 AbstractFactory
,其中包含多个创建产品的抽象方法:
public interface AbstractFactory {
Product createProduct();
// 创建其他相关产品的抽象方法
}
2
3
4
5
在具体的工厂类中实现抽象工厂接口,完成相关产品的创建:
public class ConcreteFactory1 implements AbstractFactory {
@Override
public Product createProduct() {
return new ConcreteProduct1();
}
}
2
3
4
5
6
public class ConcreteFactory2 implements AbstractFactory {
@Override
public Product createProduct() {
return new ConcreteProduct2();
}
}
2
3
4
5
6
public class ConcreteProduct1 extends Product {
@Override
public void operate() {
System.out.println("Concrete product 1 is operating.");
}
}
2
3
4
5
6
public class ConcreteProduct2 extends Product {
@Override
public void operate() {
System.out.println("Concrete product 2 is operating.");
}
}
2
3
4
5
6
使用抽象工厂模式时,客户端代码可以通过具体的工厂类来创建相关产品的对象:
public class Client {
public static void main(String[] args) {
AbstractFactory factory1 = new ConcreteFactory1();
Product product1 = factory1.createProduct();
product1.operate();
AbstractFactory factory2 = new ConcreteFactory2();
Product product2 = factory2.createProduct();
product2.operate();
}
}
2
3
4
5
6
7
8
9
10
11
抽象工厂适用于:
- 一个系统要独立于它的产品的创建、组合和表示时。
- 一个系统要由多个产品系列中的一个来配置时。
- 当要强调一系列相关的产品对象的设计以便进行联合使用时。
- 当提供一个产品类库,只想显示它们的接口而不是实现时。
- 为图形用户界面(GUI)组件定义不同平台的并行类层次结构。
# 五、生成器模式
生成器模式(Builder Pattern)是一种创建型设计模式,用于将复杂对象的构建过程与其表示分离,使得相同的构建过程可以创建不同的表示。
生成器模式的核心概念是将对象的构建过程分解为一系列的步骤,并通过一个指导者(Director)来控制构建过程。具体来说,生成器模式包括以下几个角色:
产品(Product):表示最终构建出的复杂对象。产品类通常具有多个组成部分。
抽象生成器(Builder):定义了构建产品的抽象接口,包括一系列的构建方法。
具体生成器(Concrete Builder):实现了抽象生成器接口,具体实现对象的构建方法,以及返回最终构建出的产品。
指导者(Director):负责定义构建过程的顺序,以及调用具体生成器来构建产品。
下面通过一个简单的示例来详细解释生成器模式的代码实现。
// 产品类
public class Product {
private String part1;
private String part2;
// ...
public void setPart1(String part1) {
this.part1 = part1;
}
public void setPart2(String part2) {
this.part2 = part2;
}
// ...
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 抽象生成器接口
public interface Builder {
void buildPart1();
void buildPart2();
// ...
Product getResult();
}
2
3
4
5
6
7
// 具体生成器类
public class ConcreteBuilder implements Builder {
private Product product;
public ConcreteBuilder() {
product = new Product();
}
@Override
public void buildPart1() {
// 构建第一个部分的具体逻辑
product.setPart1("Part 1");
}
@Override
public void buildPart2() {
// 构建第二个部分的具体逻辑
product.setPart2("Part 2");
}
// ...
@Override
public Product getResult() {
// 返回最终构建出的产品
return product;
}
}
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 Director {
public void construct(Builder builder) {
// 按照一定的顺序调用构建方法
builder.buildPart1();
builder.buildPart2();
// ...
}
}
2
3
4
5
6
7
8
9
// 客户端代码
public class Client {
public static void main(String[] args) {
// 创建具体生成器对象
Builder builder = new ConcreteBuilder();
// 创建指导者对象
Director director = new Director();
// 指导者控制构建过程
director.construct(builder);
// 通过生成器获取最终构建出的产品
Product product = builder.getResult();
System.out.println(product);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
在上面的示例中, Product
表示最终构建出的产品,抽象生成器 Builder
定义了构建产品所需要的方法,具体生成器 ConcreteBuilder
实现了这些方法来完成具体的构建逻辑。指导者 Director
负责控制构建过程的顺序,将具体生成器传入指导者的 construct
方法中进行构建。最后在客户端代码中,我们创建了具体生成器对象和指导者对象,并通过指导者来控制构建过程,最终通过生成器获取构建完成的产品。
生成器模式的优点是将构建过程与表示分离,使得构建过程的变化不会影响到最终的产品表示。同时,通过指导者的控制,可以实现创建不同表示的产品。这种模式适用于构建复杂对象的场景,尤其是当一个对象的构建过程具有多个可选的步骤时。它提供了更好的灵活性和可维护性。
Builder 模式适用于
- 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。
- 当构造过程必须允许被构造的对象有不同的表示时。
# 六、原型模式
原型模式是一种创建型设计模式,用于通过复制现有对象来创建新对象,而无需通过实例化类和使用构造函数。它通过复制现有对象的属性,创建一个新的对象,并对其进行修改以满足需要。
原型模式的核心是一个原型(Prototype)接口或抽象类,它定义了用于复制自身的方法。具体的原型类实现了原型接口,并实现了复制自身的方法。
// 原型接口或抽象类
public interface Prototype {
Prototype clone();
}
2
3
4
// 具体原型类
public class ConcretePrototype implements Prototype {
private String property;
public ConcretePrototype(String property) {
this.property = property;
}
// 复制实例方法
@Override
public Prototype clone() {
return new ConcretePrototype(this.property);
}
// 设置和获取属性的方法
public String getProperty() {
return property;
}
public void setProperty(String property) {
this.property = property;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 客户端代码
public class Client {
public static void main(String[] args) {
// 创建原型对象
ConcretePrototype prototype = new ConcretePrototype("Property 1");
// 复制原型对象
ConcretePrototype clone = (ConcretePrototype) prototype.clone();
System.out.println("Clone Property: " + clone.getProperty());
// 修改复制的对象
clone.setProperty("Property 2");
System.out.println("Clone Property after modification: " + clone.getProperty());
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- 原型模式的优点:可以避免重复创建相似对象的成本,提高性能。它也支持动态配置,允许在运行时克隆复杂对象。
- 原型模式的缺点:在具体原型类中实现了克隆方法,需要维护原型类与具体类的层次结构。
Prototype 模式适用于:
- 当一个系统应该独立于它的产品创建、构成和表示时。
- 当要实例化的类是在运行时刻指定时,例如,通过动态装载。
- 为了避免创建一个与产品类层次平行的工厂类层次时。
- 当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们,可能比每次用合适的状态手工实例化该类更方便一些。
# 七、单例模式
单例模式(Singleton Pattern)是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来访问该实例。
public class Singleton {
private static Singleton instance;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Client {
public static void main(String[] args) {
Singleton instance1 = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println(instance1 == instance2); // true
}
}
2
3
4
5
6
7
8
Singleron 模式适用于:
- 当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时。
- 当这个唯一实例应该是通过子类化可扩展的,并且客户无须更改代码就能使用一个扩展的实例时。
# 八、适配器模式
适配器模式(Adapter Pattern)是一种结构型设计模式,用于将一个类的接口转换成客户端所期望的另一个接口。适配器模式能够使原本由于接口不兼容而无法一起工作的类能够协同工作。
适配器模式的核心概念是适配器(Adapter),它通过实现客户端所期望的接口,并持有一个对被适配对象的引用,将客户端的请求转发给被适配对象进行处理。被适配对象是需要被适配的已有类,它所具有的接口通常与客户端所期望的接口不兼容。
下面通过一个简单的示例来详细解释适配器模式的代码实现。
// 目标接口(Target)
public interface Target {
void request();
}
2
3
4
// 被适配的类(Adaptee)
public class Adaptee {
public void specificRequest() {
System.out.println("Adaptee: specificRequest");
}
}
2
3
4
5
6
// 适配器类(Adapter)
public class Adapter implements Target {
private Adaptee adaptee;
public Adapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
System.out.println("Adapter: request");
adaptee.specificRequest();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
// 客户端代码
public class Client {
public static void main(String[] args) {
// 创建被适配的对象
Adaptee adaptee = new Adaptee();
// 创建适配器对象,并将被适配对象传递给适配器
Target adapter = new Adapter(adaptee);
// 调用适配器的接口方法
adapter.request();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
在上面的示例中,我们有一个目标接口 Target
,一个需要被适配的类 Adaptee
,以及一个适配器类 Adapter
。适配器类实现了目标接口,并在内部持有一个被适配对象的引用。适配器的 request
方法实际上是通过调用被适配对象的 specificRequest
方法来实现的。从客户端的角度来看,它只需要与目标接口进行交互,而无需直接与被适配的类打交道。
Adapter 模式适用于
- 想使用一个已经存在的类,而它的接口不符合要求
- 想创建一个可以服用的类,该类可以与其他不相关的类或不可预见的类 (即那些接口可能不一定兼容的类) 协同工作。
- (仅适用于对象 Adapter) 想使用一个已经存在的子类,但是不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配它的父类接口。
# 九、桥接模式
桥接模式(Bridge Pattern)是一种结构型设计模式,旨在将抽象部分和它的实现部分分离,使得它们可以独立地变化。桥接模式通过将抽象和实现分离,使得它们可以独立地进行变化和扩展,同时通过组合的方式将它们关联起来。
桥接模式的核心概念是抽象部分和实现部分。抽象部分定义了高层的抽象接口,它通常包含了对实现部分的引用,以及一些抽象方法。实现部分定义了实际的实现类或接口,它通常是具体的,实现了抽象部分定义的接口。
下面通过一个简单的示例来详细解释桥接模式的代码实现。
// 抽象部分的接口
public interface Abstraction {
void operation();
}
2
3
4
// 实现部分的接口
public interface Implementor {
void operationImpl();
}
2
3
4
// 具体抽象部分的实现
public class ConcreteAbstraction implements Abstraction {
private Implementor implementor;
public ConcreteAbstraction(Implementor implementor) {
this.implementor = implementor;
}
@Override
public void operation() {
System.out.println("ConcreteAbstraction: operation");
implementor.operationImpl();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
// 具体实现部分的实现
public class ConcreteImplementor implements Implementor {
@Override
public void operationImpl() {
System.out.println("ConcreteImplementor: operationImpl");
}
}
2
3
4
5
6
7
// 客户端代码
public class Client {
public static void main(String[] args) {
// 创建具体的实现部分对象
Implementor implementor = new ConcreteImplementor();
// 创建具体的抽象部分对象,并将实现部分对象注入
Abstraction abstraction = new ConcreteAbstraction(implementor);
// 调用抽象部分的接口方法
abstraction.operation();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
在上面的示例中,我们有一个抽象部分的接口 Abstraction
,它定义了高层抽象接口并包含了对实现部分接口的引用。实现部分的接口 Implementor
定义了实际的实现类或接口。具体抽象部分的实现类 ConcreteAbstraction
实现了抽象部分接口,并在内部持有一个对实现部分的引用。具体实现部分的实现类 ConcreteImplementor
实现了实现部分的接口。
在客户端代码中,我们首先创建了具体的实现部分对象 ConcreteImplementor
,然后创建了具体的抽象部分对象 ConcreteAbstraction
,并将实现部分对象注入到抽象部分对象中。最后,我们通过调用抽象部分的接口方法来使用桥接模式。
Bridge 模式适用于:
- 不希望在抽象和它的实现部分之间有一个固定的绑定关系。例如,这种情况可能是因为,在程序运行时刻实现部分应可以被选择或者切换。
- 类的抽象以及它的实现都应该可以通过生成子类的方法加以扩充。这是 Bridge 模式使得开发者可以对不同的抽鲁接口和实现部分进行组合,并分别对它们进行扩充。
- 对一个抽象的实现部分的修改应对客户不产生影响,即客户代码不必重新编译。
- (C++) 想对客户完全隐藏抽象的实现部分。
- 有许多类要生成的类层次结构。
- 想在多个对象间共享实现 (可能使用引用计数),但同时要求客户并不知道这一点。
# 十、组合模式
组合模式是一种结构型设计模式,它通过将对象组合成树形结构以表示 "整体 - 部分" 的层次关系。组合模式使得客户端可以统一地处理单个对象和组合对象,无需区分它们的差异。
// 组件接口
public interface Component {
void operation();
}
2
3
4
// 叶子组件实现类
public class Leaf implements Component {
@Override
public void operation() {
System.out.println("Leaf: operation");
}
}
2
3
4
5
6
7
// 容器组件实现类
public class Composite implements Component {
private List<Component> components = new ArrayList<>();
public void addComponent(Component component) {
components.add(component);
}
public void removeComponent(Component component) {
components.remove(component);
}
@Override
public void operation() {
System.out.println("Composite: operation");
for (Component component : components) {
component.operation();
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 客户端代码
public class Client {
public static void main(String[] args) {
// 创建叶子组件
Component leaf = new Leaf();
// 创建容器组件
Composite composite = new Composite();
composite.addComponent(leaf);
// 调用组件的操作方法
composite.operation();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
Composite 模式适用于:
- 想表示对象的部分 - 整体层次结构。
- 希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。
# 十一、装饰者模式
装饰者模式是一种结构型设计模式,用于动态地将责任附加到对象上。它提供了一种灵活的方式来扩展对象的功能,而无需使用继承。
// 组件接口
public interface Component {
void operation();
}
2
3
4
// 原始组件实现类
public class ConcreteComponent implements Component {
@Override
public void operation() {
System.out.println("ConcreteComponent: operation");
}
}
2
3
4
5
6
7
// 装饰者基类
public abstract class Decorator implements Component {
protected Component component;
public Decorator(Component component) {
this.component = component;
}
@Override
public void operation() {
component.operation();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
// 具体装饰者类
public class ConcreteDecorator extends Decorator {
public ConcreteDecorator(Component component) {
super(component);
}
@Override
public void operation() {
super.operation();
additionalOperation();
}
private void additionalOperation() {
System.out.println("ConcreteDecorator: additionalOperation");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 客户端代码
public class Client {
public static void main(String[] args) {
// 创建原始组件
Component component = new ConcreteComponent();
// 添加装饰者
component = new ConcreteDecorator(component);
// 调用组件的操作方法
component.operation();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
Decorator 模式适用于:
- 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
- 处理那些可以撤销的职责。
- 当不能采用生成子类的方式进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是,由于类定义被隐藏,或类定义不能用于生成子类。
# 十二、外观模式
外观模式是一种结构型设计模式,提供了一个简化的接口以便于访问复杂系统的接口。它封装了一组复杂的接口,提供一个更加简单、方便的接口供客户端使用。
// 子系统A
public class SubsystemA {
public void operationA() {
System.out.println("SubsystemA: operationA");
}
}
2
3
4
5
6
// 子系统B
public class SubsystemB {
public void operationB() {
System.out.println("SubsystemB: operationB");
}
}
2
3
4
5
6
// 外观类
public class Facade {
private SubsystemA subsystemA;
private SubsystemB subsystemB;
public Facade() {
subsystemA = new SubsystemA();
subsystemB = new SubsystemB();
}
public void operation() {
subsystemA.operationA();
subsystemB.operationB();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 客户端代码
public class Client {
public static void main(String[] args) {
// 创建外观对象
Facade facade = new Facade();
// 调用外观对象的操作方法
facade.operation();
}
}
2
3
4
5
6
7
8
9
10
Facade 模式适用于:
- 要为一个复杂子系统提供一个简单接口时,子系统往往因为不断演化而变得越来越复杂。大多数模式使用时都会产生更多更小的类,这使得子系统更具有可重用性,也更容易对子系统进行定制,但也给那些不需要定制子系统的用户带来一些使用上的困难。Facade 可以提供一个简单的默认视图,这一视图对大多数用户来说已经足够,而那些需要更多的可定制性的用户可以越过 Facade 层。
- 客户程序与抽象类的实现部分之间存在着很大的依赖性。引入 Facade 将这个子系统与客户以及其他的子系统分离,可以提高子系统的独立性和可移植性。
- 当需要构建一个层次结构的子系统时,使用 Facade 模式定义子系统中每层的入口点。如果子系统之间是相互依赖的,则可以让它们仅通过 Facade 进行通信,从而简化了它们之间的依赖关系。
# 十三、享元模式
享元模式是一种结构型设计模式,用于共享对象以支持大量的细粒度对象。享元模式通过共享相同状态的对象来减少内存使用和对象创建次数。
// 享元接口
public interface Flyweight {
void operation();
}
2
3
4
// 具体享元类
public class ConcreteFlyweight implements Flyweight {
private String state;
public ConcreteFlyweight(String state) {
this.state = state;
}
@Override
public void operation() {
System.out.println("ConcreteFlyweight: operation " + state);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
// 享元工厂类
public class FlyweightFactory {
private Map<String, Flyweight> flyweights = new HashMap<>();
public Flyweight getFlyweight(String state) {
if (!flyweights.containsKey(state)) {
Flyweight flyweight = new ConcreteFlyweight(state);
flyweights.put(state, flyweight);
}
return flyweights.get(state);
}
}
2
3
4
5
6
7
8
9
10
11
12
// 客户端代码
public class Client {
public static void main(String[] args) {
// 创建享元工厂对象
FlyweightFactory factory = new FlyweightFactory();
// 获取享元对象
Flyweight flyweight1 = factory.getFlyweight("state1");
flyweight1.operation();
Flyweight flyweight2 = factory.getFlyweight("state2");
flyweight2.operation();
Flyweight flyweight3 = factory.getFlyweight("state1");
flyweight3.operation();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Flyweight 模式适用于:
- 一个应用程序使用了大量的对象。
- 完全由于使用大量的对象,造成很大的存储开销。
- 对象的大多数状态都可变为外部状态。
- 如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象。
- 应用程序不依赖于对象标识。由于 Flywveight 对象可以被共享,所以对于概念上明显有别的对象,标识测试将返回真值。
# 十四、代理模式
代理模式是一种结构型设计模式,提供了一个代理对象,可以控制对真实对象的访问,并允许在访问对象之前和之后执行额外的操作。
// 定义一个接口
interface Image {
void display();
}
2
3
4
// 创建实现接口的实体类
class RealImage implements Image {
private String filename;
public RealImage(String filename) {
this.filename = filename;
loadFromDisk();
}
private void loadFromDisk() {
System.out.println("Loading image from disk: " + filename);
}
public void display() {
System.out.println("Displaying image: " + filename);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 创建代理类
class ImageProxy implements Image {
private RealImage realImage;
private String filename;
public ImageProxy(String filename) {
this.filename = filename;
}
public void display() {
if (realImage == null) {
realImage = new RealImage(filename);
}
realImage.display();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 使用代理类
public class ProxyPatternDemo {
public static void main(String[] args) {
Image image = new ImageProxy("example.jpg");
image.display();
}
}
2
3
4
5
6
7
Proxy 模式适用于在需要比较通用和复杂的对象指针代替简单的指针的时候,常见情况有:
- 远程代理 (Remote Proxy) 为一个对象在不同地址空间提供局部代表。
- 虚代理 (Virtual Proxy) 根据需要创建开销很大的对象。
- 保护代理 (Protection Proxy) 控制对原始对象的访问,用于对象应该有不同的访问权限的时候。
- 智能引用 (Smart Reference) 取代了简单的指针,它在访问对象时执行一些附加操作。典型用途包括:对指向实际对象的引用计数,这样当该对象没有引用时,可以被自动释放:当第一次引用一个持久对象时,将它装入内存:在访问一个实际对象前,检查是否已经锁定了它,以确保其他对象不能改变它。
# 十五、职责链模式
职责链模式通过将请求的发送者和接收者解耦,将多个处理对象组成一条链,并在这条链上传递请求,直到有一个处理对象能够处理它。职责链模式将每个处理对象看作一个节点,每个节点决定了请求的传递与处理。
// 定义一个统一的处理请求接口
interface Handler {
void setNext(Handler handler);
void handleRequest(int request);
}
2
3
4
5
// 实现处理请求接口的具体处理类
class ConcreteHandler1 implements Handler {
private Handler nextHandler;
public void setNext(Handler handler) {
this.nextHandler = handler;
}
public void handleRequest(int request) {
if (request <= 10) {
System.out.println("ConcreteHandler1 handles the request: " + request);
} else if (nextHandler != null) {
nextHandler.handleRequest(request);
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class ConcreteHandler2 implements Handler {
private Handler nextHandler;
public void setNext(Handler handler) {
this.nextHandler = handler;
}
public void handleRequest(int request) {
if (request > 10 && request <= 20) {
System.out.println("ConcreteHandler2 handles the request: " + request);
} else if (nextHandler != null) {
nextHandler.handleRequest(request);
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class ConcreteHandler3 implements Handler {
private Handler nextHandler;
public void setNext(Handler handler) {
this.nextHandler = handler;
}
public void handleRequest(int request) {
if (request > 20 && request <= 30) {
System.out.println("ConcreteHandler3 handles the request: " + request);
} else if (nextHandler != null) {
nextHandler.handleRequest(request);
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 使用职责链模式
public class ChainOfResponsibilityPatternDemo {
public static void main(String[] args) {
Handler handler1 = new ConcreteHandler1();
Handler handler2 = new ConcreteHandler2();
Handler handler3 = new ConcreteHandler3();
handler1.setNext(handler2);
handler2.setNext(handler3);
handler1.handleRequest(5); // ConcreteHandler1 handles the request: 5
handler1.handleRequest(15); // ConcreteHandler2 handles the request: 15
handler1.handleRequest(25); // ConcreteHandler3 handles the request: 25
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Chain of Responsibility 模式适用于以下条件:
- 有多个的对象可以处理一个请求,哪个对象处理该请求运行时刻自动确定。
- 想在不明确指定接收者的情况下向多个对象中的一个提交一个请求。
- 可处理一个请求的对象集合应被动态指定。
# 十六、命令模式
命令模式将请求的发送者和请求的接收者解耦,通过将请求封装成一个对象,从而使得请求的发送者不需要知道请求的接收者是谁,也不需要知道具体是如何执行请求的。命令模式允许请求的参数化、延迟执行请求以及支持撤销操作。
// 定义命令接口
interface Command {
void execute();
}
2
3
4
// 具体命令类
class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light) {
this.light = light;
}
public void execute() {
light.turnOn();
}
}
2
3
4
5
6
7
8
9
10
11
12
// 接收者类
class Light {
public void turnOn() {
System.out.println("The light is on");
}
}
2
3
4
5
6
// 调用者类
class RemoteControl {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void pressButton() {
command.execute();
}
}
2
3
4
5
6
7
8
9
10
11
12
// 使用命令模式
public class CommandPatternDemo {
public static void main(String[] args) {
Light light = new Light();
Command lightOnCommand = new LightOnCommand(light);
RemoteControl remoteControl = new RemoteControl();
remoteControl.setCommand(lightOnCommand);
remoteControl.pressButton(); // The light is on
}
}
2
3
4
5
6
7
8
9
10
11
Command 模式适用于:
- 抽象出待执行的动作以参数化某对象。Command 模式是过程语言中的回调 (Callback) 机制的一个面向对象的替代品。
- 在不同的时刻指定、排列和执行请求。一个 Command 对象可以有一个与初始请求无关的生存期。如果一个请求的接收者可用一种与地址空间无关的方式表达,那么就可以将负责该请求的命令对象传递给另一个不同的进程并在那儿实现该请求。
- 支持取消操作。Command 的 Execute 操作可在实施操作前将状态存储起来,在取消操作时这个状态用来消除该操作的影响。Command 接口必须添加一个 Unexecute 操作,该操作取消上一次 Execute 调用的效果。执行的命令被存储在一个历史列表中。可通过向后和向前遍历这一列表并分别调用 Unexecute 和 Execute 来实现重数不限的 “取消和 “重做”
- 支持修改日志。这样当系统崩溃时,这些修改可以被重做一遍。在 Command 接口中添加装载操作和存储操作,可以用来保持变动的一个一致的修改日志。从崩溃中恢复的过程包括从磁盘中重新读入记录下来的命令并用 Execute 操作重新执行它们。
- 用构建在原语操作上的高层操作构造一个系统。这样一种结构在支持事务 (Transaction) 的信息系统中很常见。Command 模式提供了对事务进行建模的方法。Command 有一个公共接口,使得可以用同一种方式调用所有的事务,同时使用该模式也易于添加新事务以扩展系统。
# 十七、解释器模式
解释器模式是一种行为型设计模式,它定义了一种语言的文法,并且建立一个解释器来解析这个语言中的句子。它将句子分解成一些可执行的操作,这些操作最终实现了语言的解释。
// 抽象表达式
interface Expression {
int interpret();
}
2
3
4
// 终结符表达式
class NumberExpression implements Expression {
private int number;
public NumberExpression(int number) {
this.number = number;
}
@Override
public int interpret() {
return number;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
// 非终结符表达式
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() {
return left.interpret() + right.interpret();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 客户端使用
public class Client {
public static void main(String[] args) {
Expression expression = new AddExpression(
new NumberExpression(5),
new AddExpression(
new NumberExpression(3),
new NumberExpression(2)
)
);
int result = expression.interpret();
System.out.println("解释器计算结果:" + result);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Imnterpreter 模式适用于当有一个语言需要解释执行,并且可将该语言中的句子表示为一个抽象语法树时,以下情况效果最好:
- 该文法简单。对于复杂的发文,文法的类层次变得庞大而无法管理。此时语法分析程序生成器这样的工具是更好的选择。它们无须构建抽象语法树即可解释表达式,这样可以节省空间还可能节省时间。
- 效率不是一个关键问题。最高效的解释器通常不是通过直接解释语法分析树实现的,而是首先将它们转换成另一种形式。不过,即使在这种情况下,转换器仍然可用该模式实现。
# 十八、迭代器模式
迭代器模式提供一种遍历集合对象的方法,而不需要暴露集合对象的内部表示。通过提供一个迭代器对象,可以逐个访问聚合对象中的元素,同时又不暴露聚合对象的内部实现。
// 定义迭代器接口
interface Iterator<T> {
boolean hasNext();
T next();
}
2
3
4
5
// 实现迭代器接口的聚合类
class NameList<T> implements Iterator<T> {
private T[] names;
private int position;
public NameList(T[] names) {
this.names = names;
position = 0;
}
public boolean hasNext() {
return position < names.length;
}
public T next() {
return names[position++];
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 使用迭代器模式
public class IteratorPatternDemo {
public static void main(String[] args) {
String[] names = { "Alice", "Bob", "Charlie", "Dave" };
Iterator<String> iterator = new NameList<>(names);
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
2
3
4
5
6
7
8
9
10
11
Iterator 模式适用于:
- 访问一个聚合对象的内容而无须暴露它的内部表示。
- 支持对聚合对象的多种遍历。
- 为遍历不同的聚合结构提供一个统一的接口。
# 十九、中介者模式
中介者模式(Mediator Pattern)是一种行为设计模式,它通过提供一个中介对象来集中处理不同对象之间的交互。中介者模式使得各个对象之间的耦合度降低,它们只需与中介者进行通信而不需要与其他对象直接进行通信。这种模式常用于一个系统中的对象之间的通信复杂而繁琐的情况。
// 中介者接口
interface Mediator {
void sendMessage(String message, Colleague colleague);
}
2
3
4
// 具体中介者
class ConcreteMediator implements Mediator {
private Colleague colleague1;
private Colleague colleague2;
public void setColleague1(Colleague colleague) {
this.colleague1 = colleague;
}
public void setColleague2(Colleague colleague) {
this.colleague2 = colleague;
}
@Override
public void sendMessage(String message, Colleague colleague) {
if (colleague == colleague1) {
colleague2.receiveMessage(message);
} else if (colleague == colleague2) {
colleague1.receiveMessage(message);
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 抽象同事类
abstract class Colleague {
protected Mediator mediator;
public Colleague(Mediator mediator) {
this.mediator = mediator;
}
public abstract void sendMessage(String message);
public abstract void receiveMessage(String message);
}
2
3
4
5
6
7
8
9
10
11
12
// 具体同事类
class ConcreteColleague1 extends Colleague {
public ConcreteColleague1(Mediator mediator) {
super(mediator);
}
@Override
public void sendMessage(String message) {
mediator.sendMessage(message, this);
}
@Override
public void receiveMessage(String message) {
System.out.println("ConcreteColleague1 received message: " + message);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class ConcreteColleague2 extends Colleague {
public ConcreteColleague2(Mediator mediator) {
super(mediator);
}
@Override
public void sendMessage(String message) {
mediator.sendMessage(message, this);
}
@Override
public void receiveMessage(String message) {
System.out.println("ConcreteColleague2 received message: " + message);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 客户端代码
public class MediatorPatternDemo {
public static void main(String[] args) {
ConcreteMediator mediator = new ConcreteMediator();
ConcreteColleague1 colleague1 = new ConcreteColleague1(mediator);
ConcreteColleague2 colleague2 = new ConcreteColleague2(mediator);
mediator.setColleague1(colleague1);
mediator.setColleague2(colleague2);
colleague1.sendMessage("Hello, colleague2!");
colleague2.sendMessage("Hi, colleague1!");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
该示例中, Mediator
是中介者接口,定义了一个 sendMessage
方法用于发送消息给同事对象。 ConcreteMediator
是具体中介者类,实现了 Mediator
接口,并在内部维护了两个具体同事对象的引用。 Colleague
是抽象同事类,定义了 sendMessage
和 receiveMessage
方法,子类需要根据具体需求实现这两个方法。 ConcreteColleague1
和 ConcreteColleague2
是具体同事类,它们继承了 Colleague
类,并实现了 sendMessage
和 receiveMessage
方法。
当客户端代码运行时, ConcreteColleague1
发送消息给中介者,中介者转发消息给 ConcreteColleague2
,同样地, ConcreteColleague2
也可以通过中介者发送消息给 ConcreteColleague1
。这样, ConcreteColleague1
和 ConcreteColleague2
之间不需要直接进行通信,它们只需与中介者进行交互,从而解耦了两者之间的关系。
Mediator 模式适用于:
- 一组对象以定义良好但是复杂的方式进行通信,产生的相互依赖关系结构混乱且难以理解。
- 一个对象引用其他很多对象并且直接与这些对象通信,导致难以复用该对象。
- 想定制一个分布在多个类中的行为,而又不想生成太多的子类。
# 二十、备忘录模式
备忘录模式(Memento Pattern)是一种行为设计模式,它用于捕获一个对象的内部状态并在需要时恢复。备忘录模式实现了数据的封装,可以使得外部对象无法访问备忘录的内容,从而保证了备忘录中的状态不会被修改。
// 备忘录类
class Memento {
private String state;
public Memento(String state) {
this.state = state;
}
public String getState() {
return state;
}
}
2
3
4
5
6
7
8
9
10
11
12
// 原发器类
class Originator {
private String state;
public void setState(String state) {
this.state = state;
}
public Memento createMemento() {
return new Memento(state);
}
public void restoreMemento(Memento memento) {
state = memento.getState();
}
public void showState() {
System.out.println("Current state: " + state);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 负责人类
class Caretaker {
private Memento memento;
public void setMemento(Memento memento) {
this.memento = memento;
}
public Memento getMemento() {
return memento;
}
}
2
3
4
5
6
7
8
9
10
11
12
// 客户端代码
public class MementoPatternDemo {
public static void main(String[] args) {
Originator originator = new Originator();
Caretaker caretaker = new Caretaker();
originator.setState("State 1");
originator.showState();
Memento memento = originator.createMemento();
caretaker.setMemento(memento);
originator.setState("State 2");
originator.showState();
memento = caretaker.getMemento();
originator.restoreMemento(memento);
originator.showState();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
在该示例中, Memento
是备忘录类,它有一个用于存储状态的私有字段 state
,以及相应的获取方法。 Originator
是原发器类,它有一个用于存储内部状态的字段 state
,并提供了 createMemento
方法用于创建备忘录, restoreMemento
方法用于恢复备忘录中的状态, showState
方法用于展示当前状态。 Caretaker
是负责人类,它用于存储备忘录对象。当需要保存原发器的内部状态时,负责人调用原发器的 createMemento
方法,并将其存储在自身中;当需要恢复状态时,负责人调用原发器的 restoreMemento
方法,并将备忘录中的状态设置给原发器。
当客户端代码运行时, Originator
对象首先设置其内部状态为 “State 1”,然后创建备忘录并存储在负责人中。接着,内部状态被修改为 “State 2”,并展示出来。最后,负责人获取备忘录并将其中的状态恢复给原发器,然后展示当前状态,可以看到,状态被成功地恢复了。
Memento 模式适用于:
- 必须保存一个对象在某一个时刻的 (部分) 状态,这样以后需要时它才能恢复到先前的状态。
- 如果一个用接口来让其他对象直接得到这些状态,将会暴露对象的实现细节并破坏对象的封装性。
# 二十一、 观察者模式
观察者模式(Observer Pattern)是一种行为设计模式,它定义了对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,其依赖对象会相应地收到通知并进行更新。观察者模式使得对象之间的耦合度降低,同时也提供了一种更灵活的方式来进行对象间的通信。
// 抽象主题接口
interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}
2
3
4
5
6
// 具体主题类
class ConcreteSubject implements Subject {
private List<Observer> observers = new ArrayList<>();
private int state;
public void setState(int state) {
this.state = state;
notifyObservers();
}
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(state);
}
}
}
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
// 抽象观察者接口
interface Observer {
void update(int state);
}
2
3
4
// 具体观察者类
class ConcreteObserver implements Observer {
private int observerState;
@Override
public void update(int state) {
observerState = state;
System.out.println("Observer state updated: " + observerState);
}
}
2
3
4
5
6
7
8
9
10
// 客户端代码
public class ObserverPatternDemo {
public static void main(String[] args) {
ConcreteSubject subject = new ConcreteSubject();
Observer observer1 = new ConcreteObserver();
Observer observer2 = new ConcreteObserver();
subject.registerObserver(observer1);
subject.registerObserver(observer2);
subject.setState(1);
subject.setState(2);
subject.removeObserver(observer2);
subject.setState(3);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
在该示例中, Subject
是抽象主题接口,定义了注册观察者、移除观察者和通知观察者的方法。 ConcreteSubject
是具体主题类,它维护了一个观察者列表以及一个状态字段,当状态发生改变时会调用 notifyObservers
方法通知所有观察者进行更新。 Observer
是抽象观察者接口,该接口中定义了一个 update
方法,用于接收并处理主题对象的状态更新。 ConcreteObserver
是具体观察者类,它实现了 Observer
接口,并在 update
方法中更新自己的状态。
Observer 模式适用于:
- 当一个抽象模型有两个方面,其中一个方面依赖于另一个方面,将这两者封装在独立的对象中以使它们可以各自独立地改变和复用。
- 当对一个对象的改变需要同时改变其他对象,而不知道具体有多少对象有待改变时。
- 当一个对象必须通知其他对象,而它又不能假定其他对象是谁,即不希望这些对象是紧耦合的。
# 二十二、状态模式
状态模式(State Pattern)是一种行为设计模式,它允许对象在内部状态发生改变时改变其行为。状态模式可以将复杂的条件语句转换为基于对象的逻辑,使代码更易于理解和维护。
在状态模式中,通常存在一个上下文(Context)类和多个具体状态(Concrete State)类。上下文类持有一个当前状态对象,在不同的状态下,上下文类的行为会发生变化。
下面是一个简单的状态模式的 Java 代码示例:
// 状态接口
interface State {
void Handle(Context context);
}
// 具体状态类A
class StateA implements State { // 有货
@Override
public void Handle(Context context) {
int count = context.getCount();
if (count >= 1) {
System.out.println("购买成功!");
context.setCount(count - 1);
if (context.getCount() == 0) {
context.setState(new StateB());
}
} else {
System.out.println("购买失败!");
}
}
}
// 具体状态类B
class StateB implements State { // 无货
@Override
public void Handle(Context context) {
int count = context.getCount();
if (count == 0) {
System.out.println("购买失败!等待补货");
context.setCount(5);
System.out.println("补货成功,请重新购买");
context.setState(new StateA());
}
}
}
// 上下文类
class Context { // 贩卖机
private int count;
private State state;
public Context() {
count = 3;
state = new StateA();
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public State getState() {
return state;
}
public void setState(State state) {
this.state = state;
}
public void Request() { // 购买一个饮料
state.Handle(this);
}
}
// 示例代码
public class StatePatternDemo {
public static void main(String[] args) {
Context context = new Context(); // count:3
System.out.println(context.getState());
context.Request(); // 购买一个饮料 count = 2
context.Request(); // 购买一个饮料 count = 1
context.Request(); // 购买一个饮料 count = 0
System.out.println(context.getState());
context.Request(); // 无货 等待补货 补货成功 count = 5
System.out.println(context.getState());
context.Request(); // 购买一个饮料 count = 4
System.out.println(context.getCount());
}
}
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
主要认识如下几个角色:Context(贩卖机),State(状态),StateA(有货状态),StateB(无货状态)。
在这个示例中,Context 类表示一个贩卖机,贩卖机中有若干个饮料。它有两个重要的属性:count(表示当前饮料数量)和 state(表示当前状态)。初始化时,贩卖机默认有三个饮料,并且状态为有货(StateA)。
State 接口定义了一个 Handle 方法,用于处理购买饮料的请求。StateA 和 StateB 类分别表示有货和无货状态,并实现了 State 接口。
在 Context 类中,Request 方法作为购买一个饮料的接口方法。它会根据当前的状态委托给具体的状态类来处理。在 StateA 中,如果饮料数量大于等于 1,购买成功,并更新状态和饮料数量。如果饮料数量为 0,则购买失败,并更新状态为 StateB。在 StateB 中,如果饮料数量为 0,购买失败并等待补货,更新饮料数量为 5,并提示补货成功,并将状态更新为 StateA。
在 main 方法中,首先创建了一个 Context 对象,并打印出当前的状态。然后依次购买三个饮料,可以看到状态从有货状态到无货状态,并打印出相应的信息。接着调用 Request 方法,相当于补货成功,再购买一个饮料,状态恢复为有货状态,并打印出饮料数量。
State 模式适用于:
- 一个对象的行为决定于它的状态,并且它必须在运行时刻根据状态改变它的行为。
- 一个操作中含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态。这个状态常用一个或多个枚举常量表示。通常,有多个操作包含这一相同的条件结构。State 模式将每一个条件分支放入一个独立的类中。这使得开发者可以根据对象自身的情况将对象的状态作为一个对象,这一对象可以不依赖于其他对象独立变化。
# 二十三、策略模式
策略模式(Strategy Pattern)是一种行为设计模式,它定义了一族算法,将每个算法都封装起来,并使它们可以互相替换。策略模式可以让算法的变化独立于使用算法的客户端。
在策略模式中,通常存在一个抽象策略(Strategy)类或接口,多个具体策略(Concrete Strategy)类和一个上下文(Context)类。上下文类持有一个策略对象,并提供一个用于执行策略的方法。
下面是一个简单的策略模式的 Java 代码示例:
public class StrategyPattern {
public static void main(String[] args) {
Strategy add = new AddStrategy();
Strategy subtraction = new SubtractionStrategy();
Strategy multiply = new MultiplyStrategy();
OperationContext context = new OperationContext(add);
context.Operation(2022, 528);
context = new OperationContext(subtraction);
context.Operation(2022, 528);
context = new OperationContext(multiply);
context.Operation(2022, 528);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class OperationContext {
private Strategy strategy;
public OperationContext(Strategy strategy) {
this.strategy = strategy;
}
public void Operation(int a, int b) {
strategy.TwoNumberOperation(a, b);
}
}
2
3
4
5
6
7
8
9
10
11
interface Strategy {
public void TwoNumberOperation(int a, int b);
}
2
3
class AddStrategy implements Strategy {
@Override
public void TwoNumberOperation(int a, int b) {
System.out.println(a + b);
}
}
2
3
4
5
6
7
class SubtractionStrategy implements Strategy {
@Override
public void TwoNumberOperation(int a, int b) {
System.out.println(a - b);
}
}
2
3
4
5
6
7
class MultiplyStrategy implements Strategy {
@Override
public void TwoNumberOperation(int a, int b) {
System.out.println(a * b);
}
}
2
3
4
5
6
7
这段代码主要包含了三个角色:Strategy(策略),OperationContext(操作环境),和具体的策略实现类。
在这个示例中,Strategy 接口定义了一个 TwoNumberOperation 方法,表示对两个数字进行操作的策略。
AddStrategy、SubtractionStrategy 和 MultiplyStrategy 分别是 Strategy 接口的具体实现类,分别表示加法、减法和乘法操作。
OperationContext 作为操作环境类,构造函数中接收一个策略对象作为参数,并提供了 Operation 方法来执行具体的操作。当调用 Operation 方法时,操作环境会将具体的操作委托给策略对象来执行。
在 main 方法中,首先创建了三个不同的策略对象:AddStrategy、SubtractionStrategy 和 MultiplyStrategy。然后创建了 OperationContext 对象,并将不同的策略对象传入。接着调用 Operation 方法,通过操作环境来执行具体的操作。可以看到,根据传入的不同策略对象,输出结果分别是加法、减法和乘法的结果。
策略模式的好处是可以在运行时动态地选择具体的策略,而不需要修改使用策略的代码。这种灵活性使得代码更易于扩展和维护。
Strategy 模式适用于:
- 许多相关的类仅仅是行为有异。“策略” 提供了一种用多个行为中的一个行为来配置一个类的方法。
- 需要使用一个算法的不同变体。例如,定义一些反映不同空间的空间 / 时间权衡的算法。当这些变体实现为一个算法的类层次时,可以使用策略模式。
- 算法使用客户不应该知道的数据。可使用策略模式以避免暴露复杂的、与算法相关的数据结构。
- 一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现,将相关的条件分支移入它们各自的 Strategy 类中,以代替这些条件语句。
# 二十四、模板方法模式
模板方法模式(Template Method Pattern)是一种行为设计模式,它定义了一个算法的骨架,并允许子类为其中的某些步骤提供具体实现。模板方法模式可以使算法框架稳定,而将具体实现延迟到子类中。
在模板方法模式中,通常存在一个抽象模板类或接口,其中定义了算法的骨架和抽象方法,以及若干具体子类来实现这些抽象方法。
public class TemplateMethodPattern {
public static void main(String[] args) {
// 父类名 对象名 = new 子类名();
Person student = new Student();
Person teacher = new Teacher();
student.TemplateMethod();
System.out.println("=====我是分割线=====");
teacher.TemplateMethod();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
abstract class Person {
public void TemplateMethod() {
System.out.println("上课 去教室"); // 1
PrimitiveOperation1(); // 2
System.out.println("下课 离开教室"); // 3
PrimitiveOperation2(); // 4
}
public abstract void PrimitiveOperation1(); // 原语操作 1 :上课过程 学生 听课…… 老师 讲课
public abstract void PrimitiveOperation2(); // 原语操作 2 :作业 学生 写作业 提交作业…… 老师 批改作业 打分数
}
2
3
4
5
6
7
8
9
10
class Student extends Person {
@Override
public void PrimitiveOperation1() {
System.out.println("学生:听课 学习 做笔记 提出问题");
}
@Override
public void PrimitiveOperation2() {
System.out.println("学生:写作业 提交作业");
}
}
2
3
4
5
6
7
8
9
10
11
12
class Teacher extends Person {
@Override
public void PrimitiveOperation1() {
System.out.println("老师:上课 讲课 解答问题 布置作业");
}
@Override
public void PrimitiveOperation2() {
System.out.println("老师:批改作业 打分数");
}
}
2
3
4
5
6
7
8
9
10
11
12
这段代码定义一个算法的骨架,将其中一些步骤的具体实现延迟到子类中。
在这个示例中,Person 是一个抽象类,定义了一个 TemplateMethod 方法作为算法的骨架。TemplateMethod 方法中包含了一些固定的步骤,如上课去教室、下课离开教室等,和一些需要延迟到子类中具体实现的抽象方法:PrimitiveOperation1 和 PrimitiveOperation2。
Student 和 Teacher 分别是 Person 的具体子类。它们必须实现 PrimitiveOperation1 和 PrimitiveOperation2 方法来完成具体的实现。
在 main 方法中,首先创建了一个 Student 对象和一个 Teacher 对象。然后分别调用它们的 TemplateMethod 方法。可以看到, TemplateMethod 方法执行了固定的步骤,而具体的实现则由不同的子类来完成。
在这个示例中,学生的上课过程是听课、学习、做笔记、提出问题,作业过程是写作业和提交作业。老师的上课过程是讲课、解答问题、布置作业,作业过程是批改作业和打分数。当调用 TemplateMethod 方法时,具体的实现会根据对象的类型进行调用。
模板方法模式的好处是把算法的具体实现细节封装在子类中,使得算法更具有扩展性和灵活性。同时,通过统一的模板方法,可以减少重复的代码,提高代码的复用性。
# 二十五、访问者模式
访问者模式(Visitor Pattern)是一种行为设计模式,它在不改变被访问类的结构的前提下,定义了一系列操作(访问者),使这些操作可以访问并处理被访问类的元素。
在访问者模式中,通常存在一个抽象访问者(Visitor)类或接口,多个具体访问者(Concrete Visitor)类,一个抽象元素(Element)类或接口,和多个具体元素(Concrete Element)类。
public class VisitorPattern {
public static void main(String[] args) {
PersonStructure structure = new PersonStructure();
Visitor1 visitor1 = new Visitor1();
System.out.println("访问者1的访问记录:");
structure.Accept(visitor1);
System.out.println("学生年龄的总和:" + visitor1.getStudentAgeSum() + " 老师年龄的总和:" + visitor1.getTeacherAgeSum());
System.out.println("=========================================");
Visitor2 visitor2 = new Visitor2();
System.out.println("访问者2的访问记录:");
structure.Accept(visitor2);
System.out.println("学生的最高成绩:" + visitor2.getMaxScore() + " 老师的最高工龄:" + visitor2.getMaxWorkYear());
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
interface Visitor {
public void visitStudent(Student student); // 访问学生
public void visitTeacher(Teacher teacher); // 访问老师
}
2
3
4
class Visitor1 implements Visitor { // 访问者1 分别统计学生和老师的年龄总和
private int studentAgeSum = 0;
private int teacherAgeSum = 0;
public int getStudentAgeSum() {
return studentAgeSum;
}
public int getTeacherAgeSum() {
return teacherAgeSum;
}
@Override
public void visitStudent(Student student) {
System.out.println("访问者1访问学生:" + student.getName() + " 年龄:" + student.getAge());
studentAgeSum += student.getAge();
}
@Override
public void visitTeacher(Teacher teacher) {
System.out.println("访问者1访问老师:" + teacher.getName() + " 年龄:" + teacher.getAge());
teacherAgeSum += teacher.getAge();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Visitor2 implements Visitor { // 访问者2 分别求出 学生的最高成绩 以及 老师的最高工龄
private int maxScore = -1;
private int maxWorkYear = -1;
public int getMaxScore() {
return maxScore;
}
public int getMaxWorkYear() {
return maxWorkYear;
}
@Override
public void visitStudent(Student student) {
System.out.println("访问者2访问学生:" + student.getName() + " 成绩:" + student.getScore());
maxScore = Math.max(maxScore, student.getScore());
}
@Override
public void visitTeacher(Teacher teacher) {
System.out.println("访问者2访问老师:" + teacher.getName() + " 工龄:" + teacher.getWorkYear());
maxWorkYear = Math.max(maxWorkYear, teacher.getWorkYear());
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class PersonStructure {
private List<Person> personList = new ArrayList<Person>();
public PersonStructure() {
personList.add(new Student("张三", 20, 70));
personList.add(new Student("李四", 21, 80));
personList.add(new Student("王五", 22, 90));
personList.add(new Teacher("李老师", 26, 3));
personList.add(new Teacher("陈老师", 27, 4));
personList.add(new Teacher("刘老师", 28, 5));
}
public void Accept(Visitor visitor) {
// for (遍历对象类型 对象名 : 遍历对象)
for (Person person : personList) {
person.Accept(visitor);
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
abstract class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public abstract void Accept(Visitor visitor);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Student extends Person {
private int score;
public Student(String name, int age, int score) {
super(name, age);
this.score = score;
}
public int getScore() {
return score;
}
@Override
public void Accept(Visitor visitor) {
visitor.visitStudent(this);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Teacher extends Person {
private int workYear;
public Teacher(String name, int age, int workYear) {
super(name, age);
this.workYear = workYear;
}
public int getWorkYear() {
return workYear;
}
@Override
public void Accept(Visitor visitor) {
visitor.visitTeacher(this);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
这段代码是一个简单的访问者模式示例。访问者模式用于将算法与对象结构分离,使得算法可以独立于对象结构的变化而变化。
在这个示例中,Visitor 是一个接口,定义了访问学生和老师的方法。具体的访问者类 Visitor1 和 Visitor2 实现了 Visitor 接口,并根据不同的需求分别统计学生和老师的年龄总和以及求出学生的最高成绩和老师的最高工龄。
Person 是一个抽象类,表示一个人,其中包含了姓名和年龄属性,同时定义了一个 Accept 方法,该方法接受一个 Visitor 对象,并调用 Visitor 对象的相应方法。
Student 和 Teacher 是 Person 的具体子类,分别表示学生和老师。它们都实现了 Accept 方法,分别调用 Visitor 对象的 visitStudent 和 visitTeacher 方法。
PersonStructure 是一个包含了一组 Person 对象的类。在构造方法中初始化了一些学生和老师的对象。它还定义了一个 Accept 方法,该方法接受一个 Visitor 对象,并遍历 Person 对象列表,依次调用每个 Person 对象的 Accept 方法。
在 main 方法中,创建了一个 PersonStructure 对象,以及两个不同的 Visitor 对象。然后依次调用 PersonStructure 对象的 Accept 方法,并传入不同的 Visitor 对象进行访问。通过 Visitor 对象的具体实现,在访问过程中实现了相应的操作和统计,并输出结果。
访问者模式的好处是可以将数据结构和操作解耦,使得增加新的操作变得更容易。同时,通过实现不同的访问者,可以对同一个对象结构进行不同的操作和处理,提高了代码的灵活性和可扩展性。
Visitor 模式适用于:
- 一个对象结构包含很多类对象,它们有不同的接口,而用户想对这些对象实施一些依赖于其具体类的操作。
- 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而又想要避免这些操作 “污染” 这些对象的类。Visitor 使得用户可以将相关的操作集中起来定义在一个类中。当该对象结构被很多应用共享时,用 Visitor 模式让每个应用仅包含需要用到的操作。
- 定义对象结构的类很少改变,但经常需要在此结构上定义新的操作。改变对象结构类需要重定义对所有访问者的接口,这可能需要很大的代价。如果对象结构类经常改变,那么可能还是在这些类中定义这些操作较好。