迭代器模式

[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分支!
}
}

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