背景
本文是《Java 后端从小白到大神》修仙系列第二十二篇
,正式进入Java后端
世界,本篇文章主要聊Java基础
。若想详细学习请点击首篇博文,我们开始把。
设计模式
由于设计模式分为创建型、结构型和行为型三大类。
一、创建型模式 (Creational Patterns)
1. 单例模式 (Singleton Pattern)
原理描述:
单例模式保证一个类只有一个实例,并提供全局访问点。这样做的好处是控制资源的共享、管理全局状态,防止多次实例化造成的资源浪费或状态不一致。
Java 示例代码:
单例模式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
public class Singleton {
// volatile 保证多线程环境下的可见性
private static volatile Singleton instance;
// 私有构造函数,防止外部直接实例化
private Singleton() {}
// 提供全局访问方法
public static Singleton getInstance() {
if (instance == null) { // 第一次检查(避免不必要的同步)
synchronized (Singleton.class) {
if (instance == null) { // 第二次检查(确保线程安全)
instance = new Singleton();
}
}
}
return instance;
}
// 示例方法
public void showMessage() {
System.out.println("Hello, I'm a Singleton instance!");
}
// 测试
public static void main(String[] args) {
Singleton singleton = Singleton.getInstance();
singleton.showMessage();
}
}
|
2. 工厂方法模式 (Factory Method Pattern)
原理描述:
工厂方法模式通过定义一个创建对象的接口,但由子类决定要实例化的类是哪一个,从而使一个类的实例化延迟到其子类。它解耦了对象的具体创建过程和使用过程。
Java 示例代码:
工厂方法模式
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
|
// 产品接口
interface Product {
void use();
}
// 具体产品A
class ConcreteProductA implements Product {
@Override
public void use() {
System.out.println("Using Product A");
}
}
// 具体产品B
class ConcreteProductB implements Product {
@Override
public void use() {
System.out.println("Using Product B");
}
}
// 工厂抽象类
abstract class Creator {
// 工厂方法:创建产品对象
public abstract Product factoryMethod();
// 业务方法,利用产品
public void doSomething() {
Product product = factoryMethod();
product.use();
}
}
// 具体工厂A
class ConcreteCreatorA extends Creator {
@Override
public Product factoryMethod() {
return new ConcreteProductA();
}
}
// 具体工厂B
class ConcreteCreatorB extends Creator {
@Override
public Product factoryMethod() {
return new ConcreteProductB();
}
}
// 测试
public class FactoryMethodDemo {
public static void main(String[] args) {
Creator creatorA = new ConcreteCreatorA();
creatorA.doSomething(); // 输出 "Using Product A"
Creator creatorB = new ConcreteCreatorB();
creatorB.doSomething(); // 输出 "Using Product B"
}
}
|
3. 抽象工厂模式 (Abstract Factory Pattern)
原理描述:
抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。它用于产品族的创建,使得产品间能协同工作。
Java 示例代码:
抽象工厂模式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
|
// 产品族:椅子和沙发
// 椅子接口
interface Chair {
void sitOn();
}
// 沙发接口
interface Sofa {
void lieOn();
}
// 具体产品:现代风格椅子
class ModernChair implements Chair {
@Override
public void sitOn() {
System.out.println("Sitting on a modern chair");
}
}
// 具体产品:现代风格沙发
class ModernSofa implements Sofa {
@Override
public void lieOn() {
System.out.println("Lying on a modern sofa");
}
}
// 具体产品:维多利亚风格椅子
class VictorianChair implements Chair {
@Override
public void sitOn() {
System.out.println("Sitting on a victorian chair");
}
}
// 具体产品:维多利亚风格沙发
class VictorianSofa implements Sofa {
@Override
public void lieOn() {
System.out.println("Lying on a victorian sofa");
}
}
// 抽象工厂接口
interface FurnitureFactory {
Chair createChair();
Sofa createSofa();
}
// 现代家具工厂
class ModernFurnitureFactory implements FurnitureFactory {
@Override
public Chair createChair() {
return new ModernChair();
}
@Override
public Sofa createSofa() {
return new ModernSofa();
}
}
// 维多利亚家具工厂
class VictorianFurnitureFactory implements FurnitureFactory {
@Override
public Chair createChair() {
return new VictorianChair();
}
@Override
public Sofa createSofa() {
return new VictorianSofa();
}
}
// 测试
public class AbstractFactoryDemo {
public static void main(String[] args) {
FurnitureFactory modernFactory = new ModernFurnitureFactory();
Chair modernChair = modernFactory.createChair();
Sofa modernSofa = modernFactory.createSofa();
modernChair.sitOn();
modernSofa.lieOn();
FurnitureFactory victorianFactory = new VictorianFurnitureFactory();
Chair victorianChair = victorianFactory.createChair();
Sofa victorianSofa = victorianFactory.createSofa();
victorianChair.sitOn();
victorianSofa.lieOn();
}
}
|
4. 建造者模式 (Builder Pattern)
原理描述:
建造者模式将一个复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。它适用于对象内部结构复杂且构造步骤较多的场景。
Java 示例代码:
建造者模式
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
|
// 产品类:一个复杂的Computer对象
class Computer {
private String cpu;
private String memory;
private String storage;
// 私有构造,必须通过Builder创建
private Computer(Builder builder) {
this.cpu = builder.cpu;
this.memory = builder.memory;
this.storage = builder.storage;
}
// 建造者内部类
public static class Builder {
private String cpu;
private String memory;
private String storage;
public Builder setCpu(String cpu) {
this.cpu = cpu;
return this;
}
public Builder setMemory(String memory) {
this.memory = memory;
return this;
}
public Builder setStorage(String storage) {
this.storage = storage;
return this;
}
public Computer build() {
return new Computer(this);
}
}
public void showSpecifications() {
System.out.println("CPU: " + cpu + ", Memory: " + memory + ", Storage: " + storage);
}
}
// 测试
public class BuilderDemo {
public static void main(String[] args) {
Computer computer = new Computer.Builder()
.setCpu("Intel i7")
.setMemory("16GB")
.setStorage("512GB SSD")
.build();
computer.showSpecifications();
}
}
|
5. 原型模式 (Prototype Pattern)
原理描述:
原型模式通过复制现有对象来创建新对象,而不是通过 new 关键字创建。这样可以减少对象创建的开销,尤其当对象的构建过程很复杂或代价较高时。
Java 示例代码:
原型模式
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
|
// 原型接口,要求实现 clone 方法
interface Prototype extends Cloneable {
Prototype clone();
}
// 具体原型类
class ConcretePrototype implements Prototype {
private String data;
public ConcretePrototype(String data) {
this.data = data;
}
// 重写 clone 方法,返回对象拷贝
@Override
public Prototype clone() {
return new ConcretePrototype(this.data);
}
public void showData() {
System.out.println("Data: " + data);
}
}
// 测试
public class PrototypeDemo {
public static void main(String[] args) {
ConcretePrototype original = new ConcretePrototype("Original Data");
ConcretePrototype copy = (ConcretePrototype) original.clone();
original.showData(); // 输出 "Data: Original Data"
copy.showData(); // 输出 "Data: Original Data"
}
}
|
二、结构型模式 (Structural Patterns)
1. 适配器模式 (Adapter Pattern)
充电插头转换器
在日常生活里,不同国家的电源插座规格可能不一样。比如,中国的插座一般是两孔或者三孔的扁平插头,而有些欧洲国家使用的是圆孔插头。如果你去欧洲旅行,携带的中国电器就没办法直接插到欧洲的插座上充电,因为接口不兼容。这时,充电插头转换器就派上用场了。它的一端可以适配欧洲的圆孔插座,另一端能让中国的扁平插头插入。通过这个转换器,原本不能在中国电器和欧洲插座之间传输电力的问题就解决了,让两者可以一起工作。在这个场景中:
- 中国电器:相当于需要适配的类,它有自己的接口(中国的扁平插头),但无法直接和欧洲插座配合。
- 欧洲插座:是客户期望的接口,电器需要适应这个接口才能正常使用。
- 充电插头转换器:就是适配器,它把中国电器的接口(扁平插头)转换成了能适配欧洲插座的接口(圆孔插头)。
Java 示例代码:
适配器模式
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
|
// 目标接口,客户所期待的接口
interface Target {
void request();
}
// 需要适配的类,拥有不同的方法名
class Adaptee {
public void specificRequest() {
System.out.println("Called specificRequest()");
}
}
// 适配器,将 Adaptee 转换为 Target 接口
class Adapter implements Target {
private Adaptee adaptee;
public Adapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
// 调用 adaptee 的方法,实现接口适配
adaptee.specificRequest();
}
}
// 测试
public class AdapterDemo {
public static void main(String[] args) {
Adaptee adaptee = new Adaptee();
Target target = new Adapter(adaptee);
target.request(); // 输出 "Called specificRequest()"
}
}
|
适配器模式-插座版
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
|
// 中国电器接口,有自己的充电方法
interface ChineseElectricalDevice {
void chargeWithChinesePlug();
}
// 具体的中国电器类
class ChinesePhone implements ChineseElectricalDevice {
@Override
public void chargeWithChinesePlug() {
System.out.println("中国手机使用中国扁平插头充电");
}
}
// 欧洲插座接口
interface EuropeanSocket {
void chargeWithEuropeanPlug();
}
// 适配器类,将中国电器的接口转换成欧洲插座的接口
class PlugAdapter implements EuropeanSocket {
private ChineseElectricalDevice device;
public PlugAdapter(ChineseElectricalDevice device) {
this.device = device;
}
@Override
public void chargeWithEuropeanPlug() {
// 适配器内部调用中国电器的充电方法
device.chargeWithChinesePlug();
}
}
// 客户端代码
public class AdapterPatternExample {
public static void main(String[] args) {
// 创建一个中国手机
ChinesePhone phone = new ChinesePhone();
// 创建适配器,并将中国手机传入
PlugAdapter adapter = new PlugAdapter(phone);
// 通过适配器使用欧洲插座给中国手机充电
adapter.chargeWithEuropeanPlug();
}
}
|
2. 桥接模式 (Bridge Pattern)
手机与手机软件的场景
想象你有不同品牌和型号的手机,同时手机上可以安装各种各样的软件,比如游戏、音乐播放器、视频播放器等。
- 抽象部分和实现部分
抽象部分:可以把手机看作抽象部分。不同品牌和型号的手机虽然有不同的外观、性能等,但它们都有一个共同的抽象概念,就是 “手机”,并且都具备安装和运行软件的功能。
实现部分:手机软件就是实现部分。不同类型的软件有不同的功能和实现方式,比如游戏软件主要是提供娱乐功能,音乐播放器软件专注于播放音乐,视频播放器软件用于播放视频。
- 分离与独立变化
抽象部分的变化:手机的品牌和型号可以不断更新换代,比如从旧款的智能手机升级到新款,或者从一个品牌的手机换成另一个品牌的手机。但无论手机如何变化,它与软件之间的基本交互方式(安装、运行软件)不会改变。
实现部分的变化:软件也可以独立发展和更新。开发者可以不断优化游戏的玩法、增加音乐播放器的新功能、提升视频播放器的画质等。软件的这些变化不会影响手机本身的功能和结构。
- 组合关系与动态切换
组合关系:手机和软件之间是通过组合的方式关联在一起的。手机可以安装多个软件,软件也可以在不同的手机上运行。就像桥接模式中通过组合将抽象部分和实现部分关联起来一样。
动态切换实现:你可以根据自己的需求在手机上动态地安装、卸载和切换不同的软件。比如今天你想玩游戏,就打开游戏软件;明天你想听音乐,就打开音乐播放器软件。在这个过程中,手机的基本功能和接口没有改变,只是切换了不同的软件实现。
Java 示例代码:
桥接模式
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
|
// 实现部分,由 DrawAPI 接口以及 RedCircle 和 GreenCircle 具体实现类组成
interface DrawAPI {
void drawCircle(int radius, int x, int y);
}
// 具体实现A
class RedCircle implements DrawAPI {
@Override
public void drawCircle(int radius, int x, int y) {
System.out.println("Drawing Circle [Color: Red, Radius: " + radius + ", x: " + x + ", y:" + y + "]");
}
}
// 具体实现B
class GreenCircle implements DrawAPI {
@Override
public void drawCircle(int radius, int x, int y) {
System.out.println("Drawing Circle [Color: Green, Radius: " + radius + ", x: " + x + ", y:" + y + "]");
}
}
// 抽象部分,由 Shape 抽象类和 Circle 扩充抽象类构成
abstract class Shape {
protected DrawAPI drawAPI; // 组合关系是指一个类包含另一个类的对象作为其成员变量
protected Shape(DrawAPI drawAPI) {
this.drawAPI = drawAPI;
}
public abstract void draw();
}
// 扩充抽象类
class Circle extends Shape {
private int x, y, radius;
public Circle(int x, int y, int radius, DrawAPI drawAPI) {
super(drawAPI);
this.x = x;
this.y = y;
this.radius = radius;
}
@Override
public void draw() {
// 委托实现部分绘制
drawAPI.drawCircle(radius, x, y);
}
}
// 测试
public class BridgeDemo {
public static void main(String[] args) {
Shape redCircle = new Circle(100, 100, 10, new RedCircle()); // 在不改变抽象部分接口的情况下,动态地切换实现部分的功能
Shape greenCircle = new Circle(200, 200, 20, new GreenCircle());
redCircle.draw();
greenCircle.draw();
}
}
|
桥接模式-手机软件版
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
|
// 软件抽象类
abstract class Software {
public abstract void run();
}
// 游戏软件类,继承自软件抽象类
class GameSoftware extends Software {
@Override
public void run() {
System.out.println("运行游戏软件");
}
}
// 音乐播放器软件类,继承自软件抽象类
class MusicPlayerSoftware extends Software {
@Override
public void run() {
System.out.println("运行音乐播放器软件");
}
}
// 手机抽象类
abstract class Phone {
protected Software software;
public Phone(Software software) {
this.software = software;
}
public abstract void openSoftware();
}
// 苹果手机类,继承自手机抽象类
class ApplePhone extends Phone {
public ApplePhone(Software software) {
super(software);
}
@Override
public void openSoftware() {
System.out.println("苹果手机");
software.run();
}
}
// 安卓手机类,继承自手机抽象类
class AndroidPhone extends Phone {
public AndroidPhone(Software software) {
super(software);
}
@Override
public void openSoftware() {
System.out.println("安卓手机");
software.run();
}
}
// 客户端代码
public class BridgePatternExample {
public static void main(String[] args) {
// 创建游戏软件对象
Software gameSoftware = new GameSoftware();
// 创建音乐播放器软件对象
Software musicPlayerSoftware = new MusicPlayerSoftware();
// 创建苹果手机并安装游戏软件
Phone appleGamePhone = new ApplePhone(gameSoftware);
appleGamePhone.openSoftware();
// 创建安卓手机并安装音乐播放器软件
Phone androidMusicPhone = new AndroidPhone(musicPlayerSoftware);
androidMusicPhone.openSoftware();
}
}
|
3. 组合模式 (Composite Pattern)
文件系统示例
再看电脑的文件系统,它也是典型的组合模式应用。
- 整体与部分的层次结构
根目录:相当于树形结构的根节点,代表整个文件系统这个大整体。
文件夹:是根目录或者其他文件夹的一部分,同时文件夹又可以包含子文件夹和文件,自身也能看作一个小整体。
文件:是最基本的部分,不能再细分。
- 对单个对象和组合对象使用的一致性
单个对象:一个具体的文件,如文本文件、图片文件等。
组合对象:一个文件夹就是组合对象,它包含多个文件和子文件夹。
当你要对文件系统进行操作,比如复制操作。对于单个文件,直接复制该文件即可;对于一个文件夹(组合对象),只需要递归地复制文件夹内的所有文件和子文件夹。用户不需要区分操作的是单个文件还是文件夹,操作方式是一致的。
Java 示例代码:
组合模式-树叶版
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
|
import java.util.ArrayList;
import java.util.List;
// 组件接口
interface Component {
void show();
}
// 叶子节点
class Leaf implements Component {
private String name;
public Leaf(String name) {
this.name = name;
}
@Override
public void show() {
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);
}
@Override
public void show() {
System.out.println("Composite: " + name);
for (Component component : children) {
component.show();
}
}
}
// 测试
public class CompositeDemo {
public static void main(String[] args) {
Composite root = new Composite("Root");
Leaf leaf1 = new Leaf("Leaf 1");
Leaf leaf2 = new Leaf("Leaf 2");
root.add(leaf1);
root.add(leaf2);
Composite subComposite = new Composite("Sub Composite");
subComposite.add(new Leaf("Leaf 3"));
root.add(subComposite);
root.show();
}
}
|
组合模式-文件夹版
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
|
// 抽象组件类,定义了所有组件(单个对象和组合对象)的公共接口
abstract class Component {
protected String name;
public Component(String name) {
this.name = name;
}
// 增加组件(对于组合对象有用,单个对象不需要实现)
public void add(Component component) {
throw new UnsupportedOperationException();
}
// 移除组件(对于组合对象有用,单个对象不需要实现)
public void remove(Component component) {
throw new UnsupportedOperationException();
}
// 获取组件(对于组合对象有用,单个对象不需要实现)
public Component getChild(int index) {
throw new UnsupportedOperationException();
}
// 执行操作,单个对象和组合对象都需要实现
public abstract void operation();
}
// 叶子节点类,代表单个对象
class Leaf extends Component {
public Leaf(String name) {
super(name);
}
@Override
public void operation() {
System.out.println("执行单个对象 " + name + " 的操作");
}
}
// 组合节点类,代表组合对象
class Composite extends Component {
private java.util.ArrayList<Component> children = new java.util.ArrayList<>();
public Composite(String name) {
super(name);
}
@Override
public void add(Component component) {
children.add(component);
}
@Override
public void remove(Component component) {
children.remove(component);
}
@Override
public Component getChild(int index) {
return children.get(index);
}
@Override
public void operation() {
System.out.println("执行组合对象 " + name + " 的操作");
for (Component component : children) {
component.operation();
}
}
}
// 客户端代码
public class CompositeDemo2 {
public static void main(String[] args) {
// 创建组合对象(文件夹)
Composite root = new Composite("根文件夹");
// 创建单个对象(文件)
Leaf file1 = new Leaf("文件1");
Leaf file2 = new Leaf("文件2");
// 创建组合对象(子文件夹)
Composite subFolder = new Composite("子文件夹");
// 将文件添加到根文件夹
root.add(file1);
root.add(file2);
// 将子文件夹添加到根文件夹
root.add(subFolder);
// 创建子文件夹中的文件
Leaf file3 = new Leaf("文件3");
subFolder.add(file3);
// 对根文件夹执行操作,会递归地对其包含的所有组件执行操作
root.operation();
}
}
|
4. 装饰器模式 (Decorator Pattern)
吃汉堡加配料的场景
想象你去一家汉堡店买汉堡,基础的汉堡就是一个简单的面包夹肉饼。但是这家店提供了很多额外的配料,比如生菜、番茄、芝士、培根等,你可以根据自己的喜好自由选择添加这些配料,而且可以添加一种或多种。
- 基础对象
基础的汉堡就相当于装饰器模式里的基础对象,它有自己原本的属性和功能,也就是面包夹肉饼这个基本的组合,能满足你最基本的饮食需求。
- 装饰器(配料)
生菜、番茄、芝士、培根等配料就相当于装饰器。每个配料都可以给汉堡添加额外的 “职责”,也就是额外的味道和口感。例如,生菜能让汉堡更清爽,芝士能增加浓郁的奶香。
- 动态添加额外职责
你可以根据自己当时的口味和需求,动态地选择要添加哪些配料。今天你可能想吃清爽一点的,就加生菜和番茄;明天你想口味丰富些,就加芝士和培根。这就好比在程序中,你可以在运行时动态地给对象添加额外的功能,而不需要修改基础对象的代码。
- 比生成子类更灵活
如果不用装饰器模式,要实现不同配料组合的汉堡,可能就得为每一种配料组合创建一个子类,比如 “生菜汉堡类”“生菜番茄汉堡类”“芝士培根汉堡类” 等等。这样会导致子类数量急剧增加,代码变得复杂且难以维护。而使用装饰器模式,只需要把不同的配料(装饰器)组合起来就可以得到各种不同的汉堡,更加灵活方便。
Java 示例代码:
装饰器模式-咖啡版
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
|
// 抽象组件接口
interface Coffee {
double cost();
String description();
}
// 具体组件:基础咖啡
class SimpleCoffee implements Coffee {
@Override
public double cost() {
return 5.0;
}
@Override
public String description() {
return "Simple Coffee";
}
}
// 装饰器抽象类
abstract class CoffeeDecorator implements Coffee {
protected Coffee decoratedCoffee;
public CoffeeDecorator(Coffee coffee) {
this.decoratedCoffee = coffee;
}
@Override
public double cost() {
return decoratedCoffee.cost();
}
@Override
public String description() {
return decoratedCoffee.description();
}
}
// 具体装饰器:加牛奶
class MilkDecorator extends CoffeeDecorator {
public MilkDecorator(Coffee coffee) {
super(coffee);
}
@Override
public double cost() {
return super.cost() + 1.5;
}
@Override
public String description() {
return super.description() + ", with milk";
}
}
// 具体装饰器:加糖
class SugarDecorator extends CoffeeDecorator {
public SugarDecorator(Coffee coffee) {
super(coffee);
}
@Override
public double cost() {
return super.cost() + 0.5;
}
@Override
public String description() {
return super.description() + ", with sugar";
}
}
// 测试
public class DecoratorDemo {
public static void main(String[] args) {
Coffee coffee = new SimpleCoffee();
System.out.println(coffee.description() + " $" + coffee.cost());
// 动态添加装饰功能
coffee = new MilkDecorator(coffee);
System.out.println(coffee.description() + " $" + coffee.cost());
coffee = new SugarDecorator(coffee);
System.out.println(coffee.description() + " $" + coffee.cost());
}
}
|
装饰器模式-汉堡版
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
|
// 汉堡接口,定义了汉堡的基本方法
interface Burger {
String getDescription();
double getCost();
}
// 基础汉堡类,实现了汉堡接口
class BasicBurger implements Burger {
@Override
public String getDescription() {
return "基础汉堡(面包夹肉饼)";
}
@Override
public double getCost() {
return 10.0;
}
}
// 装饰器抽象类,实现了汉堡接口
abstract class BurgerDecorator implements Burger {
protected Burger burger;
public BurgerDecorator(Burger burger) {
this.burger = burger;
}
@Override
public String getDescription() {
return burger.getDescription();
}
@Override
public double getCost() {
return burger.getCost();
}
}
// 生菜装饰器类,继承自装饰器抽象类
class LettuceDecorator extends BurgerDecorator {
public LettuceDecorator(Burger burger) {
super(burger);
}
@Override
public String getDescription() {
return burger.getDescription() + " + 生菜";
}
@Override
public double getCost() {
return burger.getCost() + 2.0;
}
}
// 芝士装饰器类,继承自装饰器抽象类
class CheeseDecorator extends BurgerDecorator {
public CheeseDecorator(Burger burger) {
super(burger);
}
@Override
public String getDescription() {
return burger.getDescription() + " + 芝士";
}
@Override
public double getCost() {
return burger.getCost() + 3.0;
}
}
// 客户端代码
public class DecoratorPatternExample {
public static void main(String[] args) {
// 创建一个基础汉堡
Burger basicBurger = new BasicBurger();
System.out.println(basicBurger.getDescription() + ",价格:" + basicBurger.getCost() + " 元");
// 给汉堡添加生菜
Burger lettuceBurger = new LettuceDecorator(basicBurger);
System.out.println(lettuceBurger.getDescription() + ",价格:" + lettuceBurger.getCost() + " 元");
// 再给汉堡添加芝士
Burger lettuceCheeseBurger = new CheeseDecorator(lettuceBurger);
System.out.println(lettuceCheeseBurger.getDescription() + ",价格:" + lettuceCheeseBurger.getCost() + " 元");
}
}
|
5. 外观模式 (Facade Pattern)
医院看病的例子
想象你去医院看病,医院就像是一个复杂的子系统,里面有很多不同的部门和流程,比如挂号处、各个科室(内科、外科等)、检验科、药房等等。如果没有一个清晰的指引,你可能会在医院里晕头转向,不知道该先做什么、后做什么。
- 复杂的子系统
医院里的各个部门和流程就像是子系统中的各个组件。挂号处负责挂号,不同的科室负责诊断病情,检验科负责做各种检查,药房负责拿药。每个部门都有自己的工作流程和规则,对于不熟悉医院的人来说,要把这些流程都搞清楚并且顺利完成看病的过程是很复杂的。
- 外观接口:导医台
这时,医院的导医台就相当于外观模式中的外观接口。导医台的工作人员会为你提供一个简单的指引,你只需要告诉他们你哪里不舒服,他们就会帮你安排挂号、指引你去相应的科室看病、告诉你做检查的地点和流程,最后还会告诉你去哪里拿药。你不需要了解医院内部各个部门的具体工作流程和规则,只需要和导医台的工作人员沟通,按照他们的指引去做就可以了。
- 降低耦合度
通过导医台这个外观接口,你和医院内部各个部门之间的耦合度降低了。你不需要直接和挂号处、科室医生、检验科、药房等部门打交道,只需要和导医台沟通。这样,即使医院内部的某个部门的工作流程发生了变化,比如挂号处的工作时间调整了,只要导医台知道这个变化并及时调整指引,你作为病人不需要做任何改变,仍然可以按照导医台的指引顺利看病。
Java 示例代码:
外观模式-电脑版
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
|
// 子系统类1
class CPU {
public void start() {
System.out.println("CPU starting...");
}
public void shutdown() {
System.out.println("CPU shutting down...");
}
}
// 子系统类2
class Memory {
public void load() {
System.out.println("Memory loading...");
}
public void clear() {
System.out.println("Memory clearing...");
}
}
// 子系统类3
class HardDrive {
public void read() {
System.out.println("HardDrive reading...");
}
public void write() {
System.out.println("HardDrive writing...");
}
}
// 外观类
class ComputerFacade {
private CPU cpu;
private Memory memory;
private HardDrive hardDrive;
public ComputerFacade() {
cpu = new CPU();
memory = new Memory();
hardDrive = new HardDrive();
}
// 启动计算机
public void startComputer() {
System.out.println("Starting computer...");
cpu.start();
memory.load();
hardDrive.read();
}
// 关闭计算机
public void shutdownComputer() {
System.out.println("Shutting down computer...");
cpu.shutdown();
memory.clear();
hardDrive.write();
}
}
// 测试
public class FacadeDemo {
public static void main(String[] args) {
ComputerFacade computer = new ComputerFacade();
computer.startComputer();
computer.shutdownComputer();
}
}
|
外观模式-医院版
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
|
// 挂号处类
class RegistrationDesk {
public void register() {
System.out.println("在挂号处完成挂号");
}
}
// 科室类
class Department {
public void diagnose() {
System.out.println("在科室进行病情诊断");
}
}
// 检验科类
class Laboratory {
public void test() {
System.out.println("在检验科进行检查");
}
}
// 药房类
class Pharmacy {
public void getMedicine() {
System.out.println("在药房领取药品");
}
}
// 导诊台外观类
class GuideDeskFacade {
private RegistrationDesk registrationDesk;
private Department department;
private Laboratory laboratory;
private Pharmacy pharmacy;
public GuideDeskFacade() {
this.registrationDesk = new RegistrationDesk();
this.department = new Department();
this.laboratory = new Laboratory();
this.pharmacy = new Pharmacy();
}
// 看病流程
public void seeDoctor() {
registrationDesk.register();
department.diagnose();
laboratory.test();
pharmacy.getMedicine();
System.out.println("看病流程完成");
}
}
// 客户端类
public class HospitalGuideDeskExample {
public static void main(String[] args) {
GuideDeskFacade guideDesk = new GuideDeskFacade();
// 患者通过导诊台看病
guideDesk.seeDoctor();
}
}
|
6. 享元模式 (Flyweight Pattern)
生活中的例子:共享单车
想象一下城市里的共享单车系统,这其实就运用了享元模式的思想。
- 大量细粒度对象
城市里有成千上万辆共享单车,每一辆单车就像是一个细粒度的对象。如果每一辆单车都有自己独立的管理系统和数据存储,那会占用大量的资源(比如服务器的存储空间、计算资源等),就像在编程里会消耗大量的内存。
- 相同状态的共享
共享单车有很多相同的部分,比如它们的车型、颜色、基本功能等,这些就是可以共享的状态,也被称为 “内部状态”。在共享单车系统中,不会为每一辆单车都单独记录这些相同的信息,而是将这些信息统一存储和管理。例如,所有同一款式的共享单车,它们的车型和颜色信息只需要存储一份就可以了,不同的单车只是使用了这份相同的信息。
- 不同状态的处理
每一辆共享单车也有不同的状态,比如它当前的位置、是否被租用等,这些就是 “外部状态”。这些外部状态会根据实际情况动态变化,并且会与共享的内部状态结合起来使用。比如,虽然所有同一款式的单车共享车型和颜色信息,但每一辆车都有自己独立的位置信息,通过将共享的车型、颜色信息和各自的位置信息结合,就可以准确地表示每一辆单车的完整状态。
- 降低资源消耗
通过这种共享相同状态的方式,共享单车系统可以有效地降低资源消耗。如果每一辆单车都单独存储所有信息,那么需要的存储空间和管理成本会非常高;而共享相同状态后,只需要存储一份公共信息,大大减少了资源的占用,提高了系统的效率。
Java 示例代码:
享元模式-画图版
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
|
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);
}
return circle;
}
}
// 测试
public class FlyweightDemo {
public static void main(String[] args) {
Circle circle1 = (Circle) ShapeFactory.getCircle("Red");
circle1.setX(10);
circle1.setY(20);
circle1.setRadius(5);
circle1.draw();
Circle circle2 = (Circle) ShapeFactory.getCircle("Red");
circle2.setX(30);
circle2.setY(40);
circle2.setRadius(10);
circle2.draw();
// circle1 和 circle2 实际上共享同一个 Circle 对象,但使用不同外部状态
System.out.println("circle1 和 circle2 是否是同一对象: " + (circle1 == circle2));
}
}
|
享元模式-共享单车版
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
|
import java.util.HashMap;
import java.util.Map;
public class FlyweightDemo {
public static void main(String[] args) {
// 获取一辆共享单车
SharedBike bike1 = (SharedBike) BikeFactory.getBike("山地车", "蓝色");
bike1.ride("公园门口");
// 再获取一辆相同型号和颜色的共享单车
SharedBike bike2 = (SharedBike) BikeFactory.getBike("山地车", "蓝色");
bike2.ride("商场门口");
// 检查是否是同一对象
System.out.println("bike1 和 bike2 是否是同一对象: " + (bike1 == bike2));
}
}
interface Bike {
void ride(String location);
}
class SharedBike implements Bike {
private String model; // 内部状态,共享的信息
private String color;
public SharedBike(String model, String color) {
this.model = model;
this.color = color;
}
@Override
public void ride(String location) {
System.out.println("骑一辆 " + color + " 色的 " + model + " 单车,当前位置: " + location);
}
}
class BikeFactory {
private static Map<String, Bike> bikeMap = new HashMap<>();
public static Bike getBike(String model, String color) {
String key = model + "-" + color;
if (!bikeMap.containsKey(key)) {
bikeMap.put(key, new SharedBike(model, color));
}
return bikeMap.get(key);
}
}
|
7. 代理模式 (Proxy Pattern)
生活中的租房场景:
想象你在外地工作,需要租房子。但你对当地的房源信息不太了解,也没有时间和精力一家一家去看房、和房东谈价格、签合同等。这时,你就可以找一个房屋中介来帮忙,这个房屋中介就相当于代理模式里的 “代理对象”,而房东则是 “目标对象”。
- 中介(代理对象)的中介作用
中介掌握了大量的房源信息,你只需要把自己的租房需求告诉中介,比如你想要几居室、预算多少、希望在哪个区域等,中介就会根据你的需求筛选出合适的房子,然后安排你去看房。这样,中介就在你(客户端)和房东(目标对象)之间起到了桥梁的作用,让你和房东之间的沟通更高效。
- 中介(代理对象)的控制作用
中介可以控制你和房东接触的流程和方式。比如,在你还没有确定要租某套房子之前,中介可能不会让你直接和房东联系,防止你跳过中介私下和房东达成交易。只有当你确定要租某套房子了,中介才会安排你和房东见面签合同。这就类似于代理对象控制客户端对目标对象的访问,确保访问过程符合一定的规则。
- 中介(代理对象)的增强作用
中介还可以为你提供一些额外的服务,增强你租房的体验。比如,中介可以帮你对房子的情况进行更详细的了解,包括房子的水电费缴纳方式、小区的物业服务等;在和房东谈价格的时候,中介也可以凭借自己的经验帮你争取更优惠的租金。这些额外的服务就像是代理对象为目标对象的功能进行了增强。
Java 示例代码:
代理模式-图片版
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
|
// 抽象主题接口
interface Image {
void display();
}
// 真实主题类
class RealImage implements Image {
private String filename;
public RealImage(String filename) {
this.filename = filename;
loadFromDisk();
}
private void loadFromDisk() {
System.out.println("Loading " + filename);
}
@Override
public void display() {
System.out.println("Displaying " + filename);
}
}
// 代理类
class ProxyImage implements Image {
private String filename;
private RealImage realImage;
public ProxyImage(String filename) {
this.filename = filename;
}
@Override
public void display() {
// 延迟加载
if (realImage == null) {
realImage = new RealImage(filename);
}
realImage.display();
}
}
// 测试
public class ProxyDemo {
public static void main(String[] args) {
Image image = new ProxyImage("test_image.jpg");
// 第一次调用 display() 时加载图片
image.display();
// 第二次调用时直接显示,不重复加载
image.display();
}
}
|
代理模式-租房版
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
|
// 租房接口,定义了租房的方法
interface RentHouse {
void rent();
}
// 房东类,实现了租房接口,是目标对象
class Landlord implements RentHouse {
@Override
public void rent() {
System.out.println("房东出租房子");
}
}
// 房屋中介类,实现了租房接口,是代理对象
class HouseAgent implements RentHouse {
private Landlord landlord;
public HouseAgent(Landlord landlord) {
this.landlord = landlord;
}
@Override
public void rent() {
// 中介的额外服务,相当于增强作用
System.out.println("中介筛选合适的房子");
System.out.println("中介安排看房");
// 调用房东的租房方法
landlord.rent();
// 中介的额外服务,相当于增强作用
System.out.println("中介协助签订合同");
}
}
// 客户端类
public class ProxyPatternExample {
public static void main(String[] args) {
// 创建房东对象
Landlord landlord = new Landlord();
// 创建中介对象,并将房东对象传入
HouseAgent agent = new HouseAgent(landlord);
// 通过中介租房
agent.rent();
}
}
|
三、行为型模式 (Behavioral Patterns)
1. 责任链模式 (Chain of Responsibility Pattern)
请假场景
你因为有事需要请假,公司规定请假天数不同,审批人也不同。如果请假天数小于等于 1 天,由项目经理审批;如果在 1 天到 3 天之间,由部门经理审批;如果超过 3 天,由总经理审批。
- 责任链的建立
- 这里有三个审批人:项目经理、部门经理和总经理,他们就构成了一个责任链。当你提交请假申请时,这个申请就好比是一个请求,会沿着这个责任链依次传递。
- 请求的传递与处理
- 首先,你把请假申请提交给项目经理。如果你的请假天数小于等于 1 天,项目经理就会直接处理你的请求,批准或者拒绝你的请假。
- 要是你的请假天数超过了 1 天,项目经理觉得自己权限不够,就会把你的申请传递给部门经理。如果请假天数在 1 天到 3 天之间,部门经理就会处理这个请求。
- 但如果部门经理发现你的请假天数超过了 3 天,他也处理不了,就会再把申请传递给总经理,由总经理来做最后的审批。
Java 示例代码:
责任链模式-代码示例
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
|
// 抽象处理者
abstract class Handler {
protected Handler next;
public void setNext(Handler next) {
this.next = next;
}
public abstract void handleRequest(String request);
}
// 具体处理者A
class ConcreteHandlerA extends Handler {
@Override
public void handleRequest(String request) {
if (request.contains("A")) {
System.out.println("Handler A processed the request: " + request);
} else if (next != null) {
next.handleRequest(request);
}
}
}
// 具体处理者B
class ConcreteHandlerB extends Handler {
@Override
public void handleRequest(String request) {
if (request.contains("B")) {
System.out.println("Handler B processed the request: " + request);
} else if (next != null) {
next.handleRequest(request);
}
}
}
// 测试
public class ChainOfResponsibilityDemo {
public static void main(String[] args) {
Handler handlerA = new ConcreteHandlerA();
Handler handlerB = new ConcreteHandlerB();
handlerA.setNext(handlerB);
handlerA.handleRequest("Request for A");
handlerA.handleRequest("Request for B");
handlerA.handleRequest("Request for C");
}
}
|
责任链模式-请假
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
|
// 抽象处理者类
abstract class Approver {
protected Approver nextApprover;
public void setNextApprover(Approver nextApprover) {
this.nextApprover = nextApprover;
}
public abstract void processRequest(int days);
}
// 项目经理类
class ProjectManager extends Approver {
@Override
public void processRequest(int days) {
if (days <= 1) {
System.out.println("项目经理批准了 " + days + " 天的请假申请。");
} else if (nextApprover != null) {
nextApprover.processRequest(days);
}
}
}
// 部门经理类
class DepartmentManager extends Approver {
@Override
public void processRequest(int days) {
if (days > 1 && days <= 3) {
System.out.println("部门经理批准了 " + days + " 天的请假申请。");
} else if (nextApprover != null) {
nextApprover.processRequest(days);
}
}
}
// 总经理类
class GeneralManager extends Approver {
@Override
public void processRequest(int days) {
if (days > 3) {
System.out.println("总经理批准了 " + days + " 天的请假申请。");
}
}
}
// 客户端类
public class LeaveApprovalChain {
public static void main(String[] args) {
// 创建各个审批者
Approver projectManager = new ProjectManager();
Approver departmentManager = new DepartmentManager();
Approver generalManager = new GeneralManager();
// 设置责任链顺序
projectManager.setNextApprover(departmentManager);
departmentManager.setNextApprover(generalManager);
// 模拟不同天数的请假申请
projectManager.processRequest(1);
projectManager.processRequest(2);
projectManager.processRequest(5);
}
}
|
2. 命令模式 (Command Pattern)
餐厅点餐场景
在餐厅里,顾客点餐时会向服务员提出各种菜品的请求。服务员会把这些请求记录在订单上,而不是直接让厨师去做菜。服务员可以将订单按顺序排队,也可以记录订单日志(比如记录每个订单的时间、菜品等信息)。之后,服务员会在合适的时候把订单传递给厨师,厨师根据订单上的菜品进行烹饪。
- 顾客的点餐请求:就相当于命令模式里的 “请求”。
- 订单:相当于封装了请求的 “命令对象”。
- 服务员:负责接收、排队、记录订单,类似于命令模式中对请求进行参数化、队列化和记录日志的角色。
- 厨师:负责执行具体的做菜操作,对应命令模式中执行操作的角色。
- 顾客:则是发出请求的 “客户”。通过这种方式,顾客和厨师之间的直接耦合被解除了,顾客不需要知道厨师是如何做菜的,厨师也不需要知道是哪个顾客点的菜。
Java 示例代码:
命令模式-开灯
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
|
// 命令接口
interface Command {
void execute();
}
// 具体命令
class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.on();
}
}
// 接收者
class Light {
public void on() {
System.out.println("Light is ON");
}
public void off() {
System.out.println("Light is OFF");
}
}
// 调用者
class RemoteControl {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void pressButton() {
command.execute();
}
}
// 测试
public class CommandDemo {
public static void main(String[] args) {
Light light = new Light();
Command lightOn = new LightOnCommand(light);
RemoteControl remote = new RemoteControl();
remote.setCommand(lightOn);
remote.pressButton(); // 输出 "Light is ON"
}
}
|
命令模式-餐厅点餐
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
|
// 命令接口
interface Command {
void execute();
}
// 具体命令类:点炒饭
class FriedRiceCommand implements Command {
private Chef chef;
public FriedRiceCommand(Chef chef) {
this.chef = chef;
}
@Override
public void execute() {
chef.cookFriedRice();
}
}
// 具体命令类:点炒面
class FriedNoodlesCommand implements Command {
private Chef chef;
public FriedNoodlesCommand(Chef chef) {
this.chef = chef;
}
@Override
public void execute() {
chef.cookFriedNoodles();
}
}
// 厨师类,负责执行具体的做菜操作
class Chef {
public void cookFriedRice() {
System.out.println("厨师正在做炒饭");
}
public void cookFriedNoodles() {
System.out.println("厨师正在做炒面");
}
}
// 服务员类,负责接收、排队和执行命令
class Waiter {
private Command command;
public void takeOrder(Command command) {
this.command = command;
}
public void placeOrder() {
if (command != null) {
command.execute();
}
}
}
// 客户端类
public class RestaurantCommandPattern {
public static void main(String[] args) {
// 创建厨师对象
Chef chef = new Chef();
// 创建服务员对象
Waiter waiter = new Waiter();
// 创建点炒饭的命令
Command friedRiceCommand = new FriedRiceCommand(chef);
// 服务员接收点炒饭的订单
waiter.takeOrder(friedRiceCommand);
// 服务员下单,执行命令
waiter.placeOrder();
// 创建点炒面的命令
Command friedNoodlesCommand = new FriedNoodlesCommand(chef);
// 服务员接收点炒面的订单
waiter.takeOrder(friedNoodlesCommand);
// 服务员下单,执行命令
waiter.placeOrder();
}
}
|
3. 解释器模式 (Interpreter Pattern)
生活场景名称:简单数学表达式计算器
在小学课堂上,老师教学生计算简单的数学表达式,比如 “2 + 3” 或者 “5 - 1” 等。这里的数学表达式就像是一种特定的语言,每个表达式都遵循一定的语法规则(例如先进行乘除运算,后进行加减运算,有括号先算括号内等)。老师就相当于一个解释器,能够理解这些表达式,并按照规则计算出结果。具体来说,数字(如 2、3、5 等)和运算符(如 +、-、*、/)组成了表达式的基本元素,不同的组合形成了各种句子(即数学表达式)。老师会根据这些元素和规则来解释并计算每个表达式的结果。
- 定义语法规则:确定数学表达式由数字和运算符组成,并且规定运算符的优先级等语法规则。
- 构建解释器:为每个语法元素(数字、运算符)创建对应的类,这些类实现解释器的逻辑,能够解释并计算出表达式的结果。
- 输入表达式:将用户输入的数学表达式作为句子,由解释器进行处理。
- 输出结果:解释器计算出表达式的结果并输出。
Java 示例代码:
解释器模式-示例代码
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
|
// 抽象表达式接口
interface Expression {
boolean interpret(String context);
}
// 终结符表达式
class TerminalExpression implements Expression {
private String data;
public TerminalExpression(String data) {
this.data = data;
}
@Override
public boolean interpret(String context) {
return context.contains(data);
}
}
// 非终结符表达式:或表达式
class OrExpression implements Expression {
private Expression expr1;
private Expression expr2;
public OrExpression(Expression expr1, Expression expr2) {
this.expr1 = expr1;
this.expr2 = expr2;
}
@Override
public boolean interpret(String context) {
return expr1.interpret(context) || expr2.interpret(context);
}
}
// 测试
public class InterpreterDemo {
public static void main(String[] args) {
Expression isAdmin = new TerminalExpression("Admin");
Expression isManager = new TerminalExpression("Manager");
Expression isAdminOrManager = new OrExpression(isAdmin, isManager);
System.out.println("User is admin or manager? " + isAdminOrManager.interpret("Admin"));
}
}
|
解释器模式-数学表达式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
|
// 抽象表达式类
abstract class Expression {
public abstract int interpret();
}
// 数字表达式类
class NumberExpression extends Expression {
private int number;
public NumberExpression(int number) {
this.number = number;
}
@Override
public int interpret() {
return number;
}
}
// 加法表达式类
class AdditionExpression extends Expression {
private Expression left;
private Expression right;
public AdditionExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}
@Override
public int interpret() {
return left.interpret() + right.interpret();
}
}
// 减法表达式类
class SubtractionExpression extends Expression {
private Expression left;
private Expression right;
public SubtractionExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}
@Override
public int interpret() {
return left.interpret() - right.interpret();
}
}
// 客户端类
public class InterpreterPatternExample {
public static void main(String[] args) {
// 构建表达式 2 + 3
Expression expression = new AdditionExpression(
new NumberExpression(2),
new NumberExpression(3)
);
// 解释并计算表达式
int result = expression.interpret();
System.out.println("表达式 2 + 3 的结果是: " + result);
// 构建表达式 5 - 1
Expression subtractionExpression = new SubtractionExpression(
new NumberExpression(5),
new NumberExpression(1)
);
// 解释并计算表达式
int subtractionResult = subtractionExpression.interpret();
System.out.println("表达式 5 - 1 的结果是: " + subtractionResult);
}
}
|
4. 迭代器模式 (Iterator Pattern)
生活场景名称:图书馆书架书籍遍历
在图书馆里,书架上摆满了各种书籍。图书馆管理员有时需要依次查看每一本书,比如检查书籍的摆放是否整齐、更新书籍的借阅信息等。书架就相当于一个聚合对象,它包含了许多书籍。
如果没有迭代器模式,管理员可能需要直接操作书架的内部结构(比如知道书架是按层摆放,每层又有特定的顺序等)来遍历书籍,这就暴露了书架的内部表示。而有了迭代器模式,就相当于有了一个专门的工具(迭代器),管理员可以使用这个工具按照一定的顺序(比如从左到右,从上到下)访问书架上的每一本书,却不需要了解书架具体是如何组织和存储书籍的。
也就是说,迭代器模式将遍历书架上书籍的操作从书架这个聚合对象中分离出来,提供了一种更简洁、更安全的方式来访问书架上的各个元素(书籍)。
- 定义聚合对象:创建一个表示书架的类,它包含了书籍的集合。
- 定义迭代器接口:规定迭代器应该具备的方法,比如判断是否还有下一本书、获取下一本书等。
- 实现迭代器类:为书架创建一个具体的迭代器类,实现迭代器接口,负责按照一定顺序遍历书架上的书籍。
- 使用迭代器:在客户端代码中,通过书架获取迭代器,然后使用迭代器来遍历书架上的书籍。
Java 示例代码:
迭代器模式-代码示例
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
|
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
// 集合类
class NameRepository implements Iterable<String> {
private List<String> names = new ArrayList<>();
public NameRepository() {
names.add("Alice");
names.add("Bob");
names.add("Charlie");
}
@Override
public Iterator<String> iterator() {
return names.iterator();
}
}
// 测试
public class IteratorDemo {
public static void main(String[] args) {
NameRepository names = new NameRepository();
for (String name : names) {
System.out.println(name);
}
}
}
|
迭代器模式-书架
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
|
import java.util.ArrayList;
import java.util.List;
// 书籍类
class Book {
private String title;
public Book(String title) {
this.title = title;
}
public String getTitle() {
return title;
}
}
// 迭代器接口
interface Iterator {
boolean hasNext();
Book next();
}
// 书架类,聚合对象
class Bookshelf {
private List<Book> books = new ArrayList<>();
public void addBook(Book book) {
books.add(book);
}
public Iterator createIterator() {
return new BookshelfIterator();
}
// 迭代器类
private class BookshelfIterator implements Iterator {
private int index = 0;
@Override
public boolean hasNext() {
return index < books.size();
}
@Override
public Book next() {
Book book = books.get(index);
index++;
return book;
}
}
}
// 客户端类
public class IteratorPatternExample {
public static void main(String[] args) {
Bookshelf bookshelf = new Bookshelf();
bookshelf.addBook(new Book("Book 1"));
bookshelf.addBook(new Book("Book 2"));
bookshelf.addBook(new Book("Book 3"));
Iterator iterator = bookshelf.createIterator();
while (iterator.hasNext()) {
Book book = iterator.next();
System.out.println("当前书籍: " + book.getTitle());
}
}
}
|
生活场景:机场塔台调度
在机场中,有多架飞机需要起降,每架飞机都有自己的状态(如准备起飞、正在降落等)。如果没有统一的协调,飞机之间需要相互沟通各自的状态和计划,这会导致飞机之间的交互变得非常复杂,而且耦合度很高。
而机场塔台就充当了中介者的角色。飞机不需要直接和其他飞机交互,而是和机场塔台进行沟通。飞机将自己的状态和需求告知塔台,塔台根据所有飞机的信息进行调度,协调飞机的起降顺序,确保飞行安全。这样飞机之间的耦合度就大大降低了,它们只需要和塔台进行交互。
- 定义飞机类:每架飞机有自己的状态和方法,飞机可以向塔台发送请求,并接收塔台的指令。
- 定义塔台类(中介者):塔台维护所有飞机的信息,接收飞机的请求,根据情况协调飞机的起降。
- 飞机与塔台交互:飞机通过塔台来间接与其他飞机进行交互,而不是直接和其他飞机通信。
Java 示例代码:
中介者模式-聊天中介
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
// 抽象中介者
interface ChatMediator {
void sendMessage(String msg, User user);
void addUser(User user);
}
// 具体中介者
class ChatMediatorImpl implements ChatMediator {
private List<User> users = new ArrayList<>();
@Override
public void addUser(User user) {
users.add(user);
}
@Override
public void sendMessage(String msg, User user) {
for (User u : users) {
// 不向自己发送消息
if (u != user) {
u.receive(msg);
}
}
}
}
// 同事类
abstract class User {
protected ChatMediator mediator;
protected String name;
public User(ChatMediator mediator, String name) {
this.mediator = mediator;
this.name = name;
}
public abstract void send(String msg);
public abstract void receive(String msg);
}
// 具体同事类
class ChatUser extends User {
public ChatUser(ChatMediator mediator, String name) {
super(mediator, name);
}
@Override
public void send(String msg) {
System.out.println(name + " sends: " + msg);
mediator.sendMessage(msg, this);
}
@Override
public void receive(String msg) {
System.out.println(name + " receives: " + msg);
}
}
// 测试
public class MediatorDemo {
public static void main(String[] args) {
ChatMediator mediator = new ChatMediatorImpl();
User user1 = new ChatUser(mediator, "Alice");
User user2 = new ChatUser(mediator, "Bob");
User user3 = new ChatUser(mediator, "Charlie");
mediator.addUser(user1);
mediator.addUser(user2);
mediator.addUser(user3);
user1.send("Hello everyone!");
}
}
|
中介者模式-飞机塔台
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
|
import java.util.ArrayList;
import java.util.List;
// 飞机抽象类
abstract class Plane {
protected AirportTower tower;
protected String name;
public Plane(AirportTower tower, String name) {
this.tower = tower;
this.name = name;
}
public abstract void sendRequest(String message);
public abstract void receiveMessage(String message);
}
// 具体飞机类
class ConcretePlane extends Plane {
public ConcretePlane(AirportTower tower, String name) {
super(tower, name);
}
@Override
public void sendRequest(String message) {
System.out.println(name + " 发送请求: " + message);
tower.notifyPlanes(this, message);
}
@Override
public void receiveMessage(String message) {
System.out.println(name + " 收到消息: " + message);
}
}
// 机场塔台类(中介者)
class AirportTower {
private List<Plane> planes = new ArrayList<>();
public void registerPlane(Plane plane) {
planes.add(plane);
}
public void notifyPlanes(Plane sender, String message) {
for (Plane plane : planes) {
if (plane != sender) {
plane.receiveMessage(sender.name + " 说: " + message);
}
}
}
}
// 客户端类
public class AirportMediatorPattern {
public static void main(String[] args) {
AirportTower tower = new AirportTower();
Plane plane1 = new ConcretePlane(tower, "飞机1");
Plane plane2 = new ConcretePlane(tower, "飞机2");
Plane plane3 = new ConcretePlane(tower, "飞机3");
tower.registerPlane(plane1);
tower.registerPlane(plane2);
tower.registerPlane(plane3);
plane1.sendRequest("我准备起飞");
}
}
|
6. 备忘录模式 (Memento Pattern)
生活场景:文档编辑的撤销功能
在使用文档编辑软件(如 Word)时,我们经常会进行各种文本编辑操作,比如输入文字、删除段落、修改格式等。有时候可能会误操作,或者突然改变了主意,想要回到之前的某个状态。这时,软件提供的撤销功能就派上用场了。每进行一次重要操作,软件会自动记录当前文档的状态,当我们点击撤销按钮时,软件会恢复到上一次记录的状态。
在这个场景中,文档就是需要被管理状态的对象,软件记录文档状态的机制就类似于备忘录模式。备忘录模式允许我们在不破坏文档对象封装性的前提下,捕获文档的内部状态(即备忘录),并在需要的时候恢复到之前的状态。
- 定义文档类:包含文档的内容和相关操作,如输入文字、撤销操作等。
- 定义备忘录类:用于存储文档的某个状态。
- 定义管理者类:负责管理备忘录,如保存和恢复备忘录。
- 实现撤销功能:在文档类中调用管理者类的方法来保存和恢复备忘录,实现撤销操作。
Java 示例代码:
备忘录模式-发起者
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
|
import java.util.Stack;
// 发起人
class Originator {
private String state;
public void setState(String state) {
this.state = state;
System.out.println("Setting state to " + state);
}
public String getState() {
return state;
}
public Memento saveStateToMemento() {
return new Memento(state);
}
public void getStateFromMemento(Memento memento) {
state = memento.getState();
System.out.println("State restored to " + state);
}
}
// 备忘录
class Memento {
private final String state;
public Memento(String state) {
this.state = state;
}
public String getState() {
return state;
}
}
// 管理者
class Caretaker {
private Stack<Memento> mementoStack = new Stack<>();
public void add(Memento state){
mementoStack.push(state);
}
public Memento get(){
return mementoStack.pop();
}
}
// 测试
public class MementoDemo {
public static void main(String[] args) {
Originator originator = new Originator();
Caretaker caretaker = new Caretaker();
originator.setState("State1");
caretaker.add(originator.saveStateToMemento());
originator.setState("State2");
caretaker.add(originator.saveStateToMemento());
originator.setState("State3");
// 恢复到上一个状态
originator.getStateFromMemento(caretaker.get());
}
}
|
备忘录模式-文档撤销
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
|
// 备忘录类,用于存储文档的状态
class DocumentMemento {
private String content;
public DocumentMemento(String content) {
this.content = content;
}
public String getContent() {
return content;
}
}
// 文档类,需要被管理状态的对象
class Document {
private String content;
private Caretaker caretaker;
public Document(Caretaker caretaker) {
this.caretaker = caretaker;
}
public void setContent(String content) {
this.content = content;
// 保存当前状态
caretaker.saveMemento(createMemento());
}
public String getContent() {
return content;
}
public DocumentMemento createMemento() {
return new DocumentMemento(content);
}
public void restoreFromMemento(DocumentMemento memento) {
this.content = memento.getContent();
}
public void undo() {
DocumentMemento memento = caretaker.getPreviousMemento();
if (memento != null) {
restoreFromMemento(memento);
}
}
}
// 管理者类,负责管理备忘录
class Caretaker {
private java.util.List<DocumentMemento> mementos = new java.util.ArrayList<>();
private int currentIndex = -1;
public void saveMemento(DocumentMemento memento) {
// 如果有撤销操作后又有新操作,清除撤销操作之后的所有备忘录
if (currentIndex < mementos.size() - 1) {
mementos = mementos.subList(0, currentIndex + 1);
}
mementos.add(memento);
currentIndex++;
}
public DocumentMemento getPreviousMemento() {
if (currentIndex > 0) {
currentIndex--;
return mementos.get(currentIndex);
}
return null;
}
}
// 客户端类
public class DocumentMementoPattern {
public static void main(String[] args) {
Caretaker caretaker = new Caretaker();
Document document = new Document(caretaker);
// 输入内容
document.setContent("第一段文字");
System.out.println("当前内容: " + document.getContent());
document.setContent("第一段文字,第二段文字");
System.out.println("当前内容: " + document.getContent());
// 撤销操作
document.undo();
System.out.println("撤销后内容: " + document.getContent());
}
}
|
7. 观察者模式 (Observer Pattern)
生活场景:天气预报
在日常生活中,天气预报服务就是观察者模式的典型应用。气象站作为主题,持续收集和监测天气数据。而像手机天气应用、电视台、电台等就相当于观察者,它们依赖气象站提供的天气信息。一旦气象站监测到天气状态发生变化(比如气温、降雨量等改变),就会通知所有依赖它的观察者。这些观察者在收到通知后,会自动更新并向用户展示最新的天气情况。
- 定义主题接口:规定主题需要具备注册观察者、移除观察者以及通知观察者的方法。
- 实现具体主题类:实现主题接口,维护一个观察者列表,在状态改变时通知所有观察者。
- 定义观察者接口:规定观察者需要具备接收通知并更新状态的方法。
- 实现具体观察者类:实现观察者接口,在接收到通知时更新自身状态。
Java 示例代码:
观察者模式-示例代码
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
|
import java.util.ArrayList;
import java.util.List;
// 主题接口
interface Subject {
void attach(Observer o);
void detach(Observer o);
void notifyObservers();
}
// 观察者接口
interface Observer {
void update(String message);
}
// 具体主题
class ConcreteSubject implements Subject {
private List<Observer> observers = new ArrayList<>();
private String state;
public void setState(String state) {
this.state = state;
notifyObservers();
}
@Override
public void attach(Observer o) {
observers.add(o);
}
@Override
public void detach(Observer o) {
observers.remove(o);
}
@Override
public void notifyObservers() {
for (Observer o : observers) {
o.update(state);
}
}
}
// 具体观察者
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);
}
}
// 测试
public class ObserverDemo {
public static void main(String[] args) {
ConcreteSubject subject = new ConcreteSubject();
Observer obs1 = new ConcreteObserver("Observer1");
Observer obs2 = new ConcreteObserver("Observer2");
subject.attach(obs1);
subject.attach(obs2);
subject.setState("New State");
}
}
|
观察者模式-天气预报
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
|
import java.util.ArrayList;
import java.util.List;
// 观察者接口
interface Observer {
void update(String weatherInfo);
}
// 主题接口
interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}
// 具体主题类:气象站
class WeatherStation implements Subject {
private List<Observer> observers = new ArrayList<>();
private String weatherInfo;
@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(weatherInfo);
}
}
public void setWeatherInfo(String weatherInfo) {
this.weatherInfo = weatherInfo;
notifyObservers();
}
}
// 具体观察者类:手机天气应用
class MobileWeatherApp implements Observer {
@Override
public void update(String weatherInfo) {
System.out.println("手机天气应用收到更新:" + weatherInfo);
}
}
// 具体观察者类:电视台
class TVStation implements Observer {
@Override
public void update(String weatherInfo) {
System.out.println("电视台收到更新:" + weatherInfo);
}
}
// 客户端类
public class WeatherForecastObserver {
public static void main(String[] args) {
// 创建气象站
WeatherStation weatherStation = new WeatherStation();
// 创建观察者
MobileWeatherApp mobileApp = new MobileWeatherApp();
TVStation tvStation = new TVStation();
// 注册观察者
weatherStation.registerObserver(mobileApp);
weatherStation.registerObserver(tvStation);
// 模拟天气信息更新
weatherStation.setWeatherInfo("今天有雨");
}
}
|
8. 状态模式 (State Pattern)
生活场景:自动售货机
在日常生活中,自动售货机就是状态模式的一个很好的例子。自动售货机有多种状态,比如待机状态、投币状态、出货状态等。不同的状态下,自动售货机对于用户的操作会有不同的响应。待机状态:此时自动售货机等待用户投币,用户可以进行投币操作进入投币状态。投币状态:用户已经投入了一定金额,此时可以选择商品。如果选择的商品价格不超过投入的金额,自动售货机就会进入出货状态;如果用户想要退款,自动售货机则会回到待机状态并退还硬币。出货状态:自动售货机将用户选择的商品送出,然后回到待机状态等待下一次交易。
- 定义状态接口:规定状态对象需要具备的方法,这些方法代表了在不同状态下自动售货机可以执行的操作。
- 实现具体状态类:为每个状态(如待机状态、投币状态、出货状态)创建一个具体的状态类,实现状态接口中的方法,定义在该状态下自动售货机的行为。
- 定义上下文类:即自动售货机类,维护一个当前状态对象的引用,并提供方法来改变当前状态。
- 状态转换:在上下文类中,根据用户的操作和当前状态,调用相应状态对象的方法,并在必要时改变当前状态。
Java 示例代码:
状态模式-示例代码
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
|
// 状态接口
interface State {
void doAction(Context context);
}
// 具体状态A
class StateA implements State {
@Override
public void doAction(Context context) {
System.out.println("State A handling request, switching to State B");
context.setState(new StateB());
}
}
// 具体状态B
class StateB implements State {
@Override
public void doAction(Context context) {
System.out.println("State B handling request, switching to State A");
context.setState(new StateA());
}
}
// 上下文类
class Context {
private State state;
public Context(State state) {
this.state = state;
}
public void setState(State state) {
this.state = state;
}
public void request() {
state.doAction(this);
}
}
// 测试
public class StateDemo {
public static void main(String[] args) {
Context context = new Context(new StateA());
context.request();
context.request();
}
}
|
状态模式-自动售货机
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
|
// 状态接口
interface VendingMachineState {
void insertCoin(VendingMachine vendingMachine);
void selectProduct(VendingMachine vendingMachine);
void dispenseProduct(VendingMachine vendingMachine);
void refund(VendingMachine vendingMachine);
}
// 待机状态类
class StandbyState implements VendingMachineState {
@Override
public void insertCoin(VendingMachine vendingMachine) {
System.out.println("硬币已投入,进入投币状态");
vendingMachine.setState(new CoinInsertedState());
}
@Override
public void selectProduct(VendingMachine vendingMachine) {
System.out.println("请先投币");
}
@Override
public void dispenseProduct(VendingMachine vendingMachine) {
System.out.println("请先投币并选择商品");
}
@Override
public void refund(VendingMachine vendingMachine) {
System.out.println("您还未投币,无需退款");
}
}
// 投币状态类
class CoinInsertedState implements VendingMachineState {
@Override
public void insertCoin(VendingMachine vendingMachine) {
System.out.println("您已经投过币了,可直接选择商品");
}
@Override
public void selectProduct(VendingMachine vendingMachine) {
System.out.println("商品已选择,准备出货");
vendingMachine.setState(new DispensingState());
}
@Override
public void dispenseProduct(VendingMachine vendingMachine) {
System.out.println("请先选择商品");
}
@Override
public void refund(VendingMachine vendingMachine) {
System.out.println("硬币已退还,回到待机状态");
vendingMachine.setState(new StandbyState());
}
}
// 出货状态类
class DispensingState implements VendingMachineState {
@Override
public void insertCoin(VendingMachine vendingMachine) {
System.out.println("正在出货,请稍等");
}
@Override
public void selectProduct(VendingMachine vendingMachine) {
System.out.println("正在出货,请稍等");
}
@Override
public void dispenseProduct(VendingMachine vendingMachine) {
System.out.println("商品已出货,回到待机状态");
vendingMachine.setState(new StandbyState());
}
@Override
public void refund(VendingMachine vendingMachine) {
System.out.println("正在出货,无法退款,请稍等");
}
}
// 上下文类:自动售货机
class VendingMachine {
private VendingMachineState currentState;
public VendingMachine() {
this.currentState = new StandbyState();
}
public void setState(VendingMachineState state) {
this.currentState = state;
}
public void insertCoin() {
currentState.insertCoin(this);
}
public void selectProduct() {
currentState.selectProduct(this);
}
public void dispenseProduct() {
currentState.dispenseProduct(this);
}
public void refund() {
currentState.refund(this);
}
}
// 客户端类
public class VendingMachineStatePattern {
public static void main(String[] args) {
VendingMachine vendingMachine = new VendingMachine();
// 投币
vendingMachine.insertCoin();
// 选择商品
vendingMachine.selectProduct();
// 出货
vendingMachine.dispenseProduct();
}
}
|
9. 策略模式 (Strategy Pattern)
生活场景:出行方式选择
在日常生活中,我们出行时会根据不同的情况选择不同的出行方式,比如距离远近、时间紧迫程度、个人喜好等。常见的出行方式有步行、骑自行车、乘坐公交车、打车等。每种出行方式都可以看作是一种算法,我们可以根据实际需求在这些算法之间进行切换。距离近且时间充裕:可以选择步行,既能锻炼身体,又能欣赏沿途风景。距离适中且想要快速到达:可以选择骑自行车,灵活便捷。距离较远且不想自己开车:可以选择乘坐公交车,经济实惠。时间非常紧迫:可以选择打车,快速高效。
- 定义策略接口:规定算法需要具备的方法,即出行方式的基本行为,比如计算出行时间、计算出行费用等。
- 实现具体策略类:为每种出行方式创建一个具体的策略类,实现策略接口中的方法,定义该出行方式的具体行为。
- 定义上下文类:维护一个策略对象的引用,并提供方法来设置和使用不同的策略。
- 策略切换:在上下文类中,根据用户的需求切换不同的策略对象,从而实现不同的出行方式。
Java 示例代码:
策略模式-加减乘除
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
|
// 策略接口
interface Strategy {
int doOperation(int num1, int num2);
}
// 具体策略:加法
class OperationAdd implements Strategy {
@Override
public int doOperation(int num1, int num2) {
return num1 + num2;
}
}
// 具体策略:减法
class OperationSubtract implements Strategy {
@Override
public int doOperation(int num1, int num2) {
return num1 - num2;
}
}
// 具体策略:乘法
class OperationMultiply implements Strategy {
@Override
public int doOperation(int num1, int num2) {
return num1 * num2;
}
}
// 上下文
class ContextStrategy {
private Strategy strategy;
public ContextStrategy(Strategy strategy) {
this.strategy = strategy;
}
public int executeStrategy(int num1, int num2) {
return strategy.doOperation(num1, num2);
}
}
// 测试
public class StrategyDemo {
public static void main(String[] args) {
ContextStrategy context = new ContextStrategy(new OperationAdd());
System.out.println("10 + 5 = " + context.executeStrategy(10, 5));
context = new ContextStrategy(new OperationSubtract());
System.out.println("10 - 5 = " + context.executeStrategy(10, 5));
context = new ContextStrategy(new OperationMultiply());
System.out.println("10 * 5 = " + context.executeStrategy(10, 5));
}
}
|
策略模式-出行方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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
|
// 策略接口
interface TravelStrategy {
void travel();
}
// 步行策略类
class WalkStrategy implements TravelStrategy {
@Override
public void travel() {
System.out.println("选择步行出行,欣赏沿途风景");
}
}
// 骑自行车策略类
class BikeStrategy implements TravelStrategy {
@Override
public void travel() {
System.out.println("选择骑自行车出行,灵活便捷");
}
}
// 乘坐公交车策略类
class BusStrategy implements TravelStrategy {
@Override
public void travel() {
System.out.println("选择乘坐公交车出行,经济实惠");
}
}
// 打车策略类
class TaxiStrategy implements TravelStrategy {
@Override
public void travel() {
System.out.println("选择打车出行,快速高效");
}
}
// 上下文类
class TravelContext {
private TravelStrategy strategy;
public TravelContext(TravelStrategy strategy) {
this.strategy = strategy;
}
public void setStrategy(TravelStrategy strategy) {
this.strategy = strategy;
}
public void executeTravel() {
strategy.travel();
}
}
// 客户端类
public class TravelStrategyPattern {
public static void main(String[] args) {
// 选择步行出行
TravelContext context = new TravelContext(new WalkStrategy());
context.executeTravel();
// 切换为骑自行车出行
context.setStrategy(new BikeStrategy());
context.executeTravel();
// 切换为乘坐公交车出行
context.setStrategy(new BusStrategy());
context.executeTravel();
// 切换为打车出行
context.setStrategy(new TaxiStrategy());
context.executeTravel();
}
}
|
10. 模板方法模式 (Template Method Pattern)
生活场景:制作饮品(咖啡和茶)
在日常生活中,制作咖啡和茶的过程有一些相似的步骤,比如准备材料、烧水、冲泡、添加调料等。虽然咖啡和茶的具体制作细节有所不同(例如咖啡使用咖啡豆研磨后冲泡,茶使用茶叶浸泡),但它们的基本流程是相似的。我们可以把制作饮品的通用流程看作是一个算法骨架,而将其中一些具体的步骤(如准备材料、冲泡方式)延迟到制作咖啡或茶的子类中去实现。这样,通过模板方法模式,我们可以在不改变整体制作流程的前提下,灵活地定制不同饮品的制作方式。
- 定义抽象类(模板类):创建一个抽象类,其中包含制作饮品的通用步骤,如烧水、准备材料、冲泡、添加调料等方法。将一些具体的步骤定义为抽象方法,由子类去实现。同时,定义一个模板方法,按照固定的顺序调用这些方法,形成制作饮品的算法骨架。
- 实现具体子类:分别创建制作咖啡和制作茶的子类,继承自抽象类。在子类中实现抽象类中定义的抽象方法,即具体的准备材料和冲泡方式。
- 使用模板方法:在客户端代码中,创建具体子类的对象,调用模板方法来制作相应的饮品。
Java 示例代码:
模板方法-游戏
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
|
// 抽象类定义模板方法
abstract class Game {
// 模板方法:定义算法骨架
public final void play() {
initialize();
startPlay();
endPlay();
}
abstract void initialize();
abstract void startPlay();
abstract void endPlay();
}
// 具体类实现游戏的各个步骤
class FootballGame extends Game {
@Override
void initialize() {
System.out.println("Football Game Initialized! Start playing.");
}
@Override
void startPlay() {
System.out.println("Football Game Started. Enjoy the game!");
}
@Override
void endPlay() {
System.out.println("Football Game Finished!");
}
}
// 测试
public class TemplateMethodDemo {
public static void main(String[] args) {
Game game = new FootballGame();
game.play();
}
}
|
模板方法-泡茶
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
|
// 抽象类:制作饮品
abstract class BeverageMaker {
// 模板方法,定义制作饮品的算法骨架
final void makeBeverage() {
boilWater();
prepareIngredients();
brew();
addCondiments();
System.out.println("一杯美味的饮品制作完成!");
}
// 烧水
void boilWater() {
System.out.println("正在烧水...");
}
// 准备材料,具体实现由子类完成
abstract void prepareIngredients();
// 冲泡,具体实现由子类完成
abstract void brew();
// 添加调料,具体实现由子类完成
abstract void addCondiments();
}
// 具体子类:制作咖啡
class CoffeeMaker extends BeverageMaker {
@Override
void prepareIngredients() {
System.out.println("准备咖啡豆并研磨...");
}
@Override
void brew() {
System.out.println("用研磨好的咖啡豆冲泡咖啡...");
}
@Override
void addCondiments() {
System.out.println("添加牛奶和糖...");
}
}
// 具体子类:制作茶
class TeaMaker extends BeverageMaker {
@Override
void prepareIngredients() {
System.out.println("准备茶叶...");
}
@Override
void brew() {
System.out.println("用热水浸泡茶叶...");
}
@Override
void addCondiments() {
System.out.println("添加柠檬片...");
}
}
// 客户端类
public class TemplateMethodPatternExample {
public static void main(String[] args) {
// 制作咖啡
BeverageMaker coffeeMaker = new CoffeeMaker();
coffeeMaker.makeBeverage();
System.out.println("------------------");
// 制作茶
BeverageMaker teaMaker = new TeaMaker();
teaMaker.makeBeverage();
}
}
|
11. 访问者模式 (Visitor Pattern)
生活场景:动物园动物检查
在动物园里,有各种各样的动物,像狮子、猴子、大象等。动物园会定期安排兽医对动物进行不同类型的检查,例如健康检查、口腔检查、防疫检查等。每种动物在不同检查项目下可能会有不同的表现和处理方式。比如,健康检查时,狮子可能需要检查肌肉力量,猴子可能要检查灵活性;口腔检查时,大象的牙齿检查方式和猴子又有很大区别。如果不使用设计模式,每次新增一种检查项目,就需要在每个动物类中添加对应的检查方法,这会导致动物类的代码变得复杂且难以维护。而访问者模式可以很好地解决这个问题,它能在不改变动物类结构的前提下,为不同动物添加新的检查操作。
- 定义元素接口和具体元素类:创建一个动物接口,定义动物接受检查的方法。然后为不同种类的动物创建具体的类,实现动物接口。
- 定义访问者接口和具体访问者类:创建一个兽医(访问者)接口,定义对不同动物进行检查的方法。接着为不同类型的检查(如健康检查、口腔检查)创建具体的兽医类,实现兽医接口。
- 对象结构类:创建一个动物园类,用于管理动物列表,并提供一个方法来接受兽医对动物进行检查操作。
- 使用访问者:在客户端代码中,创建动物对象和兽医对象,将兽医对象传递给动物园类,对动物进行相应的检查操作。
Java 示例代码:
访问者模式-代码示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
|
// 访问者接口
interface Visitor {
void visit(ElementA element);
void visit(ElementB element);
}
// 元素接口
interface Element {
void accept(Visitor visitor);
}
// 具体元素A
class ElementA implements Element {
public void operationA() {
System.out.println("ElementA operation");
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
// 具体元素B
class ElementB implements Element {
public void operationB() {
System.out.println("ElementB operation");
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
// 具体访问者
class ConcreteVisitor implements Visitor {
@Override
public void visit(ElementA element) {
System.out.println("Visitor processing ElementA");
element.operationA();
}
@Override
public void visit(ElementB element) {
System.out.println("Visitor processing ElementB");
element.operationB();
}
}
// 对象结构:持有多个元素
class ObjectStructure {
private List<Element> elements = new ArrayList<>();
public void addElement(Element element) {
elements.add(element);
}
public void accept(Visitor visitor) {
for (Element element : elements) {
element.accept(visitor);
}
}
}
// 测试
public class VisitorDemo {
public static void main(String[] args) {
ObjectStructure structure = new ObjectStructure();
structure.addElement(new ElementA());
structure.addElement(new ElementB());
Visitor visitor = new ConcreteVisitor();
structure.accept(visitor);
}
}
|
访问者模式-动物园
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
|
import java.util.ArrayList;
import java.util.List;
// 动物接口(元素接口)
interface Animal {
void accept(Veterinarian visitor);
}
// 狮子类(具体元素类)
class Lion implements Animal {
@Override
public void accept(Veterinarian visitor) {
visitor.visit(this);
}
}
// 猴子类(具体元素类)
class Monkey implements Animal {
@Override
public void accept(Veterinarian visitor) {
visitor.visit(this);
}
}
// 大象类(具体元素类)
class Elephant implements Animal {
@Override
public void accept(Veterinarian visitor) {
visitor.visit(this);
}
}
// 兽医接口(访问者接口)
interface Veterinarian {
void visit(Lion lion);
void visit(Monkey monkey);
void visit(Elephant elephant);
}
// 健康检查兽医类(具体访问者类)
class HealthCheckVeterinarian implements Veterinarian {
@Override
public void visit(Lion lion) {
System.out.println("对狮子进行健康检查,检查肌肉力量。");
}
@Override
public void visit(Monkey monkey) {
System.out.println("对猴子进行健康检查,检查灵活性。");
}
@Override
public void visit(Elephant elephant) {
System.out.println("对大象进行健康检查,检查心脏功能。");
}
}
// 口腔检查兽医类(具体访问者类)
class OralCheckVeterinarian implements Veterinarian {
@Override
public void visit(Lion lion) {
System.out.println("对狮子进行口腔检查,查看牙齿磨损情况。");
}
@Override
public void visit(Monkey monkey) {
System.out.println("对猴子进行口腔检查,查看牙龈健康。");
}
@Override
public void visit(Elephant elephant) {
System.out.println("对大象进行口腔检查,检查象牙状况。");
}
}
// 动物园类(对象结构类)
class Zoo {
private List<Animal> animals = new ArrayList<>();
public void addAnimal(Animal animal) {
animals.add(animal);
}
public void accept(Veterinarian visitor) {
for (Animal animal : animals) {
animal.accept(visitor);
}
}
}
// 客户端类
public class ZooVisitorPattern {
public static void main(String[] args) {
Zoo zoo = new Zoo();
zoo.addAnimal(new Lion());
zoo.addAnimal(new Monkey());
zoo.addAnimal(new Elephant());
// 进行健康检查
HealthCheckVeterinarian healthCheckVeterinarian = new HealthCheckVeterinarian();
zoo.accept(healthCheckVeterinarian);
System.out.println("----------------------");
// 进行口腔检查
OralCheckVeterinarian oralCheckVeterinarian = new OralCheckVeterinarian();
zoo.accept(oralCheckVeterinarian);
}
}
|
总结
以上就是常见的 23 种设计模式,对加深封装、继承、多态、接口的理解帮助很多。这些模式都是建立在熟练掌握基础的前提下。