设计模式的个人感悟总结

[Musings]
花了差不多断断续续一周的时间(10 天其实是,但是中间断了几天,干别的事去了哈哈哈),把所有的设计模式全部过了一遍
其实说白了设计模式在干嘛,就是在把屎山代码里写的很多 if else 来优化,以往可能觉得,用个 switch 来替换 if else 就行了,其实远远不够
设计模式说白了就是站在设计的角度来考虑,更多的去利用 java 的语法特性,抽象类,接口,多态,来使得代码的结构更清晰,减少超级类,超多 if else 的逻辑

// 从前有个”万能上帝类” ❌
public class GodClass {
public void doEverything(String type) {
if (type.equals(“A”)) { /_ 100 行代码 / }
else if (type.equals(“B”)) { /
200 行代码 / }
else if (type.equals(“C”)) { /
150 行代码 _/ }
// … 越来越多的 if-else
}
}

// 经过设计模式改造后 ✅
class AHandler { public void handle() { /_ 专注 A 的事 / } }
class BHandler { public void handle() { /
专注 B 的事 / } }
class CHandler { public void handle() { /
专注 C 的事 _/ } }

所谓的五大原则 SOLID 的大白话版本就是

原则 我的理解 通俗比喻
单一职责 S 把超级类打散 一个厨师不要又切菜又炒菜又洗碗
开闭原则 O 打散后的自然结果 加新菜不用重装修厨房
里氏替换 L 保证运行时正确性 儿子要像爹,不能乱改家规
接口隔离 I 不要定义超大接口 别造瑞士军刀,用专门的工具
依赖倒置 D 面向接口编程 订餐看菜单,不用管厨师是谁

等掌握了设计模式之后,还要记住一个原则: 不要过度设计,不要炫技!
KISS原则就出来了,keep it simple, stupid!

之前 Tony 老板问过我一个问题我记得很清楚,他说:“你觉得什么样的代码是好代码,更好的算法,更优雅的写法,还是更少的 bug“
我想了想,说”不确定,我觉得是代码的可读性吧,毕竟后续还有其他人要维护“
他说:”没错,如果我们写的代码连自己三个月后都看不懂,那凭什么要求别人能维护?“
物是人非了,都在匆匆忙忙中被记忆斑驳了,想起那些老板,忍不住有些泪目. Digital team真的让人很难不爱,只不过失去了,才显得珍贵吧.
欲买桂花同载酒,终不似,少年游。

迭代器模式

[Musihngs]
GoGoGo

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
@Data
@AllArgsConstructor
public class Book {
private String bookName;
private String author;
}

public interface Iterator<T> {
boolean hasNext();
T next();
}

public class BookIterator implements Iterator<Book> {
private List<Book> store;

public BookIterator(List store) {
this.store = store;
}

@Override
public boolean hasNext() {
return store.size() != 0;
}

@Override
public Book next() {
Book o = store.get(0);
store.remove(0);
return o;
}
}

public interface Shelf {
void addBook(Book book);
}

public class BookShelf implements Shelf {
private List<Book> store;

public BookShelf() {
store = new ArrayList<Book>();
}

@Override
public void addBook(Book book) {
this.store.add(book);
}

public Iterator createIterator() {
return new BookIterator(store);
}
}

✅ 核心方法:hasNext() + next() 是迭代器的标准接口
✅ 泛型通用:通过泛型实现类型安全的通用迭代器
✅ 职责分离:集合管存储,迭代器管遍历,各司其职
✅ 数据传递:只需传递内部数组,不暴露其他细节

备忘录模式

题目:文本编辑器撤销功能

请使用备忘录模式实现一个文本编辑器的撤销功能。

需求描述:

文本编辑器可以输入内容
文本编辑器可以保存当前状态(创建备忘录)
文本编辑器可以从备忘录恢复之前的状态
支持多次撤销操作
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
@Data
public class Memo {
private String curentData;

public Memo() {
}

public Memo(Memo memo) {
this.curentData = memo.getCurentData();
}

public void input(String data) {
System.out.println("Data input is: " + data);
curentData = data;
}
}

public class History {
List<Memo> list;
int index;

public History() {
this.index = -1;
this.list = new ArrayList<>();
}

public void save(Memo memo) {
if (index < list.size() - 1) {
list.subList(index + 1, list.size()).clear();
}
list.add(memo);
index = list.size() - 1;
}

public Memo revert() {
if (index <= 0) {
System.out.println("已经是最旧版!无法撤销!");
return list.get(0);
}
index--;
return list.get(index);
}

public Memo redo() {
if (index >= list.size() - 1) {
System.out.println("已经是最新版!无法重做!");
return list.get(list.size() - 1);
}
index++;
return list.get(index);
}
}

public class Main {
public static void main(String[] args) {
Memo memo = new Memo();
memo.input("hi, this is memo!");
History history = new History();
history.save(new Memo(memo));
memo = history.revert();
}
}

模版方法模式

题目:饮料制作系统

请使用模板方法模式实现一个饮料制作系统。

需求描述:
不同的饮料(咖啡、茶)有相似的制作步骤,但某些步骤的具体实现不同。

饮料制作通用步骤:

烧水
冲泡
倒入杯子
添加调料
具体要求:

咖啡:烧水 → 冲泡咖啡粉 → 倒入杯子 → 加糖和牛奶
茶:烧水 → 冲泡茶叶 → 倒入杯子 → 加柠檬
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
@Data
public abstract class Baverage {
private String source;
private String extra;

public void boilWater() {
System.out.println("烧水");
}

abstract void pourSource();

void pourIntoCup() {
System.out.println("倒入杯子");
}

abstract void addExtra();

//实现钩子来控制抽象类的行为,用protected只暴露给子类可以修改该行为
//钩子方法 - 子类可通过重写此方法控制是否添加调料
protected boolean needExtra() {
return true;
}

//模版方法的核心
public void template() {
boilWater();
pourSource();
pourIntoCup();
if (needExtra()) {
addExtra();

}
}
}

public class BlackCoffee extends Baverage {
public BlackCoffee() {
this.setSource("咖啡粉");
}

@Override
void pourSource() {
System.out.println(this.getSource());
}

@Override
void addExtra() {
System.out.println(this.getExtra());
}

//重写钩子方法,来控制父类的行为
@Override
protected boolean needExtra() {
return false;
}
}

public class Main {
public static void main(String[] args) {
Baverage coffee = new BlackCoffee();
coffee.template();
}
}

模版类的核心就是抽象类+模版方法,这里稍微加了个钩子个概念,就是父类通过申明protected方法来让子类控制父类的行为
protected修饰的方法,子类和子子孙孙类都能重写

修饰符 当前类 同包 子类 孙子类 其他包
private
protected
public

参观者模式:

题目:文档导出系统

请使用访问者模式实现一个文档导出系统。

需求描述:
你有一个文档结构,包含不同类型的元素(文本、图片、表格),需要支持多种导出格式(HTML、PDF、Markdown)。

文档元素:

TextElement - 文本元素(包含文字内容)
ImageElement - 图片元素(包含图片路径和标题)
TableElement - 表格元素(包含行列数据)
导出格式:

HTML导出:<p>文本内容</p>、<img src="路径" alt="标题">、<table>...</table>
PDF导出:模拟PDF格式输出(简单文本表示)
Markdown导出:普通文本、![标题](路径)、Markdown表格
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
public interface DocumentElement {
public void accept(Visitor visitor);
}

@Data
@AllArgsConstructor
public class ImageElement implements DocumentElement {
private String imgUrl;
private String imgSize;

@Override
public void accept(Visitor visitor) {
//核心在这里,把业务逻辑丢给visitor处理
//而不是这里通过instance的类型判断visitor,然后给不同的输出,这样会很烦
//直接把this传给visitor,让visitor用重载是代替这里的if else,然后利用重载来实现不同类型的元素的输出
visitor.visit(this);
}
}

@AllArgsConstructor
@Data
public class TableElement implements DocumentElement {
private int lineNumber;

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

@Data
@AllArgsConstructor
public class TextElement implements DocumentElement {
private String text;

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

public interface Visitor {
//核心在这里,重载不同类型的element的visit方式
void visit(TextElement textElement);/* 所有HTML文本逻辑 */
void visit(ImageElement imageElement);/* 所有HTML图片逻辑 */
void visit(TableElement tableElement);/* 所有HTML表格逻辑 */
}

public class HTMLVisitor implements Visitor{
@Override
public void visit(TextElement textElement) {
System.out.println("<p>" + textElement.getText() + "</p>");
}

@Override
public void visit(ImageElement imageElement) {
System.out.println("<img>" + imageElement.getImgSize() + "</img>");
}

@Override
public void visit(TableElement tableElement) {
System.out.println("<tb>" + tableElement.getLineNumber() + "</tb>");
}
}

public class PDFVisitor implements Visitor{
@Override
public void visit(ImageElement imageElement) {
}

@Override
public void visit(TableElement tableElement) {
}

@Override
public void visit(TextElement textElement) {
}
}

// 假设不用visitor模式,那么就得在documentElement的accept里去通过类型判断是什么visitor,然后在每个element对象的accept方法里,写很多的if else逻辑,这样代码的维护就会很麻烦,所以通过合理的使用重载,来代替ifelse逻辑,然后通过每个visitor的内部visit方法去把本身集成在accept里的方法都打散到各自的逻辑里

❌ 不用访问者模式(if-else地狱)
public class TextElement implements DocumentElement {
@Override
public void accept(Visitor visitor) {
// 需要判断visitor类型,每加一个新visitor就要修改这里!
if (visitor instanceof HTMLVisitor) {
System.out.println("<p>" + this.text + "</p>");
} else if (visitor instanceof PDFVisitor) {
System.out.println("PDF Text: " + this.text);
} else if (visitor instanceof MarkdownVisitor) {
System.out.println(this.text);
} else if (visitor instanceof XMLVisitor) {
System.out.println("<text>" + this.text + "</text>");
}
// 每新增一个导出格式,就要在这里加一个if分支!
}
}

设计模式告一段落,后面复盘,重新走一遍思路,拉闸!

命令模式

[Musings]
今天是把剩余的命令模式+解释器+迭代器实现下

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
public interface Command {
void execute();
void undo();
}

@Getter
public class Light {
private boolean status;

public void lightOn() {
System.out.println("Light on!");
this.status = true;
}

public void lightOff(){
System.out.println("Light off!");
this.status = false;
}

}

@AllArgsConstructor
public class LightsOnCommand implements Command{
private Light light;

@Override
public void execute() {
System.out.println("LightsOnCommand Light On");
light.lightOn();
}

@Override
public void undo() {
System.out.println("LightsOnCommand Undo Light On");
light.lightOff();
}
}

public class RemoteControl {

private Map<Integer, List<Command>> listMap;
private List<Command> undoList = new ArrayList();

public RemoteControl( ) {
this.listMap = new HashMap<>();
}

public void setCommand(int port, List<Command> list) {
listMap.put(port, list);
}

public void onButtonPressed(int port) {
this.listMap.get(port).forEach(
command -> {
command.execute();
this.undoList.add(command);
}
);
}

public void undoButtonPressed() {
if (undoList.size() == 0) return;
Command command = undoList.get(undoList.size() - 1);
command.undo();
undoList.remove(undoList.size()-1);
}
}

这里主要的思路就是command接口的声明,然后创建控制面板类,基于command接口去实现代码,这样控制面板类就可以面向接口编写,后续可以随意扩展command

然后我们写个解释器模式
📚 解释器模式题目:简单查询语言解释器

🎯 问题描述

设计一个简单的数据查询语言解释器,可以解析和执行类似SQL的查询条件。用户输入查询表达式,系统能够解析并过滤出符合条件的数据。

📋 需求说明

支持的基础表达式:

等于:field = value
大于:field > value
小于:field < value
包含:field contains value
支持的逻辑运算符:

与:and
或:or
非:not
查询示例:

text
name = 'Alice' and age > 20
department = 'IT' or (age > 25 and salary > 50000)
not name contains 'test'
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
@Data
@AllArgsConstructor
public class User {
private String name;
private int age;
private String department;
private double salary;
}


public interface Expression {
boolean interpret(User users);
}

public class AndExpression implements Expression {
private Expression left;
private Expression right;
public AndExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}

@Override
public boolean interpret(User user) {
boolean interpret = left.interpret(user);
boolean interpret1 = right.interpret(user);
return interpret && interpret1;
}
}
public class EqualsExpression implements Expression {
private String field;
private String value;

public EqualsExpression(String field, String value) {
this.field = field;
this.value = value;
}

@Override
public boolean interpret(User users) {
if("name".equals(field)){
return users.getName().equals(value);
}
return false;
}
}
public class GreaterThanExpression implements Expression{
private String field;
private int value;

public GreaterThanExpression(String field, int value) {
this.field = field;
this.value = value;
}

@Override
public boolean interpret(User users) {
if("age".equals(field)){
return users.getAge()>value;
}
return false;
}
}
public class Main {
public static void main(String[] args) {
User user = new User("Alice", 30, "IT", 6000);
GreaterThanExpression age = new GreaterThanExpression("age", 25);
EqualsExpression name = new EqualsExpression("name", "Alice");
AndExpression andExpression = new AndExpression(age, name);
boolean interpret = andExpression.interpret(user);
System.out.println("年龄大于25吗? " + interpret); // 应该输出 true
}
}

思路就是申明interpreter接口,然后去实现不同的interpreter.
这里有一个悟道,就是在写设计模式的时候,不要看了题目就凭空想实现,想怎么抽象接口和类,而是应该去考虑,最后的main方法怎么写,先考虑结果,然后基于结果去反向推导,为了这样调用,我该如何去实现,如何拆分,设计行为和类.这样做更具像化,也更高效

责任链模式

[Musings]

今天把剩下的几个模式都搞完,开始巩固下多线程

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

public interface CheckerChain {
boolean check();
}

public class Order {
private int price;

public Order(int price) {
this.price = price;
}

public void submit(){
FundalmentalChecker fundalmentalChecker = new FundalmentalChecker();
fundalmentalChecker.check();
}
}

public class FundalmentalChecker implements CheckerChain{
@Override
public boolean check() {
System.out.println("FundalmentalChecker is checking...");
/*
*
* 加入if else逻辑
*
* */
return new RiskChecker().check();
}
}

public class RiskChecker implements CheckerChain{
@Override
public boolean check() {
System.out.println("RiskChecker is checking...");
/*
*
* 加入if else逻辑
*
* */
return new ManualChecker().check();
}
}

public class ManualChecker implements CheckerChain{
@Override
public boolean check() {
System.out.println("ManualChecker is checking...");
/*
*
* 加入if else逻辑
*
* */
return false;
}
}

这是我一开始想的版本,就是通过一层层调用来做责任链的检查,在每次检查可以直接返回,也可以根据业务条件向下传递
但这里有个核心问题,就是我的责任链是固定死在代码里的,所以为了动态调整,就得再抽象一下

//先修改接口
public interface CheckerChain {
    boolean check();
    void setNext(Checker checker);
}
//定义抽象类,这样的话就可以在外部设置不同chcker的next checker了
public abstract class Checker implements CheckerChain {
    Checker nextChecker;

    @Override
    public void setNext(Checker checker) {
        this.nextChecker = checker;
    }
}

//实现类稍作修改
public class FundalmentalChecker extends  Checker{
    @Override
    public boolean check() {
        System.out.println("FundalmentalChecker is checking...");
        /*
        *
        * 加入if else逻辑
        *
        * */
        return new RiskChecker().check();
    }
}
//看如何调用
public class Order {
    public void submit() {
        // 构建责任链
        CheckerChain chain = buildChain();
        // 从链头开始执行
        chain.check(this);
    }
    
    //这里就可以在外部手动修改checker的逻辑优先级而不需要动checker类本身,更灵活,解耦
    //在此基础可以后续追加其他模式来优化这块逻辑,单独提取出来,比如工厂模式
    private CheckerChain buildChain() {
        CheckerChain fundamental = new FundalmentalChecker();
        CheckerChain risk = new RiskChecker();
        CheckerChain manual = new ManualChecker();
        
        fundamental.setNext(risk);
        risk.setNext(manual);
        
        return fundamental;  // 返回链头
    }
}

```java 

其实增加新的抽象类,目的就是可以存储nextChecker类,这样外部就可以去改动他,从而不至于把nextChecker hard code在Checker类的逻辑里.

状态模式

[Musings]
今天继续昨天的内容进行状态模式的书写

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

public enum StateStatus {
NORMAL,ALARM,URGENT,SUFFICIENT
}

public class State {
private StateStatus stateStatus;

public State(StateStatus stateStatus) {
this.stateStatus = stateStatus;
}

public void handleUpdate(Publisher publisher) {
/*logic update for status*/
statusUpdate();
}

public void handleReduce(Publisher publisher) {
/*logic update for status*/
statusUpdate();
}

private void statusUpdate(){
/*logic update for status*/
}
}

public abstract class Publisher {
private State state;
/* ... */

public Publisher(State state /* ... */) {
this.state = state;
}

/*
...
*/
}

public class Storage extends Publisher {

public Storage(int limit, HashMap storageMap, String name, Mediator mediator) {
super(new State(StateStatus.NORMAL),limit, storageMap, name, mediator);
/*如果添加初始化Status的逻辑就在这里加然后调用super*/
}

@Override
public void updateItem(String itemName, int count) {
this.getStorageMap().computeIfPresent(itemName, (k, v) -> v += count);
System.out.println(this.getStorageMap().toString());
}

@Override
public void notify(String itemName, int count, State state) {
// mediator.notify(this, itemName, count);
mediator.notify(this, this.getState());
//这里就是核心,传给mediator后,不需要考虑很复杂的业务逻辑,只需要根据state就行
//说白了就是把之前mediator里的业务逻辑抽出来,放到state里面单独管理,这样就各司其职,mediator类不会过大
}

public void add(String itemName, int count) {
this.getStorageMap().put(itemName, count);
this.getState().handleUpdate(this);
}

public void reduce(String itemName) {
int currentVal = this.getStorageMap().get(itemName);
this.getStorageMap().put(itemName, currentVal - 1);
this.notify(itemName, currentVal - 1);
this.getState().handleReduce(this);
}
}

今天就到这,明天继续

策略模式

[Musings]
继续昨天的中介模式来加上策略模式

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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
public enum Strategy {
BEST_SELLING, NORMAL, SEASONAL, LUXURY
}

public abstract class SupplyStrategy {
Publisher publisher;

public SupplyStrategy(Publisher publisher) {
this.publisher = publisher;
}

abstract void supply(String itemName);
}

enum ConditionType {
PERCENT, FIXED
}

public class NormalStrategy extends SupplyStrategy {
public NormalStrategy( Publisher publisher) {
super( publisher);
}

@Override
public void supply(String itemName) {
System.out.println("NormalStrategy supply() invoked");
if ((publisher.getStorageMap().get(itemName) / publisher.getLimit() )< 0.1) {
publisher.updateItem(itemName, (int) (publisher.getLimit() * 0.5));
}
}
}

public class LuxuryStrategy extends SupplyStrategy {
public LuxuryStrategy(Publisher publisher) {
super(publisher);
}

@Override
public void supply(String itemName) {
System.out.println("LuxuryStrategy supply() invoked");
if (publisher.getStorageMap().get(itemName) < 5) {
publisher.updateItem(itemName, 10);
}
}
}

public class BestSellingProductStrategy extends SupplyStrategy {
private double trigger;

public BestSellingProductStrategy(double trigger, Publisher publisher) {
super(publisher);
this.trigger = trigger;
}

@Override
public void supply(String itemName) {
System.out.println("BestSellingProductStrategy supply() invoked");
if ((publisher.getStorageMap().get(itemName) / publisher.getLimit()) < this.trigger) {
publisher.updateItem(itemName, (int) (publisher.getLimit() * 0.8));
}
}
}

//稍微修改下中介者类
public class Mediator {
private HashMap<Publisher, ArrayList<Observer>> hashMap;

public Mediator() {
this.hashMap = new HashMap<>();
}

public void notify(Publisher publish, String itemName, int count) {
ArrayList<Observer> observers = hashMap.get(publish);
boolean supplyFlag = false;
// 添加逻辑基于count,itemName,也可以结合多个不同的item
if (observers != null && !observers.isEmpty()) {
for (Observer observer : observers) {
switch (observer.getRole()) {
case "Procure":
if (count > 5) {
observer.push("Procure货物充足!✅");
} else {
observer.push("Procure 货物不足❌,准备补充");
supplyFlag = true;
}
break;
case "Shop":
if (count > 5) {
observer.push("Shop 货物充足!✅");
} else {
observer.push("Shop 货物不足❌,准备下架");
supplyFlag = true;
}
break;
case "Delivery":
if (count > 5) {
observer.push("Delivery 货物充足,车辆平均分配!✅");
} else {
observer.push("Delivery 货物不足❌,准备帮忙采购");
supplyFlag = true;
}
break;
}
}
}

if (supplyFlag) supply(publish, itemName);
}

public void supply(Publisher publisher, String itemName) {
//Supply Strategy invoke
switch (publisher.getStrategy()) {
case LUXURY -> new LuxuryStrategy(publisher).supply(itemName);
case BEST_SELLING -> new BestSellingProductStrategy(0.2, publisher).supply(itemName);
default -> new NormalStrategy(publisher).supply(itemName);
}
}

public void subscribe(Publisher publisher, Observer observer) {
hashMap.computeIfAbsent(publisher, k -> new ArrayList<>()).add(observer);
}

public void unsubscribe(Publisher publisher, Observer observer) {
hashMap.computeIfPresent(publisher, (k, v) -> {
v.remove(observer);
return v.isEmpty() ? null : v;
});
}

}


public class Main {
public static void main(String[] args) {
Mediator mediator = new Mediator();
Storage storage = new Storage(100, new HashMap<>(), "联华超市仓库", mediator);
Procurement procurement = new Procurement("Procure");
ShunFenDelivery delivery = new ShunFenDelivery("Delivery");
Shop shop = new Shop("Shop");

mediator.subscribe(storage, procurement);
mediator.subscribe(storage, delivery);
mediator.subscribe(storage, shop);

storage.add("Crips", 7);
storage.reduce("Crips");
storage.reduce("Crips");
storage.reduce("Crips");
storage.reduce("Crips");
}
}

**Output log:**
Procure货物充足!✅
Delivery 货物充足,车辆平均分配!✅
Shop 货物充足!✅
Procure 货物不足❌,准备补充
Delivery 货物不足❌,准备帮忙采购
Shop 货物不足❌,准备下架
NormalStrategy supply() invoked
{Crips=55}
Procure货物充足!✅
Delivery 货物充足,车辆平均分配!✅
Shop 货物充足!✅
Procure货物充足!✅
Delivery 货物充足,车辆平均分配!✅
Shop 货物充足!✅

功能没问题了,然后我们来优化下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

public void supply(Publisher publisher, String itemName) {
//Supply Strategy invoke
switch (publisher.getStrategy()) {
case LUXURY -> new LuxuryStrategy(publisher).supply(itemName);
case BEST_SELLING -> new BestSellingProductStrategy(0.2, publisher).supply(itemName);
default -> new NormalStrategy(publisher).supply(itemName);
}
}
// 这里就可以用工厂模式:
public class SupplyStrategyFactory {
public static SupplyStrategy createStrategy( Publisher publisher) {
return switch (publisher.getStrategy()){
case LUXURY -> new LuxuryStrategy(publisher);
case BEST_SELLING -> new BestSellingProductStrategy(0.2,publisher);
default -> new NormalStrategy(publisher);
};
}

//然后调用的地方就可以写
public void supply(Publisher publisher, String itemName) {
SupplyStrategyFactory.createStrategy(publisher).supply(itemName);
}

}

外观 + 桥接模式 + 观察者模式 + 中介者模式

[Musings]
学习是一件持久的事,但是也是一件会遗忘的事,所以得不停的学习

外观模式

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
//BluRayPlayer.class
class BluRayPlayer {
public void on() {
System.out.println("蓝光播放器打开");
}

public void off() {
System.out.println("蓝光播放器关闭");
}

public void play(String movie) {
System.out.println("播放电影: " + movie);
}
}

//Lighting.class
class Lighting {
public void dim() {
System.out.println("灯光调暗");
}

public void bright() {
System.out.println("灯光调亮");
}

public void off() {
System.out.println("灯光关闭");
}
}

//Projector.class
class Projector {
public void on() {
System.out.println("投影仪打开");
}

public void off() {
System.out.println("投影仪关闭");
}

public void setInput(String input) {
System.out.println("投影仪输入源设置为: " + input);
}
}

//Screen.class
class Screen {
public void down() {
System.out.println("屏幕降下");
}

public void up() {
System.out.println("屏幕升起");
}
}

//SoundSystem.class
class SoundSystem {
public void on() {
System.out.println("音响系统打开");
}

public void off() {
System.out.println("音响系统关闭");
}

public void setVolume(int volume) {
System.out.println("音响音量设置为: " + volume);
}
}

//HomeTheaterFacade.class
public class HomeTheaterFacade {
private BluRayPlayer bluRayPlayer;
private Lighting lighting;
private Projector projector;
private Screen screen;
private SoundSystem soundSystem;

public HomeTheaterFacade(BluRayPlayer bluRayPlayer, Lighting lighting, Projector projector, Screen screen, SoundSystem soundSystem) {

this.bluRayPlayer = bluRayPlayer;
this.lighting = lighting;
this.projector = projector;
this.screen = screen;
this.soundSystem = soundSystem;
}

public void watchMovie(String movie){
System.out.println("准备观看电影...");
lighting.dim(); // 1. 调暗灯光
screen.down(); // 2. 降下屏幕
projector.on(); // 3. 打开投影仪
projector.setInput("HDMI"); // 4. 设置输入源
soundSystem.on(); // 5. 打开音响
soundSystem.setVolume(20); // 6. 设置音量
bluRayPlayer.on(); // 7. 打开蓝光播放器
bluRayPlayer.play(movie); // 8. 播放电影
System.out.println("电影开始!");
}
}

//Main.class
public class Main {
public static void main(String[] args) {
Projector projector = new Projector();
BluRayPlayer bluRayPlayer = new BluRayPlayer();
Lighting lighting = new Lighting();
Screen screen = new Screen();
SoundSystem soundSystem = new SoundSystem();

HomeTheaterFacade homeTheaterFacade = new HomeTheaterFacade(bluRayPlayer, lighting, projector, screen, soundSystem);
homeTheaterFacade.watchMovie("Iron Man");
}
}


提供一个统一的简化接口,来隐藏子系统的复杂性

桥接模式

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
126
127
128
129
130
131
132
133

public interface PaymentMethod {
void pay();
void refund();
void query();
}

public class AliPay implements PaymentMethod {
@Override
public void pay() {
}
@Override
public void refund() {
}
@Override
public void query() {
}
}

public class CardPay implements PaymentMethod{
@Override
public void pay() {
}
@Override
public void refund() {
}
@Override
public void query() {
}
}

public class WeChatPay implements PaymentMethod{
@Override
public void pay() {
}
@Override
public void refund() {
}
@Override
public void query() {
}
}

public class AppPayment extends Payment {
String appname;
public AppPayment(int amount, PaymentMethod paymentMethod, String appname) {
super(amount, paymentMethod);
this.appname = appname;
}
@Override
void pay() {
paymentMethod.pay();
}
@Override
void refund() {
System.out.println("AppPayment");
}
@Override
void query() {
System.out.println("AppPayment");
}
}

public class WebPayment extends Payment {
String url;
public WebPayment(int amount, PaymentMethod paymentMethod, String url) {
super(amount, paymentMethod);
this.url = url;
}
@Override
void pay() {
System.out.println("Webpayment");
}
@Override
void refund() {
System.out.println("Webpayment");
}
@Override
void query() {
System.out.println("Webpayment");
}
}


public class Main {
public static void main(String[] args){
// 测试1: APP场景 + 支付宝
AppPayment appWithAli = new AppPayment(100, new AliPay(), "淘宝APP");
appWithAli.pay();
appWithAli.refund();
System.out.println();

// 测试2: 网页场景 + 微信支付
WebPayment webWithWechat = new WebPayment(200, new WeChatPay(), "https://shop.com");
webWithWechat.pay();
webWithWechat.query();
System.out.println();

// 测试3: APP场景 + 银行卡支付
AppPayment appWithCard = new AppPayment(150, new CardPay(), "银行APP");
appWithCard.pay();
appWithCard.refund();
System.out.println();

// 测试4: 网页场景 + 支付宝
WebPayment webWithAli = new WebPayment(300, new AliPay(), "https://mall.com");
webWithAli.pay();
webWithAli.query();
System.out.println();

// 测试5: 演示多态特性
System.out.println("=== 多态测试 ===");
Payment[] payments = {
new AppPayment(50, new WeChatPay(), "小程序"),
new WebPayment(75, new CardPay(), "https://pay.com"),
new AppPayment(120, new AliPay(), "外卖APP")
};

for (Payment payment : payments) {
payment.pay();
payment.query();
System.out.println("---");
}

// 测试6: 退款流程测试
System.out.println("=== 退款流程测试 ===");
WebPayment refundTest = new WebPayment(500, new AliPay(), "https://refund.com");
refundTest.pay();
refundTest.refund();
}
}


桥接模式和核心就是把多维度的因素抽象出来,减少排列组合,避免类爆炸
实际价值就是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 维度1:支付方式(M个)
interface PaymentMethod { ... }
class AliPay implements PaymentMethod { ... }
class WeChatPay implements PaymentMethod { ... }
class CardPay implements PaymentMethod { ... }

// 维度2:支付场景(N个)
abstract class PaymentScene { ... }
class AppPayment extends PaymentScene { ... }
class WebPayment extends PaymentScene { ... }
class PosPayment extends PaymentScene { ... }

// 通过组合获得 M×N 种可能
new AppPayment(new AliPay()) // APP+支付宝
new WebPayment(new WeChatPay()) // 网页+微信
new PosPayment(new CardPay()) // POS+银行卡
// ... 所有组合

到此总结:

创建型模式:
🏭 工厂方法:一个产品,多种实现(子类决定创建什么)
🏭 抽象工厂:一套产品,整体替换(产品族的概念)
🔨 建造者:复杂对象,分步构建(避免构造函数参数过多)
📋 原型:已有对象,复制使用(克隆代替new)
👑 单例:全局唯一,严格控制(一个类只有一个实例)

结构型模式:
🌳 组合:部分-整体,统一对待(树形结构,一致处理父子关系)
🎁 装饰器:相同接口,功能叠加(动态添加职责,像俄罗斯套娃)
🏷️ 享元:共享内在,传递外在(共享不变部分,分离变化部分,像活字印刷)
🌉 桥接:提取抽象维度,排列组合(多维度独立变化,避免类爆炸)
🕶️ 代理:控制访问,功能增强(像经纪人,控制对真实对象的访问)
🔌 适配器:接口转换,兼容协作(像电源转接头,让不兼容的接口协同工作)
🏢 外观:接口封装,统一调用(简化复杂子系统,提供统一入口)

到这就结束了所有创建型和结构型模式了,我们开始研究行为型模式,上点难度:

观察者模式

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
public interface Publisher {
void notify(int count, String info);
void subscribe(Observer observer);
void unsubscribe(Observer observer);
}

public interface Observer {
void push(int count, String info);
}

public class Item implements Publisher {
private ArrayList<Observer> observerArrayList;
private String name;
private int count = 100;

public Item(String name, int count) {
this.observerArrayList = new ArrayList<>();
this.count = count;
this.name = name;
}

public void reduceCount(){
this.count--;
System.out.println("reduceCount -- ");
if(count<10){
this.notify(this.count,"薯片快卖完了");
}
}

@Override
public void notify(int count , String name) {
observerArrayList.forEach(observer -> observer.push(count,name));
}

@Override
public void subscribe(Observer observer) {
observerArrayList.add(observer);
}

@Override
public void unsubscribe(Observer observer) {
observerArrayList.remove(observer);
}
}

public class Shop implements Observer {
private String name;

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

@Override
public void push(int count, String info) {
System.out.println(name + " received" + info + " info:" + count);
}
}

public class Main {
public static void main(String[] arg) {
Shop shop = new Shop("#1 Shop");
Shop shop1 = new Shop("#2 Shop");
Item crips = new Item("Crips", 12);
crips.subscribe(shop1);
crips.subscribe(shop);
crips.reduceCount();
crips.reduceCount();
crips.reduceCount();
}
}

中介者模式 Pattern

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
126
127
128
129
130
interface Publisher {
void notify(String itemName, int count);
}

public class Storage implements Publisher {
Mediator mediator;
private HashMap<String, Integer> storageMap;
private String name;

public Storage(String name, Mediator mediator) {
this.storageMap = new HashMap();
this.name = name;
this.mediator = mediator;
}

@Override
public void notify(String itemName, int count) {
mediator.notify(this, itemName, count);
}

public void add(String itemName, int count) {
storageMap.put(itemName, count);
}

public void reduce(String itemName) {
int currentVal = storageMap.get(itemName);
storageMap.put(itemName, currentVal - 1);
notify(itemName, currentVal - 1);
}
}

public abstract class Observer {
private String role;

public Observer(String role) {
this.role = role;
}

abstract void push(String info);

public String getRole() {
return role;
}
}

public class Shop extends Observer {
public Shop(String role) {
super(role);
}

@Override
void push(String info) {

}
}

public class ShunFenDelivery extends Observer {
@Override
void push(String info) {

}

public ShunFenDelivery(String role) {
super(role);
}
}

public class Procurement extends Observer {
public Procurement(String role) {
super(role);
}

@Override
void push(String info) {

}
}

public class Mediator {
private HashMap<Publisher, ArrayList<Observer>> hashMap;

public Mediator() {
this.hashMap = new HashMap<>();
}

public void notify(Publisher publish, String itemName, int count) {
ArrayList<Observer> observers = hashMap.get(publish);
// 添加逻辑基于count,itemName,也可以结合多个不同的item
if (observers != null && !observers.isEmpty()) {
for (Observer observer : observers) {
switch (observer.getRole()) {
case "Procure":
if (count > 5) {
observer.push("货物充足!");
} else {
observer.push("货物不足,准备补充");
}
break;
case "Shop":
if (count > 5) {
observer.push("货物充足!");
} else {
observer.push("货物不足,准备下架");
}
break;
case "Delivery":
if (count > 5) {
observer.push("货物充足,车辆平均分配!");
} else {
observer.push("货物不足,准备帮忙采购");
}
break;
}
}
}
}

public void subscribe(Publisher publisher, Observer observer) {
hashMap.computeIfAbsent(publisher, k -> new ArrayList<>()).add(observer);
}

public void unsubscribe(Publisher publisher, Observer observer) {
hashMap.computeIfPresent(publisher, (k, v) -> {
v.remove(observer);
return v.isEmpty() ? null : v;
});
}

}

中介者模式的核心逻辑就是Mediator, publisher和observer相对工作量小一点,符合全在mediator

适配器模式 + 装饰器模式 + 享元模式

[Musings]

  1. 今天回看工厂模式和抽象工厂模式,发现了其实本质上所有button/textbox抽象类和实现类都是通用的,唯独的不同在于工厂的性质变了,一个注重垂类产品,一个注重品牌哈哈哈

  2. 开始结构型的设计模式了

Adapter Pattern

题目是不同国家的充电头电压适配

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

//USBTarget.interface
public interface USBTarget {
void charge5V();
}

//AmericanCharger
public class AmericanCharger {
public void supply110V(){
System.out.println("American Charger, output 110V");
}
}

//ChineseCharger
public class ChineseCharger{
public void supply220V(){
System.out.println("Chinese Charger, output 220V");
}
}

//AmericanAdapter(核心)
public class AmericanAdapter implements USBTarget {
private AmericanCharger americanCharger;

public AmericanAdapter(AmericanCharger americanCharger) {
this.americanCharger = americanCharger;
}

@Override
public void charge5V() {
americanCharger.supply110V();
System.out.println("American Adapter, output 5V!");
}
}

//ChineseAdapter(核心)
public class ChineseAdapter implements USBTarget {
private ChineseCharger chineseCharger;

public ChineseAdapter(ChineseCharger chineseCharger) {
this.chineseCharger = chineseCharger;
}

@Override
public void charge5V() {
chineseCharger.supply220V();
System.out.println("Chinese Adapter, output 5V!");
}
}

//AdapterType
enum AdapterType {
CHINESE, AMERICAN
}

//AdapterFactory
public class AdapterFactory {
private AdapterFactory() {
}//私有化实例

public static USBTarget createChineseAdapter() {
return new ChineseAdapter(new ChineseCharger());
}

public static USBTarget createAmericanAdapter() {
return new AmericanAdapter(new AmericanCharger());
}
}

//Phone.class
public class Phone {
public static USBTarget withAdapter(AdapterType adapterType) {
return switch (adapterType) {
case CHINESE -> AdapterFactory.createChineseAdapter();
case AMERICAN -> AdapterFactory.createAmericanAdapter();
default -> throw new IllegalArgumentException();
};
}

public void charging(USBTarget usbTarget) {
System.out.println("Start Charging...");
usbTarget.charge5V();
}
}

//Main.class
public class Main {
public static void main(String[] args) {
Phone phone = new Phone();
phone.charging(Phone.withAdapter(AdapterType.CHINESE));
phone.charging(Phone.withAdapter(AdapterType.AMERICAN));
}
}

加了点工厂模式进去,为了方便实例化,本质的核心是Adapter,通过多封装了一层适配器,去解决,其实就是解耦,phone不需要去考虑对应的charger

装饰器模式

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
//Weapon.interface
public interface Weapon {
int getAttack();
String getDescription();
}

//Arrow.class
public class Arrow implements Weapon{

@Override
public int getAttack() {
return 30;
}

@Override
public String getDescription() {
return "木弓";
}
}

//Sword.class
public class Sword implements Weapon {
@Override
public int getAttack() {
return 50;
}

@Override
public String getDescription() {
return "钢铁长剑";
}
}

//WeaponEnchantment.class
public abstract class WeaponEnchantment implements Weapon {
Weapon enchantmentWeapon;

public WeaponEnchantment(Weapon weapon) {
this.enchantmentWeapon = weapon;
}
}


//FireEnchantment.class 这是核心类,主要就是这里去读取旧攻击力和新的攻击力,就等于是附魔了
//这就是装饰器模式,可以给不同的武器附魔
public class FireEnchantment extends WeaponEnchantment {
private String enchantmentAttribute = "🔥";
private int enchantmentAttack = 5;

public FireEnchantment(Weapon weapon) {
super(weapon);
}

@Override
public int getAttack() {
int originAttack = this.enchantmentWeapon.getAttack();
int enchantment = originAttack + this.enchantmentAttack;
return enchantment;
}

@Override
public String getDescription() {
String originDesc = this.enchantmentWeapon.getDescription();
return originDesc + "[" + this.enchantmentAttribute + "附魔]";
}

}

//Main.class
public class Main {
public static void main(String[] args) {
FireEnchantment fireEnchantmentArrow = new FireEnchantment(new Arrow());
System.out.println(fireEnchantmentArrow.getDescription());
System.out.println(fireEnchantmentArrow.getAttack());

Weapon fireSword = new FireEnchantment(new Sword());
System.out.println(fireSword.getDescription()); // 钢铁长剑附🔥
System.out.println(fireSword.getAttack()); // 55
}
}

装饰器模式就是一个附魔的过程,需要去记忆的就是 抽象类去实现weapon接口,然后附魔类去给武器附不同属性的魔

🏭 工厂方法:一个产品,多种实现
🏭 抽象工厂:一套产品,整体替换
🔨 建造者:复杂对象,分步构建
📋 原型:已有对象,复制使用
👑 单例:全局唯一,严格控制
🎁 装饰器:相同接口,功能叠加(像俄罗斯套娃)
🔌 适配器:接口转换,兼容协作(像电源转接头)

享元模式

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
//Particle.class
public interface Particle {
void render(int x, int y, int life);
}

//Particle.type
public enum ParticleType {
RED_SPARK,
BLUE_SPARK
}

//SparkParticle.class
public class SparkParticle implements Particle {
String color;
String texture;

public SparkParticle(String color, String texture) {
this.color = color;
this.texture = texture;
}

@Override
public void render(int x, int y, int life) {
System.out.println("渲染" + color + texture + "在位置(" + x + "," + y + "), 生命周期:" + life);
}
}

//ParticleFactory.class
public class ParticleFactory {
private final Map<ParticleType, Particle> pool = new HashMap<>();

public Particle getParticle(ParticleType type) {
// 如果存在则返回共享对象,否则创建并缓存
return pool.computeIfAbsent(type, this::createParticle);
}

public Particle createParticle(ParticleType type) {
return switch (type) {
case RED_SPARK -> new SparkParticle("red", "spark");
case BLUE_SPARK -> new SparkParticle("blue", "spark");
default -> throw new IllegalArgumentException();
};
}
}

//Main.class
public class Main {
public static void main(String[] args) {
ParticleFactory particleFactory = new ParticleFactory();
Particle particle = particleFactory.getParticle(ParticleType.RED_SPARK);
particle.render(1,2,3);
Particle particle2 = particleFactory.getParticle(ParticleType.RED_SPARK);
particle2.render(1,2,3);
}
}

享元模式的核心: 分清内部元素和外部元素,控制动与静,动静分离是核心

🏭 工厂方法:一个产品,多种实现
🏭 抽象工厂:一套产品,整体替换
🔨 建造者:复杂对象,分步构建
📋 原型:已有对象,复制使用
👑 单例:全局唯一,严格控制
🎁 装饰器:相同接口,功能叠加(像俄罗斯套娃)
🔌 适配器:接口转换,兼容协作(像电源转接头)
🏷️ 享元模式:共享内在,传递外在(像活字印刷)

拉闸!

建造者模式 & 原型模式

[Musings]
最近沉迷设计模式了,挺好玩的,今天是建造者模式,最常见的builder,通过链式调用来方便构造,这个是我日常使用中觉得最实用的

建造者模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
//Order.class
public class Order {
private String mainFood;
private int number;
private String address;

Order(String address) {
this.address = address;
}

public void setMainFood(String mainFood) {
this.mainFood = mainFood;
}

public void setAddress(String address) {
this.address = address;
}

public void setNumber(int number) {
this.number = number;
}
}
//OrderBuilder.interface
public interface OrderBuilder {
OrderBuilder setMainFood(String mainFood);

OrderBuilder setNumber(int number);

Order build();
}
//MeituanOrderBuilder.class
public class MeituanOrderBuilder implements OrderBuilder {
private Order order;

public MeituanOrderBuilder(String address) {
this.order = new Order(address);
}

@Override
public OrderBuilder setMainFood(String mainFood) {
this.order.setMainFood(mainFood);
return this;
}

@Override
public OrderBuilder setNumber(int number) {
this.order.setNumber(number);
return this;
}

@Override
public Order build() {
return this.order;
}
}
//Main.class
public class Main {
public static void main(String[] args) {
MeituanOrderBuilder builder = new MeituanOrderBuilder("123");
Order abc = builder.setMainFood("abc")
.setNumber(12)
.build();
}
}

这是第一个简单版本的,然后我就想,如果在调用的时候可以写成下面这个版本,就更舒服了

1
2
3
4
5
6
7
8
9
public class Main {
public static void main(String[] args) {
// MeituanOrderBuilder builder = new MeituanOrderBuilder("123");
Order abc = Order.withMeituanBuilder()
.setMainFood("abc")
.setNumber(12)
.build();
}
}

然后我就开始班门弄斧,其实就是抽象工厂+建造者模式,builder返回不同的抽象工厂,然后每个工厂去做建造

1
2
3
4
5
6
7
8
package org.example.builder;

public class Order {
// {...}
public static MeituanOrderBuilder withMeiTuanBuilder(String address){
return new MeituanOrderBuilder(address);
}
}

主要这里考虑的几个点:

  1. 建造者通过对超多参数的情况进行优化,防止构造函数过于复杂
  2. 通过链式调用来清晰明了的书写代码
  3. 考虑是否使用final来修饰字段,这样可以确保建造者模式在set完以后是固定的

第三点其实根据场景和设计需求考虑,设计模式本身就是灵活的,一起都要基于业务场景的上下文来实现,而不是单纯的照抄,理解核心原理,就像打太极一样,学会了,又好像没学,什么都忘了,想用的时候,让灵感飘动起来

所以到此为止,记住特性的区别
🏭 工厂方法:一个产品,多种实现
🏭 抽象工厂:一套产品,整体替换
🔨 建造者:复杂对象,分步构建
📋 原型:已有对象,复制使用
👑 单例:全局唯一,严格控制

原型模式

原型模式的核心就是两个

  1. 实现cloneable接口,重写clone方法
  2. 注意深浅拷贝,对于引用对象实现深拷贝
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
//Address.java
public class Address implements Cloneable{
private String city;
private List<String> tags = new ArrayList<>();

public List<String> getTags() {
return tags;
}

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

public String getCity() {
return city;
}

public void setCity(String city) {
this.city = city;
}

//拷贝构造函数
public Address(Address other){
this.city = other.city;
this.tags = new ArrayList<>(other.tags);
}

//手动实现拷贝构造函数
@Override
protected Object clone() throws CloneNotSupportedException {
Address clone = (Address) super.clone();
clone.tags = new ArrayList<>(this.tags);
return clone;
}
}

//Main.class
public class Main {
public static void main(String[] args) throws CloneNotSupportedException {
Address address = new Address();
Address clone = (Address) address.clone();
address.getTags().add("1");
System.out.println(address); //tag :[1]
System.out.println(clone); // tag: []
}
}

其实很简单,就这么个逻辑

一篇搞明白JS变量提升

[Musings]
一篇文章搞清楚 JS 变量提升

最基础的变量提升

1
2
3
4
5
var a = 100;
console.log(a); // 100
console.log(b); // undefined
var b = 100;
console.log(b); // 100

真正的运行的时候编译器其实偷偷做了这一步:

1
2
3
4
5
6
var a = 100;
var b; //我提升了!升华了!✅ 声明提升到作用域顶部
console.log(a); // 100
console.log(b); // undefined
b = 100; //✅ 赋值留在原地
console.log(b); // 100

行,这是第一步,变量提升,接着看看函数提升

1
2
3
4
5
6
var a = 100;
test(); //test
console.log(a); // 100
function test() {
console.log("test");
}

看完变量和方法的变量提升,我们来看看 let 和 const

1
2
3
4
5
let a = 100;
console.log(a); // 100
console.log(b); // ReferenceError
let b = 100;
console.log(b); // 上一步就挂了,走不到这一步的

接着我们继续看看方法体作用于中的情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
(function test() {
for (var i = 0; i < 3; i++) {
console.log(i); // 0,1,2
}
console.log(i); //3
})();
console.log(i); //ReferenceError

(function test() {
for (let i = 0; i < 3; i++) {
console.log(i); // 0,1,2
}
console.log(i); //ReferenceError
})();

基本到这差不多就能理解变量提升了,但是有一种比较特殊的异步+变量提升的问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
(function test() {
// var i 被提升到这里
for (var i = 0; i < 3; i++) {
// 3个setTimeout回调都闭包引用同一个 i
setTimeout(() => {
console.log(i); //3,3,3
}, 100);
}
// 循环结束时 i = 3
// 100ms后所有回调执行,都读取同一个 i(值为3)
})();

(function test() {
for (let i = 0; i < 3; i++) {
// 每次迭代创建新的词法环境,有独立的 i
setTimeout(() => {
console.log(i); //0,1,2
}, 100);
}
})();

解释下这里发生这个情况的原因:

简洁的表达就是
“var 让所有回调共享同一个变量引用,最终都看到循环结束值 3。let 每次迭代创建新的词法环境,每个回调捕获独立的变量值。”

我脑子里的啰嗦想法就是 ⬇️

var 的作用域是函数作用域而 let 的作用与是块级作用域,所以这里所有的异步任务会被丢到异步队列,然后在执行 log 的时候,i 已经变成 3 了,所以输出了三遍
而 let 是块级作用域,所以在执行的时候每一个 log 函数都带一个变量 i,每个变量 i 在单独的内存空间里,所以不会互相污染,这就是 var 导致的变量提升所带来影响,从本身的额块级作用域被提升到了函数作用域,所以被 log 函数共享了内存空间

以上我的讲解都是自己的理解,实际上对于 let i 的变量在内存中都是独立的这点,得根据 javascript 引擎的写法来看,可能引擎会重用寄存器中的地址所以从
描述规范上来说,更准确的讲法是,let i 在词法环境里是互相隔离的

继续变量提升的case:

1
2
3
4
5
6
7
8
9
console.log(typeof func); // "function" ✅

var func = "I'm a variable";

function func() {
return "I'm a function";
}

console.log(typeof func); // "string" ✅

显而易见,函数提升的优先级高于变量

最后感受下块级作用域的隔离

1
2
3
4
5
6
7
8
9
10
11
12
{
function innerFunc() {
var secret = "我是秘密"; // 🔒 函数作用域
return secret;
}

console.log(innerFunc()); // "我是秘密" ✅
// console.log(secret); // ❌ ReferenceError ✅
}

console.log(innerFunc()); // ❌ 可能报错(取决于环境,严格模式下会报错)

最后附上一个对比图吧

特性 var let/const 函数声明
提升 ⚠️暂时性死区 ⚠️暂时性死区
作用域 函数级 块级 块级
重复说明
异步闭包 共享应用 独立 独立