java 设计模式

java设计模式

1、设计模式中常用的七大原则

  1. 单一职责原则
  2. 接口隔离原则
  3. 依赖倒转(倒置)原则
  4. 里氏替换原则
  5. 开闭原则 ocp
  6. 迪米特法则(最少知道原则)
  7. 合成复用原则(组合优于继承原则)

设计模式的原则

  • 代码的重用性(相同功能的代码不用多次编写)
  • 可读性(编程的规范性)
  • 可扩展性(当需要增加新的功能时,非常的方便,称为可维护)
  • 可靠性(当我们增加新的功能后对原来的功能没有影响)
  • 使程序呈现高内聚,低耦合的特性

学习设计模式,脑袋里要始终绷紧两根弦:

  1. 开发代码的程序员,被分为两种角色:

    作者(服务端程序员)

    用户(客户端程序员)

  2. 我们手头并不会时时刻刻都拥有作者的源码,就算有源码也不能修改,因为要符合开闭原则。

1.1、单一职责原则

每个方法、每个类、每个框架都只负责一件事情.
字符流:Reader默认查询的码表是与操作系统一致的码表,我们的操作系统是中文的,所以Reader就会使用GBK码表,而GBK码表一个汉字占两个字节,且汉字的两个字节都是以1开头的。
读取步骤:先读取到记事本中的数字45489 —> gbk —> 北 —> unicode —> 21271

优势:

  1. 代码的重用性提高了
  2. 代码的可读性提高了,此时的代码就像一个大纲一样
  3. 推荐:方法的语句在同一个抽象层级之上

1.2、开闭原则

  • 对扩展新功能开放的
  • 对修改原有功能是关闭的

比如:有一个刮胡刀,刮胡刀的作用就是刮胡子,现在想让刮胡刀具备吹风机的能力。

  1. 违反开闭原则的做法是:把吹风机的功能加上了,可是不能刮胡子了。
  2. 符合开闭原则的做法是:把吹风的功能加上了,且没有影响以前的刮胡子功能。

补充:

  • 如果一个类,从头到尾都是自己写的,那么可以随时随地修改源码,因为作者是自己。
  • 如果一个类 ,作者不是自己,就不能修改代码,因为要符合开闭原则。

1.3、接口隔离原则

客户端不应该依赖它不需要的接口,一个类对另一个类的依赖应该建立在最小的接口上。

使用多个专门的接口比使用单一的总接口要好。

IUserDao 只负责 user 表的增删改查

一个类对另外一个类的依赖性应当是建立在最小的接口上的。

1.4、依赖倒置原则

上层不能依赖于下层,它们都应该依赖于抽象。

举例:dao,service通过dao接口+dao工厂来访问下层的dao实现的。在以后扩展dao实现的时候上层service是不用改动的。

程序要依赖于抽象接口,不要依赖于具体实现,要求对抽象进行编程,不要对实现进行编程,降低客户与实现模块间的耦合。

  • 上层:调用别的方法的就是上层,被其它方法调用的就是下层。

类与类之间的关系

依赖倒置

类的实例化顺序:

  1. 先执行静态块(在整个运行期间只执行一次)
  2. 执行构造代码块
  3. 执行构造器
  • 构造代码块总是随着构造器的执行而执行,且先执行构造代码块
  • 类中的实例成员等价于构造代码块,也就是说构造代码块执行的时候,也就会执行实例成员的代码。
  • 类中的静态成员等价于静态块,也就是说,静态块执行的时候,也就会执行静态成员代码。

加载类的四种可能:

  1. 调构造器
  2. 调 class.forName
  3. 调静态属性
  4. 调静态方法

1.5、迪米特法则(最少知道原则):封装

一个类,对于其它类要知道的越深越好,对实现细节隐藏

只和朋友通信

什么是朋友?

a. 类中的字段

b. 方法的参数

c. 方法的返回值

d. 方法中实例化出来的对象

1.6、里氏替换原则

任何能使用父类对象的地方,都应该透明的替换为子类对象

也就是说:子类对象可以随时随地替换父类对象,且替换完以后,语法不会报错,业务逻辑也不会出现问题。

1
List<String> list = new ArrayList<>();

方法重写:在子类和父类中,出现了返回类型相同、方法名相同、方法参数相同的方法时,构成方法重写。

方法重写的两个限制:

  1. 子类重写父类的方法时,子类方法的访问修饰符不能比父类的更严格。
  2. 子类重写父类的方法时,子类方法不能比父类抛出更多的异常。

保证子类对象在替换父类对象时,语法不会报错。符合里氏替换原则

继承的作用:

  1. 提高代码的复用性
  2. 多态的前提

实例:正方形不是长方形、鸵鸟非鸟

除了判定 is a 的关系还有在业务逻辑上子类是否能完全替代父类。

1.7、组合优于继承

如果只是为了重用代码优先考虑组合

类与类之间的关系:

  1. 继承

  2. 依赖:一个类的对象作为另一个类的局部变量

  3. 关联:一个类的对象作为另一个类的字段

    关联可细分为:

    • 组合:关系强,鸟和翅膀的关系
    • 聚合:关系弱,大雁与雁群的关系

组合优于继承中的组合即是指关联关系

继承重写方法有改变父类调用结构的风险

如果父类作者和子类作者不是用一个人就别继承

父类的作者不知道未来的子类会重写自己的哪个方法

子类作者不知道未来的父类会添加什么新方法

自己写代码,继承、重写随便使用。

重点是:不要修改别人的代码,包括继承去重写别人的方法

2、设计模式

2.1、简单工厂模式

无需提供具体的子类类名,只需要提供一个字符串即可得到相应的实例对象,当子类的类名更换或者增加子类时我们都无需修改客户端代码,只需要在简单工厂类上增加一个分支判断代码即可。

img

  1. 产品

  2. 抽象产品

    • 抽象类、接口
  3. 产品簇

    • 多个有内在联系,或者是有逻辑关系的产品
  4. 产品等级

简单工厂:

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
//抽象产品
interface Food {
void eat();
}
//具体产品
class Hamburger implements Food {
@Override
public void eat() {
System.out.println("吃汉堡包");
}
}
//具体产品
class RiceNoddle implements Food {
@Override
public void eat() {
System.out.println("吃米线");
}
}
//简单工厂,作者只需要修改switch即可,服务端代码的变动不会对客户端的使用有任何影响,只要继续保证 1 对应 Hamburger 2 对应 RiceNoddle
class FoodFactory {
static Food food = null;
static Food getFood(int n) {
switch (n) {
case 1:
food = new Hamburger();
break;
case 2:
food = new RiceNoddle();
break;
}
return food;
}
}

public class AppTest {
public static void main(String[] args) {
Food food = FoodFactory.getFood(1);
food.eat();
}
}

优点:

  1. 把具体的产品类型从客户端代码中解耦出来。

  2. 服务器端,如果修改了具体产品的类名,客户端也不知道。

    这便符合了”面向接口编程“的思想

缺点:

  1. 客户端不得不死记硬背那些常量与具体产品的映射关系。
  2. 如果具体产品特别多,则简单工厂将会变得十分臃肿。
  3. 最重要的是,客户端需要扩展具体产品时,势必要修改简单工厂中的代码,这样就违反了”开闭原则“

简单工厂UML

简单工厂UML图

写代码需要考虑: 重用性、扩展性、能不能随意替换

2.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
//===========================服务端=====================================
//抽象产品
interface Food {
void eat();
}

//具体产品
class Hamburger implements Food {
@Override
public void eat() {
System.out.println("吃汉堡包");
}
}

//具体产品
class RiceNoddle implements Food {
@Override
public void eat() {
System.out.println("吃米线");
}
}

interface FoodFactory {
Food getFood();
}

class HamburgerFactory implements FoodFactory {

@Override
public Food getFood() {
return new Hamburger();
}
}

class RiceNoddleFactory implements FoodFactory {

@Override
public Food getFood() {
return new RiceNoddle();
}
}

//业务逻辑
class Business {
public static void taste(FoodFactory ff) {
Food food = ff.getFood();
System.out.println("评委1,品尝");
food.eat();
System.out.println("评委2,品尝");
food.eat();
System.out.println("评委3,品尝");
food.eat();
}
}

//==============================客户端=====================================
//满足可扩展性
class Noddle implements Food {

@Override
public void eat() {
System.out.println("吃面条");
}
}

class NoddleFactory implements FoodFactory {

@Override
public Food getFood() {
return new Noddle();
}
}

public class AppTest {
public static void main(String[] args) {
HamburgerFactory hamburgerFactory = new HamburgerFactory();
// Food food = hamburgerFactory.getFood();
// food.eat();
Business.taste(hamburgerFactory);
//自己扩展的业务也可以使用服务端的业务逻辑
NoddleFactory noddleFactory = new NoddleFactory();
Business.taste(noddleFactory);
}
}

优点:

  1. 仍然具有简单工厂的优点:服务器端修改了具体的产品的类名以后,客户端并不知道(最少知道原则)
  2. 当客户端需要扩展一个新的产品时,不需要修改作者原来的代码,只是扩展一个新的工厂而已。
  3. 作者有责任保证工厂名尽可能稳定。

缺点:

​ 如果有多个产品等级,那么工厂类的数量就会爆炸式的增长。

工厂方法UML

2.3、抽象工厂模式

抽象工厂中,可以生产多个产品,这多个产品之间,必须有内在联系。

同一个工厂中的产品都属于一个产品簇! 不能把不同产品簇中的产品混合到一个抽象工厂的实现类中。

面向对象三大特点:

  • 封装
    • 对外隐藏复杂的实现细节,暴露出简单的实现方法
    • 隔离变化(实现接口的方法)
    • 提高代码的重用性
    • 保护数据
  • 继承
    • 提高代码的重用性。(如果仅仅是为了重用,可以优先考虑组合。)
    • 多态的前提
  • 多态
    • 多态的前提:继承
    • 多态的作用:提高代码的扩展性(dao、jdbc)
    • 多态的体现:向上转型
    • 多态的限制:向上转型发生时,子类独有的成员无法使用
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 Food {
void eat();
}

//具体产品
class Hamburger implements Food {
@Override
public void eat() {
System.out.println("吃汉堡包");
}
}

//具体产品
class RiceNoddle implements Food {
@Override
public void eat() {
System.out.println("吃米线");
}
}

//抽象产品
interface Drink{
void drink();
}

//具体产品
class Cola implements Drink{
@Override
public void drink() {
System.out.println("可口可乐,你值得拥有");
}
}

class IcePeak implements Drink{
@Override
public void drink() {
System.out.println("从小就喝冰峰");
}
}
// 抽象工厂,名字不能具体,应为它不仅仅生产食物,也生产饮料
interface Factory {
Food getFood();
Drink getDrink();
}

class KFCFactory implements Factory {
@Override
public Food getFood() {
return new Hamburger();
}
@Override
public Drink getDrink() {
return new Cola();
}

}

class SanQinFactory implements Factory {
@Override
public Food getFood() {
return new RiceNoddle();
}
@Override
public Drink getDrink() {
return new IcePeak();
}

}

//业务逻辑
class Business {
public static void taste(Factory ff) {
Food food = ff.getFood();
Drink drink = ff.getDrink();
System.out.println("评委1,品尝");
food.eat();
drink.drink();

Food food2 = ff.getFood();
Drink drink2 = ff.getDrink();
System.out.println("评委2,品尝");
food2.eat();
drink2.drink();

Food food3 = ff.getFood();
Drink drink3 = ff.getDrink();
System.out.println("评委3,品尝");
food3.eat();
drink3.drink();
}
}

//==============================客户端=====================================
//满足可扩展性
class LP implements Food {
@Override
public void eat() {
System.out.println("吃凉皮");
}
}
class Fenta implements Drink{
@Override
public void drink() {
System.out.println("芬达真好喝");
}
}

class BaoJiFactory implements Factory {

@Override
public Food getFood() {
return new LP();
}
@Override
public Drink getDrink() {
return new Fenta();
}
}

public class AppTest {
public static void main(String[] args) {
Business.taste(new KFCFactory);
//自己扩展的业务也可以使用服务端的业务逻辑
NoddleFactory noddleFactory = new NoddleFactory();
Business.taste(noddleFactory);
}
}

抽象工厂UML

优点:

  1. 仍然有简单工厂和工厂方法的优点
  2. 更重要的是,抽象工厂把工厂类的数量减少了! 无论有多少个产品等级,工厂就一套。

缺点:

  1. 当产品等级发生变化时(增加产品等级、删除产品等级),都要引起所有以前工厂代码的修改。这就违反了”开闭原则“。

结论:

​ 当产品等级比较固定时,可以考虑使用抽象工厂。

​ 如果产品等级经常变化则不建议使用抽象工厂。

如果产品不扩充,简单工厂最好、如果产品经常需要扩展,则使用工厂方法、如果产品等级很多就用抽象工厂。

2.4、原型模式

用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

  1. 必须让目标实现 Cloneable 接口,该接口中没有任何抽象方法。这样的接口仅仅是一个”标记接口“,作用是告诉 jvm ,任何实现了该 Cloneable 接口的类的对象,可以被克隆。
  2. 必须重写 java.lang.Object 的 clone 方法,一定要把该方法的访问修饰符重写为 public 不然无法调用 clone 方法。
    • clone 方不会引起构造器的调用,clone 方法是直接复制内存中的2进制,效率更高。
    • 克隆出的对象和原先的对象地址不一致,是两个不同空间中的对象。
  3. 浅拷贝:把原来对象的二进制原样复制,导致原来对象里面包含的 引用字段对象的地址 也一块复制,原来对象和克隆对象中 引用字段对象的地址 还是 一样 的!
  4. 深拷贝:把对象中的引用字段对象也拷贝
    1. 引用字段对象较少,层级较浅时,可以在对象的 clone 方法中,将对象的应用字段对象再 clone。
    2. 使用序列化的方式,将对象序列化到内存流中再重写读取(ByteArrayOutputStream、ByteArrayInputStream),此时会重新开辟新的存储空间接收对象。

java 创建对象的四种方式:

  1. new
  2. 反射
  3. 克隆
  4. 序列化

2.5、建造者模式

工厂模式,都是直接实例化出一个类的对象即可

建造者模式,是在实例化出类的对象之后,还要给该对象的属性赋值

将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
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
class Computer {
private String name;
private String cpu;
private String gpu;
private String memory;

@Override
public String toString() {
return "Computer{" +
"name='" + name + '\'' +
", cpu='" + cpu + '\'' +
", gpu='" + gpu + '\'' +
", memory='" + memory + '\'' +
", Hd='" + Hd + '\'' +
'}';
}

private String Hd;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getCpu() {
return cpu;
}

public void setCpu(String cpu) {
this.cpu = cpu;
}

public String getGpu() {
return gpu;
}

public void setGpu(String gpu) {
this.gpu = gpu;
}

public String getMemory() {
return memory;
}

public void setMemory(String memory) {
this.memory = memory;
}

public String getHd() {
return Hd;
}

public void setHd(String hd) {
Hd = hd;
}
}

//固定建造的流程
interface ComputerBuilder {
public void setName();

public void setCpu();

public void setGpu();

public void setMemory();

public void setHd();

Computer builder();
}

class MiddleComputerBuilder implements ComputerBuilder {

private final Computer computer = new Computer();

@Override
public void setName() {
computer.setName("联想");
}

@Override
public void setCpu() {
computer.setCpu("i7 7000hq");
}

@Override
public void setGpu() {
computer.setGpu("gtx1060");
}

@Override
public void setMemory() {
computer.setMemory("32g");
}

@Override
public void setHd() {
computer.setHd("250g固态 + 1T 机械");
}

@Override
public Computer builder() {
return computer;
}
}

class AdvanceComputerBuilder implements ComputerBuilder {

private final Computer computer = new Computer();

@Override
public void setName() {
computer.setName("外星人");
}

@Override
public void setCpu() {
computer.setCpu("i9 7000hq");
}

@Override
public void setGpu() {
computer.setGpu("gtx1080T");
}

@Override
public void setMemory() {
computer.setMemory("64g");
}

@Override
public void setHd() {
computer.setHd("2T固态");
}

@Override
public Computer builder() {
return computer;
}
}

//直接封装 指挥者
class Director{
public Computer build(ComputerBuilder cb){
cb.setName();
cb.setCpu();
cb.setGpu();
cb.setMemory();
cb.setHd();
return cb.builder();
}
}
public class BuilderPatterns {
public static void main(String[] args) {
MiddleComputerBuilder middleComputerBuilder = new MiddleComputerBuilder();
Computer builder = new Director().build(middleComputerBuilder);

AdvanceComputerBuilder advanceComputerBuilder = new AdvanceComputerBuilder();
Computer build = new Director().build(advanceComputerBuilder);

System.out.println(builder);
System.out.println(build);
}
}

优点:

  1. 创建对象的过程稳定不变(因为有 ComputerBuilder 接口来稳定过程)
  2. 创建对象的过程只写了一次,没有重复代码(指挥者完成)
  3. 当需要扩展指挥者的时候,不用修改之前的代码,这符合了开闭原则

建造者模式UML

2.6、装饰器设计模式

允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。

这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。

jdk中的流就是典型的装饰器模式

java.io流装饰器模式

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
/**
* @Desc 装饰器模式
* 业务场景:星巴克卖咖啡,一开始只有四种咖啡:
* Decaf Espresso DarkRoast HouseBlend
*/

/**
* 所有咖啡都有共性,所以开发人员把它们的共性提到一个父类中:Beverage
*/
abstract class Beverage {
private String description;
public Beverage(String description) {
this.description = description;
}
public abstract double cost();
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}

/**
* 判断两个类之间能不能有继承关系,主要看这两个类之间有没有 “is a” 关系,并且还要符合里氏替换原则
* 以上只是原则,不是语法强制,也就是说,在特定的情况下,可以违反这个规则,比如在装饰器模式中就是这样
* 尽管调料不是饮料,但是为了制作出装饰器模式,只能让调料去继承饮料
*/
abstract class Condiment extends Beverage{
//让调料类关联饮料类
protected Beverage beverage;
public Condiment(Beverage beverage) {
super("调料");
this.beverage = beverage;
}
}
//=======================饮料
class Decaf extends Beverage {
public Decaf() {
super("无咖啡因咖啡");
}
@Override
public double cost() {
return 1;
}
}

class Espresso extends Beverage {
public Espresso() {
super("浓缩咖啡");
}
@Override
public double cost() {
return 2;
}
}
class HouseBlend extends Beverage {
public HouseBlend() {
super("混合咖啡");
}
@Override
public double cost() {
return 3;
}
}

class DarkRoast extends Beverage {
public DarkRoast() {
super("焦炒咖啡");
}
@Override
public double cost() {
return 1.5;
}
}
//====================调料
class Milk extends Condiment{
public Milk(Beverage beverage) {
super(beverage);
}
@Override
public double cost() {
return beverage.cost() + 0.2;
}
@Override
public String getDescription() {
return beverage.getDescription() + " 牛奶";
}
}
class Soy extends Condiment{
public Soy(Beverage beverage) {
super(beverage);
}
@Override
public double cost() {
return beverage.cost() + 0.3;
}
@Override
public String getDescription() {
return beverage.getDescription() + " 豆浆";
}
}
class MoKa extends Condiment{
public MoKa(Beverage beverage) {
super(beverage);
}
@Override
public double cost() {
return beverage.cost() + 0.4;
}
@Override
public String getDescription() {
return beverage.getDescription() + " 摩卡";
}
}

class Bubble extends Condiment{
public Bubble(Beverage beverage) {
super(beverage);
}
@Override
public double cost() {
return beverage.cost() + 0.1;
}
@Override
public String getDescription() {
return beverage.getDescription() + " 泡沫";
}
}

//==================================================================================
// 扩展饮料
class Tea extends Beverage {
public Tea() {
super("茶");
}
@Override
public double cost() {
return 2;
}
}
//扩展调料
class Rival extends Condiment {
public Rival(Beverage beverage) {
super(beverage);
}
// beverage 包装的对象
@Override
public double cost() {
return beverage.cost() + 1;
}
@Override
public String getDescription() {
return beverage.getDescription() + "枸杞";
}
}

public class DecoratorPatterns {
public static void main(String[] args) {
//一层一层装饰
Beverage b1 = new Decaf();
Beverage b2 = new Milk(b1);
Beverage b3 = new MoKa(b2);
System.out.println(b3.getDescription()+":"+b3.cost());
}
}
/**
* 变化:
* 星巴克老板为了提高自身的竞争力,想出了一个新的业务:调料,也就是可以给咖啡中放调料:牛奶、豆浆、摩卡、泡沫(只是为了好玩)
*/

装饰器模式调用图解

一层一层装饰

装饰器UML类图

装饰器模式UML

优点:

  1. 加入一个新的饮料和新的调料不会违反开闭原则

缺点:

  1. 类还是有点多

2.7、模板方法设计模式

一个抽象类公开定义了执行它的方法的方式/模板,它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方法进行,这种类型的设计模式属于行为型模式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
abstract class Template {
public void template() {
System.out.println("开始");
long start = System.currentTimeMillis();
code();
long end = System.currentTimeMillis();
System.out.println("结束:" + (end - start));
}

public abstract void code();
}

//==============================================
class TestTemplate extends Template{
@Override
public void code() {
LinkedList<Integer> list = new LinkedList<>();
for (int i = 0; i < 1000000; i++) {
list.add(i);
}
}
}

2.8、适配器模式

一个类的接口转换成客户希望的另一个接口,适配器模式让那些接口不兼容的类可以一起工作。

为什么非要转换接口?

  • 原接口和目标接口都已经存在,不易修改接口代码
  • 抽象接口希望复用已有组件的逻辑

定义接口,抽象类实现接口,并定义一些公用的逻辑,子类继承抽象父类,并实现各自特定的逻辑

适用场景:

  1. 封装第三方组件,如缓存组件
  2. 读取配置文件,支持多种文件格式
  3. 保持多态性,一个接口,多种实现

对外暴露统一的 API

合成复用原则:

  • 组合优于继承
  • 设计模式用继承对行为变化进行分类,而不是用来继承来复用逻辑
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Calc{
public int add(int a,int b){
return a+b;
}
}

//=========================
//要求加三个数
class CalcAdapter{
private Calc calc;

public CalcAdapter(Calc calc) {
this.calc = calc;
}
public int add(int a,int b,int c){
return calc.add(a,calc.add(b,c));
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
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
// 使用接口,达到完全解耦
interface Processor {
public String name();

Object process(Object input);
}

abstract class StringProcessor implements Processor {
@Override
public String name() {
return this.getClass().getSimpleName();
}
}

class UpCase extends StringProcessor {
@Override
public String process(Object input) {
//返回类型的协变,向上转型
return ((String) input).toUpperCase(Locale.ROOT);
}
}

class DownCase extends StringProcessor {
@Override
public String process(Object input) {
return ((String) input).toLowerCase(Locale.ROOT);
}
}

class Splitter extends StringProcessor {
@Override
public String process(Object input) {
return Arrays.toString(((String) input).split(" "));
}
}

class Apply {

public static void process(Processor p, Object s) {
System.out.println("Using Processor: " + p.name());
System.out.println(p.process(s));
}

public static void main(String[] args) {
String str = "how are you!";
process(new UpCase(), str);
process(new DownCase(), str);
process(new Splitter(), str);

process(new FilterAdapter(new LowPass(8)),new WaveForm());
process(new FilterAdapter(new BandPass(8,9)),new WaveForm());
}
}

//========================================

class WaveForm {
private static long counter;
//id 自增
private final long id = counter++;

@Override
public String toString() {
return "WaveForm{" +
"id=" + id +
'}';
}
}

/**
* 发现了一个电子滤波器类,恰巧与 Processor 有相同的接口结构
*/
class Filter {
public String name() {
return getClass().getSimpleName();
}

public WaveForm process(WaveForm input) {
return input;
}
}

class LowPass extends Filter {
double cutoff;

public LowPass(double cutoff) {
this.cutoff = cutoff;
}

@Override
public WaveForm process(WaveForm input) {
return input;
}
}

class BandPass extends Filter {
double lowCutoff, highCutoff;

public BandPass(double lowCutoff, double highCutoff) {
super();
this.lowCutoff = lowCutoff;
this.highCutoff = highCutoff;
}

@Override
public WaveForm process(WaveForm input) {
return input;
}
}

/**
* 适配器中的代码将接受你所拥有的接口,并产生你所需要的接口
* FilterAdapter 的构造器接受你所拥有的 Filter,然后生成具有你所需要的 Processor 接口的对象
*/
class FilterAdapter implements Processor {
private Filter filter;

public FilterAdapter(Filter filter) {
this.filter = filter;
}

@Override
public String name() {
return filter.name();
}

@Override
public WaveForm process(Object input) {
return filter.process((WaveForm) input);
}
}

2.9、策略模式

一个类的行为或其算法可以在运行时更改。

创建表示各种策略的对象和一个行为,随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。

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
public class StrategyPatterns {
public static void main(String[] args) {
MallardDuck mallardDuck = new MallardDuck();
mallardDuck.performFly();
mallardDuck.performQuack();
mallardDuck.swim();
mallardDuck.display();
}
}

interface FlyBehavior {
void fly();
}

interface QuackBehavior {
void quack();
}

class Quack implements QuackBehavior {
@Override
public void quack() {
System.out.println("嘎嘎叫");
}
}

class Squeak implements QuackBehavior {

@Override
public void quack() {
System.out.println("吱吱叫");
}
}

class MuteQuack implements QuackBehavior {

@Override
public void quack() {
System.out.println("不会叫");
}
}

class FlyWithWings implements FlyBehavior {
@Override
public void fly() {
System.out.println("用翅膀飞~~~~");
}
}

class FlyNoWay implements FlyBehavior {

@Override
public void fly() {
System.out.println("飞不起来的");
}
}

class FlyWithRocket implements FlyBehavior {
@Override
public void fly() {
System.out.println("背上绑个窜天猴飞~!!~!");
}
}

abstract class Duck {
protected FlyBehavior fb;
protected QuackBehavior qb;

public void swim() {
System.out.println("游泳...");
}

public abstract void display();

public void performFly() {
fb.fly();
}

public void performQuack() {
qb.quack();
}
}

class MallardDuck extends Duck {
public MallardDuck() {
this.fb = new FlyWithWings();
this.qb = new Quack();
}

@Override
public void display() {
System.out.println("外观是野鸡!!");
}
}

class RedHeadDuck extends Duck {
public RedHeadDuck() {
this.fb = new FlyWithWings();
this.qb = new Quack();
}

@Override
public void display() {
System.out.println("外观是红头鸭!!");
}
}

class DecoyDuck extends Duck {
public DecoyDuck() {
this.fb = new FlyNoWay();
this.qb = new MuteQuack();
}

@Override
public void display() {
System.out.println("外观是诱饵鸭!!");
}
}
281006154531426

定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。

意图:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。

主要解决:在有多种算法相似的情况下,使用 if…else 所带来的复杂和难以维护。

何时使用:一个系统有许多许多类,而区分它们的只是他们直接的行为。

如何解决:将这些算法封装成一个一个的类,任意地替换。

关键代码:实现同一个接口。

应用实例:

1、诸葛亮的锦囊妙计,每一个锦囊就是一个策略。

2、旅行的出游方式,选择骑自行车、坐汽车,每一种旅行方式都是一个策略。

3、JAVA AWT 中的 LayoutManager。

优点:

1、算法可以自由切换。

2、避免使用多重条件判断。

3、扩展性良好。

缺点:

1、策略类会增多。

2、所有策略类都需要对外暴露。

使用场景:

1、如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。

2、一个系统需要动态地在几种算法中选择一种。

3、如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。

注意事项:如果一个系统的策略多于四个,就需要考虑使用混合模式,解决策略类膨胀的问题。

2.9.1、设计思想

  1. 不变性与可变性
    • 使用抽象固定不变的
    • 使用具体子类封装可变的
    • 寻找变化,将其封装在一个单独的类,然后将这个类的抽象包含在另一个类
  2. 减少多重条件判断
    • 使用策略模式可以避免多重条件判断
    • 将策略判断逻辑转移
  3. 满足开闭原则
    • 结合工厂模式,生成具体的策略子类实例对象
    • 新增策略,符合开闭原则
  4. 重点在组织算法
    • 策略模式的重心不是如何实现算法,而是如何组织这些算法,从而让程序结构更加灵活,具有更好的维护性和扩展性

2.10、代理模式

为其它对象提供一种代理,以控制对这种对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

真实对象需要实现接口

jdk 中动态代理的使用

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
public class ProxyPatterns {
public static void main(String[] args) {
/**
* 第一个参数:类加载器
* 实例化一个对象,就必须调用类的构造器,在构造器调用之前,jvm会加载该类的字节码
* jvm 恰恰就是使用 ”类加载器“ 来加载类的字节码的,这一步通常由 jvm 自动完成
* 以下代码是动态创建一个代理对象的代码,是一种不太正常的创建对象的方式
* 但凡创建对象,势必要加载字节码,加载字节码就必须要使用类加载器,与构造器实例化不同的是:
* 使用构造器实例化对象时,jvm 会自动找到类加载器,而以下代码,必须我们手动传入类加载器
*/
ClassLoader classLoader = ProxyPatterns.class.getClassLoader();

/**
* 第二个参数,解决传入的类加载器加载的是哪个类的字节码
*
* 对比,使用构造器创建对象时,加载的字节码很明确
* new String() jvm 加载 String.class
* new Date() jvm 加载 Date.class
* new ArrayList() jvm 加载 ArrayList.class
*
* 动态代理创建对象时,加载的字节码是在运行期间 动态生成的字节码 ,它是不需要源代码的
*
* 动态代理API动态生成的字节码内容是根据第二个参数生成的,会动态生成一个实现了目标接口的类的字节码
* 在本例中就是生成一个实现了 ICalc 接口的类的字节码
*/
Class[] interfaces = {ICalc.class};

/**
* 第三个参数:调用处理器,InvocationHandler
* 动态代理会加载自己动态生成的字节码,且这个字节码是根据某些个接口生成的,
* 在本例中就是根据 ICalc 接口生成的实现了 ICalc 接口的类的字节码
* 实现了一个接口,就要实现其中的抽象方法,第三个参数决定这些抽象方法的方法体。
* 本例中 MyHandler 类的 invoke 方法就是方法体的内容,可以这样理解
*
* class 动态生成的类 implements ICalc{
* int add(int a,int b){
* new MyHandler().invoke();
* }
* int sub(int a,int b){
* new MyHandler().invoke();
* }
* int mul(int a,int b){
* new MyHandler().invoke();
* }
* int div(int a,int b){
* new MyHandler().invoke();
* }
* }
*/
//真实对象
ICalc calc = new CalcImpl();
//创建一个代理对象时,需要传入三个对象
ICalc proxy = (ICalc) Proxy.newProxyInstance(classLoader, interfaces, new MyHandler(calc));
//代理对象的调用都会进入代理对象的调用处理器中,而不会直接进入真实的方法
proxy.add(5, 3);
proxy.sub(5, 3);
proxy.mul(5, 3);
proxy.div(5, 3);
}
}

class MyHandler implements InvocationHandler {
//与真实对象关联
private Object target;

public MyHandler(Object target) {
this.target = target;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method.getName() + " 方法开始,参数是:" + Arrays.toString(args));
//执行真实对象中的方法
Object result = method.invoke(target, args);
System.out.println(method.getName() + " 方法结束,结果是:" + result);
//返回值要与抽象方法的返回值一致,这里的返回值会返回到代理对象的方法调用处。
return result;
}
}

interface ICalc {
int add(int a, int b);

int sub(int a, int b);

int mul(int a, int b);

int div(int a, int b);
}

class CalcImpl implements ICalc {

@Override
public int add(int a, int b) {
return a + b;
}

@Override
public int sub(int a, int b) {
return a - b;
}

@Override
public int mul(int a, int b) {
return a * b;
}

@Override
public int div(int a, int b) {
return a / b;
}
}

代理模式解释图

代理模式解释

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
/**
* 代理模式是一种设计模式,代码不是固定的,不一定非要使用 jdk 动态代理,jdk 动态代理不等价于代理模式
*/
public class ProxyPatterns {

public static void main(String[] args) throws Exception {
//目标对象也即真实对象
ICalc calc = new CalcImpl();
// 根据目标对象 calc 动态生成一个代理对象
ICalc proxy = (ICalc) MyProxy.getProxy(calc,new AddInterceptor());

// 把 proxy 这个代理对象,再当成一个新的目标对象,套娃
ICalc proxy1 = (ICalc)MyProxy.getProxy(proxy, new SubInterceptor());

//代理对象的调用都会进入代理对象的调用处理器中,而不会直接进入真实的方法
proxy1.add(5, 3);
proxy1.sub(5, 3);

//用户只需要 传入 真实对象,和配置 配置文件 即可
ICalc proxy2 = (ICalc)MyProxy.getProxyForConfigFile(new CalcImpl());
proxy2.add(2,3);
proxy2.sub(2,3);
}


}

class MyProxy {

/**
* 封装:对外隐藏复杂的实现细节,暴露出简单的使用方法
*/
public static Object getProxy(Object target, Interceptor interceptor) {
ClassLoader classLoader = MyProxy.class.getClassLoader();
//获取该类实现的所有接口的字节码
Class[] interfaces = target.getClass().getInterfaces();
//创建一个代理对象时,需要传入三个对象
Object proxy = Proxy.newProxyInstance(classLoader, interfaces, new MyHandler(target, interceptor));
return proxy;
}

public static Object getProxyForConfigFile(Object target) throws Exception {
//读取配置文件
Properties properties = new Properties();
InputStream in = MyProxy.class.getResourceAsStream("config.properties");
properties.load(in);
String interceptors = properties.getProperty("interceptors");
String[] split = interceptors.split(",");
List<Interceptor> interceptorList = new ArrayList<>();
for (String s : split) {
interceptorList.add((Interceptor)Class.forName(s).newInstance());
}
//拦截器正向包装
for (int i = interceptorList.size() -1; i >=0; i--) {
target = (ICalc)MyProxy.getProxy(target,interceptorList.get(i));
}
in.close();
return target;
}
}

class MyHandler implements InvocationHandler {
/**
* 与真实对象关联
*/
private final Object target;
private final Interceptor interceptor;

public MyHandler(Object target, Interceptor interceptor) {
this.target = target;
this.interceptor = interceptor;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 通过前置方法的执行结果,可以控制是否执行真实对象中的方法,从而实现对 真实对象 的控制访问。
interceptor.before(method, args);
//执行真实对象中的方法
Object result = method.invoke(target, args);
interceptor.after(method, args, result);
//返回值要与抽象方法的返回值一致,这里的返回值会返回到代理对象的方法调用处。
return result;
}
}

interface Interceptor {
void before(Method method, Object[] args);

void after(Method method, Object[] args, Object result);
}

// class LogInterceptor implements Interceptor {
// @Override
// public void before(Method method, Object[] args) {
// System.out.println(method.getName() + " 方法执行,参数是:" + Arrays.toString(args));
// }
//
// @Override
// public void after(Method method, Object[] args, Object result) {
// System.out.println(method.getName() + " 方法结束,结果是:" + result);
// }
// }

/**
* 为满足单一职责,将 LogInterceptor 类中拦截 所有方法 处理为拦截 单一方法
*/
class AddInterceptor implements Interceptor{

@Override
public void before(Method method, Object[] args) {
if("add".equals(method.getName())){
System.out.println("add开始");
}
}

@Override
public void after(Method method, Object[] args, Object result) {
if("add".equals(method.getName())){
System.out.println("add结束");
}
}
}
class SubInterceptor implements Interceptor{

@Override
public void before(Method method, Object[] args) {
if("sub".equals(method.getName())){
System.out.println("sub开始");
}
}

@Override
public void after(Method method, Object[] args, Object result) {
if("sub".equals(method.getName())){
System.out.println("sub结束");
}
}
}

interface ICalc {
int add(int a, int b);

int sub(int a, int b);

int mul(int a, int b);

int div(int a, int b);
}

class CalcImpl implements ICalc {

@Override
public int add(int a, int b) {
return a + b;
}

@Override
public int sub(int a, int b) {
return a - b;
}

@Override
public int mul(int a, int b) {
return a * b;
}

@Override
public int div(int a, int b) {
return a / b;
}
}

静态代理:自己手写的代码,写死的代理类。

动态代理:运行时,动态地生成字节码。

适配器模式与代理模式区别

  1. 代理模式中,代理对象和它所包裹的目标对象必须实现相同的接口,适配器模式中,适配器和它所包裹的对象不用实现相同的接口
  2. 代理模式中,代理对象可以控制它所包裹的目标对象的方法是否执行,适配器模式中,适配器总会调用目标对象的方法,无法控制

2.11、单例模式

在Java应用中,单例对象能保证在一个JVM中,该对象只有一个实例存在。

1、某些类创建比较频繁,对于一些大型的对象,这是一笔很大的系统开销。

2、省去了new操作符,降低了系统内存的使用频率,减轻GC压力

3、有些类如交易所的核心交易引擎,控制着交易流程,如果该类可以创建多个的话,系统完全乱了。(比如一个军队出现了多个司令员同时指挥,肯定会乱成一团),所以只有使用单例模式,才能保证核心交易服务器独立控制整个流程。

使用内部类来维护单例的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Singleton {  
 
    /* 私有构造方法,防止被实例化 */  
    private Singleton() {  
    }  
    /* 此处使用一个内部类来维护单例 */  
    private static class SingletonFactory {  
        private static Singleton instance = new Singleton();  
    }  
    /* 获取实例 */  
    public static Singleton getInstance() {  
        return SingletonFactory.instance;  
    }  
    /* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */  
    public Object readResolve() {  
        return getInstance();  
    }  
}  

在创建类的时候进行同步,将创建和getInstance()分开,单独为创建加synchronized关键字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class SingletonTest {  
    private static SingletonTest instance = null;  
    private SingletonTest() {  
    }  
    private static synchronized void syncInit() {  
        if (instance == null) {  
            instance = new SingletonTest();  
        }  
    }  
    public static SingletonTest getInstance() {  
        if (instance == null) {  
            syncInit();  
        }  
        return instance;  
    }  
}  

2.12、迭代器设计模式

提供一种方法顺序访问一个容器对象中各个元素,而又不暴露该对象的内部细节

如:集合中的迭代器

iterator-pattern-1

定义集合 MyCollection。通过迭代器 iterator,按照正序返回集合中的对象

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
class MyCollection<T>{
private List<T> list = new ArrayList<>();
public void add(T item){
list.add(item);
}
public void remove(T item){
list.remove(item);
}
public Iterator<T> iterator(){
return new MyIterator(list);
}
/**
* 自定义迭代器
* */
private class MyIterator implements Iterator<T>{
private List<T> dataSource;
/**
* 当前位置的指针
*/
private int curPos;

public MyIterator(List<T> data){
this.dataSource = data;
}
@Override
public boolean hasNext() {
return curPos < dataSource.size();
}
@Override
public T next() {
T item = dataSource.get(curPos);
curPos ++;
return item;
}
}
}

2.13、组合模式

将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。

组合模式可以让客户端像修改配置文件一样简单的完成本来需要流程控制语句来完成的功能。

但凡需要制作树形结构的地方,就可以使用组合模式。

  • 向上转型时,父类不能调用子类的对象。
  • 向上转型时,调用的方法只和 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
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
public class CombinationPatterns {
public static void main(String[] args) {
Menu menu = new Menu("餐厅菜单", "aaaaaaaaaaaa");
Menu menu1 = new Menu("湘菜", "bbbbbbbbb");
Menu menu2 = new Menu("粤菜", "cccccccccccc");
Menu menu3 = new Menu("川菜", "dddddddddddddd");

MenuItem mi1 = new MenuItem("辣椒炒肉", "aaaaa", false, 6);
MenuItem mi2 = new MenuItem("凉皮", "aaaaa", true, 6);
MenuItem mi3 = new MenuItem("胡辣汤", "aaaaa", true, 6);

MenuItem mi4 = new MenuItem("剁椒鱼头", "aaaaa", false, 16);
MenuItem mi5 = new MenuItem("干煸斗鱼", "aaaaa", false, 16);
MenuItem mi6 = new MenuItem("鱼头", "aaaaa", false, 16);

MenuItem mi7 = new MenuItem("牛肉拉面", "aaaaa", false, 12);
MenuItem mi8 = new MenuItem("土豆丝", "aaaaa", true, 7);
MenuItem mi9 = new MenuItem("热干面", "aaaaa", true, 7);

menu1.add(mi1);
menu1.add(mi2);
menu1.add(mi3);

menu2.add(mi4);
menu2.add(mi5);
menu2.add(mi6);

menu3.add(mi7);
menu3.add(mi8);
menu3.add(mi9);

menu.add(menu1);
menu.add(menu2);
menu.add(menu3);

printV(menu);
}

private static void printV(MenuComponent menu){
Iterator<MenuComponent> iterator = menu.getList().iterator();
while (iterator.hasNext()){
MenuComponent next = iterator.next();
/*
如果为 MenuItem 则打印是否为素食的项
如果为 Menu 则调用 isVegetarian 方法抛出异常,则进入 catch 中递归
此时,客户端只依赖于 MenuComponent,而不再知道 Menu 和 MenuItem 的存在
这样符合最少知道原则
*/
try {
if(next.isVegetarian()){
next.print("");
}
} catch (Exception e) {
printV(next);
}
}
}
}

/**
*
*/
abstract class MenuComponent {
private String name;
private String description;

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

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getDescription() {
return description;
}

public void setDescription(String description) {
this.description = description;
}

/**
* 属于菜单的方法: add remove getChild
* 属于菜单的方法,对菜单项而言是没有意义的,为什么非要定义在父类中?
* 目的是:满足组合模式中 —— 单个对象和组合对象的使用具有一致性
*/
public void add(MenuComponent mc) {
throw new UnsupportedOperationException();
}
public void remove(MenuComponent mc) {
throw new UnsupportedOperationException();
}

public MenuComponent getChild(int i) {
throw new UnsupportedOperationException();
}

public List<MenuComponent> getList(){
throw new UnsupportedOperationException();
}

/**
* 属于菜单项的方法:getPrice isVegetarian
*/
public double getPrice(){
throw new UnsupportedOperationException();
}
public boolean isVegetarian(){
throw new UnsupportedOperationException();
}


public abstract void print(String prefix);
}

class Menu extends MenuComponent{

private List<MenuComponent> list = new ArrayList<>();

public Menu(String name, String description) {
super(name, description);
}

@Override
public List<MenuComponent> getList() {
return list;
}

@Override
public void add(MenuComponent mc) {
list.add(mc);
}

@Override
public void remove(MenuComponent mc) {
super.remove(mc);
}

@Override
public MenuComponent getChild(int i) {
return super.getChild(i);
}

@Override
public void print(String prefix) {
System.out.println(prefix + "<<" + super.getName() + ">>" +super.getDescription());
Iterator<MenuComponent> iterator = list.iterator();
while (iterator.hasNext()){
MenuComponent next = iterator.next();
next.print("\t"+prefix);
}
}
}


class MenuItem extends MenuComponent{

private boolean vegetarian;
private double price;

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

@Override
public boolean isVegetarian() {
return vegetarian;
}

public void setVegetarian(boolean vegetarian) {
this.vegetarian = vegetarian;
}

@Override
public double getPrice() {
return price;
}

public void setPrice(double price) {
this.price = price;
}

@Override
public void print(String prefix) {
String str = vegetarian ? "(素食)" : "";
System.out.println(prefix + super.getName() +str + ":" + super.getDescription());
}
}

2.14、观察者模式

意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。

何时使用:一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。

如何解决:使用面向对象技术,可以将这种依赖关系弱化。

关键代码:在抽象类里有一个 ArrayList 存放观察者们。

优点:

  1. 观察者和被观察者是抽象耦合的。

  2. 建立一套触发机制。

缺点:

  1. 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
  2. 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
  3. 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。

观察者设计模式UML

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
public class ObserverPatterns {
public static void main(String[] args) {
WeatherStation weatherStation = new WeatherStation();
Phone phone = new Phone(weatherStation);
Window window = new Window(weatherStation);
Billboard billboard = new Billboard(weatherStation);
weatherStation.addObserver(phone);
weatherStation.addObserver(window);
weatherStation.addObserver(billboard);
weatherStation.setData(20,11,40);
}
}
interface Subject{
void addObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}

class WeatherStation implements Subject{
private Integer temperature;
private Integer humidity;
private Integer pressure;

private final List<Observer> observerList = new ArrayList<>();

@Override
public void addObserver(Observer observer){
observerList.add(observer);
}
@Override
public void removeObserver(Observer observer){
observerList.remove(observer);
}

@Override
public void notifyObservers(){
observerList.forEach(Observer::update);
}

/**
* 传感器可以感知:温度、湿度、气压,一旦感知到就给自己的属性赋值
*/
public void setData(Integer temperature, Integer humidity, Integer pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
//通知其它观察者,数据发生改变
notifyObservers();
}

@Override
public String toString() {
return "WeatherStation{" +
"temperature=" + temperature +
", humidity=" + humidity +
", pressure=" + pressure +
", observerList=" + observerList +
'}';
}
}

interface Observer{
void update();
}
/**
* 观察者
*/
class Phone implements Observer{
private WeatherStation weatherStation;

public Phone(WeatherStation weatherStation) {
this.weatherStation = weatherStation;
}

@Override
public void update() {
System.out.println("手机显示:"+weatherStation.toString());
}
}

class Window implements Observer{
private WeatherStation weatherStation;

public Window(WeatherStation weatherStation) {
this.weatherStation = weatherStation;
}

@Override
public void update() {
System.out.println("智能家具显示:"+weatherStation.toString());
}
}

//==============扩展观察者


class Billboard implements Observer{
private WeatherStation weatherStation;

public Billboard(WeatherStation weatherStation) {
this.weatherStation = weatherStation;
}

@Override
public void update() {
System.out.println("广告牌显示:"+weatherStation.toString());
}
}

2.15、责任链模式

java web 中的过滤器链,Filter 接口、FilterChain 接口、ApplicationFilterChain 实现 FilterChain 接口、自定义 Filter 实现 Filter 接口

每一个 Filter 只干一件事,质检规则可以应用上。

Filter 是什么模式?

  • Servlet 是 java web 应用最基础的标准
  • 在其中定义了 Filter 过滤器机制
  • 经常使用 Filter 实现各种权限、身份控制等功能
  • 一次 Http 请求进来,经过各种 filter 实例进行处理,每个 filter 都可以做自己的功能,可以直接返回请求,也可以交给下一个 filter 继续处理。

2.15.1、责任链模式结构类图

责任链模式

为了避免请求发送者与多个请求处理者耦合在一起,于是将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。

2.15.2、效果

  1. 对同一个请求数据可以制定多个处理逻辑
  2. 处理逻辑可以固定一个顺序
  3. 处理逻辑可以自定决定是否处理这个请求数据
  4. 处理逻辑可以决定是否继续后续的处理

2.15.3、意图

  1. 责任链模式的本质是解耦请求与处理,让请求在处理链中能进行传递与被处理
  2. 责任链模式的独到之处是将其节点处理者组合成了链式结构,并允许节点自身决定是否进行请求处理或转发,相当于让请求流动起来

2.15.4、设计思想

  1. 降低耦合
    • 一个对象无需了解谁处理它,也不了解处理过程
    • 多个处理类之间没有相互依赖,减少if else
  2. 职责明确
    • 每个处理类只实现一个明确的处理逻辑
    • 不该处理的直接交过其他处理类
    • 符合单一职责原则
  3. 增强扩展
    • 可以方便的增减处理类
    • 满足开闭原则
    • 处理类可以自行决定是否处理
  4. 不足
    • 只能顺序执行,可能会有性能问题
    • 具体处理类可以自行结束处理链,不能确保每个接收者都会执行

2.16、命令模式

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
String cc8800bIP = "xxxxxxx";
String spectrographIP = "xxxxxxxxxx";

// 两种设备
CC880OB cc880OB = new CC880OB(cc8800bIP);
Spectrograph spec = new Spectrograph(spectrographIP);

// 两种命令
AutoTestCommand command1 = new SpectrographSetModeMER(spec);
AutoTestCommand command2 = new CC880BOpenChannel1(cc880OB);

// 调用者
Invoker invoker = new Invoker();
invoker.setCommond(commond1);
invoker.call();
invoker.setCommond(commond2);
invoker.call();

// ***********************************
public interface AutoTestCommand {
public boolean excute();
}

// ***********************************
public class SpectrographSetModeMER implements AutoTestCommand{

private Spectrograph device;

public SpectrographSetModeMER(Spectrograph device){
this.device = device;
}

@Override
public boolean excute(){

String modeName = "MER";

return device.setMode(modeName);
}
}

// **********************************************
public class Invoker{

private AutoTestCommand command;
public void setCommand(AutoTestCommand command){
this.command = command;
}

public boolean call(){
// 记录统一日志等信息
return this.command.excute();
}
}

2.16.1、命令模式结构类图

设计模式之命令模式(Command)详解及代码示例- kosamino - 博客园

2.16.2、效果

  1. 待测试设备升级,只需要修改设备相关的 API,整个测试流程不用修改
  2. 测试流程如果有变化,新增命令即可,不用动设备的代码

2.16.3、意图

  1. 将调用者与实现者解耦
  2. 命令很容易新增、删除
  3. 命令可以组合

2.16.4、降低耦合性

  1. 配合各种工厂模式可以达到调用者与实现者完全解耦
  2. 扩展性好
    • 新增删除命令非常方便
    • 满足开闭原则
    • 命令可以组合
    • 还可以实现命令的撤销和恢复
    • 命令可以增加统一功能:日志,权限等
  • Copyrights © 2022-2023 hqz

请我喝杯咖啡吧~

支付宝
微信