设计原则
- 找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。即把会变化的部分取出并封装起来,以便以后可以轻易地改动或扩充此部分,而不影响不需要变化的其他部分。
- 针对接口编程,而不是针对实现编程。
- 多用组合,少用继承。
- 为了交互对象之间的松耦合设计而努力。
- 开放-关闭原则:类应该对扩展开放,对修改关闭。(装饰者模式)
- 依赖倒置原则:要依赖抽象,不要依赖具体类。(工厂方法模式)
- 最少知识原则(德墨忒尔法则 Law of Demeter):最少知识原则,只和你密友对话。
- 好莱坞原则:别调用(打电话给)我们,我们会调用(打电话给)你。高层组件对底层组件的方式是“别调用我们,我们会调用你。”,无绝对,例如底层组件在结束时,常常会调用从超类中继承来的方法。我们要做的是避免让高层和底层组件之间有明显的环状依赖,进行解耦。
- 单一责任原则:一个类应该只有一个引起变化的原因。—— 类的每个责任都有改变的潜在区域。超过一个责任,意味着超过一个改变的区域。
内聚(cohesion) 度量一个类或模块紧密地达到单一目的或责任。当一个模块或一个类被设计成只支持一组相关的功能时,我们说它具有高内聚;反之,当被设计成支持一组不想关的功能时,我们说它具有低内聚。
方针:
- 该对象本身
- 被当做方法的参数而传递进来的对象
- 此方法所创建或实例化的任何对象
- 对象的任何组件
策略模式
定义:策略模式定义了算法簇,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
鸭子实例
(1)鸭子有不同的种类,有的鸭子不会飞,有的鸭子叫声不同,显然继承的方法很笨拙,牵一发而动全身。例如fly方法如果用继承显然不合适;
(2)如果将fly和quack抽离出来,形成两种接口,不同的鸭子实现这个接口就更灵活了!但是,问题在于接口的实现会导致代码重复!例如两种不同的鸭子实现了同一种的飞行或鸣叫的行为,显然这是不合适的!代码重复啊!
(3)应用原则 找出可能需要变化的部分独立出来并封装,这里变化的是fly和quack行为,再应用原则 针对借口变编程,而不是针对实现编程,将行为抽象为接口,每一种行为封装成 行为类,这样同一种行为用一个类就行表示!避免(2)的代码重复,最后运用 组合 将行为设计成Duck类的变量,就可以做到同一个鸭子类切换不同行为类的目的,即动态设置行为,更为灵活!
注意;针对接口编程,这里的“接口”是一个概念,可解释针对超类型编程,通常是一个抽象类或者一个接口,利用多态在执行时根据实际类型执行真正的行为!
1 | package StrategyPattern; |
游戏人物实例UML
游戏角色的类和角色可以使用的武器行为的类,每个角色一次只能使用一种武器,但是可以在游戏的过程中换武器。
观察者模式
定义:观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
出版者+订阅者=观察者模式
订阅者可订阅或取消订阅,出版者需要推送或者根据用户需求进行推送。
报纸订阅实例
设计气象站
(1)WeatherData从气象站获得数据,而后将信息推送给三个布告板;
(2)WeatherData需要setter和getter进行设置/获取数据;
(3)系统必须可扩展,即可以让其他开发人员建立定制的布告板,用户可以随心所欲地添加或删除任何布告板,所以WeatherData需要注册、移除、以及通知布告板(即关注了WeatherData的布告板们)。
(4)错误示范:
- 此图中三个具体的布告板针对具体实现编程,而非针对接口!
- 对于每个新的布告板,我们都得修改WeatherData代码!
- 无法在运行时动态地增加或删除布告板!
- 尚未封装改变的部分,即不同布告板可能增加、删除
(5)再来看正确的UML图: - 主题Subject设计为接口,因为观察者可以订阅不同的主题,而不同主题有共同相似的方法
- 观察者需要更新数据,以及根据需求进行展示数据(有些不需要展示数据),所以设计成两个接口Observer和DisplayElement
- 通过组合,即Observer的数组(这里使用的是超类型,方便多态和新的观察者的扩展)动态管理观察者,主题的代码不需要修改!
- 以此可以独立地复用主题或观察者,因为二者并非紧耦合!符合为了交互对象之间的松耦合设计而努力的原则。
- 缺点:主题盲推,不管订阅者的需求,订阅者可能只需要一部分更新而非全部,没有提供公开的getter方法让观察者实现自主的消息更新。——具体解决方法参见后面的Java.util.obversable的实现。
1 | package ObserverPattern; |
使用Java.util.Observable以及Observer实现
(1)注意Observable是一个类而非接口,且其中的setChanged()方法为protected,所以只有继承才能实现对应功能,限制了其复用和灵活性,违反了多用组合,少用继承的原则;
(2)不过,其update(Observable o, Object arg)的第一个参数可以根据观察者依赖的Subject进行更新,如果不是其依赖的Subject则无法更新,且更新数据可以通过该参数调用Observable的getter获得属性,而不用Subject全推信息!而arg方法则避免了方法参数的冗长,通过对象封装数据,更具拓展性。
(3)针对其缺点,可以从头实现接口式的观察者模式。
1 | package ObserverPattern_Observable; |
JDK中观察者模式
Swing API:JButtion的超类AbstractButtion,会看到许多增加与删除倾听者listener的方法,这些方法可以让观察者感应到Swing组件的不同类型事件。
装饰者模式
定义:装饰者模式动态地将责任附加到对象上,若要扩展功能,装饰者提供了比继承更有弹性的替代方案。
辛巴克咖啡实例
(1)不同顾客需求不同,需要在基础的咖啡上加不同的辅料,不可能对枚举所有情况然后每个都设计为一个类;
(2)如果饮料基类包含不同辅料的设置和判断方法是否可行?扩展性差!每当有新的辅料加入或者辅料删除都得修改基类代码,而继承带来的缺点牵一发而动全身,也不可取;
(3)运用开放-关闭原则,对扩展开放,对修改关闭,做到咖啡基类与辅料适度解耦,底料咖啡,用不同辅料装饰。基类为咖啡,装饰基类继承基类,因为包装后的东西肯定还是咖啡,所以通过咖啡基类和装饰基类可以扩展不同的具体的咖啡子类和辅料子类,最后计算价格。
(4)思考:如果通过组合来实现呢?比如装饰基类改成辅料基类而不继承自饮料基类,饮料基类中通过组合维护一个辅料基类的数组,只需要追加添加辅料的方法即可,但是如果饮料分为不同大小,这个如何设计?辅料需要知道饮料的大小进行辅料的计算,不同辅料计算方式肯定不同,肯定需要将饮料大小传入辅料类,这样做会很麻烦,很不方便。
1 | package DecoratorPattern; |
装饰Java.io类
1 | package DecoratorPattern.io; |
星巴克咖啡带Size版本
1 | package DecoratorPattern.StarbuzzCoffeeWithSize; |
工厂模式
工厂方法模式
工厂方法模式定义:工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。
所有工厂模式都用来封装对象的创建。工厂模式(Factory Method Pattern)通过让子类决定该创建的对象是什么,来达到将对象创建的过程封装的目的。
- 继承
抽象工厂模式
抽象工厂模式定义:抽象工厂模式提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。
- 对象的组合
Pizaa简单工厂实例
(1)披萨的制作流程一致,包含固定的几种原料,例如面团、芝士、蔬菜等
(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
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
130package FactoryPattern.PizzaStore_Simple_Factory;
import java.util.ArrayList;
//Pizza抽象类
abstract class Pizza{
String name;
String dough;
String sauce;
ArrayList<String> toppings = new ArrayList<>();
public String getName(){return name;}
public void prepare(){
System.out.println("Preparing " + name);
}
public void bake(){
System.out.println("Baking " + name);
}
public void cut(){
System.out.println("Cutting " + name);
}
public void box(){
System.out.println("Boxing " + name);
}
public String toString(){
StringBuffer display = new StringBuffer();
display.append("---- " + name + " ----\n");
display.append(dough + "\n");
display.append(sauce + "\n");
for(String topping:toppings){
display.append(topping + "\n");
}
return display.toString();
}
}
//Pizza类
class CheesePizza extends Pizza{
public CheesePizza(){
name = " Cheese Pizza";
dough = "Regular Crust";
sauce = "Marinara Pizza Sauce";
toppings.add("Fresh Mozzarella");
toppings.add("Parmesan");
}
}
class ClamPizza extends Pizza{
public ClamPizza(){
name = "Clam Pizza";
dough = "Thin crust";
sauce = "White garlic sauce";
toppings.add("Clams");
toppings.add("Grated parmesan cheese");
}
}
class PepperoniPizza extends Pizza{
public PepperoniPizza(){
name = "Pepperoni Pizza";
dough = "Crust";
sauce = "Marinara sauce";
toppings.add("Sliced Pepperoni");
toppings.add("Sliced Onion");
toppings.add("Grated parmesan cheese");
}
}
class VeggiePizza extends Pizza{
public VeggiePizza(){
name = "Veggie Pizza";
dough = "Crust";
sauce = "Marinara sauce";
toppings.add("Shredded mozzarella");
toppings.add("Grated parmesan");
toppings.add("Diced onion");
toppings.add("Sliced mushrooms");
toppings.add("Sliced red pepper");
toppings.add("Sliced black olives");
}
}
//简单工厂类
class SimplePizzaFactory{
public Pizza createPizza(String type){
Pizza pizza = null;
if(type.equals("cheese")){
pizza = new CheesePizza();
}else if(type.equals("pepperoni")){
pizza = new PepperoniPizza();
}else if(type.equals("clam")){
pizza = new ClamPizza();
}else if(type.equals("veggie")){
pizza = new VeggiePizza();
}
return pizza;
}
}
class PizzaStore {
SimplePizzaFactory factory;
public PizzaStore(SimplePizzaFactory factory){
this.factory = factory;
}
public Pizza orderPizza(String type){
Pizza pizza = factory.createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
public class PizzaTestDrive{
public static void main(String[] args){
SimplePizzaFactory factory = new SimplePizzaFactory();
PizzaStore store = new PizzaStore(factory);
Pizza pizza = store.orderPizza("cheese");
System.out.println("We ordered a " + pizza.getName() + "\n");
System.out.println(pizza);
pizza = store.orderPizza("veggie");
System.out.println("We ordered a " + pizza.getName() + "\n");
System.out.println(pizza);
}
}Pizaa工厂实例
(1)下面来到工厂方法模式,与简单工厂不同,工厂方法模式将对象创建移到商店类的createPizza方法中,不同的商店可以创建自己的不同风味的pizza。
(2)不同的加盟店制作的Pizza流程相同,但假如的原料不同,这才形成了不同加盟店独特风味的披萨。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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273package FactoryPattern.PizzaStore_Factory_pattern;
import java.util.ArrayList;
//Pizza抽象类
abstract class Pizza {
String name;
String dough;
String sauce;
ArrayList<String> toppings = new ArrayList<>();
void prepare() {
System.out.println("Prepare " + name);
System.out.println("Tossing dough...");
System.out.println("Adding toppings: ");
for (String topping : toppings) {
System.out.println(" " + topping);
}
}
void bake() {
System.out.println("Bake for 25 minutes at 350");
}
void cut(){
System.out.println("Cut the pizza into diagonal slices");
}
void box(){
System.out.println("Place pizza in official PizzaStore box");
}
public String getName(){
return name;
}
public String toString(){
StringBuffer display = new StringBuffer();
display.append("---- " + name + " ----\n");
display.append(dough + "\n");
display.append(sauce + "\n");
for(String topping : toppings){
display.append(topping + "\n");
}
return display.toString();
}
}
//Pizza商店抽象类
abstract class PizzaStore{
abstract Pizza createPizza(String item);
public Pizza orderPizza(String type){
Pizza pizza = createPizza(type);
System.out.println("--- Making a " + pizza.getName() + " ---");
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
//不同商店不同风格的Pizza类
class ChicagoStyleCheesePizza extends Pizza{
public ChicagoStyleCheesePizza(){
name = "Chicago Style Deep Dish Cheese Pizza";
dough = "Extra Thick Crust Dough";
sauce = "Plum Tomato Sauce";
toppings.add("Shredded Mozzarella Cheese");
}
void cut(){
System.out.println("Cutting the pizza into square slices");
}
}
class ChicagoStyleClamPizza extends Pizza{
public ChicagoStyleClamPizza(){
name = "Chicago Style Clam Pizza";
dough = "Extra Thick Crust Dough";
sauce = "Plum Tomato Sauce";
toppings.add("Shredded Mozzarella Cheese");
toppings.add("Frozen Clams from Chesapeake Bay");
}
void cut(){
System.out.println("Cutting the pizza into square slices");
}
}
class ChicagoStylePepperoniPizza extends Pizza{
public ChicagoStylePepperoniPizza(){
name = "Chicago Style Pepperoni Pizza";
dough = "Extra Thick Crust Dough";
sauce = "Plum Tomato Sauce";
toppings.add("Shredded Mozzarella Cheese");
toppings.add("Black Olives");
toppings.add("Spinach");
toppings.add("Eggplant");
toppings.add("Sliced Pepperoni");
}
void cut(){
System.out.println("Cutting the pizza into square slices");
}
}
class ChicagoStyleVeggiePizza extends Pizza{
public ChicagoStyleVeggiePizza(){
name = "Chicago Deep Dish Veggie Pizza";
dough = "Extra Thick Crust Dough";
sauce = "Plum Tomato Sauce";
toppings.add("Shredded Mozzarella Cheese");
toppings.add("Black Olives");
toppings.add("Spinach");
toppings.add("Eggplant");
}
void cut(){
System.out.println("Cutting the pizza into square slices");
}
}
class NYStyleCheesePizza extends Pizza{
public NYStyleCheesePizza(){
name = "NY Style Sauce and Cheese Pizza";
dough = "Thin Crust Dough";
sauce = "Marinara Sauce";
toppings.add("Grated Reggiano Cheese");
}
}
class NYStyleClamPizza extends Pizza{
public NYStyleClamPizza(){
name = "NY Style Clam Pizza";
dough = "Thin Crust Dough";
sauce = "Marinara Sauce";
toppings.add("Grated Reggiano Cheese");
toppings.add("Fresh Clams from Long Island Sound");
}
}
class NYStylePepperoniPizza extends Pizza{
public NYStylePepperoniPizza(){
name = "NY Style Pepperoni Pizza";
dough = "Thin Crust Dough";
sauce = "Marinara Sauce";
toppings.add("Grated Reggiano Cheese");
toppings.add("Sliced Pepperoni");
toppings.add("Garlic");
toppings.add("Onion");
toppings.add("Mushrooms");
toppings.add("Red Pepper");
}
}
class NYStyleVeggiePizza extends Pizza{
public NYStyleVeggiePizza(){
name = "NY Style Veggie Pizza";
dough = "Thin Crust Dough";
sauce = "Marinara Sauce";
toppings.add("Grated Reggiano Cheese");
toppings.add("Garlic");
toppings.add("Onion");
toppings.add("Mushrooms");
toppings.add("Red Pepper");
}
}
//不同商店类
class ChicagoPizzaStore extends PizzaStore{
Pizza createPizza(String item) {
if(item.equals("cheese")) {
return new ChicagoStyleCheesePizza();
}else if(item.equals("veggie")){
return new ChicagoStyleVeggiePizza();
}else if(item.equals("clam")){
return new ChicagoStyleClamPizza();
}else if(item.equals("pepperoni")){
return new ChicagoStylePepperoniPizza();
}else return null;
}
}
class NYPizzaStore extends PizzaStore{
Pizza createPizza(String item) {
if(item.equals("cheese")){
return new NYStyleCheesePizza();
}else if(item.equals("veggie")){
return new NYStyleVeggiePizza();
}else if(item.equals("clam")){
return new NYStyleClamPizza();
}else if(item.equals("pepperoni")){
return new NYStylePepperoniPizza();
}else return null;
}
}
//自主商店类
class DependentPizzaStore{
public Pizza createPizza(String style, String type){
Pizza pizza = null;
if(style.equals("NY")){
if(type.equals("cheese")) {
pizza = new ChicagoStyleCheesePizza();
}else if(type.equals("veggie")){
pizza = new ChicagoStyleVeggiePizza();
}else if(type.equals("clam")){
pizza = new ChicagoStyleClamPizza();
}else if(type.equals("pepperoni")) {
pizza = new ChicagoStylePepperoniPizza();
}
}else if(style.equals("Chicago")){
if(type.equals("cheese")){
return new NYStyleCheesePizza();
}else if(type.equals("veggie")){
return new NYStyleVeggiePizza();
}else if(type.equals("clam")){
return new NYStyleClamPizza();
}else if(type.equals("pepperoni")) {
return new NYStylePepperoniPizza();
}
}else{
System.out.println("Error: invalid type of pizza");
return null;
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
public class PizzaTestDrive {
public static void main(String[] args){
PizzaStore nyStore = new NYPizzaStore();
PizzaStore chicagoStore = new ChicagoPizzaStore();
Pizza pizza = nyStore.orderPizza("cheese");
System.out.println("Ethan ordered a " + pizza.getName() + "\n");
pizza = chicagoStore.orderPizza("cheese");
System.out.println("Joel ordered a " + pizza.getName() + "\n");
pizza = nyStore.orderPizza("clam");
System.out.println("Ethan ordered a " + pizza.getName() + "\n");
pizza = chicagoStore.orderPizza("clam");
System.out.println("Joel ordered a " + pizza.getName() + "\n");
pizza = nyStore.orderPizza("pepperoni");
System.out.println("Ethan ordered a " + pizza.getName() + "\n");
pizza = chicagoStore.orderPizza("pepperoni");
System.out.println("Joel ordered a " + pizza.getName() + "\n");
pizza = nyStore.orderPizza("veggie");
System.out.println("Ethan ordered a " + pizza.getName() + "\n");
pizza = chicagoStore.orderPizza("veggie");
System.out.println("Joel ordered a " + pizza.getName() + "\n");
}
}Pizza抽象工厂实例
(1)与之前又不同,抽象工厂模式创建一组相关或依赖的对象,这里就是为不同的商店的披萨配备不同的佐料;
(2)现在需要考虑确保每家加盟店使用高质量的原料,并将原料运送到各家加盟店。当然原料类是相同的,但是不同加盟店需要的同种原料可能不同,用料的组合也不同,这就需要继承抽象工厂类拓展成不同风格的原料家族,不同加盟店选择不同的工厂类生产的原料进行披萨制作即可。
(3)同一类的披萨,比如CheesePizza,不同加盟店都有!但是用料不同!通过不同加盟店自己的工厂类进行佐料添加即可,避免了上一个工厂方法中,Pizza子类过多的问题,因为同类型的披萨,只需要通过不同的原料工厂类就能够得到不同加盟店自己风味的披萨了!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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440package FactoryPattern.PizzaStore_Abstract_Factory_Pattern;
//原料接口
interface Cheese { //芝士
String toString();
}
interface Clams { //蛤蜊
String toString();
}
interface Dough { //面团
String toString();
}
interface Sauce { //调味汁
String toString();
}
interface Veggies { //蔬菜
String toString();
}
interface Pepperoni { //意大利辣香肠
String toString();
}
//Pizza原料工厂接口
interface PizzaIngredientFactory{
Dough createDough();
Sauce createSauce();
Cheese createCheese();
Veggies[] createVeggies();
Pepperoni createPepperoni();
Clams createClams();
}
//Pizza抽象类
abstract class Pizza {
String name;
Dough dough;
Sauce sauce;
Veggies[] veggies;
Cheese cheese;
Pepperoni pepperoni;
Clams clams;
abstract void prepare();
void bake() {
System.out.println("Bake for 25 minutes at 250");
}
void cut() {
System.out.println("Cutting the pizza into diagonal slices");
}
void box() {
System.out.println("Place pizza in official PizzaStore box");
}
void setName(String name) {
this.name = name;
}
String getName() {
return name;
}
public String toString() {
StringBuffer result = new StringBuffer();
result.append("---- " + name + " ----\n");
if (dough != null) {
result.append(dough);
result.append("\n");
}
if (sauce != null) {
result.append(sauce);
result.append("\n");
}
if (cheese != null) {
result.append(cheese);
result.append("\n");
}
if (veggies != null) {
for (int i = 0; i < veggies.length; i++) {
result.append(veggies[i]);
if(i<veggies.length-1){
result.append(", ");
}
}
result.append("\n");
}
if(clams != null){
result.append(clams);
result.append("\n");
}
if(pepperoni != null){
result.append(pepperoni);
result.append("\n");
}
return result.toString();
}
}
//Pizza商店抽象类
abstract class PizzaStore{
protected abstract Pizza createPizza(String item);
public Pizza orderPizza(String type){
Pizza pizza = createPizza(type);
System.out.println("--- Making a " + pizza.getName() + " ---");
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
//原料类
class Eggplant implements Veggies{
public String toString(){
return "Eggplant";
}
}
class Garlic implements Veggies{
public String toString(){
return "Garlic";
}
}
class Mushroom implements Veggies{
public String toString(){
return "Mushrooms";
}
}
class Onion implements Veggies{
public String toString(){
return "Onion";
}
}
class RedPepper implements Veggies{
public String toString(){
return "Red Pepper";
}
}
class BlackOlives implements Veggies{
public String toString(){
return "Black Olives";
}
}
class Spinach implements Veggies{
public String toString(){
return "Spinach";
}
}
class FreshClams implements Clams{
public String toString(){
return "Fresh Clams from Long Island Sound";
}
}
class FrozenClams implements Clams{
public String toString(){
return "Frozen Clams from Chesapeake Bay";
}
}
class MarinaraSauce implements Sauce{
public String toString(){
return "Marinara Sauce";
}
}
class PlumTomatoSauce implements Sauce{
public String toString(){
return "Tomato sauce with plum tomatoes";
}
}
class MozzarellaCheese implements Cheese{
public String toString(){
return "Shredded Mozzarella";
}
}
class ParmesanCheese implements Cheese{
public String toString(){
return "Shredded Parmesan";
}
}
class ReggianoCheese implements Cheese{
public String toString(){
return "Reggiano Cheese";
}
}
class SlicedPepperoni implements Pepperoni{
public String toString(){
return "Sliced Pepperoni";
}
}
class ThickCrustDough implements Dough{
public String toString(){
return "ThickCrust style extra thick crust dough";
}
}
class ThinCrustDough implements Dough{
public String toString(){
return "Thin Crust Dough";
}
}
//Pizza类
class CheesePizza extends Pizza{
PizzaIngredientFactory ingredientFactory;
public CheesePizza(PizzaIngredientFactory ingredientFactory){
this.ingredientFactory = ingredientFactory;
}
void prepare() {
System.out.println("Preparing " + name);
dough = ingredientFactory.createDough();
sauce = ingredientFactory.createSauce();
cheese = ingredientFactory.createCheese();
}
}
class ClamPizza extends Pizza{
PizzaIngredientFactory ingredientFactory;
public ClamPizza(PizzaIngredientFactory ingredientFactory){
this.ingredientFactory = ingredientFactory;
}
void prepare() {
System.out.println("Preparing " + name);
dough = ingredientFactory.createDough();
sauce = ingredientFactory.createSauce();
cheese = ingredientFactory.createCheese();
clams = ingredientFactory.createClams();
}
}
class PepperoniPizza extends Pizza{
PizzaIngredientFactory ingredientFactory;
public PepperoniPizza(PizzaIngredientFactory ingredientFactory){
this.ingredientFactory = ingredientFactory;
}
void prepare() {
System.out.println("Preparing " + name);
dough = ingredientFactory.createDough();
sauce = ingredientFactory.createSauce();
cheese = ingredientFactory.createCheese();
veggies = ingredientFactory.createVeggies();
pepperoni = ingredientFactory.createPepperoni();
}
}
class VeggiePizza extends Pizza{
PizzaIngredientFactory ingredientFactory;
public VeggiePizza(PizzaIngredientFactory ingredientFactory){
this.ingredientFactory = ingredientFactory;
}
void prepare() {
System.out.println("Preparing " + name);
dough = ingredientFactory.createDough();
sauce = ingredientFactory.createSauce();
cheese = ingredientFactory.createCheese();
veggies = ingredientFactory.createVeggies();
}
}
//原料工厂类
class ChicagoPizzaIngredientFactory implements PizzaIngredientFactory{
public Dough createDough() {
return new ThickCrustDough();
}
public Sauce createSauce() {
return new PlumTomatoSauce();
}
public Cheese createCheese() {
return new MozzarellaCheese();
}
public Veggies[] createVeggies() {
Veggies[] veggies = {
new BlackOlives(),
new Spinach(),
new Eggplant()
};
return veggies;
}
public Pepperoni createPepperoni() {
return new SlicedPepperoni();
}
public Clams createClams() {
return new FrozenClams();
}
}
class NYPizzaIngredientFactory implements PizzaIngredientFactory{
public Dough createDough() {
return new ThinCrustDough();
}
public Sauce createSauce() {
return new MarinaraSauce();
}
public Cheese createCheese() {
return new ReggianoCheese();
}
public Veggies[] createVeggies() {
Veggies[] veggies = {
new Garlic(),
new Onion(),
new Mushroom(),
new RedPepper()
};
return veggies;
}
public Pepperoni createPepperoni() {
return new SlicedPepperoni();
}
public Clams createClams() {
return new FreshClams();
}
}
//Pizza商店类
class ChicagoPizzaStore extends PizzaStore{
protected Pizza createPizza(String item) {
Pizza pizza = null;
PizzaIngredientFactory ingredientFactory = new ChicagoPizzaIngredientFactory();
if(item.equals("cheese")){
pizza = new CheesePizza(ingredientFactory);
pizza.setName("Chicago Style Cheese Pizza");
}else if(item.equals("veggie")){
pizza = new VeggiePizza(ingredientFactory);
pizza.setName("Chicago Style Veggie Pizza");
}else if(item.equals("clam")){
pizza = new ClamPizza(ingredientFactory);
pizza.setName("Chicago Style Clam Pizza");
}else if(item.equals("pepperoni")){
pizza = new PepperoniPizza(ingredientFactory);
pizza.setName("Chicago Style Pepperoni Pizza");
}
return pizza;
}
}
class NYPizzaStore extends PizzaStore{
protected Pizza createPizza(String item) {
Pizza pizza = null;
PizzaIngredientFactory ingredientFactory = new NYPizzaIngredientFactory();
if(item.equals("cheese")){
pizza = new CheesePizza(ingredientFactory);
pizza.setName("New York Style Cheese Pizza");
}else if(item.equals("veggie")){
pizza = new VeggiePizza(ingredientFactory);
pizza.setName("New York Style Veggie Pizza");
}else if(item.equals("clam")){
pizza = new ClamPizza(ingredientFactory);
pizza.setName("New York Style Clam Pizza");
}else if(item.equals("pepperoni")){
pizza = new PepperoniPizza(ingredientFactory);
pizza.setName("New York Style Pepperoni Pizza");
}
return pizza;
}
}
public class PizzaTestDrive {
public static void main(String[] args){
PizzaStore nyStore = new NYPizzaStore();
PizzaStore chicagoStore = new ChicagoPizzaStore();
Pizza pizza = nyStore.orderPizza("cheese");
System.out.println("Ethan ordered a " + pizza + "\n");
pizza = chicagoStore.orderPizza("cheese");
System.out.println("Joel ordered a " + pizza + "\n");
pizza = nyStore.orderPizza("clam");
System.out.println("Ethan ordered a " + pizza + "\n");
pizza = chicagoStore.orderPizza("clam");
System.out.println("Joel ordered a " + pizza + "\n");
pizza = nyStore.orderPizza("pepperoni");
System.out.println("Ehan ordered a " + pizza + "\n");
pizza = chicagoStore.orderPizza("pepperoni");
System.out.println("Joel ordered a " + pizza + "\n");
pizza = nyStore.orderPizza("veggie");
System.out.println("Ethan ordered a " + pizza + "\n");
pizza = chicagoStore.orderPizza("veggie");
System.out.println("Joel ordered a " + pizza + "\n");
}
}四则运算简单工厂实例
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
114package com.louris.demo;
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
// try{
// Scanner in = new Scanner(System.in);
// System.out.println("Please input number A: ");
// String strNumberA = in.nextLine();
// System.out.println("Please input opreator: ");
// String strOperate = in.nextLine();
// char chOperate = strOperate.charAt(0);
// System.out.println("Please input number B: ");
// String strNumberB = in.nextLine();
// String strResult = "";
// strResult = String.valueOf(Operation.GetResult(Double.parseDouble(strNumberA), Double.parseDouble(strNumberB), chOperate));
// System.out.println("Result: ");
// System.out.println(strResult);
// }catch (Exception e){
// System.out.println("Input error!");
// }
Operation op = OperationFactory.createOperation('/');
op.setNumberA(1);
op.setNumberB(0);
try{
double result = op.getResult();
System.out.println(result);
}catch (Exception e){
System.out.println(e.toString());
}
}
}
class Operation {
// public static double GetResult(double numberA, double numberB, char operate) {
// double result = switch (operate) {
// case '+' -> numberA + numberB;
// case '-' -> numberA - numberB;
// case '*' -> numberA * numberB;
// case '/' -> numberA / numberB;
// default -> 0d;
// };
// return result;
// }
private double _numberA = 0;
private double _numberB = 0;
public void setNumberA(double value){
_numberA = value;
}
public void setNumberB(double value){
_numberB = value;
}
public double getNumberA(){return _numberA;}
public double getNumberB(){return _numberB;}
// Virtual function for override
public double getResult() throws Exception {
double result = 0;
return result;
}
}
class OperationAdd extends Operation{
public double getResult(){
double result = 0;
result = getNumberA() + getNumberB();
return result;
}
}
class OperationSub extends Operation{
public double getResult(){
double result = 0;
result = getNumberA() - getNumberB();
return result;
}
}
class OperationMul extends Operation{
public double getResult(){
double result = 0;
result = getNumberA() * getNumberB();
return result;
}
}
class OperationDiv extends Operation{
public double getResult() throws Exception {
double result = 0;
if(getNumberB() == 0)
throw new Exception("Divisor cannot be zero!");
result = getNumberA() / getNumberB();
return result;
}
}
class OperationFactory{
public static Operation createOperation(char operate){
Operation op = switch (operate) {
case '+' -> new OperationAdd();
case '-' -> new OperationSub();
case '*' -> new OperationMul();
case '/' -> new OperationDiv();
default -> null;
};
return op;
}
}
单例模式
定义:单例模式确保一个类只有一个实例,并提供一个全局访问点。
多线程的选择
(1)如果getInstance()的性能对应用程序不是很关键,就什么都别做
即使用给方法加同步锁的方式,如果应用程序可以接受该方法造成的额外负担,就无需改动。同步getInstance()方法既简单又有效,但是必须知道,同步一个方法可能造成程序执行率下降100倍,因此,如果将getInstance()的程序使用在频繁运行的地方,就得重新考虑其他方式了。
(2)使用“急切”创建实例,而不用延迟实例化的做法
如果应用程序总是创建并使用单例,或者再创建和运行时方面的负担不太繁重,可能需要急切(early)创建次单例,即使用静态初始化的方式。
(3)用“双重检查加锁”,在getInstance()中减少使用同步
利用双重检查加锁(double-checked locking),首先检查是否实例已经创建了,如果尚未创建,“才”进行同步。这样一来,只要第一次会同步,效率更高!
经典单例模式
线程不安全
1 | package SingletonPattern.Classic; |
线程安全单例模式
线程安全,但是每次调用getInstance()方法不管是否已经实例化了都需要同步操作,效率低下!
1 | package SingletonPattern.ThreadSafe; |
静态初始化单例模式
1 | package SingletonPattern.Stat; |
双重检查单例模式
相较于之前的线程安全的给方法加同步锁的方式来说,这样更为高效!
1 | package SingletonPattern.DoubleCheckLocking; |
巧克力工厂锅炉实例
1 | package SingletonPattern.Chocolate; |
命令模式
定义:命令模式将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象,命令模式也支持可撤销的操作。
(1)首先,来看看本章的问题:遥控器有七个插槽,每个插槽都有其对应的开关按钮,并且另外还有一个撤销按钮;而由一堆厂商类,例如Light,CeilingLight,TV,GarageDoor等等,每种类分别有其自己的on(),off()等方法。
(2)一个比较简单而可行的设计思路:遥控器应该知道如何解读按钮被按下的动作,然后发出正确的请求,但是遥控器不需要知道这些家电自动化的细节,或者如何打开热水器等。 —— 总不想在代码中包含一堆if语句判断各个插槽是啥类型,然后相对应操作吧?不利于拓展和维护!
(3)命令模式可以将“动作的请求者”从“动作的执行者”对象中解耦。如果对每个按钮都存储一个命令对象,那么当按钮按下的时候,就可以请命令对象做相关的工作。遥控器并不需要知道工作内容是什么,只要有个命令对象能和正确的对象沟通,把事情做好就可以了。所以,遥控器就和具体的电气类对象解耦了!
(4)创建一个API,将这些命令对象加载到按钮插槽,让遥控器的代码尽量保持简单。而把家电自动化的工作和进行该工作的对象一起封装在命令对象中。
简单命令模式实例
1 | package CommandPattern.SimpleRemote; |
简单命令模式Lambda
1 | package CommandPattern.SimpleRemoteWL; |
餐厅实例
1 | package CommandPattern.Diner; |
餐厅实例Lambda
1 | package CommandPattern.DinerLambda; |
遥控器实例
1 | package CommandPattern.Remote; |
遥控器实例带撤销以及组合命令
1 | package CommandPattern.RemoteWithUndo; |
命令模式的更多用途:
队列请求
日志请求
适配器模式
对象适配器
定义:适配器模式将一个类的接口,转换成客户端期望的另一个接口,适配器让原本接口不兼容的来可以合作无间。
问
1.一个适配器只能够封装一个类吗?
- 适配器模式的工作是将一个接口转换成另一个,虽然大多数的适配器模式所采取的例子都是让一个适配器包装一个被适配者,但我们都知道这个世界其实复杂多了,所以你可能遇到一些状况,需要让一个适配器包装多个被适配者。这设计另一个模式——外观模式(Facade Pattern),人们常常将外观模式和适配器模式混为一谈。
2.万一系统中新旧并存,旧的部分期望旧的厂商接口,但我们却已经使用新厂商的接口编写了这一部分,这时候该怎么办?这里使用适配器,那里却使用未包装的接口,这实在是让人感到混乱。如果我只是固守着旧的代码,完全不要管适配器,这样子会不会好一些?
- 不需要如此,可以创建一个双向的适配器,支持两边的接口。想创建一个双向的适配器,就必须实现所涉及的两个接口,这样,这个适配器可以当做旧的接口,或者当做新的接口使用。
类适配器
类适配器与对象适配器唯一的差别在于适配器继承了Target和Adaptee,而对象适配器利用组合的方式将请求传送给被适配者。
对象适配器与类适配器对比
1.对象适配器使用组合,可以适配某个类及其子类;
2.类适配器只能适配某个特定的被适配类,但其不需要重新实现整个被适配者,必要的时候,也可以覆盖被适配者的行为,因为其利用继承的方式;
适配器模式与装饰者模式区别
(1)适配器模式的工作是将一个接口转换成另一个;
(2)装饰者模式是对包装的对象的行为和责任进行扩展;
1 | // 已存在的、具有特殊功能、但不符合我们既有的标准接口的类 |
Duck与Turkey适配器实例
1 | package AdapterPattern; |
迭代器适配器实例
1 | package AdapterPattern; |
外观模式
定义:外观模式提供了一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统更容易使用。
外观模式与适配器模式的区别
1.外观模式不止简化了接口,也将客户从组件的子系统中解耦;
2.外观和适配器可以包装许多类,但是外观的意图是简化接口,而适配器的意图是将接口转换成不同接口。
家庭影院外观模式实例
1 | package FacadePattern; |
模板方法模式
定义:模板方法模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
饮料冲泡法实例
(1)考虑两种饮料,咖啡和茶,二者冲泡方法:
①咖啡:a.把水煮沸;b.用沸水冲泡咖啡;c.把咖啡倒进被子;d.加糖和牛奶。
②茶:a.把水煮沸;b.用沸水浸泡茶叶;c.把茶倒进被子;d.加柠檬。
(2)可以发现二者有些类似,可以将相同的部分封装成抽象类,其他需要改变的方法通过子类实现;
(3)更进一步,将采用相同的prepare方法:
1 | package TemplateMethod.SimpleBarista; |
模板方法挂钩
钩子是一种被声明在抽象类中的方法,但只有空的或者默认的实现。钩子的存在,可以让子类有能力对算法的不同点进行挂钩。要不要挂钩,由子类自行决定。
1.何时使用抽象方法,何时使用钩子
当子类“必须”提供算法中某个方法或步骤的实现时,就应当使用抽象方法;
如果算法的这个部分是“可选的”,就用钩子。子类可以选择实现这个钩子,但并不强制这么做。
2.使用钩子真正的目的是什么?
①钩子可以让子类实现算法中的可选部分,或者再钩子对于子类的实现并不重要的时候,子类可以对此钩子置之不理。
②钩子的另一个用法,是让子类能够有机会对模板方法中的某些即将发生的(或刚刚发生的)步骤作出反应。(例如在屏幕上重新显示数据)
3. 抽象方法的数目越少越好
让算法内的步骤不要切割得太细,但是如果步骤太少的话,会比较没有弹性,所以要看情况折衷。—— 某些步骤是可选的,也可以将这些步骤实现成钩子,而不是实现成抽象方法,这样就可以让抽象类的子类负荷减轻。
1 | package TemplateMethod.Barista; |
JDK中Sort的模板方法模式
(1)实际的模板方法模式并非总是如同教科书例子一般得中规中矩,为了负荷当前的环境和实现的约束,它们总是要被适当地修改。
(2)与策略模式在某种程度上类似,但在策略模式中,所组合的类实现了整个算法。数组所实现的排序算法并不完整,它需要一个类填补compareTo()方法的实现。因此,认为这更像模板方法。
JDK的Sort源码
分析:
(1)可以看到整个排序采用二路归并排序,不过为了排序稳定以及速度,长度小于等于7的进行插入排序,而后将前后两部分排好序的进行合并(如果已经整体有序又可以直接优化);
(2)针对部分排序,即{fromIdx, toIdx}这一部分进行排序时,只复制了这一范围内的数据作为src,而源数组可能会更长!所以用了off偏置,每次交替src和dest的位置!!所以-off表示也要交替!很巧妙,避免多开空间!。
(3)另外还有通过函数式接口Comparator
1 | /** |
排序实例
1 | package TemplateMethod.sort; |
1 | Before sorting (Ducks): |
迭代器模式
定义:迭代器模式提供了一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。
(1)针对不同的集合(collection)类型所造成的遍历进行同一封装;
(2)迭代器模式让我们能游走于聚合内的每一个元素,而又不暴露其内部的表示;
(3)游走的任务放在迭代器上,而不是聚合上。这样简化了聚合的接口和实现,也让责任各得其所。
(3)可以使得类与遍历解耦,可以遍历各种所需的对象组。
菜单Implicit
1 | package IteratorPattern.implicit; |
菜单Iterator实例
1 | package IteratorPattern.dinermerger; |
组合模式
定义:组合模式允许将对象组合成树形结构来表现“整体/部分”层次结构。组合能让客户以一致的方式处理个别对象以及对象组合。
(1)以一致的方式处理 个别对象 以及 对象组合;
(2)大多数情况下,可以忽略对象组合和个别对象之间的差别;
(3)组合模式以单一责任原则换取透明性(transparency),透明性是通过让组件的接口同时包含一些管理子节点和叶节点的操作,客户就可以将组合和叶节点一视同仁。也就是说,一个元素究竟是组合还是叶节点,对客户是透明的!
(4)客户有机会对一个元素做一些不恰当或没有意义的操作,失去了一些“安全性”;
(5)组件可以有一个指向父亲的指针,以便在游走时更容易;
(6)孩子的次序:万一需要保持特定孩子次序的组合对象,就需要使用更复杂的管理方案来进行孩子的增加和删除,而且当你在这个层次结构内游走时,应该要更加小心;
(7)缓存(caching):如果组合结构很复杂的,或者遍历的代价太高,那么实现组合节点的缓存就很有帮助。比方说,如果你要不断地遍历一个组合,而且它的每一个子节点都需要进行某些计算,那你就应该使用缓存来临时保存结果,省去遍历的开支。
(8)让客户更简单。客户不再需要操心面对的是组合对象还是叶节点对象了。也就不需要写一大堆if语句来保证他们对正确的对象调用了正确的方法。通常,他们只需要对整个结构调用一个方法并执行操作就可以了。
菜单实例
(1)print()方法递归调用!
(2)叶节点和子节点对用户来讲透明,用户不知道具体细节,保证了安全性。
1 | package CompositePattern.menu; |
菜单实例+迭代器
空迭代器:
对于菜单项内没什么可以遍历的,有两种选择:
(1)返回null:可以让createIterator()返回null,如果这么做,需要条件判断语句;
(2)返回一个空迭代器,而这个迭代器的hasNext()永远返回false:这似乎是更好的方案。我们依然可以返回一个迭代器,客户不用再担心返回值是否为null。即创建了一个迭代器,作用是“没作用”,可以是代码统一。
1 | package CompositePattern.MenuIterator; |
状态模式
定义:状态模式允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。
状态模式与策略模式的区别
- 以状态模式而言,其将一群行为封装在状态对象中,context的行为随时可委托到那些状态对象中的一个。随着时间的流逝,当前状态在状态对象集合中游走改变,以反映出context内部的状态,因此context的行为也会跟着改变,但是context的客户对于状态对象了解不多,甚至根本是浑然不觉。
- 以策略模式而言,客户通常主动指定Context所要组合的策略对象是哪一个。现在,固然策略模式让我们具有弹性,能够在运行时改变策略,但对某各context对象来说,通常都只有一个最适当的策略对象。
GumballMachine实例
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
140package State.gumball;
class GumballMachine {
final static int SOLD_OUT = 0;
final static int NO_QUARTER = 1;
final static int HAS_QUARTER = 2;
final static int SOLD = 3;
int state = SOLD_OUT;
int count = 0;
public GumballMachine(int count) {
this.count = count;
if (count > 0) {
state = NO_QUARTER;
}
}
public void insertQuarter() {
if (state == HAS_QUARTER) {
System.out.println("You can't insert another quarter");
} else if (state == NO_QUARTER) {
state = HAS_QUARTER;
System.out.println("You inserted a quarter");
}else if (state == SOLD_OUT){
System.out.println("You can't insert a quarter, the machine is sold out");
}else if(state == SOLD){
System.out.println("Please wait, we're already giving you a gumball");
}
}
public void ejectQuarter(){
if(state == HAS_QUARTER){
System.out.println("Quarter returned");
state = NO_QUARTER;
}else if(state == NO_QUARTER){
System.out.println("You haven't inserted a quarter");
}else if(state == SOLD){
System.out.println("Sorry, you already turned the crank");
}else if(state == SOLD_OUT){
System.out.println("You can't eject, you haven't inserted a quarter yet");
}
}
public void turnCrank(){
if(state == SOLD){
System.out.println("Turning twice doesn't get you another gumball!");
}else if(state == NO_QUARTER){
System.out.println("You turned but there's no quarter");
}else if(state == SOLD_OUT){
System.out.println("You turned, but there are no gumballs");
}else if(state == HAS_QUARTER){
System.out.println("You turned...");
state = SOLD;
dispense();
}
}
public void dispense(){
if(state == SOLD){
System.out.println("A gumball comes rolling out the slot");
count = count - 1;
if(count==0){
System.out.println("Oops, out of gumballs");
state = SOLD_OUT;
}else{
state = NO_QUARTER;
}
}else if(state == NO_QUARTER){
System.out.println("You need to pay first");
}else if(state == SOLD_OUT){
System.out.println("No gumball dispensed");
}else if(state == HAS_QUARTER){
System.out.println("No gumball dispensed");
}
}
public void refill(int numGumBalls){
this.count = numGumBalls;
state = NO_QUARTER;
}
public String toString(){
StringBuffer result = new StringBuffer();
result.append("\nMighty Gumball, Inc.");
result.append("\nJava-enabled Standing Gumball Model #2004\n");
result.append("Inventory: " + count + " gumball");
if(count != 1){
result.append("s");
}
result.append("\nMachine is ");
if(state == SOLD_OUT){
result.append("sold out");
}else if(state == NO_QUARTER){
result.append("waiting for quarter");
}else if(state == HAS_QUARTER){
result.append("waiting for turn of crank");
}else if(state == SOLD){
result.append("delivering a gumball");
}
result.append("\n");
return result.toString();
}
}
public class GumballMachineTestDrive {
public static void main(String[] args){
GumballMachine gumballMachine = new GumballMachine(5);
System.out.println(gumballMachine);
gumballMachine.insertQuarter();
gumballMachine.turnCrank();
System.out.println(gumballMachine);
gumballMachine.insertQuarter();
gumballMachine.ejectQuarter();
gumballMachine.turnCrank();
System.out.println(gumballMachine);
gumballMachine.insertQuarter();
gumballMachine.turnCrank();
gumballMachine.insertQuarter();
gumballMachine.turnCrank();
gumballMachine.ejectQuarter();
System.out.println(gumballMachine);
gumballMachine.insertQuarter();
gumballMachine.insertQuarter();
gumballMachine.turnCrank();
gumballMachine.insertQuarter();
gumballMachine.turnCrank();
gumballMachine.insertQuarter();
gumballMachine.turnCrank();
System.out.println(gumballMachine);
}
}GumballMachineState状态模式实例
设计原则
- 首先,我们定义了一个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
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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339package State.gumballState;
import java.awt.*;
import java.util.Random;
interface State{
void insertQuarter();
void ejectQuarter();
void turnCrank();
void dispense();
void refill();
}
class GumballMachine{
State soldOutState;
State noQuarterState;
State hasQuarterState;
State soldState;
State winnerState;
State state;
int count = 0;
public GumballMachine(int numberGumballs){
soldOutState = new SoldOutState(this);
noQuarterState = new NoQuarterState(this);
hasQuarterState = new HasQuarterState(this);
soldState = new SoldState(this);
winnerState = new WinnerState(this);
this.count = numberGumballs;
if(numberGumballs > 0){
state = noQuarterState;
}else{
state = soldOutState;
}
}
public void insertQuarter(){
state.insertQuarter();
}
public void ejectQuarter(){
state.ejectQuarter();
}
public void turnCrank(){
state.turnCrank();
state.dispense();
}
public void setState(State state){
this.state = state;
}
public State getState(){
return state;
}
public State getSoldOutState(){
return soldOutState;
}
public State getWinnerState(){
return winnerState;
}
public void releaseBall(){
System.out.println("A gumball comes rolling out the slot...");
if(count>0){
count -= 1;
}
}
public int getCount(){return count;}
public void refill(int count){
this.count += count;
System.out.println("The gumball machine was just refilled; its new count is: " + this.count);
state.refill();
}
public State getNoQuarterState(){
return noQuarterState;
}
public State getHasQuarterState(){
return hasQuarterState;
}
public State getSoldState(){
return soldState;
}
public String toString(){
StringBuffer result = new StringBuffer();
result.append("\nMighty Gumball, Inc.");
result.append("\nJava-enabled Standing Gumball Model #2004");
result.append("\nInventory: " + count + " gumball");
if(count != 1){
result.append("s");
}
result.append("\n");
result.append("Machine is " + state + "\n");
return result.toString();
}
}
class HasQuarterState implements State{
GumballMachine gumballMachine;
Random randomWinner = new Random(System.currentTimeMillis());
public HasQuarterState(GumballMachine gumballMachine){
this.gumballMachine = gumballMachine;
}
public void insertQuarter(){
System.out.println("You can't insert another quarter");
}
public void ejectQuarter(){
System.out.println("Quarter returned");
gumballMachine.setState(gumballMachine.getNoQuarterState());
}
public void turnCrank(){
System.out.println("You turned...");
//gumballMachine.setState(gumballMachine.getSoldState());
int winner = randomWinner.nextInt(10);
if((winner == 0) && (gumballMachine.getCount() > 1)){
gumballMachine.setState(gumballMachine.getWinnerState());
}else{
gumballMachine.setState(gumballMachine.getSoldState());
}
}
public void dispense(){
System.out.println("No gumball dispensed");
}
public void refill(){
}
public String toString(){
return "waiting for turn of crank";
}
}
class NoQuarterState implements State{
GumballMachine gumballMachine;
public NoQuarterState(GumballMachine gumballMachine){
this.gumballMachine = gumballMachine;
}
public void insertQuarter(){
System.out.println("You inserted a quarter");
gumballMachine.setState(gumballMachine.getHasQuarterState());
}
public void ejectQuarter() {
System.out.println("You haven't inserted a quarter");
}
public void turnCrank() {
System.out.println("You turned, but there's no quarter");
}
public void dispense() {
System.out.println("You turned, but there's no quarter");
}
public void refill() {
}
public String toString(){
return "waiting for quarter";
}
}
class SoldOutState implements State{
GumballMachine gumballMachine;
public SoldOutState(GumballMachine gumballMachine){
this.gumballMachine = gumballMachine;
}
public void insertQuarter(){
System.out.println("You can't insert a quarter, the machine is sold out");
}
public void ejectQuarter(){
System.out.println("You can't eject, you haven't inserted a quarter yet");
}
public void turnCrank(){
System.out.println("You turned, but there are no gumballs");
}
public void dispense(){
System.out.println("No gumball dispensed");
}
public void refill(){
gumballMachine.setState(gumballMachine.getNoQuarterState());
}
public String toString(){
return "sold out";
}
}
class SoldState implements State{
GumballMachine gumballMachine;
public SoldState(GumballMachine gumballMachine){
this.gumballMachine = gumballMachine;
}
public void insertQuarter(){
System.out.println("Please wait, we're already giving you a gumball");
}
public void ejectQuarter(){
System.out.println("Sorry, you already turned the crank");
}
public void turnCrank(){
System.out.println("Turning twice doesn't get you another gumball!");
}
public void dispense(){
gumballMachine.releaseBall();
if(gumballMachine.getCount()>0){
gumballMachine.setState(gumballMachine.getNoQuarterState());
}else{
System.out.println("Oops, out of gumballs!You can eject quarter!");
//gumballMachine.setState(gumballMachine.getSoldOutState()); //吞金兽?
gumballMachine.setState(gumballMachine.getHasQuarterState());
}
}
public void refill() {
}
public String toString(){
return "dispensing a gumball";
}
}
class WinnerState implements State{
GumballMachine gumballMachine;
public WinnerState(GumballMachine gumballMachine){
this.gumballMachine = gumballMachine;
}
public void insertQuarter(){
System.out.println("Please wait, we're already giving you a Gumball");
}
public void ejectQuarter(){
System.out.println("Please wait, we're already giving you a Gumball");
}
public void turnCrank(){
System.out.println("Turning again doesn't get you another gumball!");
}
public void dispense(){
gumballMachine.releaseBall();
if(gumballMachine.getCount() == 0){
gumballMachine.setState(gumballMachine.getSoldOutState());
}else{
gumballMachine.releaseBall();
System.out.println("YOU'RE A WINNER! You got two gumballs for your quarter");
if(gumballMachine.getCount() > 0){
gumballMachine.setState(gumballMachine.getNoQuarterState());
}else{
System.out.println("Oops, out of gumballs!");
gumballMachine.setState(gumballMachine.getSoldOutState());
}
}
}
public void refill(){}
public String toString(){
return "despensing two gumballs for your quarter, because YOU'RE A WINNER!";
}
}
public class GumballTestDrive {
public static void main(String[] args){
GumballMachine gumballMachine = new GumballMachine(10);
System.out.println(gumballMachine);
gumballMachine.insertQuarter();
gumballMachine.turnCrank();
System.out.println(gumballMachine);
gumballMachine.insertQuarter();
gumballMachine.turnCrank();
gumballMachine.insertQuarter();
gumballMachine.turnCrank();
System.out.println(gumballMachine);
gumballMachine.insertQuarter();
gumballMachine.turnCrank();
gumballMachine.insertQuarter();
gumballMachine.turnCrank();
System.out.println(gumballMachine);
gumballMachine.insertQuarter();
gumballMachine.turnCrank();
gumballMachine.insertQuarter();
gumballMachine.turnCrank();
System.out.println(gumballMachine);
gumballMachine.refill(5);
gumballMachine.insertQuarter();
gumballMachine.turnCrank();
System.out.println(gumballMachine);
}
}代理模式
定义:代理模式为另一个对象提供一个替身或占位符以控制对这个对象的访问。
代理与装饰者模式的区别:
(1)装饰者为对象增加行为,而代理是控制对象的访问。
(2)代理将客户从具体对象中解耦,比如ImageProxy中,如果客户没有从ImageIcon中解耦,那么客户就必须等到每幅图像都被取回,然后才能把它绘制在界面上。
(3)代理代表对象,不光是为对象加上动作。客户使用我作为主题的替身,因为我可以保护对象避免不想要的访问,也可以避免在加载大对象的过程中GUI会挂起,或者隐藏主题在远程运行的事实。
(4)虚拟代理中,当客户第一次用代理的时候,主题甚至还根本不存在,所以此时并没有装饰者模式的包装对象的概念。
代理分类
(1)防火墙代理(Firewall Proxy):控制网络资源的访问,保护主题免于“坏客户”的侵害;
(2)智能引用代理(Smart Reference Proxy):当主题被引用时,进行额外的动作,例如计算一个对象被引用的次数;
(3)缓存代理(Caching Proxy):为开销大的运算结果提供暂时存储:它也允许多个客户共享结果,以减少计算或网络延迟;
(4)同步代理(Synchronization Proxy):在多线程的情况下为主题提供安全的访问;
(5)复杂隐藏代理(Complexity Hiding Proxy):用来隐藏一个类的复杂集合的复杂度,并进行访问控制。有时候也称为外观代理(Facade Proxy),这不难理解。复杂隐藏代理和外观模式是不一样的,因为代理控制访问,而外观模式只提供另一组接口;
(6)写入时复制代理(Copy-On-Write-Proxy):用来控制对象的复制,方法是延迟对象的复制,直到客户真的需要为止。这是虚拟代理的变体。
远程代理
GumballMachineRemote
1 | package ProxyPattern.Gumball; |
GumballMachine
1 | package ProxyPattern.Gumball; |
GumballMonitor
1 | package ProxyPattern.Gumball; |
State
1 | package ProxyPattern.Gumball; |
HasQuarterState
1 | package ProxyPattern.Gumball; |
NoQuarterState
1 | package ProxyPattern.Gumball; |
SoldOutState
1 | package ProxyPattern.Gumball; |
SoldState
1 | package ProxyPattern.Gumball; |
WinnerState
1 | package ProxyPattern.Gumball; |
GumballMonitorTestDrive
1 | package ProxyPattern.Gumball; |
虚拟代理
ImageComponent
1 | package ProxyPattern.VirtualProxy; |
ImageProxy
1 | package ProxyPattern.VirtualProxy; |
ImageProxyTestDrive
1 | package ProxyPattern.VirtualProxy; |
Java代理
Person
1 | package ProxyPattern.JavaProxy; |
PersonImpl
1 | package ProxyPattern.JavaProxy; |
OwnerInvocationHandler
1 | package ProxyPattern.JavaProxy; |
NonOwnerInvocationHandler
1 | package ProxyPattern.JavaProxy; |
MatchMakingTestDrive
1 | package ProxyPattern.JavaProxy; |
复合模式
定义:模式通常被一起使用,并被组合在同一个设计解决方案中。复合模式在一个解决方案中结合两个或多个模式,以解决一般或重复发生的问题。
模型携手工作
1 | package CombinedPattern; |
1 | Duck Simulator: With Observer |
MVC(模型-视图-控制器)
Model(模型)—— 代表一个存取数据的对象或JAVA POJO。它也可以带有逻辑,在数据变化时更新控制器;
View(视图)—— 代表模型包含的数据的可视化;
Controller(控制器)—— 作用于模型和视图上。它控制数据流向模型对象,并在数据变化时更新视图。它使视图与模型分离开。
1 | package CombinedPattern; |