xiaohuihui
for me

Java设计模式

2020-12-01 11:34:24
Word count: 47.9k | Reading time: 209min

Java设计模式

设计模式常用的七大原则:

  1. 单一职责原则
  2. 接口隔离原则
  3. 依赖倒置原则
  4. 里氏替换原则
  5. 开闭原则ocp
  6. 迪米特法则
  7. 合成复用原则

1.单一职责原则(Single Principle Responsibility)

对类来说,即一个类应该只负责一项职责。如果类A负责两个不同职责:职责1,职责2。当职责1需求变更而改变A时,可能造成职责2执行错误,所以需要将类A的粒度分解为A1,A2。

  • 第一例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package DesignPattern.principle.singleResponsibility;

/**
* @Author coderYang
* @Date 2020/12/1 14:19
*/
public class SingleResponsibility1 {
public static void main(String[] args) {
Vehicle vehicle = new Vehicle();
vehicle.run("法拉利"); //法拉利正在公路上运行
vehicle.run("飞机"); //飞机正在公路上运行
vehicle.run("泰坦尼克号"); //泰坦尼克号正在公路上运行
}
}

//交通工具类
//在方式1的run方法中,违反了单一职责原则
//解决方案:根据交通工具的运行方法不同,分解不同的类即可
class Vehicle{
public void run(String vehicle){
System.out.println(vehicle+"正在公路上运行");
}
}
  • 第二例
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
package DesignPattern.principle.singleResponsibility;

/**
* @Author coderYang
* @Date 2020/12/1 14:24
*/
public class SingleResponsibility2 {
public static void main(String[] args) {
RoadVehicle roadVehicle = new RoadVehicle();
roadVehicle.run("法拉利"); //法拉利在公路上运行

AirVehicle airVehicle = new AirVehicle();
airVehicle.run("飞机"); //飞机在天上运行

WaterVehicle waterVehicle = new WaterVehicle();
waterVehicle.run("泰坦尼克号"); //泰坦尼克号在水中运行
}
}

/**
* 方案2分析:
* 1.遵守了单一职责原则
* 2.但是这样做的改动很大,即将类分解,同时修改客户端
* 3.改进:直接改动Vehicle类,改动的代码会比较少=>方案3
*/

class RoadVehicle{
public void run(String vehicle){
System.out.println(vehicle+"在公路上运行");
}
}

class AirVehicle{
public void run(String vehicle){
System.out.println(vehicle+"在天上运行");
}
}

class WaterVehicle{
public void run(String vehicle){
System.out.println(vehicle+"在水中运行");
}
}
  • 第三例
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
package DesignPattern.principle.singleResponsibility;

/**
* @Author coderYang
* @Date 2020/12/1 14:31
*/
public class SingleResponsibility3 {
public static void main(String[] args) {
Vehicle2 vehicle2 = new Vehicle2();
vehicle2.run("汽车"); //汽车在公路运行
vehicle2.runWater("轮船"); //轮船在水中运行
vehicle2.runAir("飞机"); //飞机在天空运行
}
}


/**
* 方式3分析
* 1.这种修改方法没有对原来的类做大的修改,只是增加了方法
* 2.这里虽然没有在类这个级别上遵守单一职责原则,但是在方法级别上是仍然遵守单一职责
*/
class Vehicle2{
public void run(String vehicle){
System.out.println(vehicle+"在公路运行");
}

public void runAir(String vehicle){
System.out.println(vehicle+"在天空运行");
}

public void runWater(String vehicle){
System.out.println(vehicle+"在水中运行");
}
}

单一职责原则注意事项和细节:

  1. 降低类的复杂度,一个类只复杂一项职责
  2. 提高类的可读性,可维护性
  3. 降低变更引起的风险
  4. 通常情况下,我们应该遵守单一职责原则,只要逻辑足够简单,才可以在代码级违反单一职责原则:只有类中方法足够少,可以在方法级别保持单一职责原则。

2.接口隔离原则(Interface Segregation Principle)

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

问题:

image-20201202150538851

此图类A通过接口Interface1依赖类B,类C通过接口Interface1依赖类D,如果接口Interface1对于类A和类C来说不是最小接口,那么类B和类D必须去实现他们不需要的方法。

代码:

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
package DesignPattern.principle.segregation;

/**
* @Author coderYang
* @Date 2020/12/2 15:08
*/
public class Segregation {
public static void main(String[] args) {
A a = new A();
a.depend1(new B()); //B实现了operation1
a.depend2(new B()); //B实现了operation2
a.depend3(new B()); //B实现了operation3

C c = new C();
c.depend1(new D()); //D实现了operation1
c.depend4(new D()); //D实现了operation4
c.depend5(new D()); //D实现了operation5
}
}

//接口
interface Interface1{
void operation1();
void operation2();
void operation3();
void operation4();
void operation5();
}

//类B
class B implements Interface1{

@Override
public void operation1() {
System.out.println("B实现了operation1");
}

@Override
public void operation2() {
System.out.println("B实现了operation2");
}

@Override
public void operation3() {
System.out.println("B实现了operation3");
}

@Override
public void operation4() {
System.out.println("B实现了operation4");
}

@Override
public void operation5() {
System.out.println("B实现了operation5");
}
}

//类D
class D implements Interface1{

@Override
public void operation1() {
System.out.println("D实现了operation1");
}

@Override
public void operation2() {
System.out.println("D实现了operation2");
}

@Override
public void operation3() {
System.out.println("D实现了operation3");
}

@Override
public void operation4() {
System.out.println("D实现了operation4");
}

@Override
public void operation5() {
System.out.println("D实现了operation5");
}
}


class A{ //A通过interface1依赖(使用)B类,但是只会用到1,2,3方法
public void depend1(Interface1 i){
i.operation1();
}
public void depend2(Interface1 i){
i.operation2();
}
public void depend3(Interface1 i){
i.operation3();
}
}

class C{ //C类通过interface1依赖(使用)D类,但是只会使用到1,4,5方法
public void depend1(Interface1 i){
i.operation1();
}

public void depend4(Interface1 i){
i.operation4();
}

public void depend5(Interface1 i){
i.operation5();
}
}

按隔离原则应当这样处理:

将接口Interface1拆分为独立的几个接口,类A和类C分别与他们需要的接口建立依赖关系。也就是采用接口隔离原则 。

接口Interface1中出现的方法,根据实际情况拆分为三个接口。

image-20201202161447282

代码:

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
package DesignPattern.principle.segregation.improve;

/**
* @Author coderYang
* @Date 2020/12/2 15:08
*/
public class Segregation {
public static void main(String[] args) {
A a = new A();
//A类通过接口依赖B类
a.depend1(new B()); //B实现了operation1
a.depend2(new B()); //B实现了operation2
a.depend3(new B()); //B实现了operation3

C c = new C();
//C类通过皆空依赖(使用)D类
c.depend1(new D()); //D实现了operation1
c.depend4(new D()); //D实现了operation4
c.depend5(new D()); //D实现了operation5
}
}

//接口1
interface Interface1{
void operation1();
}

//接口2
interface Interface2{
void operation2();
void operation3();
}

//接口3
interface Interface3{
void operation4();
void operation5();
}


//类B
class B implements Interface1,Interface2{

@Override
public void operation1() {
System.out.println("B实现了operation1");
}

@Override
public void operation2() {
System.out.println("B实现了operation2");
}

@Override
public void operation3() {
System.out.println("B实现了operation3");
}

}

//类D
class D implements Interface1,Interface3{

@Override
public void operation1() {
System.out.println("D实现了operation1");
}

@Override
public void operation4() {
System.out.println("D实现了operation4");
}

@Override
public void operation5() {
System.out.println("D实现了operation5");
}
}


class A{ //A通过interface1依赖(使用)B类,但是只会用到1,2,3方法
public void depend1(Interface1 i){
i.operation1();
}
public void depend2(Interface2 i){
i.operation2();
}
public void depend3(Interface2 i){
i.operation3();
}
}

class C{ //C类通过interface1依赖(使用)D类,但是只会使用到1,4,5方法
public void depend1(Interface1 i){
i.operation1();
}

public void depend4(Interface3 i){
i.operation4();
}

public void depend5(Interface3 i){
i.operation5();
}
}

3.依赖倒转原则(Dependence Inversion Principle)

1)高层模块不应该依赖底层模块,二者都应该依赖其抽象

2)抽象不应该依赖细节,细节应该依赖抽象

3)依赖倒转(倒置)的中心思想是面向接口编程

4)依赖倒转原则是基于这样的设计理念:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建的架构比以细节为基础的架构要稳的多。在java中,抽象指的是接口或者抽象类,细节就是具体的实现类。

5)使用接口或抽象类的目的是制定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成。

方案1:

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
package DesignPattern.principle.inversion;

/**
* @Author coderYang
* @Date 2020/12/4 11:03
*/
public class DependencyInversion {
public static void main(String[] args) {
Person person = new Person();
person.receive(new Email()); //电子邮件信息:Hello World
}
}

class Email{
public String getInfo(){
return "电子邮件信息:Hello World";
}
}

//完成Person接收消息的功能
//方式1分析
//1.如果我们获取的对象是微信,短信等等,而不是email,则新增类,同时Person也要增加相应的接收方法
//2.解决思路:引入一个抽象的接口IReceiver,表示一个接收者,这样Person类与接口发生依赖
// 因为Email,WeiXin等都属于接收的范围,他们各自实现IReceiver接口就ok,这样就符合依赖倒转原则
class Person{
public void receive(Email email){
System.out.println(email.getInfo());
}
}

方案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
package DesignPattern.principle.inversion.improve;

/**
* @Author coderYang
* @Date 2020/12/4 11:03
*/
public class DependencyInversion {
public static void main(String[] args) {
Person person = new Person();
person.receive(new Email()); //电子邮件信息:Hello World
person.receive(new WeiXin()); //微信消息:Hello,welcome using WeChat
}
}

//定义接口
interface IReceiver{
public String getInfo();
}

class Email implements IReceiver{
public String getInfo(){
return "电子邮件信息:Hello World";
}
}

class WeiXin implements IReceiver{
public String getInfo(){
return "微信消息:Hello,welcome using WeChat";
}
}

class Person{
//面向接口编程
public void receive(IReceiver receiver){
System.out.println(receiver.getInfo());
}
}

依赖关系传递的三方式

  1. 接口传递
  2. 构造方法传递
  3. setter方式传递
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
package DesignPattern.principle.inversion;

/**
* @Author coderYang
* @Date 2020/12/6 13:24
*/
public class DependencyPass {
public static void main(String[] args) {
//第一种方式 通过接口传递
// Xiaomi xiaomi = new Xiaomi();
// OpenAndClose openAndClose = new OpenAndClose();
// openAndClose.open(xiaomi); //小米电视机,打开

//第二种方式 通过构造器进行依赖的传递
// Xiaomi xiaomi = new Xiaomi();
// OpenAndClose openAndClose = new OpenAndClose(xiaomi);
// openAndClose.open(); //小米电视机,打开

//第三种方式 通过setter的方式进行依赖的传递
Xiaomi xiaomi = new Xiaomi();
OpenAndClose openAndClose = new OpenAndClose();
openAndClose.setTv(xiaomi);
openAndClose.open(); //小米电视机,打开
}
}



/**
* 方式1:通过接口传递实现依赖
*/
//interface IOpenAndClose{
// public void open(ITV tv); //抽象方法,接收接口
//}
//
//interface ITV{ //ITV接口
// public void play();
//}
//
////实现接口
//class OpenAndClose implements IOpenAndClose{
// public void open(ITV tv){
// tv.play();
// }
//}
//

////例子:小米电视机
//class Xiaomi implements ITV{
//
// @Override
// public void play() {
// System.out.println("小米电视机,打开");
// }
//}







/**
* 方式2:通过构造方法传递依赖
*/
//interface IOpenAndClose{
// public void open(); //抽象方法
//}
//
//interface ITV{ //ITV接口
// public void play();
//}
//
//class OpenAndClose implements IOpenAndClose{
// public ITV tv; //成员
//
// public OpenAndClose(ITV tv) { //构造器
// this.tv = tv;
// }
//
// @Override
// public void open() {
// this.tv.play();
// }
//}
//
////例子
//class Xiaomi implements ITV{
//
// @Override
// public void play() {
// System.out.println("小米电视机,打开");
// }
//}







/**
* 方式3:通过setter方法传递
*/
interface IOpenAndClose{
public void open(); //抽象方法

public void setTv(ITV tv);
}

interface ITV{ //ITV接口
public void play();
}

class OpenAndClose implements IOpenAndClose{
private ITV tv;

@Override
public void setTv(ITV tv) {
this.tv = tv;
}

@Override
public void open() {
this.tv.play();
}

}

//例子
class Xiaomi implements ITV{

@Override
public void play() {
System.out.println("小米电视机,打开");
}
}

依赖倒转原则的注意事项和细节:

  1. 底层模块尽量都要有抽象类或接口,或两者都有,程序稳定性更好。
  2. 变量的声明类型尽量是抽象类或接口,这样我们的变量引用和实际对象间,就存在一个缓冲层,利于程序扩展和优化。
  3. 继承时遵循里氏替换原则。

4.里氏替换原则(Liskov Substitution principle)

OO中的继承性质的思考和说明

  1. 继承包含这样一层含义:父类中凡是已经实现好的方法,实际上是在设定规范和契约,虽然它不强制要求所有子类必须遵守这些契约,但是如果子类对这些已经实现的方法任意修改,就会对整个继承体系造成破坏。
  2. 继承在给程序设计带来便利的同时,也带来了弊端。比如使用继承会给程序带来侵入性,程序的可移植性降低,增加对象间的耦合性,如果一个类被其他的类所继承,则当这个类需要修改时,必须考虑到所有的子类,并且父类修改后,所有涉及到子类的功能都有可能产生故障。
  3. 正确的使用继承=>里氏替换原则

里氏替换原则

  1. 如果对每个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序p在所有的对象o1都替换为o2时,程序p的行为没有发生变化,那么类型T2是类型T1的子类型。换句话说,所有引用基类的地方必须能透明地使用其子类的对象。
  2. 在使用继承时,遵循里氏替换原则,在子类中尽量不要重写父类的方法
  3. 里氏替换原则告诉我们,继承实际上让两个类的耦合性增强了,在适当的情况下,可以通过聚合,组合,依赖来解决问题

代码1:

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
package DesignPattern.principle.liskov;

/**
* @Author coderYang
* @Date 2020/12/6 19:12
*/
public class Liskov {
public static void main(String[] args) {
A a = new A();
System.out.println("11-3="+a.func1(11,3)); //11-3=8
System.out.println("1-8="+a.func1(1,8)); //1-8=-7

System.out.println("----------------------");
B b = new B();
System.out.println("11-3="+b.func1(11,3)); //11-3=14
System.out.println("1-8="+b.func1(1,8)); //1-8=9
System.out.println("11+3+9="+b.func2(11,3)); //11+3+9=23
}
}

//A类
class A{
//返回两个数的差
public int func1(int num1,int num2){
return num1-num2;
}
}

//B类,继承A类
//增加了一个功能:完成两个数相加,再和9求和
class B extends A{
//重写了A类方法,可能是无意识重写的
public int func1(int a,int b){
return a+b;
}

public int func2(int a,int b){
return func1(a,b)+9;
}
}

通过以上代码,我们发现原来运行正常的相减功能发生了错误。原因就是类B无意中重写了父类的方法,造成原有功能出现错误。在实际编程中,我们常常会通过重写父类的方法完成新的功能,这样写起来虽然简单,但整个继承体系的复用性会比较差。特别是运行多态比较频繁的时候。

解决方法:原来的父类和子类都继承一个更通俗的基类,原有的继承关系去掉,采用依赖,聚合,组合等关系代替。

image-20201206194922807

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
package DesignPattern.principle.liskov.improve;

/**
* @Author coderYang
* @Date 2020/12/6 19:12
*/
public class Liskov {
public static void main(String[] args) {
A a = new A();
System.out.println("11-3="+a.func1(11,3)); //11-3=8
System.out.println("1-8="+a.func1(1,8)); //1-8=-7

System.out.println("----------------------");
B b = new B();
//因为B类不再继承A类,因此调用者不会再认为func1是在求减法,调用完成的功能就很明确
System.out.println("11-3="+b.func1(11,3)); //11-3=14
System.out.println("1-8="+b.func1(1,8)); //1-8=9
System.out.println("11+3+9="+b.func2(11,3)); //11+3+9=23

//使用组合仍然可以使用到A类相关方法
System.out.println("11-3="+b.func3(11,3)); //11-3=8
}
}

//创建一个更加基础的集类
class Base{
//把更加基础的方法和成员写到此Base类

}


//A类,继承集类Base
class A extends Base{
//返回两个数的差
public int func1(int num1,int num2){
return num1-num2;
}
}

//B类,继承基类Base
//增加了一个功能:完成两个数相加,再和9求和
class B extends Base{
//如果B需要使用A类的方法,使用组合关系
private A a = new A();

//重写了A类方法,可能是无意识重写的
public int func1(int a,int b){
return a+b;
}

public int func2(int a,int b){
return func1(a,b)+9;
}

//使用A的方法
public int func3(int a,int b){
return this.a.func1(a,b);
}
}

5.开闭原则(Open Closed Principle)

  1. 开闭原则是编程中最基础的,最重要的设计原则。
  2. 一个软件实体如类,模块和函数应该对扩展开放(对提供方),对修改关闭(对使用方)。用抽象构建框架,用实现扩展细节。
  3. 当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。
  4. 编程中遵循其它原则,以及使用设计模式的目的就是遵循开闭原则。

image-20201212230827178

实例代码:

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
package DesignPattern.principle.ocp;


/**
* @Author coderYang
* @Date 2020/12/8 14:03
*/
public class Ocp {
public static void main(String[] args) {
GraphicEditor graphicEditor = new GraphicEditor();
graphicEditor.drawShape(new Rectangle()); //绘制圆形
graphicEditor.drawShape(new Circle()); //绘制矩形
graphicEditor.drawShape(new Triangle()); //绘制三角形
}
}

//这是一个用于绘图的类 [使用方]
class GraphicEditor{
//接收Shape对象,根据type来绘制不同的图形
public void drawShape(Shape s){
if(s.m_type == 1){
drawRectangle(s);
}else if(s.m_type == 2){
drawCircle(s);
}else if(s.m_type == 3){
drawTriangle(s);
}
}

//绘制矩形
public void drawRectangle(Shape r){
System.out.println("绘制矩形");
}
//绘制圆形
public void drawCircle(Shape r){
System.out.println("绘制圆形");
}

//绘制新增加的三角形
public void drawTriangle(Shape r){
System.out.println("绘制三角形");
}
}

//Shape类,基类
class Shape{
int m_type;
}

//矩形类
class Rectangle extends Shape{
//构造方法
Rectangle() {
super.m_type = 1;
}
}

//圆形类
class Circle extends Shape{
Circle(){
super.m_type = 2;
}
}

//新增三角形类
class Triangle extends Shape{
Triangle(){
super.m_type = 3;
}
}

以上代码优缺点:

  1. 优点比较好理解,简单易操作。
  2. 缺点是违反了设计模式的ocp原则,即对扩展开放(提供方),对修改关闭(使用方)。即当我们给类增加新功能的时候,尽量不要修改代码,或尽可能少修改代码。
  3. 比如我们这时要新增一个图形种类,我们需要修改的地方就会很多。(会在使用方中进行修改代码,这是不合常理的,不利于功能的扩展)。

遵守OCP原则对方式一进行改进

思路:把创建Shape类做成抽象类,并提供一个抽象的draw方法,让子类去实现即可,这样我们有新的图形种类时,只需要让新的图形类继承Shape,并实现draw方法即可,使用方的代码就不需要修改了,这满足了开闭原则

修改方案:

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
package DesignPattern.principle.ocp.improve;


/**
* @Author coderYang
* @Date 2020/12/8 14:03
*/
public class Ocp {
public static void main(String[] args) {
GraphicEditor graphicEditor = new GraphicEditor();
graphicEditor.drawShape(new Rectangle()); //绘制圆形
graphicEditor.drawShape(new Circle()); //绘制矩形
graphicEditor.drawShape(new Triangle()); //绘制三角形
graphicEditor.drawShape(new OtherGraphic()); //绘制其它图形
}
}

//这是一个用于绘图的类 [使用方]
class GraphicEditor{
//接收Shape对象,调用draw方法,此处跟面向接口编程一个道理
public void drawShape(Shape s){
s.draw();
}
}

//Shape类,基类
abstract class Shape{
int m_type;

public abstract void draw(); //抽象方法
}

//矩形类
class Rectangle extends Shape{
//构造方法
Rectangle() {
super.m_type = 1;
}

@Override
public void draw() {
System.out.println("绘制矩形");
}
}

//圆形类
class Circle extends Shape{
Circle(){
super.m_type = 2;
}

@Override
public void draw() {
System.out.println("绘制圆形");
}
}

//新增三角形类
class Triangle extends Shape{
Triangle(){
super.m_type = 3;
}

@Override
public void draw() {
System.out.println("绘制三角形");
}
}

//新增其它一个图形
class OtherGraphic extends Shape{
OtherGraphic(){
super.m_type = 4;
}

@Override
public void draw() {
System.out.println("绘制其它图形");
}
}

6.迪米特法则(Demeter Principle)

基本介绍:

  1. 一个对象应该对其他对象保持最少的了解。
  2. 类与类关系越密切,耦合度越大。
  3. 迪米特法则(Demeter Principle)又叫最少知道原则,即一个类对自己依赖的类知道的越少越好。也就是说,对于依赖的类不管多么复杂,都尽量将逻辑封装在类的内部。对外除了提供的public方法,不要对外泄露任何信息。
  4. 迪米特法则还有个更简单的定义:只与直接的朋友通信。
  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
package DesignPattern.principle.demeter;

import java.util.ArrayList;
import java.util.List;

/**
* @Author coderYang
* @Date 2020/12/8 15:23
*/
public class Demeter1 {
public static void main(String[] args) {
SchoolManager schoolManager = new SchoolManager();
schoolManager.printAllEmployee(new CollegeManager());
/*
* 输出:
*---------学院员工-----------
* 学院员工id=0
* 学院员工id=1
* 学院员工id=2
* 学院员工id=3
* 学院员工id=4
* 学院员工id=5
* 学院员工id=6
* 学院员工id=7
* 学院员工id=8
* 学院员工id=9
* --------学校总部员工-----------
* 学校总部员工id=0
* 学校总部员工id=1
* 学校总部员工id=2
* 学校总部员工id=3
* 学校总部员工id=4
*/
}
}

//学校总部员工类
class Employee{
private String id;

public void setId(String id) {
this.id = id;
}

public String getId() {
return id;
}
}

//学院员工类
class CollegeEmployee{
private String id;

public void setId(String id) {
this.id = id;
}

public String getId() {
return id;
}
}


//管理学院员工的管理类
class CollegeManager{
//返回学院的所有员工
public List<CollegeEmployee> getAllEmployee(){
List<CollegeEmployee> list = new ArrayList<CollegeEmployee>();
for (int i=0;i<10;i++){ //此处增加了10个员工到list集合
CollegeEmployee emp = new CollegeEmployee();
emp.setId("学院员工id="+i);
list.add(emp);
}
return list;
}
}

//SchoolManager类的直接朋友:Employee,CollegeManager
//CollegeEmployee 不是直接朋友,而是一个陌生类,所以违反了迪米特原则
//学校管理类
class SchoolManager{
//返回学校总部的员工
public List<Employee> getAllEmployee(){
List<Employee> list = new ArrayList<Employee>();
for (int i=0;i<5;i++){
Employee emp = new Employee();
emp.setId("学校总部员工id="+i);
list.add(emp);
}
return list;
}

//该方法完成输出学校总部和学院员工信息(id)
public void printAllEmployee(CollegeManager sub){

/*
* 分析问题:
* 1.这里的CollegeEmployee不是SchoolManager的直接朋友
* 2.CollegeEmployee是以局部变量的方式出现在SchoolManager
* 3.违反了迪米特原则
*/
//获取学院的员工
List<CollegeEmployee> list1 = sub.getAllEmployee();
System.out.println("---------学院员工-----------");
for (CollegeEmployee e:list1){
System.out.println(e.getId());
}
//获取学校总部的员工
List<Employee> list2 = this.getAllEmployee();
System.out.println("--------学校总部员工-----------");
for (Employee e:list2){
System.out.println(e.getId());
}
}
}

改进方式:

  1. 前面代码设计的问题在于SchoolManager中,CollegeEmployee类并不是SchoolManager类的直接朋友(分析)
  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
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
package DesignPattern.principle.demeter.improve;

import java.util.ArrayList;
import java.util.List;

/**
* @Author coderYang
* @Date 2020/12/8 15:23
*/
public class Demeter1 {
public static void main(String[] args) {
SchoolManager schoolManager = new SchoolManager();
schoolManager.printAllEmployee(new CollegeManager());
/*
* 输出:
*---------学院员工-----------
* 学院员工id=0
* 学院员工id=1
* 学院员工id=2
* 学院员工id=3
* 学院员工id=4
* 学院员工id=5
* 学院员工id=6
* 学院员工id=7
* 学院员工id=8
* 学院员工id=9
* --------学校总部员工-----------
* 学校总部员工id=0
* 学校总部员工id=1
* 学校总部员工id=2
* 学校总部员工id=3
* 学校总部员工id=4
*/
}
}

//学校总部员工类
class Employee{
private String id;

public void setId(String id) {
this.id = id;
}

public String getId() {
return id;
}
}

//学院员工类
class CollegeEmployee{
private String id;

public void setId(String id) {
this.id = id;
}

public String getId() {
return id;
}
}


//管理学院员工的管理类
class CollegeManager{
//返回学院的所有员工
public List<CollegeEmployee> getAllEmployee(){
List<CollegeEmployee> list = new ArrayList<CollegeEmployee>();
for (int i=0;i<10;i++){ //此处增加了10个员工到list集合
CollegeEmployee emp = new CollegeEmployee();
emp.setId("学院员工id="+i);
list.add(emp);
}
return list;
}

/*输出学院员工的信息
就是自己的方法应该在自己的类中存在,不要写到别人的方法体中
*/
public void printEmployee(){
List<CollegeEmployee> list1 = this.getAllEmployee();
System.out.println("---------学院员工-----------");
for (CollegeEmployee e:list1){
System.out.println(e.getId());
}
}

}

//SchoolManager类的直接朋友:Employee,CollegeManager
//CollegeEmployee 不是直接朋友,而是一个陌生类,所以违反了迪米特原则
//学校管理类
class SchoolManager{
//返回学校总部的员工
public List<Employee> getAllEmployee(){
List<Employee> list = new ArrayList<Employee>();
for (int i=0;i<5;i++){
Employee emp = new Employee();
emp.setId("学校总部员工id="+i);
list.add(emp);
}
return list;
}

//该方法完成输出学校总部和学院员工信息(id)
public void printAllEmployee(CollegeManager sub){

/*
1.将输出学院的员工方法,封装到CollegeManager
*/
sub.printEmployee();

//获取学校总部的员工
List<Employee> list2 = this.getAllEmployee();
System.out.println("--------学校总部员工-----------");
for (Employee e:list2){
System.out.println(e.getId());
}
}
}

迪米特法则注意事项和细节

  1. 迪米特法则的核心是降低类之间的耦合。
  2. 但是注意:由于每个类都减少了不必要的依赖,因为迪米特法则只是要求降低类间(对象间)耦合关系,并不是完全没有依赖。

7.合成复用原则(Composite Reuse Principle)

基本介绍:

原则是尽量使用合成/聚合的方式,而不是使用继承。

image-20201213192257278

8.设计原则核心思想

  1. 找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。
  2. 针对接口编程,而不是针对实现编程。
  3. 为了交互对象之间的松耦合设计而努力。

类和类之间的关系

类与类之间的关系:依赖、泛化(继承)、实现、关联、聚合与组合

1.依赖(Dependence)

只要是类中用到了对方,那么它们之间就存在依赖关系。如果没有对方,连编译都通过不了

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
package DesignPattern.uml.dependence;

/**
* @Author coderYang
* @Date 2020/12/13 20:19
*/
public class PersonServiceBean {
private PersonDao personDao; //类

public void save(Person person){

}

public IDCard getIDCard(Integer personid){
return null;
}

public void modify(){
//此处违反了迪米特法则
Department department = new Department();
}
}


class Department{

}

class IDCard{

}


class Person{

}


class PersonDao{

}

image-20201213203405516

结论(构成依赖的条件):

  1. 类中用到了对方
  2. 类的成员属性
  3. 方法的返回类型
  4. 方法接收的参数类型
  5. 方法中使用到

2.泛化(Generalization)

泛化关系实际上就是继承关系,他是依赖关系的特例

image-20201213210434581

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package DesignPattern.uml.generalization;

/**
* @Author coderYang
* @Date 2020/12/13 20:43
*/
public abstract class DaoSupport {
public void save(Object entity){

}

public void delete(Object id){

}
}


class PersonServiceBean extends DaoSupport{

}

结论:

  1. 泛化关系实际上就是继承关系.
  2. 如果A类继承了B类,我们就说A和B存在泛化关系.

3.实现(Implementation)

实现关系实际上就是A类实现B类,他就是依赖关系的特例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package DesignPattern.uml.implementation;

/**
* @Author coderYang
* @Date 2020/12/13 20:56
*/
public class PersonServiceBean implements PersonService{

@Override
public void delete(Integer id) {
System.out.println("delete..");
}
}

interface PersonService{
public void delete(Integer id);
}

image-20201213210626944

4.关联(Association)

关联关系实际上就是类与类之间的关系,他是依赖关系的特例

关联关系具有导航性:即双向关系或者单项关系

关系具有多重性:如”1”(表示有且只有一个),”0…”(表示0个或者多个),”0,1”(表示0个或者一个),”n…m”(表示n到m个都可以),”m…*”(表示至少m个)。

image-20201214090940531

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
package DesignPattern.uml.association;

/**
* @Author coderYang
* @Date 2020/12/14 9:07
*/

//单项一对一关系
//public class Person {
// private IDCard card;
//}
//
//class IDCard{
//
//}


//单项一对多关系
public class Person {
private IDCard card;
}

class IDCard{
private Person person;
}

5.聚合(Aggregation)

聚合关系(Aggregation)表示的是整体和部分的关系整体与部分可以分开聚合关系是关联关系的特例,所以他具有关联的导航性与多重性。

如:一台电脑由键盘(keyboard)、显示器(monitor),鼠标等组成;组成电脑的各个配件是可以从电脑上分离出来的,使用带空心菱形的实线来表示:

image-20201214092212886

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
package DesignPattern.uml.aggregation;

/**
* @Author coderYang
* @Date 2020/12/14 9:17
*/
public class Computer {
private Mouse mouse; //鼠标可以和computer分离
private Monitor monitor; //显示器可以和computer分离

public void setMouse(Mouse mouse) {
this.mouse = mouse;
}

public void setMonitor(Monitor monitor) {
this.monitor = monitor;
}
}

class Mouse{

}

class Monitor{

}

如果我们人,Mouse,Monitor和Computer是不可分离的,则升级为组合关系:

代码如下:

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
package DesignPattern.uml.aggregation;

/**
* @Author coderYang
* @Date 2020/12/14 9:17
*/




public class Computer {
//如此,耦合关系增强.即为组合关系
private Mouse mouse = new Mouse();
private Monitor monitor = new Monitor();

public void setMouse(Mouse mouse) {
this.mouse = mouse;
}

public void setMonitor(Monitor monitor) {
this.monitor = monitor;
}
}

class Mouse{

}

class Monitor{

}

6.组合(Composition)

组合关系:整体与部分的关系,但是整体与部分不可以分开,组合关系是关联关系的特例

image-20201214093134897

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
package DesignPattern.uml.composition;

/**
* @Author coderYang
* @Date 2020/12/14 9:17
*/

public class Computer {
private Mouse mouse = new Mouse(); //鼠标可以和computer不可分离,耦合关系增强
private Monitor monitor = new Monitor(); //显示器可以和computer不可分离

public void setMouse(Mouse mouse) {
this.mouse = mouse;
}

public void setMonitor(Monitor monitor) {
this.monitor = monitor;
}
}

class Mouse{

}

class Monitor{

}

第二个案例:在程序中我们定义实体:Person与IDCard、Head,那么Head和Person就是组合,IDCard和Person就是聚合。

但是如果在程序中Person实体中定义了对IDCard进行级联删除,即删除Person时连同IDCard一起删除,那么IDCard和Person就是组合了。(只要是同生共死关系就是组合)

image-20201214093625178

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package DesignPattern.uml.composition;

/**
* @Author coderYang
* @Date 2020/12/14 9:33
*/
public class Person {
private IDCard idcard; //聚合关系
private Head head = new Head(); //组合关系,人不能没有头
}

class IDCard{

}

class Head{

}

设计模式类型

设计模式分为三种类型,共23钟

  1. 创建型模式单例模式、抽象工厂模式、原型模式、建造者模式、工厂模式
  2. 结构型模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式
  3. 行为型模式:模板方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式(Interpreter模式)、状态模式、策略模式、职责链模式(责任链模式)。

1.单例设计模式

所谓类的单例设计模式,就是采取一定的方法保证在整个软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)。

比如Hibernate的SessionFactory,它充当数据存储源的代理,并负责创建Session对象。SessionFactory并不是轻量级的,一般情况下,一个项目通常只需要一个SessionFactory就够了,这就会使用到单例模式。

单例设计模式八种形式
  1. 饿汉式(静态常量)
  2. 饿汉式(静态代码块)
  3. 懒汉式(线程不安全)
  4. 懒汉式(线程安全,同步方法)
  5. 懒汉式(线程安全,同步代码块)
  6. 双重检查
  7. 静态内部类
  8. 枚举
饿汉式(静态常量)

步骤如下:

  1. 构造器私有化(防止new)
  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
package DesignPattern.singleton.type1;

/**
* @Author coderYang
* @Date 2020/12/16 22:30
*/
public class SingletonTest01 {
public static void main(String[] args) {
//测试
Singleton instance = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println(instance == instance2); //true
System.out.println("instance.hashCode="+instance.hashCode()); //instance.hashCode=1504109395
System.out.println("instance2.hashCode="+instance2.hashCode()); //instance2.hashCode=1504109395
}
}

//饿汉式(静态变量)
class Singleton{
//1.构造器私有化,外部不能new
private Singleton(){

}

//2.本类内部创建对象实例
private final static Singleton instance = new Singleton();

//对外提供一个公有的静态方法,返回实例对象
public static Singleton getInstance(){
return instance;
}
}

优缺点说明:

  1. 优点:这种写法比较简单,就是在类装载的时候就完成了实例化。避免了线程同步问题。
  2. 缺点:在类装载的时候就完成实例化,没有达到Lazy Loading的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费。
  3. 这种方式基于classloader机制避免了多线程的同步问题,不过,instance在类装载时就实例化,在单例模式中大多数都是调用getInstance方法,但是导致类装载的原因有很多种,因此不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化instance就没有达到lazy loading的效果。
  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
package DesignPattern.singleton.type2;

/**
* @Author coderYang
* @Date 2020/12/16 22:30
*/
public class SingletonTest02 {
public static void main(String[] args) {
//测试
Singleton instance = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println(instance == instance2); //true
System.out.println("instance.hashCode="+instance.hashCode()); //instance.hashCode=1504109395
System.out.println("instance2.hashCode="+instance2.hashCode()); //instance2.hashCode=1504109395
}
}

//饿汉式(静态代码块)
class Singleton{
//1.构造器私有化,外部不能new
private Singleton(){

}

//2.本类内部创建对象实例
private static Singleton instance;

static { //在静态代码块中,创建单例对象
instance = new Singleton();
}

//对外提供一个公有的静态方法,返回实例对象
public static Singleton getInstance(){
return instance;
}
}

优缺点说明:

  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
package DesignPattern.singleton.type3;

/**
* @Author coderYang
* @Date 2020/12/17 10:23
*/
public class SingletonTest03 {
public static void main(String[] args) {
System.out.println("懒汉式1(线程不安全)");
Singleton instance = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println(instance == instance2); //true
System.out.println("instance.hashCode="+instance.hashCode()); //instance.hashCode=1504109395
System.out.println("instance2.hashCode="+instance2.hashCode()); //instance2.hashCode=1504109395
}
}


class Singleton{
private static Singleton instance;

//私有构造器
private Singleton(){

}

//提供一个静态公有方法,当使用到该方法时,才去创建instance
//即懒汉式
public static Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}

}

优缺点说明:

  1. 起到了Lazy Loading的效果,但是只能在单线程下使用。
  2. 如果在多线程下,一个线程进入了if(singleton == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时变会产生多个实例。所以在多线程环境下不可使用这种方式。
  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
33
34
35
36
package DesignPattern.singleton.type3;

/**
* @Author coderYang
* @Date 2020/12/17 10:23
*/
public class SingletonTest03 {
public static void main(String[] args) {
System.out.println("懒汉式1(线程安全)"); //懒汉式1(线程不安全)
Singleton instance = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println(instance == instance2); //true
System.out.println("instance.hashCode="+instance.hashCode()); //instance.hashCode=1504109395
System.out.println("instance2.hashCode="+instance2.hashCode()); //instance2.hashCode=1504109395
}
}


class Singleton{
private static Singleton instance;

//私有构造器
private Singleton(){

}

//提供一个静态公有方法,当使用到该方法时,才去创建instance
//即懒汉式
public static Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}

}

优缺点说明:

  1. 解决了线程不安全问题。
  2. 效率太低了,每个线程在想获得类的实例的时候,执行getInstance()方法都要进行同步。而其实这个方法只执行一次实例化代码就够了,后面想要获得该类实例,直接return就行了。方法进行同步效率太低。
  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
33
34
35
36
37
38
39
40
41
42
package DesignPattern.singleton.type5;

/**
* @Author coderYang
* @Date 2020/12/17 10:23
*/
public class SingletonTest05 {
public static void main(String[] args) {
System.out.println("双重检查"); //双重检查
Singleton instance = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println(instance == instance2); //true
System.out.println("instance.hashCode="+instance.hashCode()); //instance.hashCode=1504109395
System.out.println("instance2.hashCode="+instance2.hashCode()); //instance2.hashCode=1504109395
}
}


class Singleton{
//volatile 用于将变量标记为“存储于主内存中”,volatile防止指令重排
private static volatile Singleton instance;

//私有构造器
private Singleton(){

}

//提供一个静态公有方法,加入双重检查代码,解决线程安全问题,同时解决懒加载问题
//同时保证了效率,推荐使用
public static Singleton getInstance(){
if(instance == null){
//同步代码块
synchronized (Singleton.class){
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}

}

对象的创建可能发生指令的重排序,使用 volatile 可以禁止指令的重排序,保证多线程环境内的系统安全。

优缺点说明:

  1. Double-Check概念是多线程开发中经常遇到的,如代码中所示,我们进行了两次if(singleton == null)检查,这样就可以保证线程安全了。
  2. 这样,实例化代码只用执行一次,后面再次访问时,判断if(singleton == null),直接retrun实例化对象,也避免了反复进行方法同步。
  3. 线程安全:延迟加载(用到的时候再new);效率极高。
  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
package DesignPattern.singleton.type6;

/**
* @Author coderYang
* @Date 2020/12/17 10:23
*/
public class SingletonTest06 {
public static void main(String[] args) {
System.out.println("使用静态内部类完成单例模式"); //使用静态内部类完成单例模式
Singleton instance = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println(instance == instance2); //true
System.out.println("instance.hashCode="+instance.hashCode()); //instance.hashCode=2047526627
System.out.println("instance2.hashCode="+instance2.hashCode()); //instance2.hashCode=2047526627
}
}


// 静态内部类完成单例模式,JVM底层装载类是同步的,所以是线程安全的
class Singleton{

//私有构造器
private Singleton(){

}

//静态内部类,该类中有一个静态属性Singleton,内部类不会在其外部类被加载的同时被加载
private static class SingletonInstance{
private static final Singleton INSTANCE = new Singleton();
}

//提供一个静态公有方法,直接返回SingletonInstance.INSTANCE
public static Singleton getInstance(){
return SingletonInstance.INSTANCE;
}

}

优缺点说明:

  1. 外部类的装载,不会导致内部类的装载。这种方式采用类装载的机制来保证初始化实例时只有一个线程。
  2. 静态内部类方式在Singleton类被装载的时候并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载SIngletonInstance类,从而完成Singleton的实例化。
  3. 类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全,在类初始化时,别的线程是无法进入的。
  4. 优点:避免了线程不安全,利用静态内部类特点实现延迟加载,效率高
  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
package DesignPattern.singleton.type7;

/**
* @Author coderYang
* @Date 2020/12/17 16:14
*/
public class SingletonTest07 {
public static void main(String[] args) {
Singleton instance = Singleton.INSTANCE;
Singleton instance2 = Singleton.INSTANCE;
System.out.println(instance == instance2); //true
System.out.println(instance.hashCode()); //1504109395
System.out.println(instance2.hashCode()); //1504109395

instance.sayOK(); //OK
}
}

//使用枚举,可以实现单例
enum Singleton{
INSTANCE; //属性
public void sayOK(){
System.out.println("OK");
}
}

优缺点说明:

  1. 借助JDK1.5添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象
  2. 这种方式是Effective Java作者Josh Bloch提倡的方式。
  3. 结论:推荐使用。
单例模式在JDK中应用分析

image-20201217163157262

image-20201217163219313

单例模式注意事项和细节
  1. 单例模式保证了系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能。
  2. 当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用new
  3. 单例模式使用的场景:需要频繁的进行创建和销毁的对象、创建对象时消耗过多或耗费资源过多(即:重量级对象),但又经常用到的对象、工具类对象、频繁访问数据库或文件的对象(比如数据源session工厂等)

2.工厂设计模式

需求:

要便于披萨种类的扩展,要便于维护。

1.披萨的种类很多(比如GreekPizz、CheesePizz等)

2.披萨的制作有prepare,bake,cut,box

3.完成披萨店的订购功能。

传统方式

image-20201218140429673

image-20201218140826910

Pizza.java

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
package DesignPattern.factory.simplefactory.pizzastore.pizza;

/**
* @Author coderYang
* @Date 2020/12/18 13:24
*/
//将Pizza类做成抽象类
public abstract class Pizza {
protected String name; //披萨名字

//准备原材料,不同的披萨不一样,因此我们做成抽象方法
public abstract void prepare();

public void bake(){
System.out.println(name+"baking;");
}

public void cut(){
System.out.println(name+"cutting;");
}

//打包
public void box(){
System.out.println(name+"boxing;");
}

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

GreekPizza.java

1
2
3
4
5
6
7
8
9
10
11
12
13
package DesignPattern.factory.simplefactory.pizzastore.pizza;

/**
* @Author coderYang
* @Date 2020/12/18 13:38
*/
public class GreekPizza extends Pizza{

@Override
public void prepare() {
System.out.println("给希腊披萨准备原材料");
}
}

CheesePizza.java

1
2
3
4
5
6
7
8
9
10
11
12
package DesignPattern.factory.simplefactory.pizzastore.pizza;

/**
* @Author coderYang
* @Date 2020/12/18 13:38
*/
public class CheesePizza extends Pizza{
@Override
public void prepare() {
System.out.println("给希腊披萨准备原材料");
}
}

OrderPizza.java

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
package DesignPattern.factory.simplefactory.pizzastore.order;

import DesignPattern.factory.simplefactory.pizzastore.pizza.CheesePizza;
import DesignPattern.factory.simplefactory.pizzastore.pizza.GreekPizza;
import DesignPattern.factory.simplefactory.pizzastore.pizza.Pizza;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/**
* @Author coderYang
* @Date 2020/12/18 13:31
*/
public class OrderPizza {

//构造器
public OrderPizza() {
Pizza pizza = null;
String orderType; //订购披萨的类型
do{
orderType = getType();

if (orderType.equals("greek")){
pizza = new GreekPizza();
pizza.setName("希腊披萨");
}else if (orderType.equals("cheese")){
pizza = new CheesePizza();
pizza.setName("奶酪披萨");
}else {
break;
}
//输出披萨制作过程
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
}while (true);
}

//动态获取客户希望订购的披萨种类
private String getType(){

try {
BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
System.out.println("input pizza type:");
String str = strin.readLine();
return str;
} catch (IOException e) {
e.printStackTrace();
}
return "";
}
}

PizzaStore.java

1
2
3
4
5
6
7
8
9
10
11
12
13
package DesignPattern.factory.simplefactory.pizzastore.order;

/**
* @Author coderYang
* @Date 2020/12/18 13:50
*/

//相当于一个客户端,发出订购
public class PizzaStore {
public static void main(String[] args) {
new OrderPizza();
}
}

传统方式的优缺点

  1. 优点就是比较好理解,简单容易操作。

  2. 缺点就是违反了设计模式的OCP原则,即对扩展开发,对修改关闭。即当我们给类增加新功能的时候,尽量不修改代码,或者尽可能少修改代码。

  3. 比如我们这时要新增加一个Pizaa的种类(Pepper披萨),我们需要做以下修改。

image-20201218142221629

image-20201218142132582

如此一来,代码就会变得及其冗余,耦合性高。

image-20201218142535914

改进思路

分析:修改代码可以接受,但是如果我们在其它的地方创建有Pizza的代码,就意味着,也需要修改,而创建Pizza的代码,往往有很多处。

思路:把创建Pizza对象封装到一个类中,这样我们有新的Pizza种类时,只需要修改该类即可,其它有创建到Pizza对象的代码就不需要修改了(简单工厂模式)

简单工厂模式

基本介绍:

  1. 简单工厂模式是属于创建型模式,是工厂模式的一种。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式。
  2. 简单工厂模式:定义了一个创建对象的类,由这个类来封装实例化对象的行为(代码)
  3. 在软件开发中,当我们会用到大量的创建某种、某类或某批对象时,就会使用到工厂模式。

image-20201219124941944

代码如下:

image-20201219110042354

order包

SimpleFactory.java

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
package DesignPattern.factory.simplefactory.pizzastore.order;

import DesignPattern.factory.simplefactory.pizzastore.pizza.CheesePizza;
import DesignPattern.factory.simplefactory.pizzastore.pizza.GreekPizza;
import DesignPattern.factory.simplefactory.pizzastore.pizza.PepperPizza;
import DesignPattern.factory.simplefactory.pizzastore.pizza.Pizza;

/**
* @Author coderYang
* @Date 2020/12/18 14:46
*/

//简单工厂类
public class SimpleFactory {
//根据orderType返回对应的Pizza对象
public Pizza createPizza(String orderType){

Pizza pizza = null;

System.out.println("使用简单工厂模式");
if (orderType.equals("greek")){
pizza = new GreekPizza();
pizza.setName("希腊披萨");
}else if (orderType.equals("cheese")){
pizza = new CheesePizza();
pizza.setName("奶酪披萨");
}else if (orderType.equals("pepper")){
pizza = new PepperPizza();
pizza.setName("胡椒披萨");
}
return pizza;
}
}

OrderPizza.java

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
package DesignPattern.factory.simplefactory.pizzastore.order;

import DesignPattern.factory.simplefactory.pizzastore.pizza.Pizza;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/**
* @Author coderYang
* @Date 2020/12/18 13:31
*/
public class OrderPizza {

//定义一个简单工厂对象
SimpleFactory simpleFactory;

Pizza pizza = null;

//构造器
public OrderPizza(SimpleFactory simpleFactory) {
this.setFactory(simpleFactory);
}

public void setFactory(SimpleFactory simpleFactory){
String orderType = ""; //用户输入的
this.simpleFactory = simpleFactory; //简单工厂对象

do {
orderType = getType();
pizza = this.simpleFactory.createPizza(orderType);

//输出pizza
if(pizza != null){
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
}else{
System.out.println("订购披萨失败");
break;
}
}while (true);
}

//动态获取客户希望订购的披萨种类
private String getType(){

try {
BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
System.out.println("input pizza type:");
String str = strin.readLine();
return str;
} catch (IOException e) {
e.printStackTrace();
}
return "";
}
}

PizzaStore.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package DesignPattern.factory.simplefactory.pizzastore.order;

/**
* @Author coderYang
* @Date 2020/12/18 13:50
*/

//相当于一个客户端,发出订购
public class PizzaStore {
public static void main(String[] args) {
new OrderPizza(new SimpleFactory());
System.out.println("程序退出....");
}
}

pizza包:

Pizza.java

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
package DesignPattern.factory.simplefactory.pizzastore.pizza;

/**
* @Author coderYang
* @Date 2020/12/18 13:24
*/
//将Pizza类做成抽象类
public abstract class Pizza {
protected String name; //披萨名字

//准备原材料,不同的披萨不一样,因此我们做成抽象方法
public abstract void prepare();

public void bake(){
System.out.println(name+"baking;");
}

public void cut(){
System.out.println(name+"cutting;");
}

//打包
public void box(){
System.out.println(name+"boxing;");
}

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

CheesePizza.java

1
2
3
4
5
6
7
8
9
10
11
12
package DesignPattern.factory.simplefactory.pizzastore.pizza;

/**
* @Author coderYang
* @Date 2020/12/18 13:38
*/
public class CheesePizza extends Pizza{
@Override
public void prepare() {
System.out.println("给奶酪披萨准备原材料");
}
}

Greek.java

1
2
3
4
5
6
7
8
9
10
11
12
13
package DesignPattern.factory.simplefactory.pizzastore.pizza;

/**
* @Author coderYang
* @Date 2020/12/18 13:38
*/
public class GreekPizza extends Pizza{

@Override
public void prepare() {
System.out.println("给希腊披萨准备原材料");
}
}

如果把工厂类的createPizza()方法改为静态的,那么就变成了静态工厂方法.

SimpleFactory.java

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
package DesignPattern.factory.simplefactory.pizzastore.order;

import DesignPattern.factory.simplefactory.pizzastore.pizza.CheesePizza;
import DesignPattern.factory.simplefactory.pizzastore.pizza.GreekPizza;
import DesignPattern.factory.simplefactory.pizzastore.pizza.PepperPizza;
import DesignPattern.factory.simplefactory.pizzastore.pizza.Pizza;

/**
* @Author coderYang
* @Date 2020/12/18 14:46
*/

//简单工厂类
public class SimpleFactory {
//根据orderType返回对应的Pizza对象
// public Pizza createPizza(String orderType){
//
// Pizza pizza = null;
//
// System.out.println("使用简单工厂模式");
// if (orderType.equals("greek")){
// pizza = new GreekPizza();
// pizza.setName("希腊披萨");
// }else if (orderType.equals("cheese")){
// pizza = new CheesePizza();
// pizza.setName("奶酪披萨");
// }else if (orderType.equals("pepper")){
// pizza = new PepperPizza();
// pizza.setName("胡椒披萨");
// }
// return pizza;
// }


//静态工厂模式
public static Pizza createPizza2(String orderType){

Pizza pizza = null;

System.out.println("使用简单工厂模式");
if (orderType.equals("greek")){
pizza = new GreekPizza();
pizza.setName("希腊披萨");
}else if (orderType.equals("cheese")){
pizza = new CheesePizza();
pizza.setName("奶酪披萨");
}else if (orderType.equals("pepper")){
pizza = new PepperPizza();
pizza.setName("胡椒披萨");
}
return pizza;
}
}

OrderPizza2.java

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
package DesignPattern.factory.simplefactory.pizzastore.order;

import DesignPattern.factory.simplefactory.pizzastore.pizza.Pizza;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/**
* @Author coderYang
* @Date 2020/12/19 11:14
*/
public class OrderPizza2 {
Pizza pizza;
String orderType = ""; //接收用户输入

public OrderPizza2() {
do {
orderType = this.getType();
pizza = SimpleFactory.createPizza2(orderType);

//输出pizza
if(pizza != null){
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
}else{
System.out.println("订购披萨失败");
break;
}
}while (true);
}



//动态获取客户希望订购的披萨种类
private String getType(){

try {
BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
System.out.println("input pizza type:");
String str = strin.readLine();
return str;
} catch (IOException e) {
e.printStackTrace();
}
return "";
}

}

PizzaStore.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package DesignPattern.factory.simplefactory.pizzastore.order;

/**
* @Author coderYang
* @Date 2020/12/18 13:50
*/

//相当于一个客户端,发出订购
public class PizzaStore {
public static void main(String[] args) {
// new OrderPizza(new SimpleFactory());
// System.out.println("程序退出....");

//静态工厂
new OrderPizza2();
}
}
工厂方法模式

新需求:

披萨项目新的需求:客户在点披萨时,可以点击不同口味的披萨,比如北京的奶酪pizza、北京的胡椒pizza或者是伦敦的奶酪pizza、伦敦的胡椒pizza。

  • 思路一:使用简单工厂模式,创建不同的简单工厂类,比如BJPizzaSimpleFactory、LDPizzaSimpleFactory等等。从这个案例来说,也是可以的,但是考虑到项目的规模,以及软件的可维护性、可扩展性并不是特别好。
  • 思路二:使用工厂方法模式。

工厂方法模式介绍

工厂方法模式设计方案:将披萨项目的实例化功能抽象成抽象方法,在不同的口味点餐子类中具体实现。

工厂方法模式定义了一个创建对象的抽象方法,由子类决定要实例化的类。工厂方法模式将对象的实例化推迟到子类

image-20201219155138394

项目目录

image-20201219154825085

Pizza包

Pizza.java

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
package DesignPattern.factory.factorymethod.pizzastore.pizza;

/**
* @Author coderYang
* @Date 2020/12/18 13:24
*/
//将Pizza类做成抽象类
public abstract class Pizza {
protected String name; //披萨名字

//准备原材料,不同的披萨不一样,因此我们做成抽象方法
public abstract void prepare();

public void bake(){
System.out.println(name+"baking;");
}

public void cut(){
System.out.println(name+"cutting;");
}

//打包
public void box(){
System.out.println(name+"boxing;");
}

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

BJCheesePizza.java

1
2
3
4
5
6
7
8
9
10
11
12
13
package DesignPattern.factory.factorymethod.pizzastore.pizza;

/**
* @Author coderYang
* @Date 2020/12/18 13:38
*/
public class BJCheesePizza extends Pizza {
@Override
public void prepare() {
setName("北京奶酪披萨");
System.out.println("北京奶酪披萨 准备原材料");
}
}

BJPepperPizza.java

1
2
3
4
5
6
7
8
9
10
11
12
13
package DesignPattern.factory.factorymethod.pizzastore.pizza;

/**
* @Author coderYang
* @Date 2020/12/18 13:38
*/
public class BJCheesePizza extends Pizza {
@Override
public void prepare() {
setName("北京奶酪披萨");
System.out.println("北京奶酪披萨 准备原材料");
}
}

LDCheesePizza.java

1
2
3
4
5
6
7
8
9
10
11
12
13
package DesignPattern.factory.factorymethod.pizzastore.pizza;

/**
* @Author coderYang
* @Date 2020/12/18 13:38
*/
public class LDCheesePizza extends Pizza {

@Override
public void prepare() {
System.out.println("伦敦胡椒披萨 准备原材料");
}
}

LDPepperPizza.java

1
2
3
4
5
6
7
8
9
10
11
12
package DesignPattern.factory.factorymethod.pizzastore.pizza;

/**
* @Author coderYang
* @Date 2020/12/19 13:17
*/
public class LDPepperPizza extends Pizza{
@Override
public void prepare() {
System.out.println("伦敦胡椒披萨 准备原材料");
}
}

order包

OrderPizza.java

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
package DesignPattern.factory.factorymethod.pizzastore.order;

import DesignPattern.factory.factorymethod.pizzastore.pizza.Pizza;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/**
* @Author coderYang
* @Date 2020/12/19 11:14
*/
public abstract class OrderPizza {

Pizza pizza;
String orderType; //接受用户输入

//定义一个抽象方法,createPizza,让各个工厂子类自己实现
abstract Pizza createPizza(String orderType);

//构造器
public OrderPizza() {

do {
orderType = this.getType();
pizza = createPizza(orderType); //抽象方法,由工厂子类完成

//输出pizza
if(pizza != null){
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
}else{
System.out.println("订购披萨失败");
break;
}
}while (true);
}



//动态获取客户希望订购的披萨种类
private String getType(){

try {
BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
System.out.println("input pizza type:");
String str = strin.readLine();
return str;
} catch (IOException e) {
e.printStackTrace();
}
return "";
}

}

BJOrderPizza.java

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
package DesignPattern.factory.factorymethod.pizzastore.order;


import DesignPattern.factory.factorymethod.pizzastore.pizza.BJCheesePizza;
import DesignPattern.factory.factorymethod.pizzastore.pizza.BJPepperPizza;
import DesignPattern.factory.factorymethod.pizzastore.pizza.Pizza;

/**
* @Author coderYang
* @Date 2020/12/19 13:38
*/
public class BJOrderPizza extends OrderPizza{
@Override
Pizza createPizza(String orderType) {
Pizza pizza = null;
if("cheese".equals(orderType)){
pizza = new BJCheesePizza();
pizza.setName("北京奶酪披萨");
}else if("pepper".equals(orderType)){
pizza = new BJPepperPizza();
pizza.setName("北京胡椒披萨");
}
return pizza;
}
}

LDOrderPizza.java

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
package DesignPattern.factory.factorymethod.pizzastore.order;


import DesignPattern.factory.factorymethod.pizzastore.pizza.LDCheesePizza;
import DesignPattern.factory.factorymethod.pizzastore.pizza.LDPepperPizza;
import DesignPattern.factory.factorymethod.pizzastore.pizza.Pizza;

/**
* @Author coderYang
* @Date 2020/12/19 13:58
*/
public class LDOrderPizza extends OrderPizza{
@Override
Pizza createPizza(String orderType) {
Pizza pizza = null;
if("cheese".equals(orderType)){
pizza = new LDCheesePizza();
pizza.setName("伦敦奶酪披萨");
}else if("pepper".equals(orderType)){
pizza = new LDPepperPizza();
pizza.setName("伦敦胡椒披萨");
}
return pizza;
}
}

PizzaStore.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package DesignPattern.factory.factorymethod.pizzastore.order;

/**
* @Author coderYang
* @Date 2020/12/19 14:06
*/
public class PizzaStore {
public static void main(String[] args) {
//订购北京口味的pizza
// new BJOrderPizza();

//订购伦敦口味的pizza
new LDOrderPizza();
}
}
抽象工厂模式

基本介绍:

  1. 抽象工厂模式:定义了一个interface用于创建相关或有依赖关系的对象簇,而无需指明具体的类
  2. 抽象工厂模式可以将简单工厂模式工厂方法模式进行整合。
  3. 从设计层面看,抽象工厂模式就是对简单工厂模式的改进(或者称为进一步的抽象)。
  4. 将工厂抽象成两层,AbsFactory(抽象工厂)和具体实现的工厂子类。程序员可以根据创建的对象类型使用对应的工厂子类。这样将单个的简单工厂类变成了工厂簇,更利于代码的维护和扩展。

image-20201219163155758

项目目录:

image-20201220105029597

pizza包:

Pizza.java

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
package DesignPattern.factory.absfactory.pizzastore.pizza;

/**
* @Author coderYang
* @Date 2020/12/18 13:24
*/
//将Pizza类做成抽象类
public abstract class Pizza {
protected String name; //披萨名字

//准备原材料,不同的披萨不一样,因此我们做成抽象方法
public abstract void prepare();

public void bake(){
System.out.println(name+"baking;");
}

public void cut(){
System.out.println(name+"cutting;");
}

//打包
public void box(){
System.out.println(name+"boxing;");
}

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

BJCheesePizza.java

1
2
3
4
5
6
7
8
9
10
11
12
13
package DesignPattern.factory.absfactory.pizzastore.pizza;

/**
* @Author coderYang
* @Date 2020/12/18 13:38
*/
public class BJCheesePizza extends Pizza {
@Override
public void prepare() {
setName("北京奶酪披萨");
System.out.println("北京奶酪披萨 准备原材料");
}
}

BJPepperPizza.java

1
2
3
4
5
6
7
8
9
10
11
12
package DesignPattern.factory.absfactory.pizzastore.pizza;

/**
* @Author coderYang
* @Date 2020/12/18 14:18
*/
public class BJPepperPizza extends Pizza {
@Override
public void prepare() {
System.out.println("北京胡椒披萨 准备原材料");
}
}

LDCheesePizza.java

1
2
3
4
5
6
7
8
9
10
11
12
13
package DesignPattern.factory.absfactory.pizzastore.pizza;

/**
* @Author coderYang
* @Date 2020/12/18 13:38
*/
public class LDCheesePizza extends Pizza {

@Override
public void prepare() {
System.out.println("伦敦奶酪披萨 准备原材料");
}
}

LDPepperPizza.java

1
2
3
4
5
6
7
8
9
10
11
12
package DesignPattern.factory.absfactory.pizzastore.pizza;

/**
* @Author coderYang
* @Date 2020/12/19 13:17
*/
public class LDPepperPizza extends Pizza {
@Override
public void prepare() {
System.out.println("伦敦胡椒披萨 准备原材料");
}
}

order包:

AbsFactory.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package DesignPattern.factory.absfactory.pizzastore.order;

import DesignPattern.factory.absfactory.pizzastore.pizza.Pizza;

/**
* @Author coderYang
* @Date 2020/12/19 16:51
*/

//一个抽象工厂模式的抽象层(接口)
public interface AbsFactory {
//让下面的工厂子类来具体实现
public Pizza createPizza(String orderType);
}

BJFactory.java

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
package DesignPattern.factory.absfactory.pizzastore.order;

import DesignPattern.factory.absfactory.pizzastore.pizza.BJCheesePizza;
import DesignPattern.factory.absfactory.pizzastore.pizza.BJPepperPizza;
import DesignPattern.factory.absfactory.pizzastore.pizza.Pizza;

/**
* @Author coderYang
* @Date 2020/12/19 16:53
*/
public class BJFactory implements AbsFactory{
@Override
public Pizza createPizza(String orderType) {
System.out.println("抽象工厂模式");
Pizza pizza = null;
if ("cheese".equals(orderType)){
pizza = new BJCheesePizza();
pizza.setName("北京奶酪披萨");
}else if ("pepper".equals(orderType)){
pizza = new BJPepperPizza();
pizza.setName("北京胡椒披萨");
}
return pizza;
}
}

LDFactory.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package DesignPattern.factory.absfactory.pizzastore.order;

import DesignPattern.factory.absfactory.pizzastore.pizza.*;

/**
* @Author coderYang
* @Date 2020/12/19 16:57
*/
public class LDFactory implements AbsFactory{

@Override
public Pizza createPizza(String orderType) {
System.out.println("抽象工厂模式");
Pizza pizza = null;
if ("cheese".equals(orderType)){
pizza = new LDCheesePizza();
pizza.setName("伦敦奶酪披萨");
}else if ("pepper".equals(orderType)){
pizza = new LDPepperPizza();
pizza.setName("伦敦胡椒披萨");
}
return pizza;
}
}

OrderPizza.java

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
package DesignPattern.factory.absfactory.pizzastore.order;

import DesignPattern.factory.absfactory.pizzastore.pizza.Pizza;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/**
* @Author coderYang
* @Date 2020/12/19 16:59
*/
public class OrderPizza {
AbsFactory factory;

//构造器
public OrderPizza(AbsFactory factory){
setFactory(factory);
}

private void setFactory(AbsFactory factory) {
this.factory = factory;
Pizza pizza = null;
String orderType; //用户输入

do {
orderType = getType();
//factory可能是北京的工厂子类,也可能是伦敦的工厂子类
pizza = factory.createPizza(orderType);
if (pizza != null){
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
}else {
System.out.println("订购失败");
break;
}
}while (true);
}


//动态获取客户希望订购的披萨种类
private String getType(){

try {
BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
System.out.println("input pizza type:");
String str = strin.readLine();
return str;
} catch (IOException e) {
e.printStackTrace();
}
return "";
}


}

PizzaStore.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package DesignPattern.factory.absfactory.pizzastore.order;

/**
* @Author coderYang
* @Date 2020/12/19 17:09
*/
public class PizzaStore {
public static void main(String[] args) {
//订购北京的披萨
// new OrderPizza(new BJFactory());

//订购伦敦的披萨
new OrderPizza(new LDFactory());
}
}
JDK中的简单工厂模式

JDK在Calendar类中,就是用了简单工厂模式。

image-20201220125441399

image-20201220125857350

image-20201220130022358

image-20201220130038232

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
private static Calendar createCalendar(TimeZone zone,
Locale aLocale)
{
CalendarProvider provider =
LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale)
.getCalendarProvider();
if (provider != null) {
try {
return provider.getInstance(zone, aLocale);
} catch (IllegalArgumentException iae) {
// fall back to the default instantiation
}
}

Calendar cal = null;

if (aLocale.hasExtensions()) {
String caltype = aLocale.getUnicodeLocaleType("ca");
if (caltype != null) {
switch (caltype) {
case "buddhist":
cal = new BuddhistCalendar(zone, aLocale);
break;
case "japanese":
cal = new JapaneseImperialCalendar(zone, aLocale);
break;
case "gregory":
cal = new GregorianCalendar(zone, aLocale);
break;
}
}
}
if (cal == null) {
// If no known calendar type is explicitly specified,
// perform the traditional way to create a Calendar:
// create a BuddhistCalendar for th_TH locale,
// a JapaneseImperialCalendar for ja_JP_JP locale, or
// a GregorianCalendar for any other locales.
// NOTE: The language, country and variant strings are interned.
if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
cal = new BuddhistCalendar(zone, aLocale);
} else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"
&& aLocale.getCountry() == "JP") {
cal = new JapaneseImperialCalendar(zone, aLocale);
} else {
cal = new GregorianCalendar(zone, aLocale);
}
}
return cal;
}
工厂模式总结
  1. 工厂模式的意义:将实例化对象的代码提取出来,放到一个类中统一管理和维护,达到和主项目的依赖关系的解耦。从而提高项目的扩展性和维护性。
  2. 三种工厂模式(简单工厂模式、工厂方法模式、抽象工厂模式)。
  3. 设计模式的依赖抽象原则。
  • 创建实例对象时,不要直接new类,而是把这个new类的当作放在一个工厂的方法中,并返回。变量不要直接持有具体类的引用。
  • 不要让类继承具体类,而是继承抽象或者是实现interface(接口)
  • 不要覆盖基类中已经实现的方法。

3.原型模式

克隆羊问题:

​ 现有一只羊tom,姓名为:tom,年龄为:1,颜色为:白色,请编写程序创建和tom羊属性完全相同的十只羊。

传统的方式解决克隆羊问题

image-20201221155858413

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
package DesignPattern.prototype;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
* @Author coderYang
* @Date 2020/12/21 16:01
*/
public class Client {
public static void main(String[] args) {
Sheep sheep = new Sheep("tom",1,"white");
System.out.println(sheep);
int i = 1;
List<Sheep> sheepList = new ArrayList<Sheep>();
//创建十头羊
while (i<=10){
Sheep sheep1 = new Sheep(sheep.getName(),sheep.getAge(),sheep.getColor());
sheepList.add(sheep1);
++i;
}

//创建迭代器对象
Iterator iterator = sheepList.iterator();
int j = 0;
while (iterator.hasNext()){
Object obj = iterator.next();
System.out.println("sheep"+ ++j +"="+obj);
}
}
}

/*
Sheep{name='tom', age=1, color='white'}
sheep1=Sheep{name='tom', age=1, color='white'}
sheep2=Sheep{name='tom', age=1, color='white'}
sheep3=Sheep{name='tom', age=1, color='white'}
sheep4=Sheep{name='tom', age=1, color='white'}
sheep5=Sheep{name='tom', age=1, color='white'}
sheep6=Sheep{name='tom', age=1, color='white'}
sheep7=Sheep{name='tom', age=1, color='white'}
sheep8=Sheep{name='tom', age=1, color='white'}
sheep9=Sheep{name='tom', age=1, color='white'}
sheep10=Sheep{name='tom', age=1, color='white'}
*/

传统方式的优缺点

  1. 优点就是比较好理解,简单易操作。

  2. 在创建对象时,总是需要重新获取原始对象的属性,如果创建的对象比较复杂时,效率较低。

  3. 总是需要重新初始化对象,而不是动态的获取对象运行时的状态,不够灵活。改进思路:原型模式。

原型模式基本介绍

  1. 原型模式(Prototype模式)是指:用原型实例指定创建对象的种类,并通过拷贝这些原型,创建新的对象。
  2. 原型模式是一种创建型模式,允许一个对象再创建另外一个可定制的对象,无需知道如何创建的细节。
  3. 工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们来实施创建,即:对象.clone()
  4. 形象的理解:孙大圣拔出猴毛,变出其它孙大圣。

image-20201221170047766

ConcretePrototype1和2就是复制出来的对象。

改进(原型模式)

​ Java中Object类是所有类的根类,Object类提供了一个clone()方法,该方法可以将一个Java对象复制一份,但是需要实现clone的Java类必须要实现一个接口Cloneable,该接口表示该类能复制且具有复制的能力 => 原型模式。

代码:

Sheep.java

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
package DesignPattern.prototype.improve;

/**
* @Author coderYang
* @Date 2020/12/21 15:59
*/
//此处接口Cloneable里面是没有东西的,可理解为一种标识
public class Sheep implements Cloneable{
private String name;
private int age;
private String color;

public Sheep(String name, int age, String color) {
super();
this.name = name;
this.age = age;
this.color = color;
}

public String getName() {
return name;
}

public int getAge() {
return age;
}

public String getColor() {
return color;
}

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

public void setAge(int age) {
this.age = age;
}

public void setColor(String color) {
this.color = color;
}

@Override
public String toString() {
return "Sheep{" +
"name='" + name + '\'' +
", age=" + age +
", color='" + color + '\'' +
'}';
}


//克隆该实例,使用默认的克隆方法来完成
@Override
protected Object clone(){
Sheep sheep = null;
try {
sheep = (Sheep)super.clone(); //向下转型
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return sheep;
}
}

Client.java

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
package DesignPattern.prototype.improve;

/**
* @Author coderYang
* @Date 2020/12/21 17:41
*/
public class Client {
public static void main(String[] args) {
System.out.println("原型模式完成对象的创建");
Sheep sheep = new Sheep("tom",1,"白色");
Sheep sheep2 = (Sheep)sheep.clone();
Sheep sheep3 = (Sheep)sheep.clone();
Sheep sheep4 = (Sheep)sheep.clone();
Sheep sheep5 = (Sheep)sheep.clone();
Sheep sheep6 = (Sheep)sheep.clone();

System.out.println(sheep == sheep2); //false

System.out.println("sheep2="+sheep2);
System.out.println("sheep3="+sheep3);
System.out.println("sheep4="+sheep4);
System.out.println("sheep5="+sheep5);
System.out.println("sheep6="+sheep6);
}
}

/*
原型模式完成对象的创建
sheep2=Sheep{name='tom', age=1, color='白色'}
sheep3=Sheep{name='tom', age=1, color='白色'}
sheep4=Sheep{name='tom', age=1, color='白色'}
sheep5=Sheep{name='tom', age=1, color='白色'}
sheep6=Sheep{name='tom', age=1, color='白色'}
*/
原型模式在Spring框架中源码分析
浅拷贝与深拷贝

浅拷贝基本介绍:

  1. 对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。
  2. 对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值。
  3. 前面我们所说的克隆羊就是浅拷贝。
  4. 浅拷贝是使用默认的clone()方法来实现 => sheep = (Sheep)super.clone()

Sheep.java

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
package DesignPattern.prototype.improve;


/**
* @Author coderYang
* @Date 2020/12/21 15:59
*/
//此处接口Cloneable里面是没有东西的,可理解为一种标识
public class Sheep implements Cloneable{
private String name;
private int age;
private String color;
private Sheep friend; //对象引用

public Sheep(String name, int age, String color) {
super();
this.name = name;
this.age = age;
this.color = color;
}

public String getName() {
return name;
}

public int getAge() {
return age;
}

public String getColor() {
return color;
}

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

public void setAge(int age) {
this.age = age;
}

public void setColor(String color) {
this.color = color;
}

public Sheep getFriend() {
return friend;
}

public void setFriend(Sheep friend) {
this.friend = friend;
}

@Override
public String toString() {
return "Sheep{" +
"name='" + name + '\'' +
", age=" + age +
", color='" + color + '\'' +
'}';
}


//克隆该实例,使用默认的克隆方法来完成
@Override
protected Object clone(){
Sheep sheep = null;
try {
sheep = (Sheep)super.clone(); //向下转型
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return sheep;
}
}

Client.java

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
package DesignPattern.prototype.improve;

/**
* @Author coderYang
* @Date 2020/12/21 17:41
*/
public class Client {
public static void main(String[] args) {
System.out.println("原型模式完成对象的创建");
Sheep sheep = new Sheep("tom", 1, "白色");
sheep.setFriend(new Sheep("jack", 2, "黑色"));

Sheep sheep2 = (Sheep) sheep.clone();
Sheep sheep3 = (Sheep) sheep.clone();
Sheep sheep4 = (Sheep) sheep.clone();
Sheep sheep5 = (Sheep) sheep.clone();
Sheep sheep6 = (Sheep) sheep.clone();

System.out.println(sheep == sheep2); //false

System.out.println("sheep2=" + sheep2 + sheep2.getFriend().hashCode());
System.out.println("sheep3=" + sheep3 + sheep3.getFriend().hashCode());
System.out.println("sheep4=" + sheep4 + sheep4.getFriend().hashCode());
System.out.println("sheep5=" + sheep5 + sheep5.getFriend().hashCode());
System.out.println("sheep5=" + sheep5 + sheep6.getFriend().hashCode());
}
}


/*
原型模式完成对象的创建
false
sheep2=Sheep{name='tom', age=1, color='白色'}1504109395
sheep3=Sheep{name='tom', age=1, color='白色'}1504109395
sheep4=Sheep{name='tom', age=1, color='白色'}1504109395
sheep5=Sheep{name='tom', age=1, color='白色'}1504109395
sheep5=Sheep{name='tom', age=1, color='白色'}1504109395
*/

深拷贝基本介绍

  1. 复制对象的所有基本数据类型的成员变量值。
  2. 为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象直到该对象可达的所有对象。也就是说,对象进行深拷贝要对整个对象(包括对象的引用类型)进行拷贝
  3. 深拷贝实现方式1重写clone方法来实现深拷贝。
  4. 深拷贝实现方式2:通过对象序列化实现深拷贝(推荐)。

目录:

image-20201222142710421

DeepCloneableTarget.java

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
package DesignPattern.prototype.deepclone;

import java.io.Serializable;

/**
* @Author coderYang
* @Date 2020/12/22 11:22
*/
public class DeepCloneableTarget implements Serializable,Cloneable {

private static final long serialVersionUID = 1L;

private String cloneName;
private String cloneClass;

public DeepCloneableTarget(String cloneName, String cloneClass) {
this.cloneName = cloneName;
this.cloneClass = cloneClass;
}

//因为该类的属性都是String,因此我们这里使用默认的clone方法完成即可
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}

DeepProtoType.java

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
package DesignPattern.prototype.deepclone;

import java.io.*;

/**
* @Author coderYang
* @Date 2020/12/22 11:32
*/
public class DeepProtoType implements Serializable,Cloneable {

public String name; //String属性
public DeepCloneableTarget deepCloneableTarget; //引用类型


//深拷贝方式一:使用clone方法
@Override
protected Object clone() throws CloneNotSupportedException {
Object deep = null;
//这里完成对基本数据类型(属性)和String的克隆
deep = super.clone();
//对引用类型的属性进行单独处理
DeepProtoType deepProtoType = (DeepProtoType)deep;
deepProtoType.deepCloneableTarget = (DeepCloneableTarget)deepCloneableTarget.clone();
return deepProtoType;
}



//深拷贝方式二:通过对象的序列化实现(推荐)
public Object deepClone(){
//创建流对象
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
ByteArrayInputStream bis = null;
ObjectInputStream ois = null;

try {
//序列化
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
oos.writeObject(this); //把当前对象以对象流的方式输出

//反序列化
bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis);
DeepProtoType copyObj = (DeepProtoType)ois.readObject();
return copyObj;
}catch (Exception e){
e.printStackTrace();
return null;
}finally {
//关闭流
try {
bos.close();
oos.close();
bis.close();
ois.close();
} catch (IOException e2) {
e2.printStackTrace();
}
}

}

}

Client.java

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
package DesignPattern.prototype.deepclone;

/**
* @Author coderYang
* @Date 2020/12/22 12:39
*/
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
DeepProtoType deepProtoType = new DeepProtoType();
deepProtoType.name = "小灰灰";
deepProtoType.deepCloneableTarget = new DeepCloneableTarget("程序员小李","程序猿类");

//方式一完成深拷贝
DeepProtoType deepProtoType1 = (DeepProtoType)deepProtoType.clone();
DeepProtoType deepProtoType2 = (DeepProtoType)deepProtoType.clone();

System.out.println("重写clone深拷贝:deepProType1.name="+deepProtoType1.name+"deepProType1.deepCloneableTarget="+deepProtoType1.deepCloneableTarget.hashCode());
System.out.println("重写clone深拷贝:deepProType2.name="+deepProtoType2.name+"deepProType2.deepCloneableTarget="+deepProtoType2.deepCloneableTarget.hashCode());



//方式二完成深拷贝
DeepProtoType deepProtoType3 = (DeepProtoType)deepProtoType.deepClone();
DeepProtoType deepProtoType4 = (DeepProtoType)deepProtoType.deepClone();

System.out.println("序列化深拷贝:deepProType3.name="+deepProtoType3.name+"deepProType3.deepCloneableTarget="+deepProtoType3.deepCloneableTarget.hashCode());
System.out.println("序列化深拷贝:deepProType4.name="+deepProtoType4.name+"deepProType4.deepCloneableTarget="+deepProtoType4.deepCloneableTarget.hashCode());


}
}

/*
输出结果:可以看到
重写clone深拷贝:deepProType1.name=小灰灰deepProType1.deepCloneableTarget=2047526627
重写clone深拷贝:deepProType2.name=小灰灰deepProType2.deepCloneableTarget=760563749
序列化深拷贝:deepProType3.name=小灰灰deepProType3.deepCloneableTarget=703504298
序列化深拷贝:deepProType4.name=小灰灰deepProType4.deepCloneableTarget=201556483

*/
原型模式注意事项及缺点
  1. 创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高效率。
  2. 不用重新初始化对象,而是动态的获取对象运行时的状态
  3. 如果原始对象发生变化(增加或减少属性),其它克隆对象的也会发生相应的改变,无需修改代码。
  4. 在实现深克隆的时候可能需要比较复杂的代码。
  5. 缺点:需要为每一个类配备一个克隆方法,这对全新的类来说不是很难,但对已有的类进行改造时,需要修改其源代码,违背了ocp原则,这点请同学们注意。

4.建造者模式

盖房子需求:

  1. 需要建房子:这一过程为打桩、砌墙、封顶。
  2. 房子有各种各样的,比如普通房,高楼,别墅,各种房子的过程虽然一样,但是要求不要相同。
  3. 请编写程序,完成需求。
传统方式解决建房子需求:

image-20201222211340125

项目目录

image-20201222211411659

AbstractHouse.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package DesignPattern.builder;

/**
* @Author coderYang
* @Date 2020/12/22 21:04
*/
public abstract class AbstractHouse {

//打地基
public abstract void buildBasic();
//砌墙
public abstract void buildWalls();
//封顶
public abstract void roofed();

public void build(){
buildBasic();
buildWalls();
roofed();
}

}

CommonHouse.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package DesignPattern.builder;

/**
* @Author coderYang
* @Date 2020/12/22 21:06
*/
public class CommonHouse extends AbstractHouse {
@Override
public void buildBasic() {
System.out.println("普通房子打地基");
}

@Override
public void buildWalls() {
System.out.println("普通房子砌墙");
}

@Override
public void roofed() {
System.out.println("普通房子封顶");
}
}

Client.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package DesignPattern.builder;

/**
* @Author coderYang
* @Date 2020/12/22 21:10
*/
public class Client {
public static void main(String[] args) {
CommonHouse commonHouse = new CommonHouse();
commonHouse.build();
}
}


/*
普通房子打地基
普通房子砌墙
普通房子封顶
*/

传统方式解决盖房子需求问题分析

  1. 优点是比较好理解,简单易操作。
  2. 设计的程序结构,过于简单,没有设计缓存层对象,程序的扩展和维护不好,也就是说,这种设计方案,把产品(即:房子)和创建产品的过程(即:建房子流程)封装在一起了,耦合性增强了。
  3. 解决方案:将产品和产品建造过程解耦 => 建造者模式
建造者模式介绍

基本介绍:

  1. 建造者模式(Builder Pattern)又叫生成器模式,是一种对象构建模式。它可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。
  2. 建造者模式是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构造它们,用户不需要知道内部的具体构建细节。
image-20201222223258974

建造者模式的四个角色

  1. Product(产品角色):一个具体的产品对象。
  2. Builder(抽象建造者):创建一个Product对象的各个部件指定的接口/抽象类
  3. ConcreteBuilder(具体建造者):实现接口,构建和装配各个部件。
  4. Director(指挥者):构建一个使用Builder接口的对象。它主要是用于创建一个复杂的对象。它主要有两个作用,一是:隔离了客户与对象的生产过程,二是:复杂控制产品对象的生产过程。

image-20201223140955119

建造者模式解决建房子需求

image-20201223112849024

项目目录

image-20201223141130235

House.java

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
package DesignPattern.builder.improve;

/**
* @Author coderYang
* @Date 2020/12/23 10:55
*/
public class House {
private String basic;
private String wall;
private String roofed;

public String getBasic() {
return basic;
}

public String getWall() {
return wall;
}

public String getRoofed() {
return roofed;
}

public void setBasic(String basic) {
this.basic = basic;
}

public void setWall(String wall) {
this.wall = wall;
}

public void setRoofed(String roofed) {
this.roofed = roofed;
}
}

HouseBuilder.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package DesignPattern.builder.improve;

/**
* @Author coderYang
* @Date 2020/12/23 10:56
*/

//抽象的建造者
public abstract class HouseBuilder {
protected House house = new House();

//将建造的流程写好,抽象方法
public abstract void buildBasic();
public abstract void buildWalls();
public abstract void roofed();

//房子建好后,将产品(房子)返回
public House buildHouse(){
return house;
}

}

CommonHouse.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package DesignPattern.builder.improve;

/**
* @Author coderYang
* @Date 2020/12/23 11:00
*/
public class CommonHouse extends HouseBuilder{
@Override
public void buildBasic() {
System.out.println("普通房子打地基,5m");
}

@Override
public void buildWalls() {
System.out.println("普通房子砌墙,10cm");
}

@Override
public void roofed() {
System.out.println("普通房子封顶");
}
}

HighBuilding.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package DesignPattern.builder.improve;

/**
* @Author coderYang
* @Date 2020/12/23 11:01
*/
public class HighBuilding extends HouseBuilder{

@Override
public void buildBasic() {
System.out.println("高楼打地基,100m");
}

@Override
public void buildWalls() {
System.out.println("高楼砌墙,20cm");
}

@Override
public void roofed() {
System.out.println("高楼封顶");
}
}

HouseDirector.java

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
package DesignPattern.builder.improve;

/**
* @Author coderYang
* @Date 2020/12/23 11:03
*/
//指挥者,这里去指定制作流程,返回具体的产品
public class HouseDirector {
HouseBuilder houseBuilder = null;

//构造器传入houseBuilder
public HouseDirector(HouseBuilder houseBuilder) {
this.houseBuilder = houseBuilder;
}

//通过setter传入houseBuilder
public void setHouseBuilder(HouseBuilder houseBuilder) {
this.houseBuilder = houseBuilder;
}

//建造房子的流程交给指挥者
public House constructHouse(){
houseBuilder.buildBasic();
houseBuilder.buildWalls();
houseBuilder.roofed();
return houseBuilder.buildHouse();
}
}

Client.java

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
package DesignPattern.builder.improve;

/**
* @Author coderYang
* @Date 2020/12/23 11:11
*/
public class Client {
public static void main(String[] args) {
//盖普通房子
CommonHouse commonHouse = new CommonHouse();
//指挥者
HouseDirector houseDirector = new HouseDirector(commonHouse);
//完成盖房子,返回产品(普通房子)
House house = houseDirector.constructHouse();

System.out.println("---------------------");

//盖高楼
HighBuilding highBuilding = new HighBuilding();
//重置建造者
houseDirector.setHouseBuilder(highBuilding);
//完成盖房子,返回产品(高楼)
House highHouse = houseDirector.constructHouse();
}
}


/*

普通房子打地基,5m
普通房子砌墙,10cm
普通房子封顶
---------------------
高楼打地基,100m
高楼砌墙,20cm
高楼封顶

*/
建造者模式在JDK中的应用和源码分析

源码中建造者模式角色分析:

  • Appendable接口定义了多个append方法(抽象方法),即Appendable为抽象建造者,定义了抽象方法。
  • AbstractStringBuilder实现了Appendable接口方法,这里的AbstractStringBuilder已经是建造者,只是不能实例化。
  • StringBuilder即充当了指挥者角色,同时充当了具体的建造者,建造方法的实现是由AbstractStringBuilder完成,而StringBuilder继承了AbstractStringBuilder

image-20201224143425503

建造者模式注意事项和细节
  1. 客户端(使用程序)不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。

  2. 每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者,用户使用不同的具体建造者即可得到不同的产品对象

  3. 可以更加精细地控制产品地创建过程。将复杂产品的插件步骤分解在不同方法中,使得创建过程更加清晰,也更加方便使用程序来控制创建过程。

  4. 增加新的具体建造者无须修改原有类库的代码,指挥者类针对抽象建造者类编程,系统扩展方便,符合开闭原则

  5. 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。

  6. 如果产品的内部变化复杂,可能会导致需要定义很多具体的建造者类来实现这种变化,导致系统变得很庞大,因此在这种情况下,要考虑是否选择建造者模式。

  7. 抽象模式跟建造者模式区别

    抽象工厂模式实现对产品家族的创建,一个产品家族是这样的一系列产品:具有不同分类维度的产品组合,采用抽象工厂模式不需要关心构造过程,只关心什么产品由什么工厂生产即可。而建造者模式则是要求按照指定的蓝图建造产品,它的主要目的是通过组装零配件而产生一个新产品。

5.适配器模式

现实生活中的转接头就是适配器的例子。

基本介绍

  1. 适配器模式(Adapter Pattern)将某个类的接口转换成客户端期望的另一个接口表示,主要目的是兼容性,让原本因接口不匹配不能一起工作的两个类可以协同工作。其别名为包装器(Wrapper)
  2. 适配器模式属于结构型模式
  3. 主要分为三类:类适配器模式对象适配器模式接口适配器模式

工作原理

  1. 适配器模式:将一个类的接口转换成另一种接口,让原本接口不兼容的类可以兼容
  2. 从用户的角度看不到被适配器,是解耦的。
  3. 用户调用适配器转化出来的目标接口方法,适配器再调用被适配者的相关接口方法
  4. 用户收到反馈结果,感觉只是和目标接口交互,如图:
image-20201226163501498
类适配器模式

基本介绍:Adapter类,通过继承src类,实现dst类接口,完成src->dst的适配。

案例

​ 以生活中充电器的例子来讲解适配器,充电器本身相当于Adapter,220V交流电相当于src(即被适配者),我们的dst(即目标)是5V直流电。

image-20201226165645679

项目目录

image-20201226171246412

Voltage220V.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package DesignPattern.adapter.classadapter;

/**
* @Author coderYang
* @Date 2020/12/26 16:57
*/
//被适配的类
public class Voltage220V {
//输出220V电压
public int output220V(){
int src = 220;
System.out.println("电压="+src+"V");
return src;
}
}

IVoltage220V.java

1
2
3
4
5
6
7
8
9
10
package DesignPattern.adapter.classadapter;

/**
* @Author coderYang
* @Date 2020/12/26 17:02
*/
//适配接口
public interface IVoltage5V {
public int output5V();
}

VoltageAdapter.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package DesignPattern.adapter.classadapter;

/**
* @Author coderYang
* @Date 2020/12/26 17:02
*/

//适配器类
public class VoltageAdapter extends Voltage220V implements IVoltage5V{

@Override
public int output5V() {
//获取220V电压
int srcV = output220V();
int dstV = srcV / 44;
return dstV;
}
}

Phone.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package DesignPattern.adapter.classadapter;

/**
* @Author coderYang
* @Date 2020/12/26 17:05
*/
public class Phone {
//充电
public void charging(IVoltage5V iVoltage5V){
if (iVoltage5V.output5V() == 5){
System.out.println("电压为5V,可以充电");
}else if (iVoltage5V.output5V() > 5){
System.out.println("电压大于,无法充电");
}
}
}

Client.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package DesignPattern.adapter.classadapter;

/**
* @Author coderYang
* @Date 2020/12/26 17:08
*/
public class Client {
public static void main(String[] args) {
System.out.println("==类适配器==");
Phone phone = new Phone();
phone.charging(new VoltageAdapter());
}
}

/*
==类适配器==
电压=220V
电压为5V,可以充电
*/

类适配器模式注意事项和细节

  1. Java是单继承机制,所以类适配器需要继承src类这一点算是一个缺点,因为这要求dst必须是接口,有一定局限性。
  2. src类的方法在Adapter中会暴露出来,也增加了使用的成本。
  3. 由于其继承了src类,所以它可以根据需求重写src类的方法,使得Adapter的灵活性增强了。
对象适配器模式

对象适配器模式介绍:

  1. 基本思路和类的适配器模式相同,只是将Adapter类作修改,不是继承src类,而是持有src类的实例,以解决兼容的问题。即:持有src类,实现dst类接口,完成src->dst的适配。
  2. 根据合成复用原则,在系统中尽量使用关联关系来替代继承关系。
  3. 对象适配器模式是适配器模式常用的一种

使用对象适配器改进充电器案例

image-20210101170944833

项目目录:

image-20210101172316743

Voltage220V.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package DesignPattern.adapter.objectadapter;

/**
* @Author coderYang
* @Date 2020/12/26 16:57
*/
//被适配的类
public class Voltage220V {
//输出220V电压
public int output220V(){
int src = 220;
System.out.println("电压="+src+"V");
return src;
}
}

IVoltageV.java

1
2
3
4
5
6
7
8
9
10
package DesignPattern.adapter.objectadapter;

/**
* @Author coderYang
* @Date 2020/12/26 17:02
*/
//适配接口
public interface IVoltage5V {
public int output5V();
}

VoltageAdapter.java

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
package DesignPattern.adapter.objectadapter;

/**
* @Author coderYang
* @Date 2020/12/26 17:02
*/

//适配器类
public class VoltageAdapter implements IVoltage5V {

private final Voltage220V voltage220V; //关联关系中的聚合关系

//通过构造器,传入一个Voltage220V的实例
public VoltageAdapter(Voltage220V voltage220V) {
this.voltage220V = voltage220V;
}

@Override
public int output5V() {
int dst = 0;
if(null != voltage220V){
int src = voltage220V.output220V(); //获取220V电压
System.out.println("使用对象适配器进行适配");
dst = src / 44;
System.out.println("适配完成,输出的电压为:"+dst);
}
return dst;
}
}

Phone.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package DesignPattern.adapter.objectadapter;

/**
* @Author coderYang
* @Date 2020/12/26 17:05
*/
public class Phone {
//充电
public void charging(IVoltage5V iVoltage5V){
if (iVoltage5V.output5V() == 5){
System.out.println("电压为5V,可以充电");
}else if (iVoltage5V.output5V() > 5){
System.out.println("电压大于,无法充电");
}
}
}

Client.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package DesignPattern.adapter.objectadapter;

/**
* @Author coderYang
* @Date 2020/12/26 17:08
*/
public class Client {
public static void main(String[] args) {
System.out.println("==对象适配器==");
Phone phone = new Phone();
phone.charging(new VoltageAdapter(new Voltage220V()));
}
}

/*
==类适配器==
电压=220V
使用对象适配器进行适配
适配完成,输出的电压为:5
电压为5V,可以充电

Process finished with exit code 0

*/

对象适配器模式注意事项和细节

  1. 对象适配器和类适配器其实算是同一种思想,只不过实现的方式不同,根据合成复用原则,使用组合替代继承,所以它解决了类适配器必须继承src的局限性问题,也不再要求dst必须是接口。
  2. 使用成本更低,更灵活。
接口适配器模式

接口适配器模式介绍:

  1. 一些书籍称为:适配器模式(Default Adapter Pattern)或缺省适配器模式。
  2. 当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法),那么该抽象类的字类可有选择地覆盖父类的某些方法实现需求。
  3. 适用于一个接口不想使用其所有的方法的情况。

image-20210101190752427

项目目录:

image-20210101190824944

InterfaceA.java

1
2
3
4
5
6
7
8
9
10
11
12
package DesignPattern.adapter.interfaceadapter;

/**
* @Author coderYang
* @Date 2021/1/1 18:54
*/
public interface InterfaceA {
public void m1();
public void m2();
public void m3();
public void m4();
}

AbsAdapter.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package DesignPattern.adapter.interfaceadapter;

/**
* @Author coderYang
* @Date 2021/1/1 18:55
*/

//在AbsAdapter抽象类中,我们将InterfaceA的方法进行默认实现
public abstract class AbsAdapter implements InterfaceA{
public void m1(){

}
public void m2(){

}
public void m3(){

}
public void m4(){

}
}

Client.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package DesignPattern.adapter.interfaceadapter;

/**
* @Author coderYang
* @Date 2021/1/1 18:57
*/
public class Client {
public static void main(String[] args) {
//使用匿名内部类的方式去覆盖我们需要使用的方法
AbsAdapter absAdapter = new AbsAdapter(){
//只需要去覆盖我们需要使用的接口方法
@Override
public void m1() {
System.out.println("使用了m1的方法");
}
};
absAdapter.m1();
}
}
适配器模式在SpringMVC框架应用的源码分析

适配器模式在SpringMVC框架应用的源码剖析

  1. SpringMVC中的HandlerAdapter,就使用了适配器模式。

  2. SpringMVC处理请求的流程

  3. 使用HandlerAdapter的原因分析:

    可以看到处理器的类型不同,有多重实现的方式,那么调用方式就不是确定的,如果需要直接调用Controller方法,需要调用的时候就得不断是使用if else来进行判断是哪一种子类然后执行。那么如果后面要扩展Controller,就得修改原来的代码,这样就违背了OCP原则。

手写SpringMVC通过适配器设计模式获取到对应的Controller的源码

image-20210102145118196

项目目录

image-20210102145214837

Controller.java

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
package DesignPattern.adapter.springmvc;

/**
* @Author coderYang
* @Date 2021/1/2 13:30
*/
//多种Controller实现
public interface Controller {
}

class HttpController implements Controller{
public void doHttpHandler(){
System.out.println("http...");
}
}

class SimpleController implements Controller{
public void doSimpleHandler(){
System.out.println("simple...");
}
}

class AnnotationController implements Controller{
public void doAnnotationHandler(){
System.out.println("annotation...");
}
}

HandlerAdapter.java

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
package DesignPattern.adapter.springmvc;


/**
* @Author coderYang
* @Date 2021/1/2 13:33
*/
//定义一个Adapter接口
public interface HandlerAdapter {
public boolean supports(Object handler);
public void handle(Object handler);
}

//多种适配器种类
class SimpleHandlerAdapter implements HandlerAdapter{

@Override
public boolean supports(Object handler) {
return (handler instanceof SimpleController);
}

@Override
public void handle(Object handler) {
((SimpleController)handler).doSimpleHandler();
}
}

class HttpHandlerAdapter implements HandlerAdapter{

@Override
public boolean supports(Object handler) {
return (handler instanceof HttpController);
}

@Override
public void handle(Object handler) {
((HttpController)handler).doHttpHandler();
}
}


class AnnotationHandlerAdapter implements HandlerAdapter{

@Override
public boolean supports(Object handler) {
return (handler instanceof AnnotationController);
}

@Override
public void handle(Object handler) {
((AnnotationController)handler).doAnnotationHandler();
}
}

DispatchServlet.java

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
package DesignPattern.adapter.springmvc;

import java.util.ArrayList;
import java.util.List;

/**
* @Author coderYang
* @Date 2021/1/2 13:47
*/
public class DispatchServlet {

public static List<HandlerAdapter> handlerAdapters = new ArrayList<HandlerAdapter>();

//构造器
public DispatchServlet(){
handlerAdapters.add(new AnnotationHandlerAdapter());
handlerAdapters.add(new HttpHandlerAdapter());
handlerAdapters.add(new SimpleHandlerAdapter());
}

public void doDispatch(){
//此处模拟SpringMVC从request取handler的对象
//适配器可以获取到希望得到的Controller
// AnnotationController controller = new AnnotationController();
HttpController controller = new HttpController(); //http...
// SimpleController controller = new SimpleController();

//得到对应的适配器
HandlerAdapter adapter = getHandler(controller);
//通过适配器执行对应的controller对应方法
adapter.handle(controller);
}

public HandlerAdapter getHandler(Controller controller){
//遍历:根据得到的controller(handler),返回对应的适配器
for (HandlerAdapter adapter : handlerAdapters){
if (adapter.supports(controller)){
return adapter;
}
}
return null;
}

public static void main(String[] args) {
new DispatchServlet().doDispatch();
}

}

说明:

  • Spring定义了一个适配接口,使得每一种Controller有一种对应的适配器实现类
  • 适配器代替controller执行相应的方法
  • 扩展Controller时,只需要增加一个适配器类就完成了SpringMVC的扩展了。
适配器模式的注意事项和细节
  1. 三种命名方式,是根据src是以怎样的形式给到Adapter(在Adapter里的形式)来命名的。

  2. 类适配器:以类给到,在Adapter里,就是将src当作类,继承。

    对象适配器:以对象给到,在Adapter里,将src作为一个对象,持有。

    接口适配器:以接口给到,在Adapter里,将src作为一个接口,实现。

  3. Adapter模式最大的作用还是将原本不兼容的接口融合在一起工作。

  4. 实际开发中,实现起来不拘泥于我们将的三种方式。

6.桥接模式

手机操作问题:

现在对不同手机类型的不同品牌实现操作编程(比如:开机、关机、上网、打电话等),如图:

image-20210102153254255

类图:

image-20210102154033427

传统问题解决手机操作问题分析
  1. 扩展性问题(类爆炸),如果我们再增加手机的样式(旋转式),就需要增加各个品牌手机的类,同样如果我们增加一个手机品牌,也要在各个手机样式类下增加。
  2. 违反了单一职责原则,当我们增加手机样式时,要同时增加所有品牌的手机,这样就增加了代码维护成本。
  3. 解决方案:使用桥接模式。
桥接模式基本介绍

基本介绍:

  1. 桥接模式(Bride模式)是指:将实现与抽象放在两个不同的类层次中,使两个层次可以独立改变
  2. 桥接模式是一种结构型设计模式
  3. Bride模式基于类的最小设计原则,通过使用封装、聚合及继承等行为让不同的类承担不同的职责。它的主要特点是把抽象(Abstraction)与行为实现(Implementation)分离开来,从而可以保持各部分的独立性以及应对他们的功能扩展。

原理类图

将抽象部分与它的实现部分分离,使它们都可以独立地变化。

image-20210102225218070

原理类图说明:

  1. Client类:桥接模式的调用者。
  2. 抽象类(Abstraction):维护了Implementor/即它的实现类ConcretImplentorA,ConcretImplementorB,二者是聚合关系,Abstraction充当桥接类
  3. RefinedAbstraction:Abstraction抽象类的子类。
  4. Implementor:行为实现的接口。
  5. ConcreteImplementorA/B:行为的具体实现类。
  6. 从UML图:这里的抽象类和接口是聚合的关系,其实是调用和被调用的关系。
桥接模式解决手机问题

image-20210102225250562

此处我们理解为手机样式是手机抽象部分,而颜色是手机的实现部分。桥接模式使这两个部分分离,使它们可以独立的变化。

项目目录

image-20210102225302262

Brand.java

1
2
3
4
5
6
7
8
9
10
11
package DesignPattern.bridge;

/**
* @Author coderYang
* @Date 2021/1/2 16:46
*/
public interface Brand {
void open();
void close();
void call();
}

Vivo.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package DesignPattern.bridge;

/**
* @Author coderYang
* @Date 2021/1/2 16:53
*/
public class Vivo implements Brand{

@Override
public void open() {
System.out.println("Vivo手机打开");
}

@Override
public void close() {
System.out.println("Vivo手机关闭");
}

@Override
public void call() {
System.out.println("Vivo手机打电话");
}
}

XiaoMi.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package DesignPattern.bridge;

/**
* @Author coderYang
* @Date 2021/1/2 16:47
*/
public class XiaoMi implements Brand{
@Override
public void open() {
System.out.println("小米手机打开");
}

@Override
public void close() {
System.out.println("小米手机关闭");
}

@Override
public void call() {
System.out.println("小米手机打电话");
}
}

Phone.java

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
package DesignPattern.bridge;

/**
* @Author coderYang
* @Date 2021/1/2 16:54
*/
public abstract class Phone {
//组合品牌
private final Brand brand;

//构造器
public Phone(Brand brand) {
this.brand = brand;
}

protected void open(){
this.brand.open();
}

protected void close(){
this.brand.close();
}

protected void call(){
this.brand.close();
}

}

FoldedPhone.java

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
package DesignPattern.bridge;

/**
* @Author coderYang
* @Date 2021/1/2 16:59
*/

//折叠式手机,继承抽象类Phone
public class FoldedPhone extends Phone{
//构造器
public FoldedPhone(Brand brand) {
super(brand);
}

public void open(){
super.open();
System.out.println("折叠样式手机");
}

public void close(){
super.close();
System.out.println("折叠样式手机");
}

public void call(){
super.call();
System.out.println("折叠样式手机");
}

}

UpRightPhone.java

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
package DesignPattern.bridge;

/**
* @Author coderYang
* @Date 2021/1/2 17:04
*/

//翻盖式手机,继承抽象类Phone
public class UpRightPhone extends Phone{
public UpRightPhone(Brand brand) {
super(brand);
}

@Override
protected void open() {
super.open();
System.out.println("翻盖样式手机");
}

@Override
protected void close() {
super.close();
System.out.println("翻盖样式手机");
}

@Override
protected void call() {
super.call();
System.out.println("翻盖样式手机");
}
}

Client.java

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
package DesignPattern.bridge;

/**
* @Author coderYang
* @Date 2021/1/2 17:06
*/
public class Client {
public static void main(String[] args) {
//获取折叠式手机(Vivo品牌)
Phone phone = new FoldedPhone(new Vivo());
phone.open();
phone.call();
phone.close();

System.out.println("====================");
//获取折叠式手机(XiaoMi品牌)
Phone phone1 = new FoldedPhone(new XiaoMi());
phone.open();
phone.call();
phone.close();

System.out.println("=======================");
//获取翻盖式手机(Vivo品牌)
Phone phone2 = new UpRightPhone(new Vivo());
phone2.open();
phone2.call();
phone2.close();

System.out.println("=====================");
Phone phone3 = new UpRightPhone(new XiaoMi());
phone3.open();
phone3.call();
phone3.close();
}
}

/*
Vivo手机打开
折叠样式手机
Vivo手机关闭
折叠样式手机
Vivo手机关闭
折叠样式手机
====================
Vivo手机打开
折叠样式手机
Vivo手机关闭
折叠样式手机
Vivo手机关闭
折叠样式手机
=======================
Vivo手机打开
翻盖样式手机
Vivo手机关闭
翻盖样式手机
Vivo手机关闭
翻盖样式手机
=====================
小米手机打开
翻盖样式手机
小米手机关闭
翻盖样式手机
小米手机关闭
翻盖样式手机

Process finished with exit code 0

*/
桥接模式在JDBC的源码剖析

桥接模式在JDBC的源码剖析

  1. JDBC的Driver接口,如果从桥接模式来看,Driver就是一个皆空,下面有MySQL的Driver,Oracle的Driver,这些就可以当作实现接口类。

image-20210121153631868

桥接模式的注意事项和细节
  1. 实现了抽象和实现部分的分离,从而极大的提供了系统的灵活性,让抽象部分和实现部分独立开来,这有助于系统进行分层设计,从而产生更好的结构化系统。
  2. 对于系统的高层部分,只需要知道抽象部分和实现部分的接口就可以了,其它的部分有具体业务来完成。
  3. 桥接模式替代多层继承方案,可以减少子类的个数,降低系统的管理和维护成本。
  4. 桥接模式的引入增加了系统的理解和设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计和编程。
  5. 桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围有一定的局限性,即需要有这样的应用场景。

桥接模式其它应用场景

  1. 对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。
  2. 常见的应用场景:
    • JDBC驱动程序
    • 银行转账系统:
      • 转账分类:网上转账,柜台转账,AMT转账
      • 转账用户类型:普通用户,银行用户,金卡用户…
    • 消息管理:
      • 消息类型:即使消息,延时消息
      • 消息分类:手机短信,邮件信息,QQ消息…

7.装饰者模式

星巴克咖啡订单项目(咖啡馆):

  1. 咖啡种类/单品咖啡:Espresso(意大利浓咖啡)、ShortBlack、LongBlack(美式咖啡)、Decaf(无因咖啡)
  2. 调料:Milk、Soy(豆浆)、Chocolate
  3. 要求在扩展新的咖啡种类时,具有良好的扩展性、改动方便、维护方便
  4. 使用OO的方法来计算不同种类咖啡的费用:客户可以点单品咖啡,也可以点单品咖啡+调料组合。

方案1:

Drink为抽象类

image-20210121181543257

分析:

  1. Drink是一个抽象类,表示饮料
  2. description是对咖啡的描述,比如咖啡的名字
  3. cost()方法就是计算费用,Drink类中做成一个抽象方法
  4. Decaf就是单品咖啡,继承Drink,并实现cost()方法
  5. Espress&&Milk就是单品咖啡+调料,这个组合很多
  6. 问题:这样设计就会有很多类,当我们增加一个单品咖啡或者新的调料,类的数量就会倍增,就会出现类爆炸

方案2:

前面分析到方案1因为咖啡单品+调料组合会造成类的倍增,因此可以做改进,将调料内置到Drink类,这样就不会造成类数量过多。从而提高项目的可维护性(如图)。milk,soy,chocolate可以设计为Boolean,表示是否要添加相应的调料。

image-20210121183619616

问题分析

  1. 方案2可以控制类的数量,不至于造成很多的类。
  2. 增加或者删除调料种类时,代码的维护量很大。
  3. 考虑到用户可以添加多份调料时,可以将hasMilk返回一个对应的int
  4. 考虑使用装饰者模式。
装饰者模式定义
  1. 装饰者模式:动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性,装饰者模式也体现了开闭原则(ocp)
  2. 这里提到的动态的将新功能附加到对象和ocp原则,在后面的应用实例上会以代码的形式体现。
装饰者模式(Decorator)原理
  1. 装饰者模式就像打包一个快递

    ​ 主体(Component(被装饰者)):比如:陶瓷、衣服

    ​ 包装(Decorator(装饰者)):比如:报纸填充、塑料泡沫、纸板、木板

  2. Component:主体:比如类似前面的Drink

  3. ConcreteComponent:具体的主体,比如前面的各个单品咖啡。Decorator:装饰者,比如各调料。

  4. 在如图的Component与ConcreteComponent之间,如果ConcreteComponent类很多,还可以设计一个缓冲层,将共有的部分提取出来,抽象成一个类。

image-20210121192549305

装饰者模式解决星巴克咖啡订单

image-20210203143333816

说明:

  1. Drink类就是前面说的抽象类,Component
  2. ShortBlack就是单品咖啡
  3. Decorator是一个装饰类,含有一个被装饰的对象(Drink obj)
  4. Decorator的cost方法进行了一个费用的叠加计算,递归的计算出价格

装饰者模式下的订单:2份巧克力+一份牛奶的LongBlack

image-20210121194323869

说明:

  1. Milk包含了LongBlack
  2. 一份Chocolate包含了(Milk+LongBlack)
  3. 一份Chocolate包含了(Chocolate+Milk+LongBlack)
  4. 这样不管是什么形式的单品咖啡+调料组合,通过递归方式可以方便的组合和维护

项目目录:

image-20210203142822857

Drink.java

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
package DesignPattern.decorator;

/**
* @Author coderYang
* @Date 2021/1/21 19:49
*/
public abstract class Drink {
public String des; //描述
private float price = 0.0f;

public void setDes(String des) {
this.des = des;
}

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

public String getDes() {
return des;
}

public float getPrice() {
return price;
}

//计算费用的抽象方法,被子类调用
public abstract float cost();
}

抽象层:Coffee.java

把各个种类咖啡的共同点=>咖啡特性抽取出来,抽象为一个Coffee文件

1
2
3
4
5
6
7
8
9
10
11
12
package DesignPattern.decorator;

/**
* @Author coderYang
* @Date 2021/1/21 19:51
*/
public class Coffee extends Drink{
@Override
public float cost() {
return super.getPrice();
}
}

ShortBlack.java

1
2
3
4
5
6
7
8
9
10
11
12
package DesignPattern.decorator;

/**
* @Author coderYang
* @Date 2021/1/21 19:55
*/
public class ShortBlack extends Coffee{
public ShortBlack(){
setDes("short black");
setPrice(4.0f);
}
}

Espresso.java

1
2
3
4
5
6
7
8
9
10
11
12
package DesignPattern.decorator;

/**
* @Author coderYang
* @Date 2021/1/21 19:52
*/
public class Espresso extends Coffee{
public Espresso(){
setDes("意大利咖啡");
setPrice(6.0f);
}
}

LongBlack.java

1
2
3
4
5
6
7
8
9
10
11
12
package DesignPattern.decorator;

/**
* @Author coderYang
* @Date 2021/1/21 19:54
*/
public class LongBlack extends Coffee{
public LongBlack(){
setDes("long black");
setPrice(5.0f);
}
}

装饰类:Decorator.java

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
package DesignPattern.decorator;

/**
* @Author coderYang
* @Date 2021/1/21 19:56
*/
public class Decorator extends Drink{
private Drink obj;

public Decorator(Drink obj){ //组合,同生共死是组合
this.obj = obj;
}

@Override
public float cost() {
//getPrice是自己的价格
return super.getPrice() + obj.cost();
}

@Override
public String getDes() {
//obj.getDes()输出被装饰者的信息
return super.des + " "+super.getPrice()+" && "+obj.getDes();
}
}

Chocolate.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package DesignPattern.decorator;

/**
* @Author coderYang
* @Date 2021/1/21 20:04
*/
//具体的Decorator,这里指的是调味品
public class Chocolate extends Decorator{
public Chocolate(Drink obj) {
super(obj);
setDes(" 巧克力 ");
setPrice(3.0f); //调味品的价格
}
}

Milk.java

1
2
3
4
5
6
7
8
9
10
11
12
13
package DesignPattern.decorator;

/**
* @Author coderYang
* @Date 2021/1/21 20:06
*/
public class Milk extends Decorator{
public Milk(Drink obj) {
super(obj);
setDes(" 牛奶 ");
setPrice(2.0f);
}
}

Soy.java

1
2
3
4
5
6
7
8
9
10
11
12
13
package DesignPattern.decorator;

/**
* @Author coderYang
* @Date 2021/1/21 20:06
*/
public class Soy extends Decorator{
public Soy(Drink obj) {
super(obj);
setDes(" 豆浆 ");
setPrice(1.5f);
}
}

CoffeeBar.java

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
package DesignPattern.decorator;

/**
* @Author coderYang
* @Date 2021/1/21 20:08
*/
public class CoffeeBar {
public static void main(String[] args) {
//装饰者模式下订单:2份巧克力+一份牛奶的LongBlack
//1.点一份LongBlack
Drink order = new LongBlack();
System.out.println("费用1="+order.cost());
System.out.println("描述="+order.getDes());

//2.order 加入一份牛奶
order = new Milk(order);
System.out.println("加入一份牛奶 费用="+order.cost());
System.out.println("加入一份牛奶 描述="+order.getDes());

//3.order 加入一份巧克力
order = new Chocolate(order);
System.out.println("order 加入一份牛奶,加入一份巧克力 费用="+order.cost());
System.out.println("order 加入一份牛奶,加入一份巧克力 描述="+order.getDes());

//4.order 加入一份巧克力
order = new Chocolate(order);
System.out.println("order 加入一份牛奶 加入两份巧克力 费用="+order.cost());
System.out.println("order 加入一份牛奶 加入两份巧克力 描述="+order.getDes());
}
}

输出:

image-20210203143954016

装饰者模式在JDK应用的源码分析

Java的IO结构,FilterInputStream就是一个装饰者。

image-20210203151415862

FileInputStram是InputStream的子类,类似我们前面的LongBlack,Decaf等。

1
2
3
4
5
6
7
public abstract class InputStream implements Closeable{}	//是一个抽象类,即Component。

public class FilterInputStream extends InputStream{ //装饰者类Decorator
protected volatile InputStream in; //被装饰的对象
}

class DataInputStream extends FilterInputStream implements DataInput{} //FilterInputStream子类,也继承了被装饰的对象in,它是具体的修饰者,类似于前面的Mile,Soy等

image-20210203152521681

8.组合模式

学院院系展示需求:

编写程序展示一个学校院系结构:需求是这样,要在一个页面中展示出学校的院系组成,一个学校有多少个学院,一个学院有多少个系。

传统方案解决学校院系展示(类图):

image-20210203205135898
传统方案解决学校院系展示存在问题的分析
  1. 将学院看做是学校的子类,系是学院的子类,这样实际上是站在组织大小来进行分层次的
  2. 实际上我们的要求是:在一个页面中展示出学校的院系组成,一个学校有多少个学院,一个学院有多少个系,因此这种方案,不能很好的实现管理的操作,比如对学院、系的添加,删除,遍历等
  3. 解决方案:把学校,院,系都看做是组织结构,他们之间没有继承的关系,而是一个树形结构,可以更好的实现管理操作=>组合模式
组合模式基本介绍
  1. 组合模式(Composite Pattern),又叫部分整体模式,它创建了对象组的树形结构,将对象组合成树状结构以表示”整体-部分“的层次关系。
  2. 组合模式依据树形结构来组合对象,用来表示部分以及整体层次。
  3. 这种类型的设计模式属于结构型模式。
  4. 组合模式使得用户对单个对象和组合对象的访问具有一致性,即:组合能让客户以一致的方式处理对象以及组合对象。

image-20210203223026587

  • Component:这是组合中对象声明接口,在适当情况下,实现所有类共有的接口默认行为,用于访问和管理Component子部件,Component可以是抽象类或者接口。
  • Leaf:在组合中表示叶子节点,叶子节点没有子结点
  • Composite:非叶子节点,用于存储子部件,在Component接口中实现子部件的相关操作,比如增加(add),删除等操作。
组合模式解决的问题
  1. 组合模式解决这样的问题,当我们要处理的对象可以生成一颗树形结构,而我们要对树上的节点和叶子进行操作时,它能够提供一致的方式,而不用考虑它是节点还是叶子。

  2. 对应的示意图:

    image-20210203212342984
组合模式解决学校院系展示

image-20210203222904479

项目目录

image-20210203223116127

OrganizationComponent.java

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
package DesignPattern.composite;

/**
* @Author coderYang
* @Date 2021/2/3 21:42
*/
public abstract class OrganizationComponent {

private String name; //名字
private String desc; //说明

protected void add(OrganizationComponent organizationComponent){
//默认实现
throw new UnsupportedOperationException();
}

protected void remove(OrganizationComponent organizationComponent){
throw new UnsupportedOperationException();
}

//构造器
public OrganizationComponent(String name, String desc) {
this.name = name;
this.desc = desc;
}

public String getName() {
return name;
}

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

public String getDesc() {
return desc;
}

public void setDesc(String desc) {
this.desc = desc;
}

//print方法,做成抽象
protected abstract void print();
}

University.java

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
package DesignPattern.composite;

import java.util.ArrayList;
import java.util.List;

/**
* @Author coderYang
* @Date 2021/2/3 21:48
*/
//University就是Composite,可以管理College
public class University extends OrganizationComponent{

//List中存放的是College
List<OrganizationComponent> organizationComponents = new ArrayList<OrganizationComponent>();

public University(String name, String desc) {
super(name, desc);
}

//重写add
@Override
protected void add(OrganizationComponent organizationComponent) {
organizationComponents.add(organizationComponent);
}

//重写remove方法
@Override
protected void remove(OrganizationComponent organizationComponent) {
organizationComponents.remove(organizationComponent);
}

@Override
public String getName() {
return super.getName();
}

@Override
public String getDesc() {
return super.getDesc();
}

//输出University包含的学院
@Override
protected void print() {
System.out.println("---------"+getName()+"--------");
for (OrganizationComponent organizationComponent : organizationComponents){
System.out.println(organizationComponent.getName());
}
}
}

College.java

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
package DesignPattern.composite;

import java.util.ArrayList;
import java.util.List;

/**
* @Author coderYang
* @Date 2021/2/3 21:57
*/
public class College extends OrganizationComponent{

//List中存放的是Department
List<OrganizationComponent> organizationComponents = new ArrayList<OrganizationComponent>();

public College(String name, String desc) {
super(name, desc);
}

@Override
protected void add(OrganizationComponent organizationComponent) {
//将来实际业务中,College的add方法和University的add方法不一定完全相同
organizationComponents.add(organizationComponent);
}

@Override
protected void remove(OrganizationComponent organizationComponent) {
organizationComponents.remove(organizationComponent);
}

@Override
public String getName() {
return super.getName();
}

@Override
public String getDesc() {
return super.getDesc();
}

//输出University包含的学院
@Override
protected void print() {
System.out.println("---------"+getName()+"--------");
for (OrganizationComponent organizationComponent : organizationComponents){
System.out.println(organizationComponent.getName());
}
}
}

Department.java

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
package DesignPattern.composite;

/**
* @Author coderYang
* @Date 2021/2/3 22:01
*/
public class Department extends OrganizationComponent{
public Department(String name, String desc) {
super(name, desc);
}

//add和remove方法就不用写了,因为Department在此处是叶子节点


@Override
public String getName() {
return super.getName();
}

@Override
public String getDesc() {
return super.getDesc();
}

@Override
protected void print() {
System.out.println(getName());
}


}

Client.java

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
package DesignPattern.composite;

/**
* @Author coderYang
* @Date 2021/2/3 22:04
*/
public class Client {

public static void main(String[] args) {
//从大到小创建对象 学校
OrganizationComponent university = new University("清华大学", "中国顶级大学");

//创建学院
OrganizationComponent computerCollege = new College("计算机学院", "计算即学院");
OrganizationComponent infoEngineerCollege = new College("信息工程学院", "信息工程学院");

//创建各个学院的系
computerCollege.add(new Department("软件工程","对软件的开发"));
computerCollege.add(new Department("网络工程","对网络的应用"));
computerCollege.add(new Department("计算机科学与技术","对计算机进行科学研究"));

//创建信息工程学院的系
infoEngineerCollege.add(new Department("通信工程","数据通信方面"));
infoEngineerCollege.add(new Department("信息工程","信息通信方面"));

//将两个学院加入学校中
university.add(computerCollege);
university.add(infoEngineerCollege);

university.print();
computerCollege.print();
infoEngineerCollege.print();
}
}

输出结果:

image-20210203223339800

组合模式在JDK集合的源码分析

Java的集合类HashMap就使用了组合模式

image-20210204132723083
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
package JDKSRC;

import java.util.HashMap;
import java.util.Map;

/**
* @Author coderYang
* @Date 2021/2/3 22:37
*/
public class Composite {

public static void main(String[] args) {
//1.Map就是一个抽象的构建(类似Component)
//2.HashMap就是中间的一个构建(Composite),实现/继承了相关方法put,putAll
//3.Node是HashMap的静态内部类,类似Leaf叶子节点,里面没有put,putAll,remove等方法
Map<Integer,String> hashMap = new HashMap<Integer, String>();
hashMap.put(0,"java");

Map<Integer,String> map = new HashMap<Integer, String>();
map.put(1,"python");
map.put(2,"php");
hashMap.putAll(map);
System.out.println(hashMap);

}
}
组合模式的注意事项和细节
  1. 简化客户端操作。客户端只需要面对一致的对象而不用考虑整体部分或者节点叶子的问题。
  2. 具有较强的扩展性。当我们要更改组合对象时,我们只需要调整内部的层次关系,客户端不用做出任何改动。
  3. 方便创建出复杂的层次结构。客户端不用理会组合里面的组成细节,容易添加节点或者叶子从而创建出复杂的树形结构
  4. 需要遍历组织机构,或者处理的对象具有树形结构时,非常适合用于组合模式
  5. 要求较高的抽象性,如果节点和叶子有很多差异性的话,比如很多方法和属性都不一样,不适合使用组合模式

9.外观模式

影院管理项目

组件一个家庭影院:

DVD播放器、投影仪、自动屏幕、环绕立体声、爆米花机,要求完成使用家庭影院的功能,其过程为:

  • 直接使用遥控器:统筹各设备开关
  • 开爆米花机
  • 放下屏幕
  • 开投影仪
  • 开音响
  • 开DVD,选DVD
  • 去拿爆米花
  • 安调灯光
  • 播放
  • 观影结束后,关闭各种设备

传统方式解决影院管理

image-20210204140346483

传统方式解决影院管理问题分析
  1. 在ClientTest的main方法中,创建各个子系统的对象,并直接去调用子系统(对象)相关方法,会造成调用过程混乱,没有清晰的过程
  2. 不利于在ClientTest中,去维护对子系统的操作
  3. 解决思路:定义一个高层接口给子系统中的一组接口提供一个一致的界面(比如在高层接口提供四个方法ready,play,pause,end),用来访问子系统中的一群接口
  4. 也就是说通过定义一个一致的接口(界面类),用以屏蔽内部子系统的细节,使得调用端只需跟这个接口发生调用,而无需关心这个子系统的内部细节=>外观模式
外观模式基本介绍
  1. 外观模式(Facade),也叫过程模式:外观模式为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用
  2. 外观模式通过定义一个一致的接口,用以屏蔽内部子系统的细节,使得调用端只需跟这个接口发生调用,而无需关系这个子系统的内部细节

image-20210204143154310

  • 外观类(Facede):为调用端提供统一的调用接口,外观类知道哪些子系统负责处理请求,从而将调用端的请求代理给适当的子系统对象
  • 调用者(Client):外观接口的调用者
  • 子系统的集合:指模块或者子系统,处理Facade对象指派的任务,它是功能的提供者
外观模式解决影院管理
  1. 外观模式可以理解为转换一群接口,客户只要调用一个接口,而不调用多个接口才能达到目的。比如:在pc上安装软件的时候经常有一键安装选项(省去选择安装目录、安装的组件等等),还有就是手机的重启功能(把关机和启动合为一个操作)。
  2. 外观模式就是解决多个复杂接口带来的使用困难,起到简化用户操作的作用

image-20210204165541105

项目目录

image-20210204165600674

DVDPlayer.java

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
package DesignPattern.facade;

/**
* @Author coderYang
* @Date 2021/2/4 14:34
*/
public class DVDPlayer {

//私有构造器
private DVDPlayer(){

}

//使用单例模式,使用饿汉式
private final static DVDPlayer instance = new DVDPlayer();

public static DVDPlayer getInstance(){
return instance;
}

public void on(){
System.out.println("DVD on");
}
public void off(){
System.out.println("DVD off");
}

public void play(){
System.out.println("DVD is playing");
}

public void pause(){
System.out.println("DVD pause ...");
}
}

Popcorn.java

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
package DesignPattern.facade;

/**
* @Author coderYang
* @Date 2021/2/4 15:02
*/
public class Popcorn {

//私有构造器
private Popcorn(){

}

//使用单例模式,使用饿汉式
private final static Popcorn instance = new Popcorn();

public static Popcorn getInstance(){
return instance;
}

public void on(){
System.out.println("popcorn on");
}
public void off(){
System.out.println("popcorn off");
}

public void pop(){
System.out.println("popcorn is popping");
}
}

Projector.java

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
package DesignPattern.facade;

/**
* @Author coderYang
* @Date 2021/2/4 15:05
*/
public class Projector {

//私有构造器
private Projector(){

}

//使用单例模式,使用饿汉式
private final static Projector instance = new Projector();

public static Projector getInstance(){
return instance;
}

public void on(){
System.out.println("projector on");
}
public void off(){
System.out.println("projector off");
}

public void focus(){
System.out.println("projector is focusing");
}
}

Screen.java

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
package DesignPattern.facade;

/**
* @Author coderYang
* @Date 2021/2/4 15:09
*/
public class Screen {

//私有构造器
private Screen(){

}

//使用单例模式,使用饿汉式
private final static Screen instance = new Screen();

public static Screen getInstance(){
return instance;
}

public void up(){
System.out.println("Screen up");
}
public void down(){
System.out.println("Screen sown");
}
}

Stereo.java

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
package DesignPattern.facade;

/**
* @Author coderYang
* @Date 2021/2/4 16:07
*/
public class Stereo {

//私有构造器
private Stereo(){

}

//使用单例模式,使用饿汉式
private final static Stereo instance = new Stereo();

public static Stereo getInstance(){
return instance;
}

public void on(){
System.out.println("Stereo on");
}

public void off(){
System.out.println("Stereo off");
}

public void up(){
System.out.println("Stereo up...");
}

public void down(){
System.out.println("Stereo down...");
}
}

TheaterLight.java

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
package DesignPattern.facade;

/**
* @Author coderYang
* @Date 2021/2/4 16:11
*/
public class TheaterLight {

private TheaterLight(){

}

private final static TheaterLight instance = new TheaterLight();

public static TheaterLight getInstance(){
return instance;
}

public void on(){
System.out.println("TheaterLight on");
}

public void off(){
System.out.println("TheaterLight off");
}

public void dim(){
System.out.println("TheaterLight dim...");
}

public void bright(){
System.out.println("TheaterLight bright...");
}
}

HomeTheaterFacade.java

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
package DesignPattern.facade;

/**
* @Author coderYang
* @Date 2021/2/4 16:20
*/
public class HomeTheaterFacade {

//定义各个子系统对象
private TheaterLight theaterLight;
private Popcorn popcorn;
private Stereo stereo;
private Projector projector;
private Screen screen;
private DVDPlayer dvdPlayer;

public HomeTheaterFacade() {
this.theaterLight = TheaterLight.getInstance();
this.popcorn = Popcorn.getInstance();
this.stereo = Stereo.getInstance();
this.projector = Projector.getInstance();
this.screen = Screen.getInstance();
this.dvdPlayer = DVDPlayer.getInstance();
}

//操作分成四步
//准备操作
public void ready(){
popcorn.on();
popcorn.pop();
screen.down();
projector.on();
stereo.on();
dvdPlayer.on();
theaterLight.dim();
}

//播放
public void play(){
dvdPlayer.play();
}

//暂停操作
public void pause(){
dvdPlayer.pause();
}

//统一结束操作
public void end(){
popcorn.off();
theaterLight.bright();
screen.up();
projector.off();
stereo.off();
dvdPlayer.off();
}
}

Client.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package DesignPattern.facade;

/**
* @Author coderYang
* @Date 2021/2/4 16:15
*/
public class Client {

public static void main(String[] args) {
HomeTheaterFacade homeTheaterFacade = new HomeTheaterFacade();
homeTheaterFacade.ready();
homeTheaterFacade.play();
homeTheaterFacade.end();
}
}

运行结果:

image-20210204165850682

外观模式在Mybatis框架应用的源码分析

Mybatis中的Configuration去创建MetaObject对象使用到外观模式

image-20210204185747433

外观模式的注意事项和细节
  1. 外观模式对外屏蔽了子系统的细节,因此外观模式降低了客户端对于子系统使用的复杂性
  2. 外观模式对客户端与子系统的耦合关系-解耦,让子系统内部的模块更易于维护和扩展
  3. 通过合理的使用外观模式,可以帮助我们更好的划分访问的层次
  4. 当系统需要进行分层设计时,可以考虑使用Facade模式
  5. 在维护一个遗留的大型系统时,可能这个系统已经变得非常难以维护和扩展,此时可以考虑为新系统开发一个Facade类,来提供遗留系统的比较清晰简单的接口,让新系统与Facade类交互,提高复用性
  6. 不能过多的或者不合理的使用外观模式,使用外观模式好,还是直接调用模块好。要以让系统有层次,利于维护为目的。
  7. 调用子系统很麻烦的时候,就使用外观模式

10.享元模式

展示网站项目需求

小型的外包项目,给客户A做一个产品展示网站,客户A的朋友感觉效果不错,也希望做这样的产品展示网站,但是要求有些不同:

  1. 有客户要求以新闻的形式发布
  2. 有客户要求以博客的形式发布
  3. 有客户希望以微信公众号的形式发布

传统方式解决网站展现项目

image-20210205125927179

传统方式解决网站展现项目-问题分析
  1. 需要的网站结构相似度很高,而且都不是高访问量网站,如果分成多个虚拟空间来处理,相当于一个相同网站的实例对象很多,造成服务器的资源浪费
  2. 解决思路:整合到一个网站中,共享其相关代码和数据,对于硬盘、内存、CPU、数据库空间等服务器资源都可以达成共享,减少服务器资源
  3. 对于代码来说,由于是一份实例,维护和扩展更加容易
  4. 上面的解决思路就可以使用享元模式来解决
享元模式基本介绍
  1. 享元模式(Flyweight Pattern)也叫蝇量模式:运用共享的技术有效地支持大量细粒度地对象
  2. 常用于系统底层开发,解决系统的性能问题。像数据库连接池,里面都是创建好的连接对象,在这些连接对象中有我们需要的则直接拿来用,避免重复创建,如果没有我们需要的,则创建一个
  3. 享元模式能够解决重复对象的内存浪费问题,当系统中有大量相似对象,需要缓存池时。不需要总是创建新对象,可以从缓冲池里拿。这样可以降低系统内存,同时提高效率
  4. 享元模式经典的应用场景就是池技术了,String常量池、数据库连接池、缓冲池等等都是享元模式的应用,享元模式时池技术的重要实现方式。
image-20210205132342610

image-20210205133424317

  • Flyweight:是抽象的享元角色,他是产品的抽象类,同时定义出对象的外部状态和内部状态的接口或实现
  • ConcreteFlyWeight:是具体的享元角色,是具体的产品类,实现抽象角色定义的相关业务
  • UnShareConcreteFlyWeight:是不可共享的角色,一般不出现在享元工厂。
  • FlyWeightFactory:享元工厂类,用于构建一个池容器(集合),同时提供从池中获取对象方法

内部状态和外部状态

比如围棋、五子棋、跳棋,它们都有大量的棋子对象,围棋和五子棋只有黑白两色,跳棋颜色多一点,所以棋子颜色就是棋子的内部状态;而各个棋子之间的差别就是位置不同,当我们落子后,落子颜色是固定的,但位置是变化的,所以棋子坐标就是棋子的外部状态。

  1. 享元模式提出了两个要求:细粒度和共享对象。这里就涉及到内部状态和外部状态了,即将对象的信息分为两个部分:内部状态外部状态
  2. 内部状态指对象共享出来的信息,存储在享元对象内部且不会随环境的改变而改变
  3. 外部状态指对象得以依赖一个标记,是随环境改变而改变的、不可共享的状态
  4. 举个例子:围棋理论上有361个空位可以放棋子,每盘棋都有可能有两三百个棋子对象产生,因为内存空间有限,一台服务器很难支持更多的玩家玩围棋游戏,如果用享元模式来处理棋子,那么棋子对象就可以减少到只有两个实例,这样就很好的解决了对象的开销问题。
享元模式解决网站外包问题

image-20210205152549932

项目目录

image-20210205152739682

WebSite.java

1
2
3
4
5
6
7
8
9
10
package DesignPattern.flyweight;

/**
* @Author coderYang
* @Date 2021/2/5 14:57
*/
public abstract class WebSite {

public abstract void use(User user); //抽象方法
}

ConcreteWebSite.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package DesignPattern.flyweight;

/**
* @Author coderYang
* @Date 2021/2/5 14:58
*/
//具体的网站
public class ConcreteWebSite extends WebSite{

//共享的部分(内部状态)
private String type = ""; //网站发布的形式(类型)

public ConcreteWebSite(String type) {
this.type = type;
}

@Override
public void use(User user) {
System.out.println("网站的发布形式为:"+type+"|"+"使用者是:"+user.getName());
}
}

User.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package DesignPattern.flyweight;

/**
* @Author coderYang
* @Date 2021/2/5 15:15
*/
public class User {

private String name;

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

public String getName() {
return name;
}

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

WebSiteFactory.java

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
package DesignPattern.flyweight;

import java.util.HashMap;

/**
* @Author coderYang
* @Date 2021/2/5 15:00
*/
//网站工厂类,根据需求返回一个具体的网站
public class WebSiteFactory {

//集合,充当池的作用
private HashMap<String,ConcreteWebSite> pool = new HashMap<>();

//根据网站的类型,返回一个网站,如果没有就创建一个网站,并放入到池中
public WebSite getWebSiteCategory(String type){
if (!pool.containsKey(type)){
//创建一个并放入池中
pool.put(type,new ConcreteWebSite(type));
}
return (WebSite)pool.get(type);
}

//获取网站分类的总数(池中有多少个网站类型)
public int getWebSiteCount(){
return pool.size();
}
}

Client.java

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
package DesignPattern.flyweight;

/**
* @Author coderYang
* @Date 2021/2/5 15:08
*/
public class Client {

public static void main(String[] args) {
//创建一个工厂类
WebSiteFactory factory = new WebSiteFactory();

//以一个新闻形式发布的网站
WebSite newsWebsite = factory.getWebSiteCategory("新闻");
newsWebsite.use(new User("小明"));

//以一个博客形式发布的发布的网站
WebSite blogWebsite = factory.getWebSiteCategory("博客");
blogWebsite.use(new User("小红"));

//再以一个形式发布的网站
WebSite blogWebsite2 = factory.getWebSiteCategory("博客");
blogWebsite2.use(new User("coderYang"));

//再以一个形式发布的网站
WebSite blogWebsite3 = factory.getWebSiteCategory("博客");
blogWebsite3.use(new User("路人甲"));

System.out.println("网站的分类共有:"+factory.getWebSiteCount());
}
}

运行结果:

image-20210205152933690

享元模式在JDK-Integral的应用源码分析
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
package JDKSRC;

/**
* @Author coderYang
* @Date 2021/2/5 15:31
*/
public class FlyWeight {


public static void main(String[] args) {
/**
* Integer在常量池的缓存范围是-128~127
* 如果Integral.valueOf(x),x在-127~128之间,就通过享元模式返回对象
*/
Integer x = Integer.valueOf(127);
Integer y = new Integer(127);
Integer z = Integer.valueOf(127);
Integer w = new Integer(127);
System.out.println(x.equals(y)); //比较大小,true
System.out.println(x == y); //false
System.out.println(x == z); //true
System.out.println(w == z); //false
System.out.println(w == y); //false


//不在范围内,没有使用享元模式返回对象,而是新建了一个对象
Integer x1 = Integer.valueOf(128);
Integer x2 = Integer.valueOf(128);
System.out.println(x1 == x2); //false
}
}

image-20210205154755088

image-20210205154821701

总结:

valueOf方法使用到享元模式,执行速度要比new对象快,还节省内存。

享元模式的注意事项和细节
  1. 在享元模式这样理解,”享”就表示共享,”元”表示对象
  2. 系统中有大量对象,这些对象消耗大量的内存,并且对象的状态大部分可以外部化时,我们就可以考虑选用享元模式
  3. 唯一标识码判断,如果在内存中有,则返回这个唯一标识码所标识的对象,用HashMap/HashTable存储
  4. 享元模式大大减少了对象的创建,降低了程序内存的占用,提高效率
  5. 享元模式提高了系统的复杂度。需要分离出内部状态和外部状态,而外部状态具有固化特性,不应该随着内部状态的改变而改变,这就是我们使用享元模式需要注意的
  6. 使用享元模式时,注意划分内部状态和外部状态,并且需要有一个工厂类加以控制。
  7. 享元模式经典的应用场景是需要缓冲池的场景,比如String常量池、数据库连接池

11.代理模式

代理模式基本介绍
  1. 代理模式:为一个对象提供一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象。这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。
  2. 被代理的对象可以是远程对象创建开销大的对象或者需要安全控制的对象
  3. 代理模式有不同的形式,主要有三种:静态代理动态代理(JDK代理、接口代理)Cglib代理(可以在内存中动态创建对象,而不需要实现接口,他是属于动态代理的范畴)
静态代理

静态代理在使用时,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者继承相同父类

应用实例

具体要求:

  1. 定义一个接口:ITeacherDao
  2. 目标对象TeacherDao实现接口ITeacherDao
  3. 使用静态代理方式,就需要在代理对象TeacherDaoProxy中实现ITeacherDao
  4. 调用的时候通过调用代理对象的方法来调用目标对象
  5. 特别提醒:代理对象与目标对象要实现相同的接口,然后通过调用相同的方法来调用目标对象的方法。

image-20210206145527362

项目目录

image-20210206145646434

ITeacherDao.java

1
2
3
4
5
6
7
8
9
10
package DesignPattern.proxy.staticproxy;

/**
* @Author coderYang
* @Date 2021/2/6 14:44
*/
public interface ITeacherDao {

void teach(); //授课方法
}

TeacherDao.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package DesignPattern.proxy.staticproxy;

/**
* @Author coderYang
* @Date 2021/2/6 14:45
*/
public class TeacherDao implements ITeacherDao{

@Override
public void teach() {
//老师授课中
System.out.println("老师授课中");
}
}

TeacherDaoProxy.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package DesignPattern.proxy.staticproxy;

/**
* @Author coderYang
* @Date 2021/2/6 14:46
*/
//代理对象,静态代理
public class TeacherDaoProxy implements ITeacherDao{

private ITeacherDao target; //目标对象,通过接口来聚合

//构造器
public TeacherDaoProxy(ITeacherDao target) {
this.target = target;
}

@Override
public void teach() {
System.out.println("代理开始...正在完成某些操作...");
target.teach();
System.out.println("提交...");
}
}

Client.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package DesignPattern.proxy.staticproxy;

/**
* @Author coderYang
* @Date 2021/2/6 14:51
*/
public class Client {

public static void main(String[] args) {
//创建目标对象(被代理对象)
TeacherDao teacherDao = new TeacherDao();

//创建被代理对象,同时将被代理对象传递给代理对象
TeacherDaoProxy teacherDaoProxy = new TeacherDaoProxy(teacherDao);

//通过代理对象方法,就是调用了被代理对象的方法,只是我们在代理对象中加了一些操作
teacherDaoProxy.teach();
}
}

运行结果:

image-20210206145816498

静态代理优缺点

  1. 优点:在不修改目标对象的功能前提下,能通过代理对象目标功能扩展
  2. 缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类。一旦接口增加方法,目标对象与代理对象都要维护。
动态代理

动态代理基本介绍:

  1. 代理对象不需要实现接口,但是目标对象要实现接口,否则不能用动态代理
  2. 代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象
  3. 动态代理也叫做:JDK代理、接口代理

JDK中生成代理对象的API

  1. 代理类所在包:java.lang.reflect.Proxy

  2. JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数:

    完整的写法是:

    1
    static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)

应用实例

将前面的静态代理改为动态代理模式(即JDK代理模式)

image-20210206155843113

项目目录

image-20210206155926358

ITeacherDao.java

1
2
3
4
5
6
7
8
9
10
11
package DesignPattern.proxy.dynamicproxy;

/**
* @Author coderYang
* @Date 2021/2/6 15:26
*/
public interface ITeacherDao {

void teach(String name); //授课方法

}

TeacherDao.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package DesignPattern.proxy.dynamicproxy;

/**
* @Author coderYang
* @Date 2021/2/6 15:27
*/
public class TeacherDao implements ITeacherDao{

@Override
public void teach(String name) {
System.out.println(name+"老师正在授课...");
}

}

ProxyFactory.java

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
package DesignPattern.proxy.dynamicproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
* @Author coderYang
* @Date 2021/2/6 15:28
*/
public class ProxyFactory {

//维护一个目标对象,Object
private Object target;

//通过构造器,传入目标对象
public ProxyFactory(Object target) {
this.target = target;
}

//给目标对象生成一个代理对象
public Object getProxyInstance(){
/**
* 参数说明
* 1.ClassLoader loader:指定当前目标对象使用的类加载器,获取加载器的方法固定
* 2.Class<?>[] interfaces:目标对象实现的接口类型,使用泛型的方式确认类型
* 3.InvocationHandler h:事件处理,执行目标对象方法时,会触发事件处理器方法,
* 会把当前执行的目标对象方法作为参数传入
*/
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("JDK代理开始...");
//反射机制调用目标对象的方法
Object res = method.invoke(target, args);
System.out.println("方法:"+method+",参数:"+args[0].toString());
System.out.println("JDK代理提交...");
return res;
}
});
}

}

Client.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package DesignPattern.proxy.dynamicproxy;

/**
* @Author coderYang
* @Date 2021/2/6 15:46
*/
public class Client {

public static void main(String[] args) {
//1.创建一个目标对象
ITeacherDao target = new TeacherDao();

//给目标对象创建代理对象
ITeacherDao proxyInstance = (ITeacherDao) new ProxyFactory(target).getProxyInstance();

System.out.println(proxyInstance.getClass());
proxyInstance.teach("coderYang");
}
}

运行结果

image-20210206161840874

Cglib代理

Cglib代理模式的基本介绍

  1. 静态代理和JDK代理模式都要求目标对象是实现一个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候可使用目标对象子类来实现代理,这就是Cglib代理
  2. Cglib代理也叫子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能扩展,有些书也将Cglib代理归属到动态代理。
  3. Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口,它广泛的被许多AOP的框架使用,例如Spring AOP,实现方法拦截
  4. 在AOP编程中如何选择代理模式:
    1. 目标对象需要实现接口,用JDK代理
    2. 目标对象不需要实现接口,用Cglib代理
  5. Cglib包的底层是通过字节码处理框架ASM来转换字节码并生成新的类。

Cglib实现步骤

  1. 需要引入Cglib的jar文件

    image-20210206172903068

  2. 在内存中动态构建子类,注意代理的类不能为final,否则报错

  3. 目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法。

image-20210206172956915

TeacherDao.java

1
2
3
4
5
6
7
8
9
10
11
12
package DesignPattern.proxy.cglib;

/**
* @Author coderYang
* @Date 2021/2/6 16:50
*/
public class TeacherDao {

public void teach(){
System.out.println("老师授课中,我是cglib代理,不需要实现接口");
}
}

ProxyFactory.java

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
package DesignPattern.proxy.cglib;


import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
* @Author coderYang
* @Date 2021/2/6 16:51
*/
public class ProxyFactory implements MethodInterceptor {

//维护一个目标对象
private Object target;

//构造器,传入一个被代理的对象
public ProxyFactory(Object target) {
this.target = target;
}

//返回一个代理对象,是target对象的代理对象
public Object getProxyInstance(){
//1.创建一个工具类
Enhancer enhancer = new Enhancer();
//2.设置父类
enhancer.setSuperclass(target.getClass());
//3.设置回调函数
enhancer.setCallback(this);
//4.创建子类对象,即代理对象
return enhancer.create();
}

//重写intercept方法,会调用目标对象的方法
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("Cglib代理模式开始...");
Object returnVal = method.invoke(target, objects);
System.out.println("Cglib代理模式提交...");
return returnVal;
}
}

Client.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package DesignPattern.proxy.cglib;

/**
* @Author coderYang
* @Date 2021/2/6 17:07
*/
public class Client {

public static void main(String[] args) {
//创建目标对象
TeacherDao target = new TeacherDao();
//获取到代理对象,并且将目标对象传递给代理对象
TeacherDao proxyInstance = (TeacherDao) new ProxyFactory(target).getProxyInstance();
//执行代理对象的方法,触发intercept方法,从而实现对目标对象的调用
proxyInstance.teach();
}
}

运行结果:

image-20210206173056257

代理模式的变体

几种常见的代理模式介绍——几种变体

1.防火墙代理

​ 内网通过代理穿透防火墙,实现对公网的访问。

2.缓存代理

​ 比如:当请求图片文件等资源时,先到缓存代理取,如果领取到资源则ok,如果领取不到资源,再到公网或者数据库取,然后缓存。

3.远程代理

远程代理的本地代表,通过它可以把远程对象当作本地对象来调用。远程代理通过网络和真正的远程对象沟通信息。

4.同步代理

​ 主要使用在多线程编程中,完成多线程间同步工作。

12.模板方法模式

豆浆制作问题:

编写制作豆浆的程序,说明如下:

  1. 制作豆浆的流程:选材–>添加配料–>浸泡–>放到豆浆机打碎
  2. 通过添加不同的配料,可以制作出不同口味的豆浆
  3. 选材、浸泡和放到豆浆机打碎这几个步骤对于制作每种口味的豆浆都是一样的
  4. 请使用模板方法模式完成(说明:因为模板方法模式,比较简单,很容易就想到这个方案,因此就直接使用,不再使用传统的方案来引出模板方法模式)
模板模式基本介绍
  1. 模板方法模式(Template Method Pattern),又叫模板模式(Template Pattern),在一个抽象类公开定义了执行它的方法的模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。
  2. 简单的说,模板方法模式定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构,就可以重定义该算法的某些特定步骤
  3. 这种类型的设计模式属于行为型模式。

image-20210207132407190

  • AbstractClass:是一个抽象类,类中实现了模板方法,定义了算法的骨架,具体子类需要去实现其它的一些抽象方法operation2,3,4
  • ConcreteClass:实现抽象方法operation2,3,4,以完成算法中特定子类的步骤
模板方法模式解决豆浆制作问题

image-20210207140259935

项目目录

image-20210207140317943

SoyaMilk.java

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
package DesignPattern.template;

/**
* @Author coderYang
* @Date 2021/2/7 13:34
*/
public abstract class SoyaMilk {

//模板方法,make,模板方法可以作为final,不让子类去覆盖
final void make(){
select();
addCondiments();
soak();
beat();
}

//选材料
void select(){
System.out.println("第一步:选择新鲜黄豆");
}

//添加不同配料,抽象方法,子类具体实现
abstract void addCondiments();

//浸泡
void soak(){
System.out.println("第三步:黄豆和配料开始浸泡,三个小时");
}

void beat(){
System.out.println("第四步:黄豆和配料放到豆浆机去打碎");
}
}

BlackBeanSoyaMilk.java

1
2
3
4
5
6
7
8
9
10
11
12
package DesignPattern.template;

/**
* @Author coderYang
* @Date 2021/2/7 13:55
*/
public class BlackBeanSoyaMilk extends SoyaMilk{
@Override
void addCondiments() {
System.out.println("加上上好的黑豆");
}
}

PeanutSoyMilk.java

1
2
3
4
5
6
7
8
9
10
11
12
package DesignPattern.template;

/**
* @Author coderYang
* @Date 2021/2/7 13:56
*/
public class PeanutSoyaMilk extends SoyaMilk{
@Override
void addCondiments() {
System.out.println("加入上好的花生");
}
}

Client.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package DesignPattern.template;

/**
* @Author coderYang
* @Date 2021/2/7 13:57
*/
public class Client {

public static void main(String[] args) {
System.out.println("-------制作黑豆豆浆-------");
SoyaMilk blackBeanSoyaMilk = new BlackBeanSoyaMilk();
blackBeanSoyaMilk.make();

System.out.println("-------制作花生豆浆-------");
SoyaMilk peanutSoyaMilk = new PeanutSoyaMilk();
peanutSoyaMilk.make();
}
}

运行结果:

image-20210207140633675

模板方法模式的钩子方法

  1. 在模板方法模式的父类中,我们可以定义一个方法,它默认不做任何事情,子类可以视情况要不要覆盖它,该方法称为钩子
  2. 还是用上面做豆浆的例子来讲解,比如,我们还希望制作纯豆浆,不添加任何的配料,请使用钩子方法对前面的模板方法进行改造。

SoyaMilk.java

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
package DesignPattern.template.improve;

/**
* @Author coderYang
* @Date 2021/2/7 13:34
*/
public abstract class SoyaMilk {

//模板方法,make,模板方法可以作为final,不让子类去覆盖
final void make(){
select();
if(customerWantCondiments()){
addCondiments();
}
soak();
beat();
}

//选材料
void select(){
System.out.println("第一步:选择新鲜黄豆");
}

//添加不同配料,抽象方法,子类具体实现
abstract void addCondiments();

//浸泡
void soak(){
System.out.println("第三步:黄豆和配料开始浸泡,三个小时");
}

//打碎
void beat(){
System.out.println("第四步:黄豆和配料放到豆浆机去打碎");
}

//钩子方法,决定是否添加配料
boolean customerWantCondiments(){
return true;
}
}

PureSoyaMilk.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package DesignPattern.template.improve;

/**
* @Author coderYang
* @Date 2021/2/7 14:13
*/
public class PureSoyaMilk extends SoyaMilk{

@Override
void addCondiments() {
//空实现
}

@Override
boolean customerWantCondiments() {
return false;
}
}

Client.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package DesignPattern.template.improve;

/**
* @Author coderYang
* @Date 2021/2/7 13:57
*/
public class Client {

public static void main(String[] args) {
System.out.println("-------制作黑豆豆浆-------");
SoyaMilk blackBeanSoyaMilk = new BlackBeanSoyaMilk();
blackBeanSoyaMilk.make();

System.out.println("-------制作花生豆浆-------");
SoyaMilk peanutSoyaMilk = new PeanutSoyaMilk();
peanutSoyaMilk.make();

System.out.println("--------制作纯豆浆-------");
SoyaMilk pureSoyaMilk = new PureSoyaMilk();
pureSoyaMilk.make();
}
}
模板方法模式在Spring框架应用的源码分析

Spring IOC容器初始化时运用到的模板方法模式

image-20210207183105075

模板方法模式的注意事项和细节
  1. 基本思想是:算法只存在于一个地方,也就是在父类中,容易修改。需要修改算法时,只要修改父类的模板方法或者已经实现的某些步骤,子类就会继承这些修改。
  2. 实现了最大化代码复用。父类的模板方法和已实现的某些步骤会被子类继承而直接使用。
  3. 既统一了算法,也提供了很大的灵活性。父类的模板方法确保了算法的结构保持不变,同时由子类提供部分步骤的实现。
  4. 该模式的不足之处:每一个不同的实现都需要一个子类实现,导致类的个数增加,使得系统更加庞大。
  5. 一般模板方法都加上final关键字,防止子类重写模板方法。
  6. 模板方法模式使用场景:当要完成在某个过程,该过程要执行一系列步骤,这一系列的步骤基本相同,但其个别步骤在实现时可能不同,通常考虑用模板方法模式来处理。

13.命令模式

智能生活项目需求:

  1. 我们买了一套智能家电,有照明灯、风扇、冰箱、洗衣机,我们只要在手机上安装app就可以控制对这些家电工作。
  2. 这些智能家电来自不同的厂家,我们不想针对每一种家电都安装一个App,分别控制,我们希望只要一个app就可以控制全部智能家电。
  3. 要实现一个app控制所有智能家电的需要,则每个智能家电厂家都需要提供一个接口给app调用,这时就可以考虑使用命令模式。
  4. 命令模式可将”动作的请求者”从”动作的执行者”对象中解耦出来。
  5. 在我们的例子中,动作的请求者是手机app,动作的执行者是每个厂商的一个家电产品。
命令模式基本介绍
  1. 命令模式(Command Pattern):在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个。我们只需要在程序运行时指定具体的请求接收者即可,此时,可以使用命令模式来进行设计。
  2. 命令模式使得请求发送者与请求接收者消除彼此之间的耦合,让对象之间的调用关系更加灵活,实现解耦。
  3. 在命令模式中,会将一个请求封装为一个对象,以便使用不同参数来表示不同的请求(即命名),同时命令模式也支持可撤销的操作。
  4. 通俗易懂的理解:将军发布命令,士兵去执行。其中的几个角色:将军(命令发布者)、士兵(命令的具体执行者)、命令(连接将军和士兵)。Invoker是调用者(将军),Recevier是被调用者(士兵),MyCommand是命令,实现了Command接口,持有接收对象。

image-20210208141904825

  • Invoker:调用者角色
  • Command:命令角色,需要执行的所有命令都在这里,可以是接口,或抽象类
  • Receiver:接收者角色,知道如何实施和执行一个请求相关的操作
  • ConcreteCommand:将一个接收者对象与一个动作绑定,调用接收者相应的操作,实现execute。
命令模式解决智能生活项目

image-20210208154001129

image-20210208154500584

项目目录

image-20210208154029632

Command.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package DesignPattern.command;

/**
* @Author coderYang
* @Date 2021/2/8 14:37
*/

//创建命令接口
public interface Command {

//执行某个操作
public void execute();
//撤销某个操作
public void undo();
}

LightRecevier.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package DesignPattern.command;

/**
* @Author coderYang
* @Date 2021/2/8 14:40
*/
public class LightReceiver {

public void on(){
System.out.println("电灯打开了");
}

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

LightOnCommand.java

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
package DesignPattern.command;

/**
* @Author coderYang
* @Date 2021/2/8 14:39
*/
public class LightOnCommand implements Command{

//聚合LightReceiver
LightReceiver light;

public LightOnCommand(LightReceiver light) {
this.light = light;
}

@Override
public void execute() {
//调用接收者的方法
light.on();
}

@Override
public void undo() {
//调用接收者的方法
light.off();
}
}

LightOffCommand.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package DesignPattern.command;

/**
* @Author coderYang
* @Date 2021/2/8 14:43
*/
public class LightOffCommand implements Command{

LightReceiver light;

public LightOffCommand(LightReceiver light) {
this.light = light;
}

@Override
public void execute() {
light.off();
}

@Override
public void undo() {
light.on();
}
}

NullCommand.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package DesignPattern.command;

/**
* @Author coderYang
* @Date 2021/2/8 14:46
*/

/**
* 没有任何命令,即空执行:用于初始化每个按钮,当调用空命令时,对象什么都不做
* 这也是一种设计模式:可以省掉对空判断
*/
public class NullCommand implements Command{
@Override
public void execute() {

}

@Override
public void undo() {

}
}

RemoteController.java

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
package DesignPattern.command;

/**
* @Author coderYang
* @Date 2021/2/8 14:49
*/
public class RemoteController {

//开按钮的命令数组
Command[] onCommands; //一列打开按钮(各个功能的)
Command[] offCommands; //一列关闭按钮(各个功能的)

//执行撤销的命令
Command undoCommand;

public RemoteController() {
onCommands = new Command[5];
offCommands = new Command[5];

for (int i=0;i<5;i++){
//默认不操作
onCommands[i] = new NullCommand();
offCommands[i] = new NullCommand();
}
}

//给我们的按钮设置你需要的命令
public void setCommand(int no,Command onCommand,Command offCommand){
onCommands[no] = onCommand;
offCommands[no] = offCommand;
}

//按下开的按钮
public void onButtonWasPushed(int no){
//找到按下的按钮,并调用对应的方法
onCommands[no].execute();
//记录这次的操作,用于撤销
undoCommand = onCommands[no];
}

//按下关的按钮
public void offButtonWasPushed(int no){
offCommands[no].execute();
undoCommand = offCommands[no];
}

//按下撤销按钮
public void undoButtonWasPushed(){
undoCommand.undo();
}

}

Client.java

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
package DesignPattern.command;

/**
* @Author coderYang
* @Date 2021/2/8 15:21
*/
public class Client {

public static void main(String[] args) {
//使用命令设计模式,完成通过遥控器对电灯的操作

//创建电灯的对象(接收者)
LightReceiver lightReceiver = new LightReceiver();

//创建电灯相关的开关命令
LightOnCommand lightOnCommand = new LightOnCommand(lightReceiver);
LightOffCommand lightOffCommand = new LightOffCommand(lightReceiver);

//需要一个要控制器来控制
RemoteController remoteController = new RemoteController();

//给我们的遥控器设置相关的命令,比如no=0是电灯的开和关的操作
remoteController.setCommand(0,lightOnCommand,lightOffCommand);

System.out.println("--------按下灯的开按钮--------");
remoteController.onButtonWasPushed(0);
System.out.println("--------按下灯的关按钮--------");
remoteController.offButtonWasPushed(0);
System.out.println("--------按下灯的撤销按钮--------");
remoteController.undoButtonWasPushed();
}
}

运行结果

image-20210208154356179

命令模式在Spring框架JdbcTemplate应用的源码分析

image-20210208162010828

image-20210208162336636

image-20210208162436336

命令调用者是JdbcTemplate,其中execute(StatementCallback<T> action )方法中,调用了action.doInStatement方法。不同的实现StatementCallback接口的对象,对应不同的doInStatement实现逻辑。

image-20210208163019410

命令模式的注意事项和细节
  1. 将发起请求的对象与执行请求的对象解耦。发起请求的对象是调用者,调用者只要调用命令对象的execute()方法就可以让接收者工作,而不必知道具体的接收者对象是谁、是如何实现的,命令对象会负责让接收者执行请求的动作,也就是说:请求发起者请求执行者之间的解耦是通过命令对象实现的,命令对象起到了纽带桥梁的作用。
  2. 容易设计一个命令队列。只要把命令对象放到队列,就可以多线程的执行命令
  3. 容易实现对请求的撤销和重做
  4. 命令模式的不足:可能导致某些系统有过多的类,增加了系统的复杂度,这点在使用时要注意
  5. 空命令也是一种设计模式,它为我们省去了判空的操作。在上面的示例中,如果没有用空命令,我们每按下一个按键都要判空,这给我们编码带来了一定的麻烦。
  6. 命令模式经典的应用场景:界面的一个按钮都是一条命令、模拟CMD(DOS命令),订单的撤销/恢复、触发-反馈机制。

14.访问者模式

测评系统需求

1.将观众分为男人和女人,对歌手进行测评,当看完某个歌手表演后,得到他们对该歌手不同的评价(评价有不同的种类,比如成功、失败等)

2.传统方案

image-20210209132244065

传统方式的问题分析

1.如果系统比较小,还是ok的,但是考虑系统增加越来越多新的功能时,对代码改动较大,违反了ocp原则,不利于维护

2.扩展性不好,比如增加了新的人员类型,或者管理方法,都不好做

访问者模式基本介绍
  1. 访问者模式(Visitor Pattern),封装一些作用于某种数据结构的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。
  2. 主要将数据结构与数据操作分离,解决数据结构操作耦合性问题。
  3. 访问者模式的基本工作原理:在被访问的类里面加一个对外提供接待访问者的接口。
  4. 访问者模式主要应用场景是:需要对一个对象结构中的对象进行很多不同操作(这些操作彼此没有关联),同时需要避免让这些操作污染这些对象的类,可以选用访问者模式解决

image-20210209161808238

  • Visitor是抽象访问者,为该对象结构中的ConcreteElement的每一个类声明一个visit操作
  • ConcreteVisitor:是一个具体的访问值实现每个有Visitor声明的操作,是每个操作实现的部分
  • ObjectStructure:能枚举它的元素,可以提供一个高层的接口,用来允许访问者访问元素
  • Element:定义一个accept方法,接收一个访问者对象
  • ConcreteElement:为具体元素,实现了accept方法
访问者模式解决测评系统需求

image-20210209155608756

项目目录

image-20210209160135948

Action.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package DesignPattern.visitor;

/**
* @Author coderYang
* @Date 2021/2/9 15:33
*/
public abstract class Action {

//得到男性的测评
public abstract void getManResult(Man man);

//得到女性的测评
public abstract void getWomanResult(Woman woman);
}

Success.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package DesignPattern.visitor;

/**
* @Author coderYang
* @Date 2021/2/9 15:35
*/
public class Success extends Action{

@Override
public void getManResult(Man man) {
System.out.println("女人给歌手的评价是很成功");
}

@Override
public void getWomanResult(Woman woman) {
System.out.println("女人给该歌手的评价是很成功");
}
}

Fail.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package DesignPattern.visitor;

/**
* @Author coderYang
* @Date 2021/2/9 15:37
*/
public class Fail extends Action{
@Override
public void getManResult(Man man) {
System.out.println("男人给该歌手的评价是失败");
}

@Override
public void getWomanResult(Woman woman) {
System.out.println("女人给该歌手的评价是失败");
}
}

Person.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package DesignPattern.visitor;

/**
* @Author coderYang
* @Date 2021/2/9 15:34
*/
public abstract class Person {

//提供一个方法,让访问者可以访问
public abstract void accept(Action action);



}

Man.java

1
2
3
4
5
6
7
8
9
10
11
12
package DesignPattern.visitor;

/**
* @Author coderYang
* @Date 2021/2/9 15:35
*/
public class Man extends Person{
@Override
public void accept(Action action) {
action.getManResult(this);
}
}

Woman.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package DesignPattern.visitor;

/**
* @Author coderYang
* @Date 2021/2/9 15:35
*/

/**
* 1.这里我们使用到双分派,即首先在客户端程序中,将具体状态作为参数传递Woman中(第一次分派)
* 2.然后Woman类调用作为参数的"具体方法"中方法getWomanResult,同时将自己(this)作为参数传入
* ,完成第二次的分派
*/
public class Woman extends Person{
@Override
public void accept(Action action) {
action.getWomanResult(this);
}
}

ObjectStructure.java

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
package DesignPattern.visitor;

import java.util.LinkedList;
import java.util.List;

/**
* @Author coderYang
* @Date 2021/2/9 15:46
*/
//数据结构,管理很多人(Man,Woman...)
public class ObjectStructure {

//维护了一个集合
private List<Person> persons = new LinkedList<>();

//增加元素到list
public void attach(Person person){
persons.add(person);
}

//从list移除元素
public void detach(Person person){
persons.remove(person);
}

//显示测评情况
public void display(Action action){
for (Person person : persons){
person.accept(action);
}
}
}

Client.java

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
package DesignPattern.visitor;

/**
* @Author coderYang
* @Date 2021/2/9 15:51
*/
public class Client {

public static void main(String[] args) {
//创建ObjectStructure
ObjectStructure objectStructure = new ObjectStructure();

objectStructure.attach(new Man());
objectStructure.attach(new Woman());

//成功
Success success = new Success();
objectStructure.display(success);

//失败fail
System.out.println("===========");
Fail fail = new Fail();
objectStructure.display(fail);

}
}

运行结果:

image-20210209160441778

双分派

上面提到了双分派,所谓双分派是不管类怎么变化,我们都能找到期望的方法运行。双分派意味着得到执行的操作取决于请求的种类和两个接收者的类型。

以上述实例为例,假设我们要添加一个Wait的状态类,考察Man类和Woman类的反应,由于使用了双分派,只需要增加一个Action子类即可在客户端调用即可,不需要改动任何其他类的代码

访问者模式的注意事项和细节

优点:

  1. 访问者模式符合单一职责原则、让程序具有优秀的扩展性、灵活性非常高
  2. 访问者模式可以对功能进行统一,可以做报表、UI、拦截器与过滤器,适用于数据结构相对稳定的系统

缺点:

  1. 具体元素对访问者公布细节,也就是说访问者关注了其他类的内部细节,这就是迪米特原则所不建议的,这样造成了具体元素变更比较困难
  2. 违背了依赖倒转原则。访问者依赖的是具体元素,而不是抽象元素
  3. 因此,如果一个系统有比较稳定的数据结构,又有经常变化的功能需求,那么访问者模式就是比较适合的。

15.迭代器模式

学院院系展示需求:

编写程序展示一个学校院系结构:需求是这样,要在一个页面中展示出学校的院系组成,一个学校有多少个学院,一个学院有多少个系。

传统方案解决学校院系展示(类图):

image-20210203205135898
传统方案的问题分析
  1. 将学院看做是学校的子类,系是学院的子类,这样实际上是站在组织大小来进行分层次的
  2. 实际上我们的要求是:在一个页面中展示出学校的院系组成,一个学校有多少个学院,一个学院有多少的系,因此这种方案,不能很好的实现遍历的操作
  3. 解决方案=>迭代器模式
迭代器模式基本介绍
  1. 迭代器模式(Iterator Pattern)是常用的设计模式,属于行为型模式
  2. 如果我们的集合元素是用不同的方式实现的,有数组,还有java的集合类,或者还有其他方式,当客户端要遍历这些集合元素的时候就要使用多种遍历方式,而且还会暴露元素的内部结构,可以考虑使用迭代器模式解决。
  3. 迭代器模式提供一种遍历集合元素的统一接口,用一致的方法遍历集合元素,不需要知道集合对象的低层表示,即:不暴露其内部的结构。

image-20210210195614829

  • Iterator:迭代器接口,系统提供,含有hasNext,next,remove等方法
  • ConcreteIterator:具体的迭代器类,管理迭代
  • Aggregate:一个统一的聚合接口,将客户端和具体聚合解耦
  • ConcreteAggregate:具体的聚合持有对象集合,并提供一个方法,返回一个迭代器,该迭代器可以遍历集合
  • Client:客户端,通过Iterator和Aggregate依赖子类
迭代器模式解决学校院系输出问题

image-20210210215138933

项目目录

image-20210210215205470

College.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package DesignPattern.iterator;

import java.util.Iterator;

/**
* @Author coderYang
* @Date 2021/2/10 20:30
*/
public interface College {

public String getName();

//增加系的方法
public void addDepartment(String name,String desc);

//返回一个迭代器,遍历
public Iterator createIterator();

}

Department.java

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
package DesignPattern.iterator;

/**
* @Author coderYang
* @Date 2021/2/10 20:04
*/
public class Department {

private String name;
private String desc;

public Department(String name, String desc) {
this.name = name;
this.desc = desc;
}

public String getName() {
return name;
}

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

public String getDesc() {
return desc;
}

public void setDesc(String desc) {
this.desc = desc;
}
}

ComputerCollegeIterator.java

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
package DesignPattern.iterator;

import java.util.Iterator;

/**
* @Author coderYang
* @Date 2021/2/10 20:05
*/
public class ComputerCollegeIterator implements Iterator {

//这里我们需要知道Department是以怎样的方式存放的,此处为数组
Department[] departments;
int position = 0; //遍历的位置

public ComputerCollegeIterator(Department[] departments) {
this.departments = departments;
}

//判断是否还有下一个元素
@Override
public boolean hasNext() {
if(position >= departments.length || departments[position] == null){
return false;
}else{
return true;
}
}

@Override
public Object next() {
Department department = departments[position];
position += 1;
return department;
}

//删除的方法默认空实现
@Override
public void remove() {

}
}

InfoCollegeIterator.java

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
package DesignPattern.iterator;

import java.util.Iterator;
import java.util.List;

/**
* @Author coderYang
* @Date 2021/2/10 20:14
*/
public class InfoCollegeIterator implements Iterator {

List<Department> departmentList; //信息工程学院是以List的方式存放系
int index = -1;

public InfoCollegeIterator(List<Department> departmentList) {
this.departmentList = departmentList;
}

@Override
public boolean hasNext() {
if(index >= departmentList.size()-1){
return false;
}else {
index += 1;
return true;
}
}

@Override
public Object next() {
return departmentList.get(index);
}

@Override
public void remove() {

}
}

ComputerCollege.java

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
package DesignPattern.iterator;

import java.util.Iterator;

/**
* @Author coderYang
* @Date 2021/2/10 20:32
*/
public class ComputerCollege implements College{

Department[] departments;
int numOfDepartment = 0;

public ComputerCollege() {
departments = new Department[5];
this.addDepartment("Java专业","java");
this.addDepartment("PHP专业","php");
this.addDepartment("大数据专业","大数据");
this.addDepartment("Python专业","python");
}

@Override
public String getName() {
return "计算机信息学院";
}

@Override
public void addDepartment(String name, String desc) {
Department department = new Department(name, desc);
departments[numOfDepartment] = department;
numOfDepartment += 1;
}

@Override
public Iterator createIterator() {
return new ComputerCollegeIterator(departments);
}
}

InfoCollege.java

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
package DesignPattern.iterator;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
* @Author coderYang
* @Date 2021/2/10 20:50
*/
public class InfoCollege implements College{

List<Department> departments;

public InfoCollege() {
departments = new ArrayList<Department>();
this.addDepartment("信息安全专业","信息安全专业");
this.addDepartment("网络安全专业","网络安全专业");
this.addDepartment("服务器安全专业","服务器安全专业");
}

@Override
public String getName() {
return "信息工程学院";
}

@Override
public void addDepartment(String name, String desc) {
Department department = new Department(name, desc);
departments.add(department);
}

@Override
public Iterator createIterator() {
return new InfoCollegeIterator(departments);
}
}

OutputImpl.java

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
package DesignPattern.iterator;

import java.util.Iterator;
import java.util.List;

/**
* @Author coderYang
* @Date 2021/2/10 20:54
*/
public class OutputImpl {

//学院集合
List<College> collegeList;

public OutputImpl(List<College> collegeList) {
this.collegeList = collegeList;
}

//遍历所有学院,然后调用printDepartment输出各个学院的系
public void printCollege(){
Iterator<College> iterator = collegeList.iterator();
while (iterator.hasNext()){
College college = iterator.next();
System.out.println("======="+college.getName()+"========");
this.printDepartment(college.createIterator()); //得到对应的迭代器
}
}

public void printDepartment(Iterator iterator){
while (iterator.hasNext()){
Department department = (Department) iterator.next();
System.out.println(department.getName());
}
}
}

Client.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package DesignPattern.iterator;

import java.util.ArrayList;
import java.util.List;

/**
* @Author coderYang
* @Date 2021/2/10 21:17
*/
public class Client {

public static void main(String[] args) {
//创建学院
List<College> collegeList = new ArrayList<College>();
ComputerCollege computerCollege = new ComputerCollege();
InfoCollege infoCollege = new InfoCollege();

collegeList.add(computerCollege);
collegeList.add(infoCollege);

OutputImpl output = new OutputImpl(collegeList);
output.printCollege();
}
}

输出结果

image-20210210215614057

迭代器模式在JDK-ArrayList集合应用的源码分析

image-20210211112327409

  • 内部类Itr充当具体实现迭代器Iterator的类,作为ArrayList内部类
  • List就是充当了聚合接口,含有一个iterator()的方法,返回一个迭代器对象
  • ArrayList是实现了聚合接口List的子类,实现了iterator()方法
  • Iterator接口系统提供
  • 迭代器模式解决了不同的集合(ArrayList,LinkedList)统一遍历问题。
迭代器模式的注意事项和细节

优点:

  1. 提供了一个统一的方法遍历对象,客户不用再考虑聚合的类型,使用一种方法就可以遍历对象了。
  2. 隐藏了聚合的内部结构,客户端要遍历聚合的时候只能取到迭代器,而不需要知道聚合的具体组成。
  3. 提供了一种设计思想,就是一个类应该只有一个引起变化的原因(叫做单一职责原则)。在聚合类中,我们把迭代器分开,就是要把管理对象集合遍历对象集合的责任分开,这样一来集合改变的话,只影响到聚合对象。而如果遍历方式改变的话,只影响到迭代器。
  4. 当要展示一组相似对象,或者遍历一组相同对象时,适合使用迭代器模式

缺点:

  1. 每个聚合对象都要一个迭代器,会生成多个迭代器不好管理类。

16.观察者模式

天气预报项目需求:

  1. 气象站可以将每天测量到的温度,湿度,气压等等以公告的形式发布出去(比如发布到自己的网站或第三方)。
  2. 需要设计开放型API,便于其他第三方也能接入气象站获取数据。
  3. 提供温度、气压和湿度的接口。
  4. 测量数据更新时,要能实时通知给第三方。
天气预报设计方案1-普通方案

通过对气象站项目的分析,我们可以初步设计出一个WeatherData类

image-20210211125654906

说明:

1.通过getXxx()方法,可以让第三方接入,并得到相关信息。

2.当有数据更新时,气象站通过调用dataChange()去更新数据,当第三方再次获取时,就能得到最新数据,当然也可以推送。

推送:

image-20210211130246710

CurrentConditions(当前的天气情况)

可以理解成我们气象局的网站 //推送

传统方案项目目录:

image-20210211133004593

CurrentConditions.java

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
package DesignPattern.observer;

/**
* @Author coderYang
* @Date 2021/2/11 13:06
*/
public class CurrentConditions {

private float temperature;
private float pressure;
private float humidity;

//更新天气情况,有WeatherData来调用,推送
public void update(float temperature,float pressure,float humidity){
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
display();
}

//展示
public void display(){
System.out.println("***Today Temperature: "+temperature+"***");
System.out.println("***Today Pressure: "+pressure+"***");
System.out.println("***Today Humidity: "+humidity+"***");
}
}

WeatherData.java

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
package DesignPattern.observer;

/**
* @Author coderYang
* @Date 2021/2/11 13:12
*/

/**
* 核心类
* 1.包含最新的天气情况信息
* 2.含有CurrentConditions对象
* 3.当数据有更新时,就主动调用CurrentConditions对象的update方法(含display),这样他们(接入方)就看到最新信息
*/
public class WeatherData {

private float temperature;
private float pressure;
private float humidity;
private CurrentConditions currentConditions;

public WeatherData(CurrentConditions currentConditions) {
this.currentConditions = currentConditions;
}

public float getTemperature() {
return temperature;
}


public float getPressure() {
return pressure;
}


public float getHumidity() {
return humidity;
}

public void dataChange(){
//调用接入方的update
currentConditions.update(getTemperature(),getPressure(),getHumidity());
}

//当数据有更新时,就调用setData
public void setData(float temperature,float pressure,float humidity){
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
//调用dataChange(),将最新的信息推送给接入方currentConditions
dataChange();
}
}

Client.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package DesignPattern.observer;

/**
* @Author coderYang
* @Date 2021/2/11 13:25
*/
public class Client {

public static void main(String[] args) {
//创建接入方currentConditions
CurrentConditions currentConditions = new CurrentConditions();
//创建WeatherData并将接入方currentConditions传递到WeatherData中
WeatherData weatherData = new WeatherData(currentConditions);
//更新天气情况
weatherData.setData(30,150,40);
System.out.println("========天气更新后=======");
weatherData.setData(15,160,20);
}
}

运行结果:

image-20210211133117444

传统方案问题分析

  1. 其他第三方接入气象站获取数据的问题

  2. 无法在运行时动态加载第三方

  3. 在WeatherData中,当增加一个第三方,都需要创建一个对应的第三方的公告版对象,并加入到dataChange,不利于维护,也不是动态加入

  4. 违反ocp原则=>观察者模式

观察者模式基本介绍

观察者模式原理

  • 观察者模式类似于订牛奶业务
  1. 牛奶站/气象局:Subject
  2. 用户/第三方网站:Observer
  • Subject:登记注册、移除和通知
  1. registerObserver():注册
  2. removeObserver():移除
  3. notifyObservers():通知所有的注册用户,根据不同需求,可以是更新数据,让用户来取,也可能是实施推送,看具体需求定
image-20210211134636580
  • Observer:接收输入

image-20210211134805289

观察者模式:对象之间多对一依赖的一种设计方案,被依赖的对象为Subject,依赖的对象为Observer,Subject通知Observer变化,比如这里的牛奶站是Subject,是1的一方。用户是Observer,是多的一方。

观察者模式解决天气预报

image-20210211155410135

项目目录

image-20210211155251464

Observer.java

1
2
3
4
5
6
7
8
9
10
package DesignPattern.observer.improve;

/**
* @Author coderYang
* @Date 2021/2/11 14:02
*/
public interface Observer {

public void update(float temperature,float pressure,float humidity);
}

BaiduNews.java

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
package DesignPattern.observer.improve;

/**
* @Author coderYang
* @Date 2021/2/11 14:04
*/
public class BaiduNews implements Observer{

private float temperature;
private float pressure;
private float humidity;

@Override
public void update(float temperature, float pressure, float humidity) {
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
display();
}

public void display(){
System.out.println("百度新闻播报天气:");
System.out.println("current temperature: "+temperature);
System.out.println("current pressure: "+pressure);
System.out.println("current humidity: "+humidity);
}
}

WYNews.java

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
package DesignPattern.observer.improve;

/**
* @Author coderYang
* @Date 2021/2/11 14:04
*/
public class WYNews implements Observer{

private float temperature;
private float pressure;
private float humidity;

@Override
public void update(float temperature, float pressure, float humidity) {
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
display();
}

public void display(){
System.out.println("网易新闻播报天气:");
System.out.println("current temperature: "+temperature);
System.out.println("current pressure: "+pressure);
System.out.println("current humidity: "+humidity);
}
}

Subject.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package DesignPattern.observer.improve;

/**
* @Author coderYang
* @Date 2021/2/11 14:01
*/
public interface Subject {

public void registerObserver(Observer observer);

public void removeRegisterObserver(Observer observer);

public void notifyObservers();
}

WeatherData.java

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
package DesignPattern.observer.improve;

import java.util.ArrayList;
import java.util.List;

/**
* @Author coderYang
* @Date 2021/2/11 14:04
*/

/*
1.包含最新的天气信息
2.含有观察者集合,使用ArrayList管理
3.当数据有更新时,就主动调用ArrayList,通知所有的(接入方)看到最新的消息
*/
public class WeatherData implements Subject{

private float temperature;
private float pressure;
private float humidity;

//观察者集合
private List<Observer> observers;

public WeatherData() {
observers = new ArrayList<Observer>();
}

@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}

@Override
public void removeRegisterObserver(Observer observer) {
observers.remove(observer);
}


public void dataChange(){
notifyObservers();
}

public void setData(float temperature,float pressure,float humidity){
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
dataChange();
}

@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(this.temperature, this.pressure, this.humidity);
}
}
}

Client.java

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
package DesignPattern.observer.improve;

/**
* @Author coderYang
* @Date 2021/2/11 14:16
*/
public class Client {

public static void main(String[] args) {
//创建观察者
WYNews wyNews = new WYNews();
BaiduNews baiduNews = new BaiduNews();
//创建WeatherData
WeatherData weatherData = new WeatherData();
//把观察者注册到WeatherData
weatherData.registerObserver(baiduNews);
weatherData.registerObserver(wyNews);

//设置天气
weatherData.setData(15,60,50);
weatherData.notifyObservers();


System.out.println("=======天气更新后=======");
weatherData.setData(20,75,60);
weatherData.notifyObservers();
}
}

运行结果:

image-20210211161451010

观察者模式好处

  1. 观察者模式设计后,会以集合的方式来管理用户(Observer),包括注册,移除和通知。
  2. 这样,我们增加观察者(这里可以理解成一个新的公告版),就不需要去修改核心类WeatherData不会修改代码,遵守了ocp原则。
观察者模式在JDK应用的源码分析

image-20210211163052845

角色模式分析:

  • Observable:等价于我们前面说的Subject
  • Observable是类,不是接口,类中已经实现了核心的方法,即管理Observer的方法add,delete,notify等
  • Observer:等价于前面的Observer,有update方法
  • Observable和Observer的使用方法和前面讲过的一样,只是Observable是类,通过继承来实现观察者模式

17.中介者模式

智能家庭管理问题

智能家庭项目:

  1. 智能家庭包括各种设备,闹钟,咖啡机,电视机,窗帘等
  2. 主人要看电视时,各个设备可以协同工作,自动完成看电视的准备工作,比如流程为:闹钟响起->咖啡机开始做咖啡->窗帘自动落下->电视机开始播放

传统方案解决智能家庭管理问题:

image-20210212132515000

传统方式的问题分析:

  1. 当各电器对象有多种状态改变时,相互之间的调用关系会比较复杂
  2. 各个电器对象彼此联系,你中有我,我中有你,不利于松耦合
  3. 各个电器对象之间所传递的消息(参数),容易混乱
  4. 当系统增加一个新的电器对象时,或者执行流程改变时,代码的可维护性、扩展性都不理想=>考虑中介者模式

中介者模式:

image-20210212132307903

中介者模式基本介绍
  1. 中介者模式(Mediator Pattern),用一个中介对象来封装一系列的对象交互。中介者模式使各个对象不需要显示地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互
  2. 中介者模式属于行为型模式,使代码易于维护
  3. 比如MVC模式,C(Controller控制器)是M(Model模型)和V(View视图)的中介者,在前后端交互时起到了中间人的作用。

image-20210212135426026

  • Mediator:就是抽象中介者,定义了同事对象到中介者对象的接口
  • Colleague:抽象同事类
  • ConcreteMediator:具体的中介者对象,实现抽象方法,他需要知道所有的具体的同事类,即以一个集合来管理HashMap,并接受某个同事对象消息,完成相应的任务
  • ConcreteColleague:具体的同事类,有很多,每个同事只知道自己的行为,而不了解其他同事类的行为(方法),但是它们都依赖中介者对象。
中介者模式解决传统项目问题

image-20210212141943696

image-20210212142315217

项目目录

image-20210212165656299

Colleague.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package DesignPattern.mediator;

/**
* @Author coderYang
* @Date 2021/2/12 14:25
*/
//同事抽象类
public abstract class Colleague {

private Mediator mediator;
public String name;

public Colleague(Mediator mediator, String name) {
this.mediator = mediator;
this.name = name;
}

public Mediator getMediator(){
return this.mediator;
}

public abstract void sendMessage(int stateChange);

}

Mediator.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package DesignPattern.mediator;

/**
* @Author coderYang
* @Date 2021/2/12 14:25
*/
public abstract class Mediator {

//将中介者对象加入到集合中
public abstract void register(String colleagueName,Colleague colleague);

//接收具体的同事发出的消息
public abstract void getMessage(int stateChange,String colleagueName);

public abstract void sendMessage();
}

Alarm.java

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
package DesignPattern.mediator;

/**
* @Author coderYang
* @Date 2021/2/12 14:27
*/
//具体的同事类
public class Alarm extends Colleague{
public Alarm(Mediator mediator, String name) {
super(mediator, name);

//在创建同事类的同时,把自己放入到ConcreteMediator对象的集合属性中
mediator.register(name,this);
}

public void sendAlarm(int stateChange){
sendMessage(stateChange);
}


@Override
public void sendMessage(int stateChange) {
//调用中介者对象的getMessage()
this.getMediator().getMessage(stateChange,this.name);
}
}

CoffeeMachine.java

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
package DesignPattern.mediator;

/**
* @Author coderYang
* @Date 2021/2/12 14:54
*/
public class CoffeeMachine extends Colleague{
public CoffeeMachine(Mediator mediator, String name) {
super(mediator, name);
mediator.register(name,this);
}

@Override
public void sendMessage(int stateChange) {
this.getMediator().getMessage(stateChange,this.name);
}

public void startCoffee(){
System.out.println("It's time to start to made coffee");
}

public void finishCoffee(){
System.out.println("After 5 minutes!");
System.out.println("Coffee is ok");
sendMessage(0);
}
}

Curtains.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package DesignPattern.mediator;

/**
* @Author coderYang
* @Date 2021/2/12 14:58
*/
public class Curtains extends Colleague{
public Curtains(Mediator mediator, String name) {
super(mediator, name);
mediator.register(name,this);
}

@Override
public void sendMessage(int stateChange) {
this.getMediator().getMessage(stateChange,this.name);
}

public void upCurtains(){
System.out.println("The curtain is holding up");
}
}

TV.java

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
package DesignPattern.mediator;

/**
* @Author coderYang
* @Date 2021/2/12 15:01
*/
public class TV extends Colleague{
public TV(Mediator mediator, String name) {
super(mediator, name);
mediator.register(name,this);
}

@Override
public void sendMessage(int stateChange) {
this.getMediator().getMessage(stateChange,this.name);
}

public void startTV(){
System.out.println("The TV is starting...");
System.out.println("The TV is started");
}

public void stopTV(){
System.out.println("The TV is stopping...");
System.out.println("The TV is stopped");
}
}

ConcreteMediator.java

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
package DesignPattern.mediator;

import java.util.HashMap;

/**
* @Author coderYang
* @Date 2021/2/12 15:05
*/
public class ConcreteMediator extends Mediator{

//集合,放入所有的同事对象
private HashMap<String,Colleague> colleagueMap;
private HashMap<String,String> interMap;

public ConcreteMediator(){
colleagueMap = new HashMap<String, Colleague>();
interMap = new HashMap<String, String>();
}

@Override
public void register(String colleagueName, Colleague colleague) {
colleagueMap.put(colleagueName,colleague);

if (colleague instanceof Alarm){
interMap.put("Alarm",colleagueName);
}else if(colleague instanceof CoffeeMachine){
interMap.put("CoffeeMachine",colleagueName);
}else if(colleague instanceof TV){
interMap.put("TV",colleagueName);
}else if (colleague instanceof Curtains){
interMap.put("Curtains",colleagueName);
}
}

/**
*1.根据得到的消息,完成对应的任务
*2.中介者在这个方法,协调各个具体的同事对象,完成任务
*/
@Override
public void getMessage(int stateChange, String colleagueName) {
//处理闹钟发出的消息
if (colleagueMap.get(colleagueName) instanceof Alarm) {
if (stateChange == 0) {
((CoffeeMachine) (colleagueMap.get(interMap
.get("CoffeeMachine")))).startCoffee();
((TV) (colleagueMap.get(interMap.get("TV")))).startTV();
} else if (stateChange == 1) {
((TV) (colleagueMap.get(interMap.get("TV")))).stopTV();
}

} else if (colleagueMap.get(colleagueName) instanceof CoffeeMachine) {
((Curtains) (colleagueMap.get(interMap.get("Curtains"))))
.upCurtains();

} else if (colleagueMap.get(colleagueName) instanceof TV) {//如果 TV 发现消息


} else if (colleagueMap.get(colleagueName) instanceof Curtains) {
//如果是以窗帘发出的消息,这里处理...
}

}

@Override
public void sendMessage() {

}
}

Client.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package DesignPattern.mediator;

/**
* @Author coderYang
* @Date 2021/2/12 15:57
*/
public class Client {

public static void main(String[] args) {
//创建一个中介者对象
Mediator mediator = new ConcreteMediator();
//创建Alarm并加入到ConcreteMediator的HashMap
Alarm alarm = new Alarm(mediator,"alarm");
CoffeeMachine coffeeMachine = new CoffeeMachine(mediator,"coffeeMachine");
//创建Curtains,并加入到ConcreteMediator对象的HashMap
Curtains curtains = new Curtains(mediator,"curtains");
TV tv = new TV(mediator,"tV");

//让闹钟发出消息,让中介者完成相应的操作
alarm.sendMessage(0);
coffeeMachine.finishCoffee();
alarm.sendMessage(1);
}
}

运行结果:

image-20210212170029613

中介者模式的注意事项和细节
  1. 多个类相互耦合,会形成网状结构,使用中介者模式将网状结构分离为星型结构,进行解耦
  2. 减少类间依赖,降低了耦合,符合迪米特原则
  3. 中介者承担了较多的责任,一旦中介者出现了问题,整个系统就会受到影响
  4. 如果设计不当,中介者对象本身变得过于复杂,这点在实际使用时,需要特别注意

18.备忘录模式

游戏角色状态恢复问题

游戏角色有攻击力和防御力,在大战Boss前保存自身的状态(攻击力和防御力),当大战Boss后攻击力和防御力下降,从备忘录对象恢复到大战前的状态

image-20210213103658576

传统模式的问题分析
  1. 一个对象,就对应一个保存对象状态的对象,这样当我们游戏的对象很多时,不利于管理,开销也很大。
  2. 传统的方式是简单的做备份,new出另一个对象出来,再把需要备份的数据放到这个新对象中,但这就暴露了对象的内部细节
  3. 解决方案=>备忘录模式
备忘录模式基本介绍
  1. 备忘录模式(Memento Pattern)在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。
  2. 这样理解:现实生活中的备忘录是用来记录某些要去做的事情,或者是记录已经达成的共同意见的事情,以防忘记了。而在软件层面,备忘录模式有着相同的含义,备忘录对象主要用来记录一个对象的某种状态,或者某些数据,当要回退时,可以从备忘录对象里获取原来的数据进行恢复操作
  3. 备忘录模式属于行为型模式。
image-20210213112946298
  • Originator:需要保存状态的对象
  • Memento:备忘录对象,负责保存好记录,即Originator的内部状态
  • Caretaker:守护者对象,负责保存多个备忘录对象,使用集合管理,提高效率
  • 如果需要保存多个Originator对象的不同时间的状态,只需要HashMap<String,集合>即可。

项目目录

image-20210213132615338

Originator.java

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
package DesignPattern.memento;

/**
* @Author coderYang
* @Date 2021/2/13 11:11
*/

//发起人
public class Originator {

private String state; //状态信息

public String getState() {
return state;
}

public void setState(String state) {
this.state = state;
}

//保存状态对象Memento
public Memento saveStateMemento(){
return new Memento(state);
}

//通过备忘录对象,恢复状态
public void getStateFormMemento(Memento memento){
state = memento.getState();
}
}

Momento.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package DesignPattern.memento;

/**
* @Author coderYang
* @Date 2021/2/13 11:15
*/

//备忘录
public class Memento {

private String state;

public Memento(String state) {
this.state = state;
}

public String getState() {
return state;
}
}

Caretaker.java

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
package DesignPattern.memento;

import java.util.ArrayList;
import java.util.List;

/**
* @Author coderYang
* @Date 2021/2/13 11:19
*/

//状态管理者
public class Caretaker {

//在List集合中会有很多的备忘录对象
private List<Memento> mementoList = new ArrayList<Memento>();

public void add(Memento memento){
mementoList.add(memento);
}

//获取到第index个备忘录对象(即保存的对象)
public Memento get(int index){
return mementoList.get(index);
}
}

Client.java

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
package DesignPattern.memento;

/**
* @Author coderYang
* @Date 2021/2/13 11:22
*/
public class Client {

public static void main(String[] args) {
Originator originator = new Originator();
Caretaker caretaker = new Caretaker();

originator.setState(" 状态1=>100%血量 ");
caretaker.add(originator.saveStateMemento());

originator.setState(" 状态2=>80%血量 ");
caretaker.add(originator.saveStateMemento());

originator.setState(" 状态3=>50%血量");
caretaker.add(originator.saveStateMemento());

System.out.println("当前的状态是:"+originator.getState());


//恢复状态1时的状态
originator.getStateFormMemento(caretaker.get(1));
System.out.println("恢复状态后,当前状态:"+originator.getState());
}
}

运行结果:

image-20210213132812100

备忘录模式解决游戏角色恢复实例

image-20210213140726129

项目目录

image-20210213140744844

Memento.java

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
package DesignPattern.memento.gamerole;

/**
* @Author coderYang
* @Date 2021/2/13 13:40
*/
public class Memento {

private int vit;
private int def;

public Memento(int vit, int def) {
this.vit = vit;
this.def = def;
}

public int getVit() {
return vit;
}

public void setVit(int vit) {
this.vit = vit;
}

public int getDef() {
return def;
}

public void setDef(int def) {
this.def = def;
}
}

Gamerole.java

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
package DesignPattern.memento.gamerole;

/**
* @Author coderYang
* @Date 2021/2/13 13:44
*/
public class GameRole {

private int vit;
private int def;

//创建Memento
public Memento createMemento(){
return new Memento(vit,def);
}

//从备忘录对象获取保存的状态
public void recoverGameRoleFromMemento(Memento memento){
this.vit = memento.getVit();
this.def = memento.getDef();
}

//显示当前游戏角色的状态
public void display(){
System.out.println("当前游戏角色的攻击力: "+this.vit);
System.out.println("当前游戏角色的防御力: "+this.def);
}

public int getVit() {
return vit;
}

public void setVit(int vit) {
this.vit = vit;
}

public int getDef() {
return def;
}

public void setDef(int def) {
this.def = def;
}
}

Caretaker.java

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
package DesignPattern.memento.gamerole;

import java.util.ArrayList;
import java.util.HashMap;

/**
* @Author coderYang
* @Date 2021/2/13 13:41
*/

//状态管理者,保存游戏角色的状态
public class Caretaker {

//如果只保存一次状态
private Memento memento;
//对GameRole保存多次对象
//private ArrayList<Memento> mementos;
//对多个游戏角色保存状态
//private HashMap<String,ArrayList<Memento>> rolesMementos;

public Memento getMemento() {
return memento;
}

public void setMemento(Memento memento) {
this.memento = memento;
}
}

Client.java

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
package DesignPattern.memento.gamerole;

/**
* @Author coderYang
* @Date 2021/2/13 13:51
*/
public class Client {

public static void main(String[] args) {
//创建游戏角色,并赋予攻击力防御力
GameRole gameRole = new GameRole();
gameRole.setVit(100);
gameRole.setDef(100);
//存档
Caretaker caretaker = new Caretaker();
caretaker.setMemento(gameRole.createMemento());
gameRole.display();

gameRole.setVit(50);
gameRole.setDef(50);
System.out.println("打败怪兽后:");
gameRole.display();


System.out.println("恢复初始状态后:");

gameRole.recoverGameRoleFromMemento(caretaker.getMemento());
gameRole.display();
}
}

运行结果

image-20210213141009983

备忘录模式的注意事项和细节
  1. 给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态
  2. 实现了信息的封装,使得用户不需要关心状态的保存细节
  3. 如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存,这个需要注意
  4. 适用的应用场景:后悔药、打游戏时的存档、Windows里的ctrl+z、IE中的后退、数据库的事务管理
  5. 为了节约内存,备忘录模式可以和原型模式配合使用

19.解释器模式

四则运算问题

通过解释器模式来实现四则运算,如计算a+b-c的值,具体要求:

  1. 先输入表达式的形式,比如a+b+c-d+e,要求表达式的字母不能重复
  2. 再分别输入a,b,c,d,e的值
  3. 最后求出结果:如图:

image-20210214102721554

传统方案解决四则运算问题分析
  1. 编写一个方法,接收表达式的形式,然后根据用户输入的数值进行解析,得到结果
  2. 问题分析:如果加入新的运算符,比如*/(等等,不利于扩展,另外让一个方法来解析会造成程序结构混乱,不够清晰)
  3. 解决方案:可以考虑使用解释器模式,即:表达式->解释器(可以有多种)->结果
解释器模式基本介绍
  1. 在编译原理中,一个算术表达式通过词法分析器形成词法单元,而后这些词法单元再通过语法分析器构建语法分析树,最终形成一棵抽象的语法分析树。这里的词法分析器和语法分析器都可以看作是解释器
  2. 解释器模式(Interpreter Pattern):是指给定一个语言(表达式),定义它的文法的一种表示,并定义一个解释器,使用该解释器来解释语言中的句子(表达式)
  3. 应用场景
  • 应用可以将一个需要解释执行的语言中的句子表示为一个抽象语法树
  • 一些重复出现的问题可以用一种简单的语言来表达
  • 一个简单语法需要解释的场景
  1. 这样的例子还有:编译器、元素表达式、正则表达式、机器人等

image-20210214105904584

  • Context:环境角色,含有解释器之外的全局信息
  • AbstractExpression:抽象表达式,声明一个抽象的解释操作,这个方法为抽象语法树中所有的节点所共享
  • TerminalExpression:终结符表达式,实现与文法中的终结符相关的解释操作
  • NonTerminalExpression:非终结符表达式,为文法中的非终结符实现解释操作
  • 输入Context和TerminalExpression信息通过Client输入即可
解释器模式解决四则运算

image-20210215161908583

项目目录

image-20210215161927874

Expression.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package DesignPattern.interpreter;

import java.util.HashMap;

/**
* 抽象类表达式,通过HashMap键值对,可以获取到变量的值
* @Author coderYang
* @Date 2021/2/14 14:01
*/
public abstract class Expression {

//解释公式和数值,key就是表达式变量a,b,c等,value就是a,b,c的具体值
public abstract int interpreter(HashMap<String,Integer> var);
}

VarExpression.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package DesignPattern.interpreter;

import java.util.HashMap;

/**
* 变量的解释器
* @Author coderYang
* @Date 2021/2/14 14:10
*/

public class VarExpression extends Expression{

private String key;

public VarExpression(String key) {
this.key = key;
}

//interpreter根据变量的值,返回对应值
@Override
public int interpreter(HashMap<String, Integer> var) {
return var.get(this.key);
}
}

SymbolExpression.java

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
package DesignPattern.interpreter;

import java.util.HashMap;

/**
* 抽象运算符号解析器,每个运算符都只和自己左右两个数字有关系
* @Author coderYang
* @Date 2021/2/14 14:16
*/
public class SymbolExpression extends Expression{

protected Expression left;
protected Expression right;

public SymbolExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}


//让子类来实现
@Override
public int interpreter(HashMap<String, Integer> var) {
return 0;
}
}

AddExpression.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package DesignPattern.interpreter;

import java.util.HashMap;

/**
* 加法解释器
* @Author coderYang
* @Date 2021/2/14 14:20
*/
public class AddExpression extends SymbolExpression{

public AddExpression(Expression left, Expression right) {
super(left, right);
}

//处理left和right表达式相加后的结果
public int interpreter(HashMap<String,Integer> var){

//super.left.interpreter(var):返回left 表达式对应的值a=10
//super.right.interpreter(var):返回right 表达式对应的值b=20
return super.left.interpreter(var) + super.right.interpreter(var);
}
}

SubExpression.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package DesignPattern.interpreter;

import java.util.HashMap;

/**
* @Author coderYang
* @Date 2021/2/14 14:27
*/
public class SubExpression extends SymbolExpression{

public SubExpression(Expression left, Expression right) {
super(left, right);
}

//求出left和right表达式相减后的结果
public int interpreter(HashMap<String,Integer> var){
return super.left.interpreter(var) - super.right.interpreter(var);
}
}

Caculator.java

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
package DesignPattern.interpreter;

import java.util.HashMap;
import java.util.Stack;

/**
* @Author coderYang
* @Date 2021/2/14 14:29
*/
public class Calculator {

//定义表达式
private Expression expression;

//构造函数
public Calculator(String expStr){
Stack<Expression> stack = new Stack<Expression>();
//将表达式拆分成字符数组
char[] charArray = expStr.toCharArray(); //[a,+,b]

Expression left = null;
Expression right = null;
//遍历字符数组,[a,+,b]
for (int i=0;i<charArray.length;i++){
switch (charArray[i]){
case '+':
left = stack.pop(); //取出左边的表达式
right = new VarExpression(String.valueOf(charArray[++i])); //取出右边的表达式
stack.push(new AddExpression(left,right)); //然后把left和right构建的AddExpression对象压栈
break;
case '-':
left = stack.pop();
right = new VarExpression(String.valueOf(charArray[++i]));
stack.push(new SubExpression(left,right));
break;
default:
//如果是一个var就要创建VarExpression对象,并push到stack
stack.push(new VarExpression(String.valueOf(charArray[i])));
break;
}
}
//当遍历完整个charArray数组后,stack就得到了Expression
this.expression = stack.pop();
}

public int run(HashMap<String,Integer> var){
//最后将表达式a+b和var={a=10,b=20}传递给expression的interpreter进行解释执行
return this.expression.interpreter(var);
}
}

Client.java

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
package DesignPattern.interpreter;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;

/**
* @Author coderYang
* @Date 2021/2/14 14:49
*/
public class Client {

public static void main(String[] args) throws IOException {
String expStr = getExpStr();
HashMap<String,Integer> var = getValue(expStr);
Calculator calculator = new Calculator(expStr);
System.out.println("运算结果:"+expStr+"="+calculator.run(var));
}

public static String getExpStr() throws IOException {
System.out.println("请输入表达式");
return new BufferedReader(new InputStreamReader(System.in)).readLine();
}


//获取值映射,如为自己定义的a和b赋值{a=10,b=20}HashMap集合
public static HashMap<String,Integer> getValue(String expStr) throws IOException {
HashMap<String,Integer> map = new HashMap<>();

for (char ch : expStr.toCharArray()){
if (ch!='+' && ch != '-'){
if(!map.containsKey(String.valueOf(ch))){
System.out.println("请输入"+String.valueOf(ch)+"的值:");
String in = new BufferedReader(new InputStreamReader(System.in)).readLine();
map.put(String.valueOf(ch),Integer.valueOf(in));
}
}
}
return map;
}
}
解释器模式在Spring框架应用
1
2
3
4
5
6
7
@Test
public void test(){
SpelExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression("20*(2+2)/4+5");
int value = (int) expression.getValue();
System.out.println(value);
}

image-20210215163317118

  • Expression接口:表达式接口,有不同的实现类,如下:

    image-20210215163514104

​ 使用的时候根据你创建的不同的Parse对象,返回不同的Expression对象。

解释器模式注意事项和细节
  1. 当有一个语言需要解释执行时,可将该语言中的句子表示为一个抽象语法树,就可以考虑使用解释器模式,让程序具有良好的扩展性
  2. 应用场景:编译器、运算表达式计算、正则表达式、机器人等
  3. 使用解释器模式可能带来的问题:解释器模式会引起类膨胀、解释器模式采用递归调用方法,将会导致调试非常复杂、效率可能降低。

20.状态模式

App抽奖活动问题

请编写程序完成APP抽奖活动 具体要求如下:

  1. 假如每参加一次这个活动要扣除用户50积分,中将概率是10%

  2. 奖品数量固定,抽完就不能再抽奖

  3. 活动的四个状态:可以抽奖、不能抽奖、发放奖品和奖品领完

  4. 活动的四个状态如图:

    image-20210215165724399

状态模式基本介绍
  1. 状态模式(State Pattern):它主要用来解决对象在多种状态转换时,需要对外输出不同行为的问题。状态和行为是一一对应的,状态之间可以相互转换
  2. 当一个对象的内在状态改变时,允许改变其行为,这个对象看起来像是改变了其类

image-20210215171044059

  • Context:环境角色,用于维护State实例,这个实例定义当前状态
  • State:抽象状态角色,定义一个接口封装与Context的一个特点接口的相关行为
  • ConcreteState:具体的状态角色,每个子类实现一个与Context的一个状态相关行为
状态模式解决抽奖问题

image-20210215220525077

项目目录:

image-20210215213910031

State.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package DesignPattern.state;

/**
* @Author coderYang
* @Date 2021/2/15 20:05
*/

//状态抽象类
public abstract class State {

//扣除积分
public abstract void deductMoney();

//查看是否中奖
public abstract boolean raffle();

//发放奖品
public abstract void dispensePrize();
}

ReffleActivity.java

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
package DesignPattern.state;

/**
* @Author coderYang
* @Date 2021/2/15 20:09
* 抽奖活动
*/
public class RaffleActivity {

State state = null; //当前状态
int count = 0; //奖品数量

State noRaffleState = new NoRaffleState(this);
State canRaffleState = new CanRaffleState(this);

State dispenseState = new DispenseState(this);
State dispenseOutState = new DispenseOutState(this);

//构造器
// 1.初始化当前的状态为noRaffleState(不能抽奖的状态)
// 2.初始化奖品的数量
public RaffleActivity(int count) {
this.state = getNoRaffleState();
this.count = count;
}

public void deductMoney(){
state.deductMoney();
}

public void raffle(){
if (state.raffle()){
state.dispensePrize();
}
}

public int getCount() {
int curCount = count;
count--; //领取奖品后,库存奖品数量减一
return curCount;
}

public void setCount(int count) {
this.count = count;
}

public State getState() {
return state;
}

public void setState(State state) {
this.state = state;
}

public State getNoRaffleState() {
return noRaffleState;
}

public void setNoRaffleState(State noRaffleState) {
this.noRaffleState = noRaffleState;
}

public State getCanRaffleState() {
return canRaffleState;
}

public void setCanRaffleState(State canRaffleState) {
this.canRaffleState = canRaffleState;
}

public State getDispenseState() {
return dispenseState;
}

public void setDispenseState(State dispenseState) {
this.dispenseState = dispenseState;
}

public State getDispenseOutState() {
return dispenseOutState;
}

public void setDispenseOutState(State dispenseOutState) {
this.dispenseOutState = dispenseOutState;
}
}

CanReffleState.java

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
package DesignPattern.state;

import java.util.Random;

/**
* @Author coderYang
* @Date 2021/2/15 20:15
*/
public class CanRaffleState extends State{

RaffleActivity activity;

public CanRaffleState(RaffleActivity activity) {
this.activity = activity;
}

@Override
public void deductMoney() {
System.out.println("已经扣取过积分了");
}

@Override
public boolean raffle() {
System.out.println("正在抽奖,请稍等");
Random r = new Random();
int num = r.nextInt(10);

//10%中奖机会
if (num == 0){
activity.setState(activity.getDispenseState());
return true;
}else {
System.out.println("很遗憾没有抽中奖品");
activity.setState(activity.getNoRaffleState());
return false;
}

}

@Override
public void dispensePrize() {
System.out.println("没有中奖,不能发放奖品");
}
}

NoRaffleState.java

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
package DesignPattern.state;

/**
* @Author coderYang
* @Date 2021/2/15 20:08
*/
public class NoRaffleState extends State{

//初始化时,传入活动引用,扣除积分后改变其状态
RaffleActivity activity;

public NoRaffleState(RaffleActivity activity) {
this.activity = activity;
}

@Override
public void deductMoney() {
System.out.println("扣除了50积分,您可以抽奖了");
activity.setState(activity.getCanRaffleState());
}

//当前状态不能抽奖
@Override
public boolean raffle() {
System.out.println("扣了积分才能抽奖哦");
return false;
}

//当前状态不能发放奖品
@Override
public void dispensePrize() {
System.out.println("不能发放奖品");
}
}

DispenseState.java

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
package DesignPattern.state;

/**
* @Author coderYang
* @Date 2021/2/15 20:22
*/
public class DispenseState extends State{

RaffleActivity activity;

public DispenseState(RaffleActivity activity) {
this.activity = activity;
}


@Override
public void deductMoney() {
System.out.println("不能扣除积分");
}

@Override
public boolean raffle() {
System.out.println("不能抽奖");
return false;
}

@Override
public void dispensePrize() {
if (activity.getCount() > 0){
System.out.println("恭喜中奖了");
//将状态改为不能抽奖
activity.setState(activity.getNoRaffleState());
}else {
System.out.println("很遗憾,奖品发放完了");
//改为状态为奖品发放完毕,以后不能抽奖
activity.setState(activity.getDispenseState());
System.out.println("抽奖活动结束了");
System.exit(0);
}
}
}

DispenseOutState.java

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
package DesignPattern.state;

/**
* @Author coderYang
* @Date 2021/2/15 20:24
*/
public class DispenseOutState extends State{

RaffleActivity activity;

public DispenseOutState(RaffleActivity activity) {
this.activity = activity;
}

@Override
public void deductMoney() {
System.out.println("奖品发放完了,请下次再参加");
}

@Override
public boolean raffle() {
System.out.println("奖品发放完了,请下次再参加");
return false;
}

@Override
public void dispensePrize() {
System.out.println("奖品发放完成了,请下次再参加");
}
}

Client.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package DesignPattern.state;

/**
* @Author coderYang
* @Date 2021/2/15 20:46
*/
public class Client {

public static void main(String[] args) {
//只有一个奖品
RaffleActivity activity = new RaffleActivity(1);

//我们连续抽300次
for (int i=0;i<30;i++){
System.out.println("-----第"+i+"次抽奖");
//第一步:扣除积分
activity.deductMoney();
//第二步:抽奖
activity.raffle();
}
}
}
状态模式的注意事项和细节
  1. 代码有很强的可读性。状态模式将每个状态的行为封装到对应的一个类中
  2. 方便维护。将容易产生问题的if-else语句取代了,如果把每个状态的行为都放到一个类中,每次调用方法时都要判断当前是什么状态,不但会产生出很多if-else语句,而且容易出错
  3. 符合”开闭原则”,容易增删状态
  4. 会产生很多类,每个状态都要对应一个类,当状态过多的时候就会产生很多类,加大维护难度
  5. 当一个事件或者对象有很多种状态,状态之间会相互转化,对不同的状态要求有不同行为的时候,可以使用状态模式。

21.策略模式

编写鸭子问题,具体要求如下:

  1. 有各种鸭子(比如野鸭、北京鸭、水鸭等,鸭子有各种行为,比如叫、飞行等)
  2. 显示鸭子的信息

传统方案解决鸭子问题:

image-20210216141311212

传统方法解决鸭子问题分析和解决方案
  1. 所有鸭子都继承了Duck类,所以fly让所有子类都会飞了,这是不正确的
  2. 上面的问题都是继承带来的问题:对类的局部改动,尤其超类的局部改动,会影响其它部分。会有溢出效应
  3. 为了改进1的问题,我们可以通过覆盖fly方法来解决=>覆盖解决
  4. 问题又来了,如果我们有一个玩具鸭子ToyDuck,这样就需要ToyDuck去覆盖Duck的所有实现的方法=>解决思路:策略模式
策略模式基本介绍
  1. 策略模式(strategy pattern)中,定义算法族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户
  2. 这算法体现了几个设计原则,第一、把变化的代码从不变的代码中分离出来;第二、针对接口编程而不是具体类(定义了策略接口);第三、多用组合/聚合,少用继承(客户通过组合方式使用策略)。

image-20210216143742808

  • 从上图可以看到,客户Context有成员变量strategy或者其他的策略接口,至于需要使用到哪个策略,我们可以在构造器中指定。
策略模式解决鸭子问题

image-20210216163832543

项目目录

image-20210216163909268

FlyBahavior.java

1
2
3
4
5
6
7
8
9
10
package DesignPattern.stragety.improve;

/**
* @Author coderYang
* @Date 2021/2/16 16:17
*/
public interface FlyBehavior {

void fly();
}

QuackBehavior.java

1
2
3
4
5
6
7
8
9
10
package DesignPattern.stragety.improve;

/**
* @Author coderYang
* @Date 2021/2/16 16:23
*/
public interface QuackBehavior {

void quack();
}

GoodFlyBehavior.java

1
2
3
4
5
6
7
8
9
10
11
12
13
package DesignPattern.stragety.improve;

/**
* @Author coderYang
* @Date 2021/2/16 16:21
*/
public class GoodFlyBehavior implements FlyBehavior{
@Override
public void fly() {
System.out.println("飞翔技术高超");
}

}

NoFlyBehavior.java

1
2
3
4
5
6
7
8
9
10
11
12
13
package DesignPattern.stragety.improve;

/**
* @Author coderYang
* @Date 2021/2/16 16:21
*/
public class NoFlyBehavior implements FlyBehavior{

@Override
public void fly() {
System.out.println("不会飞翔");
}
}

BadBehavior.java

1
2
3
4
5
6
7
8
9
10
11
12
package DesignPattern.stragety.improve;

/**
* @Author coderYang
* @Date 2021/2/16 16:18
*/
public class BadFlyBehavior implements FlyBehavior{
@Override
public void fly() {
System.out.println("飞行技术一般");
}
}

Duck.java

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
package DesignPattern.stragety.improve;

/**
* @Author coderYang
* @Date 2021/2/16 16:17
*/
public abstract class Duck {

//策略属性,接口
FlyBehavior flyBehavior;
QuackBehavior quackBehavior;


public abstract void display(); //显示鸭子信息

public void quack(){
System.out.println("鸭子嘎嘎叫");
}

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

public void fly(){
if(flyBehavior != null){
flyBehavior.fly();
}
}

public void setFlyBehavior(FlyBehavior flyBehavior) {
this.flyBehavior = flyBehavior;
}

public void setQuackBehavior(QuackBehavior quackBehavior) {
this.quackBehavior = quackBehavior;
}
}

PekingDuck.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package DesignPattern.stragety.improve;

/**
* @Author coderYang
* @Date 2021/2/16 16:22
*/
public class PekingDuck extends Duck{

public PekingDuck(){
flyBehavior = new BadFlyBehavior();
}

@Override
public void display() {
System.out.print("北京鸭子");
}
}

WildDuck.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package DesignPattern.stragety.improve;

/**
* @Author coderYang
* @Date 2021/2/16 16:25
*/
public class WildDuck extends Duck{

public WildDuck() {
flyBehavior = new GoodFlyBehavior();
}

@Override
public void display() {
System.out.print("野鸭");
}
}

ToyDuck.java

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
package DesignPattern.stragety.improve;

/**
* @Author coderYang
* @Date 2021/2/16 16:24
*/
public class ToyDuck extends Duck{

public ToyDuck(){
flyBehavior = new NoFlyBehavior();
}

@Override
public void display() {
System.out.print("玩具鸭子");
}

@Override
public void quack() {
System.out.println("玩具鸭不能叫");
}

@Override
public void swim() {
System.out.println("玩具鸭不会游泳");
}
}

Client.java

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
package DesignPattern.stragety.improve;

/**
* @Author coderYang
* @Date 2021/2/16 16:26
*/
public class Client {

public static void main(String[] args) {
WildDuck wildDuck = new WildDuck();
wildDuck.display();
wildDuck.fly();

ToyDuck toyDuck = new ToyDuck();
toyDuck.display();
toyDuck.fly();

PekingDuck pekingDuck = new PekingDuck();
pekingDuck.display();
pekingDuck.fly();

//动态的改变某个技术的行为
System.out.println("-------动态修改鸭子的行为-------");
pekingDuck.setFlyBehavior(new NoFlyBehavior());
pekingDuck.display();
pekingDuck.fly();
}
}

运行结果:

image-20210216164300967

策略模式在JDK-Arrays应用的源码分析
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
package JDKSRC;

import java.util.Arrays;
import java.util.Comparator;

/**
* @Author coderYang
* @Date 2021/2/16 19:04
*/
public class Strategy {

public static void main(String[] args) {
Integer[] data = {9,2,4,1,8,3,4};

/**
* 1.实现了Comparator接口(策略接口)
* 2.此对象为实现了策略接口的对象
*/
Comparator<Integer> comparator = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
//实现升序排序
return o1-o2;
}
};

Arrays.sort(data,comparator);

System.out.println(Arrays.toString(data)); //升序排序


//降序排序
Integer[] data2 = {12,5,31,2,4,9};
Arrays.sort(data2,(x,y) -> {
return y-x;
});
System.out.println(Arrays.toString(data2));
}
}

策略接口:

image-20210216200157481

image-20210216194445879

策略模式注意事项和细节
  1. 策略模式的关键是:分析项目中变化部分与不变部分
  2. 策略模式的核心思想是:多用组合/聚合 少用继承;用行为类组合,而不是行为的继承。更具弹性
  3. 体现了”对修改关闭,对扩展开放“原则,客户端增加行为不用修改原有代码,只要添加一种策略(或者行为)即可,避免了使用多重转移语句(if…else)
  4. 提供了可以替换继承关系的办法:策略模式将算法封装在独立的Strategy类中使得你可以独立于其Context改变它,使它易于切换、易于理解、易于扩展
  5. 需要注意的是:每添加一个策略就要增加一个类,当策略过多是会导致类数目庞大

22.职责链模式

学校OA系统的采购审批项目

需求:

  1. 采购员采购教学器材
  2. 如果金额小于等于5000,由教学主任审批
  3. 如果金额小于等于10000,由院长审批
  4. 如果金额小于等于30000,由副校长审批
  5. 如果金额超过30000以上,由校长审批
传统方案解决OA系统审批

image-20210217135852443

传统方案解决OA系统审批问题分析
  1. 传统方式是:接收到一个采购请求后,根据采购金额来调用对应的Approver(审批人)完成审批。
  2. 传统方式的问题分析:客户端这里会使用到分支判断(比如switch)来对不同的采购请求处理,这样就存在如下问题:
    1. 如果各个级别的人员审批金额发生变化,在客户端的也需要变化
    2. 客户端必须明确的知道 有多少个审批级别和访问
  3. 这样 对一个采购请求进行处理 和Approver(审批人)就存在强耦合关系,不利于代码的扩展和维护
  4. 解决方案=>职责链模式
职责链模式基本介绍
  1. 职责链模式(Chain of Responsibility Pattern),又叫责任链模式,为请求创建一个接收者对象的链。这种模式对请求的发送者和接收者进行解耦。
  2. 职责链模式通常对每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。
  3. 这种设计模式属于行为型模式。

职责链模式使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

image-20210217142313003

  • Handler:抽象的处理者,定义了一个处理请求的接口,同时含有另外Handler
  • ConcreteHandlerA,B:具体的处理者,处理它自己负责的请求,可以访问它的后继者(即下一个处理者),如果可以处理当前请求,则自己处理,否则将该请求交给后继者处理,从而形成一个职责链
  • Request:含有很多属性,表示一个请求
职责链模式解决OA系统审批问题

image-20210217143357231

项目目录:

image-20210217172244594

PurchaseRequest.java

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
package DesignPattern.responsibilitychain;

/**
* @Author coderYang
* @Date 2021/2/17 15:04
*/
public class PurchaseRequest {

private int type = 0; //请求类型
private float price = 0.0f; //请求金额
private int id = 0;

public PurchaseRequest(int type, float price, int id) {
this.type = type;
this.price = price;
this.id = id;
}

public int getType() {
return type;
}

public float getPrice() {
return price;
}

public int getId() {
return id;
}
}

Approver.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package DesignPattern.responsibilitychain;

/**
* @Author coderYang
* @Date 2021/2/17 16:58
*/
public abstract class Approver {

Approver approver; //下一个处理者
String name; //名字

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

//设置下一个处理者
public void setApprover(Approver approver) {
this.approver = approver;
}

//处理审批请求的方法,因为是由子类完成,所以该方法抽象
public abstract void processRequest(PurchaseRequest purchaseRequest);
}

DepartmentApprover.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package DesignPattern.responsibilitychain;

/**
* @Author coderYang
* @Date 2021/2/17 17:01
*/
public class DepartmentApprover extends Approver{
public DepartmentApprover(String name) {
super(name);
}

@Override
public void processRequest(PurchaseRequest purchaseRequest) {
if (purchaseRequest.getPrice() <= 5000){
System.out.println(" 请求编号id="+purchaseRequest.getId()+"被"+this.name+"处理了");
}else{
//交给下一个处理者
approver.processRequest(purchaseRequest);
}
}
}

CollegeApprover.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package DesignPattern.responsibilitychain;

/**
* @Author coderYang
* @Date 2021/2/17 17:05
*/
public class CollegeApprover extends Approver{
public CollegeApprover(String name) {
super(name);
}

@Override
public void processRequest(PurchaseRequest purchaseRequest) {
if (purchaseRequest.getPrice() > 5000 && purchaseRequest.getPrice()<=10000){
System.out.println(" 请求编号id="+purchaseRequest.getId()+"被"+this.name+"处理了");
}else {
//交给下一个处理者
approver.processRequest(purchaseRequest);
}
}
}

ViceSchoolMaster.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package DesignPattern.responsibilitychain;

/**
* @Author coderYang
* @Date 2021/2/17 17:08
*/
public class ViceSchoolMasterApprover extends Approver{

public ViceSchoolMasterApprover(String name) {
super(name);
}

@Override
public void processRequest(PurchaseRequest purchaseRequest) {
if (purchaseRequest.getPrice() > 10000 && purchaseRequest.getPrice()<=30000){
System.out.println(" 请求编号id="+purchaseRequest.getId()+"被"+this.name+"处理了");
}else{
//交给下一个处理者
approver.processRequest(purchaseRequest);
}
}
}

SchoolMasterApprover.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package DesignPattern.responsibilitychain;

/**
* @Author coderYang
* @Date 2021/2/17 17:10
*/
public class SchoolMasterApprover extends Approver{
public SchoolMasterApprover(String name) {
super(name);
}

@Override
public void processRequest(PurchaseRequest purchaseRequest) {
if (purchaseRequest.getPrice() > 30000){
System.out.println(" 请求编号id="+purchaseRequest.getId()+"被"+this.name+"处理了");
}else{
//交给下一个处理者
approver.processRequest(purchaseRequest);
}
}
}

Client.java

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
package DesignPattern.responsibilitychain;

/**
* @Author coderYang
* @Date 2021/2/17 17:11
*/
public class Client {

public static void main(String[] args) {
//创建请求
PurchaseRequest purchaseRequest = new PurchaseRequest(1, 100, 1);

//创建审批人
DepartmentApprover departmentApprover = new DepartmentApprover("张主任");
CollegeApprover collegeApprover = new CollegeApprover("李院长");
ViceSchoolMasterApprover viceSchoolMasterApprover = new ViceSchoolMasterApprover("王副校长");
SchoolMasterApprover schoolMasterApprover = new SchoolMasterApprover("杨校长");

//建立处理链
departmentApprover.setApprover(collegeApprover);
collegeApprover.setApprover(viceSchoolMasterApprover);
viceSchoolMasterApprover.setApprover(schoolMasterApprover);
//在此处构成环状
schoolMasterApprover.setApprover(departmentApprover);

schoolMasterApprover.processRequest(purchaseRequest);
}
}

运行结果:

image-20210217172559638

职责链-SpringMVC源码
职责链模式的注意事项和细节
  1. 将请求和处理分开,实现解耦,提高系统灵活性
  2. 简化了对象,使对象不需要知道链的结构
  3. 性能会受到影响,特别是在链比较长的时候,因此需控制链中最大节点数量,一般通过在Handler中设置一个最大节点数量,在setNext()方法中判断是否已经超过阀值,超过则不允许该链建立,避免出现超长链无意识地破坏系统性能
  4. 调试不方便。采用了类似递归地方式,调试时逻辑可能比较复杂
  5. 最佳应用场景:有多个对象可以处理同一个请求时,比如多级请求、请假/加薪等审批流程、java web中Tomcat对Encoding的处理、拦截器

Author: 小灰灰

Link: http://xhh460.github.io/2020/12/01/Java%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/

Copyright: All articles in this blog are licensed.

< PreviousPost
自动化测试
NextPost >
Jsp
CATALOG
  1. 1. Java设计模式
    1. 1.0.1. 设计模式常用的七大原则:
      1. 1.0.1.1. 1.单一职责原则(Single Principle Responsibility)
      2. 1.0.1.2. 2.接口隔离原则(Interface Segregation Principle)
      3. 1.0.1.3. 3.依赖倒转原则(Dependence Inversion Principle)
      4. 1.0.1.4. 4.里氏替换原则(Liskov Substitution principle)
      5. 1.0.1.5. 5.开闭原则(Open Closed Principle)
      6. 1.0.1.6. 6.迪米特法则(Demeter Principle)
      7. 1.0.1.7. 7.合成复用原则(Composite Reuse Principle)
      8. 1.0.1.8. 8.设计原则核心思想
    2. 1.0.2. 类和类之间的关系
      1. 1.0.2.1. 1.依赖(Dependence)
      2. 1.0.2.2. 2.泛化(Generalization)
      3. 1.0.2.3. 3.实现(Implementation)
      4. 1.0.2.4. 4.关联(Association)
      5. 1.0.2.5. 5.聚合(Aggregation)
      6. 1.0.2.6. 6.组合(Composition)
    3. 1.0.3. 设计模式类型
      1. 1.0.3.1. 1.单例设计模式
        1. 1.0.3.1.1. 单例设计模式八种形式
        2. 1.0.3.1.2. 饿汉式(静态常量)
        3. 1.0.3.1.3. 饿汉式(静态代码块)
        4. 1.0.3.1.4. 懒汉式(线程不安全)
        5. 1.0.3.1.5. 懒汉式(线程安全,同步方法)
        6. 1.0.3.1.6. 双重检查
        7. 1.0.3.1.7. 静态内部类
        8. 1.0.3.1.8. 枚举
        9. 1.0.3.1.9. 单例模式在JDK中应用分析
        10. 1.0.3.1.10. 单例模式注意事项和细节
      2. 1.0.3.2. 2.工厂设计模式
        1. 1.0.3.2.1. 传统方式
        2. 1.0.3.2.2. 简单工厂模式
        3. 1.0.3.2.3. 工厂方法模式
        4. 1.0.3.2.4. 抽象工厂模式
        5. 1.0.3.2.5. JDK中的简单工厂模式
        6. 1.0.3.2.6. 工厂模式总结
      3. 1.0.3.3. 3.原型模式
        1. 1.0.3.3.1. 传统的方式解决克隆羊问题
        2. 1.0.3.3.2. 改进(原型模式)
        3. 1.0.3.3.3. 原型模式在Spring框架中源码分析
        4. 1.0.3.3.4. 浅拷贝与深拷贝
        5. 1.0.3.3.5. 原型模式注意事项及缺点
      4. 1.0.3.4. 4.建造者模式
        1. 1.0.3.4.1. 传统方式解决建房子需求:
        2. 1.0.3.4.2. 建造者模式介绍
        3. 1.0.3.4.3. 建造者模式解决建房子需求
        4. 1.0.3.4.4. 建造者模式在JDK中的应用和源码分析
        5. 1.0.3.4.5. 建造者模式注意事项和细节
      5. 1.0.3.5. 5.适配器模式
        1. 1.0.3.5.1. 类适配器模式
        2. 1.0.3.5.2. 对象适配器模式
        3. 1.0.3.5.3. 接口适配器模式
        4. 1.0.3.5.4. 适配器模式在SpringMVC框架应用的源码分析
        5. 1.0.3.5.5. 适配器模式的注意事项和细节
      6. 1.0.3.6. 6.桥接模式
        1. 1.0.3.6.1. 传统问题解决手机操作问题分析
        2. 1.0.3.6.2. 桥接模式基本介绍
        3. 1.0.3.6.3. 桥接模式解决手机问题
        4. 1.0.3.6.4. 桥接模式在JDBC的源码剖析
        5. 1.0.3.6.5. 桥接模式的注意事项和细节
      7. 1.0.3.7. 7.装饰者模式
        1. 1.0.3.7.1. 装饰者模式定义
        2. 1.0.3.7.2. 装饰者模式(Decorator)原理
        3. 1.0.3.7.3. 装饰者模式解决星巴克咖啡订单
        4. 1.0.3.7.4. 装饰者模式在JDK应用的源码分析
      8. 1.0.3.8. 8.组合模式
        1. 1.0.3.8.1. 传统方案解决学校院系展示存在问题的分析
        2. 1.0.3.8.2. 组合模式基本介绍
        3. 1.0.3.8.3. 组合模式解决的问题
        4. 1.0.3.8.4. 组合模式解决学校院系展示
        5. 1.0.3.8.5. 组合模式在JDK集合的源码分析
        6. 1.0.3.8.6. 组合模式的注意事项和细节
      9. 1.0.3.9. 9.外观模式
        1. 1.0.3.9.1. 传统方式解决影院管理问题分析
        2. 1.0.3.9.2. 外观模式基本介绍
        3. 1.0.3.9.3. 外观模式解决影院管理
        4. 1.0.3.9.4. 外观模式在Mybatis框架应用的源码分析
        5. 1.0.3.9.5. 外观模式的注意事项和细节
      10. 1.0.3.10. 10.享元模式
        1. 1.0.3.10.1. 传统方式解决网站展现项目-问题分析
        2. 1.0.3.10.2. 享元模式基本介绍
        3. 1.0.3.10.3. 享元模式解决网站外包问题
        4. 1.0.3.10.4. 享元模式在JDK-Integral的应用源码分析
        5. 1.0.3.10.5. 享元模式的注意事项和细节
      11. 1.0.3.11. 11.代理模式
        1. 1.0.3.11.1. 代理模式基本介绍
        2. 1.0.3.11.2. 静态代理
        3. 1.0.3.11.3. 动态代理
        4. 1.0.3.11.4. Cglib代理
        5. 1.0.3.11.5. 代理模式的变体
      12. 1.0.3.12. 12.模板方法模式
        1. 1.0.3.12.1. 模板模式基本介绍
        2. 1.0.3.12.2. 模板方法模式解决豆浆制作问题
        3. 1.0.3.12.3. 模板方法模式在Spring框架应用的源码分析
        4. 1.0.3.12.4. 模板方法模式的注意事项和细节
      13. 1.0.3.13. 13.命令模式
        1. 1.0.3.13.1. 命令模式基本介绍
        2. 1.0.3.13.2. 命令模式解决智能生活项目
        3. 1.0.3.13.3. 命令模式在Spring框架JdbcTemplate应用的源码分析
        4. 1.0.3.13.4. 命令模式的注意事项和细节
      14. 1.0.3.14. 14.访问者模式
        1. 1.0.3.14.1. 访问者模式基本介绍
        2. 1.0.3.14.2. 访问者模式解决测评系统需求
        3. 1.0.3.14.3. 双分派
        4. 1.0.3.14.4. 访问者模式的注意事项和细节
      15. 1.0.3.15. 15.迭代器模式
        1. 1.0.3.15.1. 传统方案的问题分析
        2. 1.0.3.15.2. 迭代器模式基本介绍
        3. 1.0.3.15.3. 迭代器模式解决学校院系输出问题
        4. 1.0.3.15.4. 迭代器模式在JDK-ArrayList集合应用的源码分析
        5. 1.0.3.15.5. 迭代器模式的注意事项和细节
      16. 1.0.3.16. 16.观察者模式
        1. 1.0.3.16.1. 天气预报设计方案1-普通方案
        2. 1.0.3.16.2. 观察者模式基本介绍
        3. 1.0.3.16.3. 观察者模式解决天气预报
        4. 1.0.3.16.4. 观察者模式在JDK应用的源码分析
      17. 1.0.3.17. 17.中介者模式
        1. 1.0.3.17.1. 中介者模式基本介绍
        2. 1.0.3.17.2. 中介者模式解决传统项目问题
        3. 1.0.3.17.3. 中介者模式的注意事项和细节
      18. 1.0.3.18. 18.备忘录模式
        1. 1.0.3.18.1. 传统模式的问题分析
        2. 1.0.3.18.2. 备忘录模式基本介绍
        3. 1.0.3.18.3. 备忘录模式解决游戏角色恢复实例
        4. 1.0.3.18.4. 备忘录模式的注意事项和细节
      19. 1.0.3.19. 19.解释器模式
        1. 1.0.3.19.1. 传统方案解决四则运算问题分析
        2. 1.0.3.19.2. 解释器模式基本介绍
        3. 1.0.3.19.3. 解释器模式解决四则运算
        4. 1.0.3.19.4. 解释器模式在Spring框架应用
        5. 1.0.3.19.5. 解释器模式注意事项和细节
      20. 1.0.3.20. 20.状态模式
        1. 1.0.3.20.1. 状态模式基本介绍
        2. 1.0.3.20.2. 状态模式解决抽奖问题
        3. 1.0.3.20.3. 状态模式的注意事项和细节
      21. 1.0.3.21. 21.策略模式
        1. 1.0.3.21.1. 传统方法解决鸭子问题分析和解决方案
        2. 1.0.3.21.2. 策略模式基本介绍
        3. 1.0.3.21.3. 策略模式解决鸭子问题
        4. 1.0.3.21.4. 策略模式在JDK-Arrays应用的源码分析
        5. 1.0.3.21.5. 策略模式注意事项和细节
      22. 1.0.3.22. 22.职责链模式
        1. 1.0.3.22.1. 传统方案解决OA系统审批
        2. 1.0.3.22.2. 传统方案解决OA系统审批问题分析
        3. 1.0.3.22.3. 职责链模式基本介绍
        4. 1.0.3.22.4. 职责链模式解决OA系统审批问题
        5. 1.0.3.22.5. 职责链-SpringMVC源码
        6. 1.0.3.22.6. 职责链模式的注意事项和细节