Hello 丨World
当机会变得稀有,当竞争变得激烈
当方向不再清晰,当风口不再有风
关键词||设计模式
全文约7685字,细读大约需要20分钟
1 认识设计模式
1.1 什么是设计模式
所谓设计模式,就是对经常出现的软件设计问题的成熟解决方案。
很多人把设计模式想象成非常高深的概念,实际上设计模式仅仅是对特定问题的一种惯性思维。一些人喜欢抱着一本设计模式的书研究,以期成为一个“高手”,实际上设计模式的理解必须以足够的代码积累量作为基础,最好是经历过某种痛苦,或者正在经历一种苦痛,就会对设计模式有较深的感受。
1.2 设计模式的目的
编写软件的过程中,程序员面临着来自耦合性、内聚性以及可维护性、可扩展性、重用性、灵活性等多方面的挑战,设计模式是为了让程序拥有更好的:
代码可重用性。相同功能的代码,不用多次编写;
可读性。便于其他程序员的阅读和理解;
可扩展性。当需要增加新功能时,非常方便;
可靠性。当增加新的功能后,对原来的功能没有影响;
使程序呈现高内聚、低耦合的特点。
1.3 什么是设计模式的原则?
设计模式原则,其实就是程序员在编程时,应当遵循的原则,也就是各种设计模式的基础,即设计模式为什么这样设计的依据。
设计模式的七大原则有:
单一职责原则
接口隔离原则
依赖倒置原则
里氏替换原则
开闭原则
迪米特法则
合成复用原则
2 单一职责原则
2.1 什么是单一职责原则
对类来说,一个类应该只负责一项职责。如类A负责两个不同职责:职责1,职责2,当职责1需求变更而改变类A时,可能造成职责2执行错误,所以需要将类A的粒度分解为A1,A2。
2.2 应用实例
方案一:
1/** 2 * 方式一的run方法中,违反了单一职责原则, 3 * 解决的方案是根据交通工具运行方法不同,分解成不同类即可 4 */
5
6public class SingleResponsebility1 {
7
8 public static void main(String[] args) {
9
10 Vehicle vehicle = new Vehicle();
11
12 vehicle.run("汽车");
13
14 vehicle.run("摩托");
15
16 vehicle.run("飞机");
17
18 }
19
20}
21
22class Vehicle {
23
24 public void run(String vehicle) {
25
26 System.out.println(vehicle+"在公路上跑");
27 }
28}
方案二:
1/** 2 3 * 方案二遵循单一职责原则 4 5 * 但是这样做的改动很大,即将类分解,同时修改客户端 6 7 * 改进:直接修改Vehicle类,改动的代码会比较少 8 9 */
10
11public class SingleResponsibility2 {
12
13 public static void main(String[] args) {
14
15 Vehicle1 vehicle1 = new Vehicle1();
16
17 vehicle1.run("汽车");
18
19 Vehicle2 vehicle2 = new Vehicle2();
20
21 vehicle2.run("轮船");
22
23 Vehicle3 vehicle3 = new Vehicle3();
24
25 vehicle3.run("飞机");
26 }
27
28}
29
30class Vehicle1{
31
32 public void run(String vehicle){
33
34 System.out.println(vehicle+"在地上跑");
35 }
36}
37
38class Vehicle2{
39
40 public void run(String vehicle){
41
42 System.out.println(vehicle+"在水上跑");
43 }
44
45}
46
47class Vehicle3{
48
49 public void run(String vehicle){
50
51 System.out.println(vehicle+"在天上跑");
52
53 }
54
55}
方案三:
1/** 2 * 这种修改方法没有对原来的类做大的修改,只是增加方法 3 * 这里虽然没有在类这个级别上遵循单一职责原则,但是在方法级别上,仍然遵守这个原则 4 */
5
6public class SingleResonsibility3 {
7
8 public static void main(String[] args) {
9
10 Vehicle4 vehicle4 = new Vehicle4();
11
12 vehicle4.run("汽车");
13
14 vehicle4.run2("轮船");
15
16 vehicle4.run3("飞机");
17 }
18
19}
20
21class Vehicle4{
22
23 public void run(String vehicle){
24
25 System.out.println(vehicle+"在地上跑");
26
27 }
28
29 public void run2(String vehicle){
30
31 System.out.println(vehicle+"在水上跑");
32
33 }
34
35 public void run3(String vehicle) {
36
37 System.out.println(vehicle + "在天上跑");
38 }
39}
2.3 注意事项
降低类的复杂度,一个类只负责一项职责;
提高类的可读性、可维护性;
降低变更引起的风险;
通常情况下,我们应当遵守单一职责原则,只有逻辑足够简单,才可以在代码级违反单一职责原则:只有类中方法数量足够少,可以在方法级别保存单一职责原则。
3 接口隔离原则
3.1 什么是接口隔离原则
客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上。
3.2 应用实例
如图,类A通过接口Interface1依赖类B,类C通过接口Interface1依赖类D,如果接口Interface1对于类A和类C来说不是最小接口,那么类B和类D必须去实现他们不需要的方法。
1public class Segregation1 {
2
3}
4
5//接口
6interface Interface1 {
7
8 void operation1();
9
10 void operation2();
11
12 void operation3();
13
14 void operation4();
15
16 void operation5();
17
18}
19
20class B implements Interface1 {
21
22 @Override
23
24 public void operation1() {
25
26 System.out.println("B实现了operation1");
27 }
28
29 @Override
30
31 public void operation2() {
32
33 System.out.println("B实现了operation2");
34 }
35
36 @Override
37
38 public void operation3() {
39
40 System.out.println("B实现了operation3");
41
42 }
43
44 @Override
45
46 public void operation4() {
47
48 System.out.println("B实现了operation4");
49 }
50
51 @Override
52
53 public void operation5() {
54
55 System.out.println("B实现了operation5");
56 }
57
58}
59
60class D implements Interface1 {
61
62 @Override
63
64 public void operation1() {
65
66 System.out.println("D实现了operation1");
67
68 }
69
70 @Override
71
72 public void operation2() {
73
74 System.out.println("D实现了operation2");
75
76 }
77
78 @Override
79
80 public void operation3() {
81
82 System.out.println("D实现了operation3");
83
84 }
85
86 @Override
87
88 public void operation4() {
89
90 System.out.println("D实现了operation4");
91 }
92
93 @Override
94
95 public void operation5() {
96
97 System.out.println("D实现了operation5");
98
99 }
100
101}
102
103class A {
104
105 public void depend1(Interface1 i) {
106
107 i.operation1();
108 }
109
110 public void depend2(Interface1 i) {
111
112 i.operation2();
113
114 }
115
116 public void depend3(Interface1 i) {
117
118 i.operation3();
119
120 }
121
122}
123
124class C {
125
126 public void depend1(Interface1 i) {
127
128 i.operation1();
129
130 }
131
132 public void depend4(Interface1 i) {
133
134 i.operation4();
135
136 }
137
138 public void depend5(Interface1 i) {
139
140 i.operation5();
141 }
142}
按隔离原则应当这样处理:将接口Interface1拆分为独立的几个接口,类A和类C分别与他们需要的接口建立依赖关系。也就是采用接口隔离原则。
1public class Segregation2 {
2
3 public static void main(String[] args) {
4
5 A a = new A();
6
7 a.depend1(new B());
8
9 a.depend2(new B());
10
11 a.depend3(new B());
12
13 C c = new C();
14
15 c.depend1(new D());
16
17 c.depend4(new D());
18
19 c.depend5(new D());
20
21 }
22
23}
24
25//接口
26interface Interface1 {
27
28 void operation1();
29
30}
31
32interface Interface2 {
33
34 void operation2();
35
36 void operation3();
37
38}
39
40interface Interface3 {
41
42 void operation4();
43
44 void operation5();
45
46}
47
48class B implements Interface1, Interface2 {
49
50 @Override
51
52 public void operation1() {
53
54 System.out.println("B实现了operation1");
55
56 }
57
58 @Override
59
60 public void operation2() {
61
62 System.out.println("B实现了operation2");
63
64 }
65
66 @Override
67
68 public void operation3() {
69
70 System.out.println("B实现了operation3");
71
72 }
73
74}
75
76class D implements Interface1, Interface3 {
77
78 @Override
79
80 public void operation1() {
81
82 System.out.println("D实现了operation1");
83
84 }
85
86 @Override
87
88 public void operation4() {
89
90 System.out.println("D实现了operation4");
91
92 }
93
94 @Override
95
96 public void operation5() {
97
98 System.out.println("D实现了operation5");
99
100 }
101
102}
103
104class A {
105
106 public void depend1(Interface1 i) {
107
108 i.operation1();
109
110 }
111
112 public void depend2(Interface2 i) {
113
114 i.operation2();
115
116 }
117
118 public void depend3(Interface2 i) {
119
120 i.operation3();
121
122 }
123
124}
125
126class C {
127
128 public void depend1(Interface1 i) {
129
130 i.operation1();
131
132 }
133
134 public void depend4(Interface3 i) {
135
136 i.operation4();
137
138 }
139
140 public void depend5(Interface3 i) {
141
142 i.operation5();
143
144 }
145
146}
4 依赖倒转原则
4.1 什么是依赖倒转原则
依赖倒转原则是指高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象;依赖倒转的中心思想是面向接口编程。
依赖倒转原则是基于这样的设计理念:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建的架构比以细节为基础的架构要稳定的多。在java中,抽象指的是接口或者抽象类,细节就是具体的实现类。
使用接口或抽象类的目的是制定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成。
4.2 应用实例
请编程完成persion接收消息的功能。
方案一:
1public class DependecyInversion {
2
3 public static void main(String[] args) {
4
5 Persion persion = new Persion();
6
7 persion.receive(new Email());
8 }
9}
10
11class Email {
12
13 public String getInfo() {
14
15 return "电子邮件:Hello World!!!";
16 }
17}
18
19class Persion {
20
21 public void receive(Email e) {
22
23 System.out.println(e.getInfo());
24 }
25}
方案二:
1/** 2 3 * 如果我们获取的对象是微信、短信等等,则新增类,同时Persion也要增加相应的接收方法 4 5 * 解决思路:引入一个抽象的接口IReceiver,表示接收者,这样Persion类与接口IReceiver发生依赖 6 7 * 因为Email、WeChat等等属于接收的范围,它们各自实现IReceiver接口就可以,这样我们就符合依赖倒转原则 8 9 */
10
11public class DependecyInversion2 {
12
13 public static void main(String[] args) {
14
15 Persion2 persion2 = new Persion2();
16
17 persion2.receive(new Email2());
18
19 persion2.receive(new WeChat());
20 }
21}
22
23interface IReceiver {
24
25 public String getInfo();
26}
27
28class Email2 implements IReceiver {
29
30 @Override
31 public String getInfo() {
32
33 return "电子邮件:Hello World!";
34 }
35}
36
37class WeChat implements IReceiver {
38
39 @Override
40 public String getInfo() {
41
42 return "微信消息:Hello weixin";
43 }
44}
45
46class Persion2 {
47
48 public void receive(IReceiver i) {
49
50 System.out.println(i.getInfo());
51 }
52}
4.3 依赖传递的三种方式
接口传递、构造方法传递、setter方法传递。
1//第一种方式:接口传递
2//开关的接口
3interface IOpenAndClose {
4
5 public void opoen(ITV tv);//抽象方法,接收接口
6}
7
8interface ITV {//ITV接口
9 public void play();
10}
11
12//实现接口
13class OpenAndColse implements IOpenAndClose {
14
15 @Override
16 public void opoen(ITV tv) {
17
18 tv.play();
19 }
20}
1//方式二:构造方法传递
2interface IOpenAndClose {
3
4 public void open();//抽象方法
5}
6
7interface ITV {//ITV接口
8 public void play();
9}
10
11class OpenAndClose implements IOpenAndClose {
12
13 public ITV tv;//成员
14
15 public OpenAndClose(ITV tv) {//构造方法
16
17 this.tv = tv;
18 }
19
20 @Override
21 public void open() {
22
23 this.tv.play();
24 }
25}
1//方式三:setter方法传递
2interface IOpenAndClose {
3
4 public void open();//抽象方法
5}
6
7interface ITV {//ITV接口
8
9 public void play();
10}
11
12class OpenAndClose implements IOpenAndClose {
13
14 private ITV tv;
15
16 public void setTv(ITV tv) {
17
18 this.tv = tv;
19 }
20
21 @Override
22 public void open() {
23
24 this.tv.play();
25 }
26}
4.4 注意事项
底层模块尽量都要有抽象类或接口,或者两者都有,程序稳定性更好。
变量的声明类型尽量是抽象类或接口,这样我们的变量引用和实际对象间,就存在一个缓冲层,利于程序扩展和优化。
继承时要遵循里氏替换原则。
- END -
在电脑飞速发展的时代 丨我们不应该感到恐惧
因为我们可以跟电脑合作
成为一个人马合一、人枪合一、人电脑合一的新物种
这样的人才是这个时代的超级个体
![db115dae6469f4b647bc748dc4e7907c.png](https://i-blog.csdnimg.cn/blog_migrate/0f6957bed34079bace4f1c064f456f05.png)
![983b9265f21a2c56599921332c75459b.png](https://i-blog.csdnimg.cn/blog_migrate/b7bd68d0fa56fa83fb6ae386aa9db823.png)