0%

基操

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
import json

#### 从 string 加载
person = '{"name": "Bob", "languages": ["English", "Fench"]}'
person_dict = json.loads(person)

#### 从文件加载
with open('path_to_file/person.json') as f:
data = json.load(f)

#### json 转 string
person_dict = {'name': 'Bob',
'age': 12,
'children': None
}
person_json = json.dumps(person_dict)
# Output: {"name": "Bob", "age": 12, "children": null}

person_dict = {"name": "Bob",
"languages": ["English", "Fench"],
"married": True,
"age": 32
}
#### json 写入文件
with open('person.txt', 'w') as json_file:
json.dump(person_dict, json_file)


#### 格式化输出
person_string = '{"name": "Bob", "languages": "English", "numbers": [2, 1.6, null]}'

# Getting dictionary
person_dict = json.loads(person_string)

# Pretty Printing JSON string back
print(json.dumps(person_dict, indent = 4, sort_keys=True))

Positive Lookahead, 零宽度正预测先行断言: 匹配以 xx 结尾的, (?=XX)

特性: 这个断言是从右边开始匹配

1
2
3
4
5
6
7
e.g.01 
use .*(?=ing) as pattern to match 'cooking, doing'
matched: 'cooking, do'

e.g.02
use [a-z]*(?=ing) as pattern to match 'cooking, doing, singing'
matched: 'cook, do, sing'

Negtive Lookahead, 负向零宽先行断言: 匹配不以 xx 结尾的, (?!XX)

特性: 这个断言是从右边开始匹配

1
2
3
4
5
6
7
e.g.01 
pattern: yolo(?!lo)
matched: yolo yololo yolo
result: 匹配到第一个 yolo 和最后一个 yolo
refer: https://regex101.com/r/UyC8Z1/1/

感觉上零宽断言适合做 词 这个level 的 match, 普通的 regex 经常会带进来空格

Positive Lookbehind, 负向零宽先行断言: 匹配以 xx 开始的, (?<=XX)

特性: 这个断言是从左边开始匹配

1
2
3
e.g.01 
use (?<=abc).* to match abcdefgabc
result: defgabc, not abcdefg

Negative Lookbehind, 负向零宽先行断言: 匹配不以 xx 开始的, (?<!XX)

特性: 这个断言是从左边开始匹配

1
2
3
e.g.01
use (?<!yo)lol to match lol yolol
result: the first lol on left

Refer

采用分治(Divide and Conquer)的思想,解题思路如下:

  1. 先将整个数组分为有序数组(对半分,直到长度为1)
  2. 两两合并,并确保合并后的数组有序 - 二路归并
  3. 重复直到所有数组合并完成

时间复杂度 O(nlogn)

实现

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
import java.util.Arrays;
import java.util.Random;

public class MergeSort {
public static void main(String[] args) {
int[] sample = new Random().ints(0, 100).limit(10).toArray();
System.out.println("Origin: " + Arrays.toString(sample));

mergeSort(sample, 0, sample.length - 1);
System.out.println("Origin: " + Arrays.toString(sample));
}

private static void mergeSort(int[] sample, int low, int high) {
// 结束条件
if (low >= high)
return;
// 对半分
int mid = (low + high) / 2;
// 分别对两个子数组做归并排序
mergeSort(sample, low, mid);
mergeSort(sample, mid + 1, high);
// 合并两个子数组
merge(sample, low, mid, high);
}

private static void merge(int[] sample, int low, int mid, int high) {
// 声明零时数组存放排序结果
int[] tmp = new int[high - low + 1];
int i = low, j = mid + 1;
int index = 0;
// 比较两个子数组的最值,并放到临时数组中
while (i <= mid && j <= high) {
if (sample[i] < sample[j]) {
tmp[index++] = sample[i++];
} else {
tmp[index++] = sample[j++];
}
}

// 将子数组剩余值放入临时数组中,经过上一步之后其中一个数组已经空了,所以下面两个 while 先后顺序没关系
while (i <= mid) {
tmp[index++] = sample[i++];
}

while (j <= high) {
tmp[index++] = sample[j++];
}

// 将临时数组中的结果覆盖到原数组中
for (int pos = 0; pos < tmp.length; pos++) {
sample[low + pos] = tmp[pos];
}
}
}

阿里云购买云服务,本地通过终端连接使用,终端工具,弹幕各种推荐 Mobaxtream, 打算试用一下

创建安全组

阿里云上创建实例后,你只能通过他们提供的终端工具访问,如果你想要通过其他终端工具访问服务器,需要设置安全组。这个安全组你可以理解为云服务形势下的开启防火墙端口。

常用端口:

  • 21: FTP
  • 22: SSH
  • 80: HTTP
  • 443: HTTPS
  • 3306: mysql
  • 8080: tomcat
  • 6379: redis

安全组 -> 创建安全组,将常用端口添加进去即可

创建实例

实例可以理解为远程虚拟机,进入阿里云主界面,顶部选择 控制台 -> 点右上角三条杠 -> 下拉单选择 实例 -> 右上角选择 创建实例

自定义购买下面选择:

  • 付费模式: 按量付费
  • 地域:就近 - 上海
  • 规格:测试 docker 选 1 核 2 G

其他只需要注意一下安全组设置即可,创建完毕后,点击你创建的实例,进入面板后就可以看到你创建的实例的公网 IP,然后点击右上角的全部操作下拉单 -> 修改密码

远程链接

下载 Mobaxtream 并安装,没什么好说的,新建 session 然后填写账号密码尝试链接,没什么问题的话都 OK 的

安装 docker

1
2
3
4
5
6
7
8
yum update

yum install docker -y

docker -v # 检查版本

service docker start # 启动 docker 服务,不然 docker ps 都没法运行
chkconfig docker on # 设置开机运行

镜像加速

控制台界面搜索 容器镜像服务 -> 镜像工具 -> 镜像加速器

他会根据账号给出加速地址和配置方法,很直观, 对于 CentOS 可以通过修改 daemon 配置文件/etc/docker/daemon.json来使用加速器

sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-‘EOF’
{
“registry-mirrors”: [“https://xxx.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker

启动 nginx 镜像测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
docker run -d -p 8080:80 --name my-nginx nginx 

# 本地测试
curl localhost:8080
# <!DOCTYPE html>
# <html>
# <head>
# <title>Welcome to nginx!</title>
# ...
# <h1>Welcome to nginx!</h1>
# <p>If you see this page, the nginx web server is successfully installed and
# working. Further configuration is required.</p>
# ...
# </body>
# </html>

# 外网直接通过浏览器输入 ip:8080 可以看到 nginx 页面显示成功!

PS: 第一次测试内部访问成功,外部失败,查看安全组,发现添加输入框末端需要点击保存…

文件名规范

  • 全英文, 包含中文的话, web 访问地址里会带转意符号, 不喜欢
  • 首字母大写
  • 单词之间用 - 链接, 这时 hexo 命令自己的规范, 如果我用命令 hexo n 'my new post' 它会自动把文件命名为 ‘my-new-post’
  • 文件名中间中间如果包含专有名词, 允许大写, 如 ‘Java-Windows-setting.md’

内容规范

  • 行文中不出现中文标点符号, 中文句点感觉上格式很奇怪, 看上去空格很大的感觉。可以在替换栏中用 , 替换 , [\s]*, 句号同理

PS: 有兴趣的话可以做一个 VS 插件玩玩

插件注意点:

  1. 替换逗号
  2. 替换所有句号
  3. 所有冒号替换成英文版+空格
  4. 去掉所有在末尾的冒号的空格
  5. 所有末尾的句号去掉空格 \. $ -> .

遇到一个问题,在写测试的时候需要产生一串随机数,找了一下解决方案,记录一下

示例

需求:

  1. 字符串以 Test 开头
  2. 中间加指定格式的日期
  3. 结尾加上前面补0的4位随机整数
1
2
3
4
5
6
7
8
9
10
11
12
13
public class RandomDemo {
public static void main(String[] args) {
String prefix = "Test";

SimpleDateFormat sdfDate = new SimpleDateFormat("yyMMdd");
String mid = sdfDate.format(new Date());

String suffix = String.format("%04d", new Random().nextInt(10000));

System.out.println(prefix + mid + suffix);
}
}
// Test2104215709

%04d 的含义:

  • 0: 前面补0
  • 4: 长度为4
  • d: 对整形做操作

算法描述

  1. 首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。
  2. 再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
  3. 重复第二步,直到所有元素均排序完毕。

实现

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
public class SelectionSortDemo {
public static void main(String[] args) {
int[] sample = new Random().ints(0, 100).limit(10).toArray();
System.out.println("Origin: " + Arrays.toString(sample));

selectionSort(sample);
System.out.println("After: " + Arrays.toString(sample));
}

private static void selectionSort(int[] sample) {
for (int i = 0; i < sample.length - 1; i++) {
int max_idx = i;
for (int j = i; j < sample.length; j++) { // 这里注意一下,是对整个数组做 selection,所以为 sample.length, 如果写为 sample.length-1 则最后一个元素会跳过排序
if (sample[max_idx] < sample[j]) {
max_idx = j;
}

}
if (max_idx != i) {
swap(sample, i, max_idx);
}
}
}

private static void swap(int[] sample, int i, int j) {
int tmp = sample[i];
sample[i] = sample[j];
sample[j] = tmp;
}
}
// Origin: [32, 47, 97, 16, 3, 81, 61, 78, 43, 65]
// After: [97, 81, 78, 65, 61, 47, 43, 32, 16, 3]

算法描述

  1. 遍历数组,比较相邻的两个元素的大小,如果前一个比后一个大就交换位置,如此循环,最后一个位置即最大值
  2. 重复上述过程,对前 n-1 个元素排序
  3. 重复直到所有元素都完成排序

操作没问题,但是你难道不觉得,这个比较中间过程中的交换过程很浪费资源吗?为了省去中间的交换过程,我们有了选择排序。

时间复杂度:O(n2)

实现

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
/**
* 1. 遍历数组,比较相邻的两个元素的大小,如果前一个比后一个大就交换位置,如此循环,最后一个位置即最大值
* 2. 重复上述过程,对前 n-1 个元素排序
* 3. 重复直到所有元素都完成排序
*/
public class BubbleSortDemo {
public static void main(String[] args) {
int[] sample = new Random().ints(0, 100).limit(10).toArray();
System.out.println("Origin: " + Arrays.toString(sample));
bubbleSort(sample);
System.out.println("After: " + Arrays.toString(sample));
}

private static void bubbleSort(int[] sample) {
for (int i = sample.length - 1; i > 0; i--) { // 这里使用 i=length-1 的表达方式,第二层直接 j < i, 书写起来比较好看
for (int j = 0; j < i; j++) {
if (sample[j] > sample[j + 1]) {
swap(sample, j, j + 1);
}
}
}
}

private static void swap(int[] arr, int pos1, int pos2) {
int tmp = arr[pos1];
arr[pos1] = arr[pos2];
arr[pos2] = tmp;
}
}
// Origin: [62, 39, 37, 64, 84, 27, 68, 90, 55, 63]
// After: [27, 37, 39, 55, 62, 63, 64, 68, 84, 90]

插入排序是一个很基础的排序方法,基本解法思路为:

将待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。)

时间复杂度:O(n2)

实现

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
/**
* 1. 第一次计算,拿二号元素和一号元素比较,如果二号小于一号,交换位置。计算后前两个元素为规则元素
* 2. 第二次计算,拿三号元素依次和二号,一号做比较,如果三号小于其中某个元素,交换位置
* 3. 重复以上规则对剩余元素进行排序
*/
public class InsertionSortDemo {
public static void main(String[] args) {
int[] sample = new Random().ints(0, 100).limit(10).toArray();
System.out.println("Origin: " + Arrays.toString(sample));

insertionSort(sample);
System.out.println("After: " + Arrays.toString(sample));
}

private static void insertionSort(int[] sample) {
for (int i = 1; i < sample.length; i++) {
for (int j = i; j > 0; j--) {
if (sample[j] < sample[j-1]) {
swap(sample, j, j-1);
}
}
}
}

private static void swap(int[] sample, int pos1, int pos2) {
int tmp = sample[pos1];
sample[pos1] = sample[pos2];
sample[pos2] = tmp;
}
}
// Origin: [54, 66, 34, 28, 0, 10, 6, 61, 42, 97]
// After: [0, 6, 10, 28, 34, 42, 54, 61, 66, 97]

The Composite Pattern allows you to compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly.
组合模式让我们可以将数据组合成树状结构。他可以让我们以统一的模式对待单个节点或整个组合体

缘起

书接上一个 Iterator 章节,现在 DinerMenu 业务扩展了,我们想要在 DinerMenu 的基础上再增加一个子目录来打印一个点心子菜单。现在已有的代码结构并不能完成我们的需求,我们将使用 组合模式 重构我们的代码。

更具上面这个需求我们可以将需要展示的结构抽象为如下结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
                                                     +------------+                                                                                  
| All Menus |
+------------+
^
-----------------------------------|-------------------------------
| | |
| | |
+--------------------+ +------------+ +------------+
| Pancake House Menu | | Diner Menu | | Cafe Menu |
+--------------------+ +------------+ +------------+
^ ^ ^
---------------|---------- |------------- |-----------
| | | | | | |
| | | | | | |
+----------+ +----------+ +----------+ +-------------+ +----------+ +----------+
| MenuItem | | MenuItem | ... | MenuItem | |Dessert Menu | | MenuItem | | MenuItem |
+----------+ +----------+ +----------+ +-------------+ +----------+ +----------+
^
------------- |--------------
| | |
| | |
+----------+ +----------+ |
| MenuItem | | MenuItem | ...
+----------+ +----------+

我们要实现用统一的接口访问 Menu 和 MenuItem,所以不难想到,我们需要在这两个概念外面包装一个统一的对外接口。

代码实现如下:

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
// 我们抽象一个组合节点和叶子结点公用的超集节点,叫做 MenuComponent, 他包含了这两种节点都要用到的方法,虽然这样做会有点冗余
public class MenuComponent {
public void add(MenuComponent menuComponent) {throw new UnsupportedOperationException();}
public void remove(MenuComponent menuComponent) {throw new UnsupportedOperationException();}
public MenuComponent getChild(int i) {throw new UnsupportedOperationException();}
public String getName() {throw new UnsupportedOperationException();}
public String getDescription() {throw new UnsupportedOperationException();}
public double getPrice() {throw new UnsupportedOperationException();}
public boolean isVegetarian() {throw new UnsupportedOperationException();}
public void print() {throw new UnsupportedOperationException();}
}

// 叶子结点实现,只需要重写叶子结点支持的方法
public class MenuItem extends MenuComponent {
String name;
String description;
boolean vegetarian;
double price;

public MenuItem(String name, String description, boolean vegetarian, double price) {
this.name = name;
this.description = description;
this.vegetarian = vegetarian;
this.price = price;
}

public String getName() {
return name;
}

public String getDescription() {
return description;
}

public double getPrice() {
return price;
}

public boolean isVegetarian() {
return vegetarian;
}

public void print() {
System.out.print(" " + getName());
if (isVegetarian()) {
System.out.print(" (v)");
}
System.out.println(", " + getPrice());
System.out.println("-- " + getDescription());
}
}

// 组合节点的实现
public class Menu extends MenuComponent {
ArrayList menuComponents = new ArrayList();
String name;
String description;

public Menu(String name, String description) {
this.name = name;
this.description = description;
}

public void add(MenuComponent menuComponent) {
menuComponents.add(menuComponent);
}

public void remove(MenuComponent menuComponent) {
menuComponents.remove(menuComponent);
}

public MenuComponent getChild(int i) {
return (MenuComponent) menuComponents.get(i);
}

public String getName() {
return name;
}

public String getDescription() {
return description;
}

public void print() {
System.out.print("\n" + getName());
System.out.println(", " + getDesc());
System.out.print("---------------------\n");

// 打印完自己的信息后,还需要循环答应子节点信息
Iterator iterator = menuComponents.iterator();
while (iterator.hasNext()) {
MenuComponent menuComponent = (MenuComponent) iterator.next();
menuComponent.print();
}
}
}

// 测试的客户端
public class CompositeClient {

public static void main(String[] args) {
MenuComponent pancakeHouseMenu = new Menu("PANCAKE HOUSE MENU", "Breakfast");
MenuComponent dinerMenu = new Menu("DINER MENU", "Lunch");
MenuComponent cafeMenu = new Menu("CAFE MENU", "Dinner");
MenuComponent dessertMenu = new Menu("DESSERT MENU", "Dessert of course !");

MenuComponent allMenus = new Menu("ALL MENUS", "All menus combined");
allMenus.add(pancakeHouseMenu);
allMenus.add(dinerMenu);
allMenus.add(cafeMenu);

pancakeHouseMenu.add(new MenuItem("K & B’s Pancake Breakfast", "Pancakes with scrambled eggs, and toast", true, 2.99));
pancakeHouseMenu.add(new MenuItem("Regular Pancake Breakfast", "Pancakes with fried eggs, sausage", false, 2.99));
pancakeHouseMenu.add(new MenuItem("Blueberry Pancakes", "Pancakes made with fresh blueberries", true, 3.49));
pancakeHouseMenu.add(new MenuItem("Waffles", "Waffles, with your choice of blueberries or strawberries", true, 3.59));

dinerMenu.add(new MenuItem("Pasta", "Spaghetti with Marinara Sauce, and a slice of sourdough bread", true, 3.89));
dinerMenu.add(new MenuItem("Vegetarian BLT", " (Fakin’)Bacon with lettuce & tomato on whole wheat", true, 2.99));
dinerMenu.add(new MenuItem("BLT", "Bacon with lettuce & tomato on whole wheat", false, 2.99));
dinerMenu.add(new MenuItem("Soup of the day", "Soup of the day, with a side of potato salad", false, 3.29));

dinerMenu.add(dessertMenu);
dessertMenu.add(new MenuItem("Apple Pie", "Apple pie with a flakey crust, topped with vanilla icecream", true, 1.59));

cafeMenu.add(new MenuItem("Veggie Burger and Air Fries", "Veggie burger on a whole wheat bun, lettuce, tomato, and fries", true, 3.99));
cafeMenu.add(new MenuItem("Soup of the day", "A cup of the soup of the day, with a side salad", false, 3.69));
cafeMenu.add(new MenuItem("Burrito", "A large burrito, with whole pinto beans, salsa, guacamole", true, 4.29));
allMenus.print();
}
}
// ALL MENUS, All menus combined
// ---------------------

// PANCAKE HOUSE MENU, Breakfast
// ---------------------
// K & B’s Pancake Breakfast, desc:'Pancakes with scrambled eggs, and toast' (v) , price:2.99
// Regular Pancake Breakfast, desc:'Pancakes with fried eggs, sausage' , price:2.99
// Blueberry Pancakes, desc:'Pancakes made with fresh blueberries' (v) , price:3.49
// Waffles, desc:'Waffles, with your choice of blueberries or strawberries' (v) , price:3.59

// DINER MENU, Lunch
// ---------------------
// Pasta, desc:'Spaghetti with Marinara Sauce, and a slice of sourdough bread' (v) , price:3.89
// Vegetarian BLT, desc:' (Fakin’)Bacon with lettuce & tomato on whole wheat' (v) , price:2.99
// BLT, desc:'Bacon with lettuce & tomato on whole wheat' , price:2.99
// Soup of the day, desc:'Soup of the day, with a side of potato salad' , price:3.29

// DESSERT MENU, Dessert of course !
// ---------------------
// Apple Pie, desc:'Apple pie with a flakey crust, topped with vanilla icecream' (v) , price:1.59

// CAFE MENU, Dinner
// ---------------------
// Veggie Burger and Air Fries, desc:'Veggie burger on a whole wheat bun, lettuce, tomato, and fries' (v) , price:3.99
// Soup of the day, desc:'A cup of the soup of the day, with a side salad' , price:3.69
// Burrito, desc:'A large burrito, with whole pinto beans, salsa, guacamole' (v) , price:4.29

上面这种 print 用了内部 Iterator 的方法,简单了很多, 那么如何实现一个外部的 Iterator 呢

我们先为基类添加 createIterator 方法

1
2
3
4
5
public class MenuComponent {
public void add(MenuComponent menuComponent) {throw new UnsupportedOperationException();}
// dup...
public Iterator createIterator() {throw new UnsupportedOperationException();};
}

然后声明一个 Composite 的 Iterator 的具体实现类, 并让 Composite 实现类返回它

CompositeIterator 说明:

这个 Iterator 说实话不是很容易看懂。我们最好结合下面的 printVegetarianMenu() 方法的使用情况一起来看。由下面的方法调用,我们可以反推出这个 Iterator 实现类的作用。

每次调用 hasNext() 时,CompositeIterator 会返回一个 MenuCompoment 类型的对象。有可能是 Menu, 也有可能是 MenuItem。

而且就算返回的是 Menu, 他之后还是会把这个 Menu 的 MenuItem 在下一次返回。由此我们可以推测出,在 next() 反 Menu 之后,还有一个隐式的将 Menu 子节点 push 到 stack 中的动作。

PS: 这段代码的关键点是,stack 中存储的是 Iterator 对象。当这个对象里面没有值的时候,需要做 pop 操作弹出 it, 这里有点绕

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
public class CompositeIterator implements Iterator<MenuComponent> {
private Stack<Iterator<MenuComponent>> stack = new Stack<>();

public CompositeIterator(Iterator<MenuComponent> it) {
stack.push(it);
}

@Override
public boolean hasNext() {
if(stack.empty()) {
return false;
} else {
Iterator<MenuComponent> it = stack.peek();
if (it.hasNext()) {
return true;
} else {
stack.pop();
return hasNext();
}
}
}

@Override
public MenuComponent next() {
if (hasNext()) {
Iterator<MenuComponent> it = stack.peek();
MenuComponent component = it.next();
if (component instanceof Menu) {
stack.push(component.createIterator());
}
return component;
} else {
return null;
}
}
}

public class Menu extends MenuComponent {
// dup...
@Override
public Iterator<MenuComponent> createIterator() {
return new CompositeIterator(nodes.iterator());
}
}

由于叶子节点是不需要迭代的,我们返回一个空的 iterator, 每次调用 hasNext() 都返回 false

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class NullIterator implements Iterator {
@Override
public boolean hasNext() {
return false;
}

@Override
public Object next() {
return null;
}
}

public class MenuItem extends MenuComponent {
// dup...
@Override
public Iterator createIterator() {
return new NullIterator();
}
}

测试代码

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 class Client {
public static void main(String[] args) {
MenuComponent pancakeHouseMenu = new Menu("PANCAKE HOUSE MENU", "Breakfast");
MenuComponent dinerMenu = new Menu("DINER MENU", "Lunch");
MenuComponent cafeMenu = new Menu("CAFE MENU", "Dinner");
MenuComponent dessertMenu = new Menu("DESSERT MENU", "Dessert of course !");

MenuComponent allMenus = new Menu("ALL MENUS", "All menus combined");
allMenus.add(pancakeHouseMenu);
allMenus.add(dinerMenu);
allMenus.add(cafeMenu);

pancakeHouseMenu.add(new MenuItem("K & B’s Pancake Breakfast", "Pancakes with scrambled eggs, and toast", true, 2.99));
pancakeHouseMenu.add(new MenuItem("Regular Pancake Breakfast", "Pancakes with fried eggs, sausage", false, 2.99));
pancakeHouseMenu.add(new MenuItem("Blueberry Pancakes", "Pancakes made with fresh blueberries", true, 3.49));
pancakeHouseMenu.add(new MenuItem("Waffles", "Waffles, with your choice of blueberries or strawberries", true, 3.59));

dinerMenu.add(new MenuItem("Pasta", "Spaghetti with Marinara Sauce, and a slice of sourdough bread", true, 3.89));
dinerMenu.add(new MenuItem("Vegetarian BLT", " (Fakin’)Bacon with lettuce & tomato on whole wheat", true, 2.99));
dinerMenu.add(new MenuItem("BLT", "Bacon with lettuce & tomato on whole wheat", false, 2.99));
dinerMenu.add(new MenuItem("Soup of the day", "Soup of the day, with a side of potato salad", false, 3.29));

dinerMenu.add(dessertMenu);
dessertMenu.add(new MenuItem("Apple Pie", "Apple pie with a flakey crust, topped with vanilla icecream", true, 1.59));

cafeMenu.add(new MenuItem("Veggie Burger and Air Fries", "Veggie burger on a whole wheat bun, lettuce, tomato, and fries", true, 3.99));
cafeMenu.add(new MenuItem("Soup of the day", "A cup of the soup of the day, with a side salad", false, 3.69));
cafeMenu.add(new MenuItem("Burrito", "A large burrito, with whole pinto beans, salsa, guacamole", true, 4.29));

Iterator<MenuComponent> it = allMenus.createIterator();
printVegetarianMenu(it);
}

private static void printVegetarianMenu(Iterator<MenuComponent> it) {
System.out.println("Vegetarian Menu\n----------");
while (it.hasNext()) {
MenuComponent menuComponent = it.next();
try {
if (menuComponent.isVegetarian()) {
menuComponent.print();
}
} catch (UnsupportedOperationException ignored) {
}
}
}
}
// Vegetarian Menu
// ----------
// K & B’s Pancake Breakfast, desc:'Pancakes with scrambled eggs, and toast' (v) , price:2.99
// Blueberry Pancakes, desc:'Pancakes made with fresh blueberries' (v) , price:3.49
// Waffles, desc:'Waffles, with your choice of blueberries or strawberries' (v) , price:3.59
// Pasta, desc:'Spaghetti with Marinara Sauce, and a slice of sourdough bread' (v) , price:3.89
// Vegetarian BLT, desc:' (Fakin’)Bacon with lettuce & tomato on whole wheat' (v) , price:2.99
// Apple Pie, desc:'Apple pie with a flakey crust, topped with vanilla icecream' (v) , price:1.59
// Apple Pie, desc:'Apple pie with a flakey crust, topped with vanilla icecream' (v) , price:1.59
// Veggie Burger and Air Fries, desc:'Veggie burger on a whole wheat bun, lettuce, tomato, and fries' (v) , price:3.99
// Burrito, desc:'A large burrito, with whole pinto beans, salsa, guacamole' (v) , price:4.29

上面用的是外部 Iterator 的方式,需要自己控制当前节点位置,所以实现上比内部的那种要复杂很多。

UML

图示说明:

  • client 到 Component 为 实线 + 普通箭头,表示含有
  • leaf, composite 到 Component 为 实线 + 空心箭头,表示实现接口
  • Composite 到 Component 为 实线 + 普通箭头,表示 1 对 n 的对应关系
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
                           +----------------+                                                                                                       
+---------+ | Component | 1..n
| Client |--------------> |----------------|<--------
+---------+ | + operation() | |
| + add(child) | |
| + remve(child) | |
| + getChild() | |
+----------------+ |
^ |
---------------------- | |
| | |
| | |
+---------------+ +----------------+ |
| Leaf | | Composite | |
|---------------| |----------------|<>-----|
| +operation() | | + operation() |
+---------------+ | + add(child) |
| + remve(child) |
| + getChild() |
+----------------+