Skip to content

Commit d0e54e9

Browse files
coffe1891gitbook-bot
authored andcommitted
GitBook: [master] 63 pages modified
1 parent 797de4f commit d0e54e9

File tree

8 files changed

+354
-47
lines changed

8 files changed

+354
-47
lines changed

6/6.1.0.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
1-
# 陆.1.0 六大设计原则导读
1+
# 陆.1.0 导读:SOLID
22

3-
//todo
3+
“SOLID”是六大设计原则的英文首字母,如下:
4+
5+
> * **S – Single Responsibility Principle 单一职责原则**
6+
> * **O – Open-Closed Principle 开放封闭原则**
7+
> * **L – Liskov Substitution Principle 里氏替换原则**
8+
> * **I – Interface Segregation Principle 接口隔离原则**
9+
> * **D – Dependency Inversion Principle 依赖倒置原则**
10+
11+
JavaScript程序员要相对其他语言程序员更难掌握六大设计原则。为什么呢?因为JavaScript是一门非常灵活的语言
412

6/6.1.1.md

Lines changed: 16 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# 陆.1.1 单一职责原则
22

3-
## **01.什么是单一职责原则**
3+
## **01.什么是单一职责原则**
44

5-
单一职责原则,英文缩写**SRP**全称Single Responsibility Principle。
5+
单一职责原则,英文缩写**SRP**全称**Single Responsibility Principle**
66

77
原英文定义:
88

@@ -22,13 +22,13 @@
2222

2323
为什么这么说呢?我们来看下面的例子。
2424

25-
## 03.OOP职责单一的示例
25+
## 03.用JAVA做一段OOP的示例
2626

2727
关于单一职责原则的原理,我们就不做过多的解释了。**重点是职责的划分!重点是职责的划分!重点是职责的划分!**重要的事情说三遍。下面根据一个示例场景来看看如何划分职责。
2828

2929
假定现在有如下场景:国际手机运营商那里定义了生产手机必须要实现的接口,接口里面定义了一些手机的属性和行为,手机生产商如果要生产手机,必须要实现这些接口。
3030

31-
### \(1\) 初始设计
31+
### 1\). 初始设计
3232

3333
我们首先以手机作为单一职责去设计接口,方案如下:
3434

@@ -80,50 +80,47 @@ public class MobilePhone:IMobilePhone
8080
get{throw new NotImplementedException();}
8181
set{throw new NotImplementedException();}
8282
}
83-
8483
public string ROM
8584
{
8685
get{throw new NotImplementedException();}
8786
set{throw new NotImplementedException();}
8887
}
89-
9088
public string CPU
9189
{
9290
get{throw new NotImplementedException();}
9391
set{throw new NotImplementedException();}
9492
}
95-
9693
public int Size
9794
{
9895
get{throw new NotImplementedException();}
9996
set{throw new NotImplementedException();}
10097
}
101-
10298
public void Charging(ElectricSource oElectricsource)
10399
{
104100
throw new NotImplementedException();
105101
}
106-
107102
public void RingUp()
108103
{
109104
throw new NotImplementedException();
110105
}
111-
112106
public void ReceiveUp()
113107
{
114108
throw new NotImplementedException();
115109
}
116-
117110
public void SurfInternet()
118111
{
119112
throw new NotImplementedException();
120113
}
121114
}
122115
```
123116

124-
这种设计有没有问题呢?这是一个很有争议的话题。单一职责原则要求一个接口或类只有一个原因引起变化,也就是一个接口或类只有一个职责,它就负责一件事情,**原则上来说,我们以手机作为单一职责去设计,也是有一定的道理的,因为我们接口里面都是定义的手机相关属性和行为,引起接口变化的原因只可能是手机的属性或者行为发生变化,从这方面考虑,这种设计是有它的合理性的,如果你能保证需求不会变化或者变化的可能性比较小,那么这种设计就是合理的。**但实际情况我们知道,现代科技日新月异,科技的进步促使着人们不断在手机原有基础上增加新的属性和功能。比如有一天,我们给手机增加了摄像头,那么需要新增一个像素的属性,我们的接口和实现就得改吧,又有一天,我们增加移动办公的功能,那么我们的接口实现是不是也得改。由于上面的设计没有细化到一定的粒度,导致任何一个细小的改动都会引起从上到下的变化,有一种“牵一发而动全身”的感觉。所以需要细化粒度,下面来看看我们如何变更设计。
117+
这种设计有没有问题呢?这是一个很有争议的话题。
118+
119+
单一职责原则要求一个接口或类只有一个原因引起变化,也就是一个接口或类只有一个职责,它就负责一件事情。**原则上来说,我们以手机作为单一职责去设计,也是有一定的道理的,因为我们接口里面都是定义的手机相关属性和行为,引起接口变化的原因只可能是手机的属性或者行为发生变化,从这方面考虑,这种设计是有它的合理性的,如果你能保证需求不会变化或者变化的可能性比较小,那么这种设计就是合理的。**
120+
121+
但实际情况我们知道,现代科技日新月异,科技的进步促使着人们不断在手机原有基础上增加新的属性和功能。比如有一天,我们给手机增加了摄像头,那么需要新增一个像素的属性,我们的接口和实现就得改吧,又有一天,我们增加移动办公的功能,那么我们的接口实现是不是也得改。由于上面的设计没有细化到一定的粒度,导致任何一个细小的改动都会引起从上到下的变化,有一种“牵一发而动全身”的感觉。所以需要细化粒度,下面来看看我们如何变更设计。
125122

126-
### \(2\) 二次变更
123+
### 2\). 二次变更
127124

128125
我们将接口细化:
129126

@@ -173,31 +170,26 @@ public interface IMobilePhoneFunction
173170
//手机属性实现类
174171
public class MobileProperty:IMobilePhoneProperty
175172
{
176-
177173
public string RAM
178174
{
179175
get{ throw new NotImplementedException();}
180176
set{ throw new NotImplementedException();}
181177
}
182-
183178
public string ROM
184179
{
185180
get{ throw new NotImplementedException();}
186181
set{ throw new NotImplementedException();}
187182
}
188-
189183
public string CPU
190184
{
191185
get{ throw new NotImplementedException();}
192186
set{throw new NotImplementedException();}
193187
}
194-
195188
public int Size
196189
{
197190
get{throw new NotImplementedException();}
198191
set{throw new NotImplementedException();}
199192
}
200-
201193
public string Pixel
202194
{
203195
get{throw new NotImplementedException();}
@@ -208,27 +200,22 @@ public class MobileProperty:IMobilePhoneProperty
208200
//手机功能实现类
209201
public class MobileFunction:IMobilePhoneFunction
210202
{
211-
212203
public void Charging(ElectricSource oElectricsource)
213204
{
214205
throw new NotImplementedException();
215206
}
216-
217207
public void RingUp()
218208
{
219209
throw new NotImplementedException();
220210
}
221-
222211
public void ReceiveUp()
223212
{
224213
throw new NotImplementedException();
225214
}
226-
227215
public void SurfInternet()
228216
{
229217
throw new NotImplementedException();
230218
}
231-
232219
public void MobileOA()
233220
{
234221
throw new NotImplementedException();
@@ -252,7 +239,7 @@ public class HuaweiMobile
252239

253240
那么,我们如何继续细化接口粒度呢?
254241

255-
### \(3\) 最终成型
242+
### 3\). 最终成型
256243

257244
接口细化粒度设计如下:
258245

@@ -392,11 +379,11 @@ public class MobilePhoneExtentionFunc : IMobilePhoneExtentionFunc
392379

393380
此种设计能解决上述问题,细分到此粒度,这种方案基本算比较完善了。能不能算完美?这个得另说。接口的粒度要设计到哪一步,取决于需求的变更程度,或者说取决于需求的复杂度。
394381

395-
## 04.勿写万能函数,分解多功能API
382+
## 04.JavaScript示例:万能函数
396383

397-
### \(1\)万能函数
384+
接着,我们暂且抛开JAVA语言和OOP,从JavaScript函数的角度来讨论单一职责原则。
398385

399-
接着我们可以暂且抛开OOP,从JavaScript函数的角度来讨论单一职责。新手程序员往往很喜欢写“功能超级强大的复杂的万能函数”,以体现自己的编程能力之不俗。
386+
还记得吗?新手程序员我们往往很喜欢写“功能超级强大的、复杂的万能函数”,以体现自己的编程能力之不俗。
400387

401388
```javascript
402389
//这是一个功能“超级强大”的函数
@@ -413,7 +400,7 @@ function aBigFunc(param){
413400
}
414401
```
415402

416-
上面这种多功能函数,理论上只要敢写if分支,什么任务都完成,俗称“万能函数”。几乎每一个程序员都写过万能函数。万能函数确实能解决问题,而且看上去似乎很强大,甚至还能让总体代码的行数更少一点儿。很多新手程序员,包括笔者刚学编程时也常常以此为借口,堂而皇之地大写特写万能函数
403+
上面这种多功能函数,理论上只要敢写if分支,什么任务都完成,俗称“万能函数”。几乎每一个程序员都写过万能函数。万能函数确实能解决问题,而且看上去似乎很强大,甚至还能让总体代码的行数更少一点儿。很多新手程序员包括笔者刚学编程时,也常常以此为借口堂而皇之地大写特写万能函数
417404

418405
然而,当我们需要修改、新增功能的时候,或者我们接手维护别人的万能函数时,这些万能函数动辄几百行的代码,经常会让我们自己读得头晕眼花,往往是新写的代码只要几分钟,阅读代码却花掉十几分钟;而且随着项目工程变大,代码行数的增加,耗费在读代码上的时间会越来越多。良好的习惯是这样的,将万能函数拆分成功能单一的函数:
419406

@@ -441,17 +428,14 @@ function doQuest(param){
441428

442429
优化之后,复杂的万能函数被分割成很多小函数,以前复杂的细节代码被一个个函数隐藏起来。这样我们以后修改/新增这个做任务的函数时候,就可以避免被细节牵绊,大大降低出错的概率;还可以利用IDE的一些语法结构分析功能,很快速地找到具体的函数位置进行添加/修改,无需耗费大量的时间查找、阅读复杂的代码。
443430

444-
### \(2\)多功能API
445-
446-
//todo
447-
448431
## 05.总结
449432

450433
使用单一职责原则时,没有最合理,只有最合适。理解单一职责原则,最重要的就是掌握好职责划分的粒度,而粒度则取决于需求的粒度。
451434

452435
## 06.参考文献
453436

454437
{% hint style="info" %}
438+
[https://en.wikipedia.org/wiki/Single-responsibility\_principle](https://en.wikipedia.org/wiki/Single-responsibility_principle)
455439
[https://zhuanlan.zhihu.com/p/24198903](https://zhuanlan.zhihu.com/p/24198903)
456440
{% endhint %}
457441

6/6.1.2-1.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# 陆.1.2开放封闭原则
2+

6/6.1.3.md

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,30 @@
1-
# 陆.1.3 最少知识原则
1+
# 陆.1.3 里氏替换原则
2+
3+
## 01.什么是里氏替换原则
4+
5+
里氏替换原则\(Liskov Substitution Principle LSP\)面向对象设计的基本原则之一。
6+
7+
> 里氏替换原则中说,任何基类可以出现的地方,子类一定可以出现。
8+
9+
LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。
10+
11+
我们一般对里氏替换原则 LSP 的解释为:
12+
13+
> **子类对象能够替换父类对象,而程序逻辑不变。**
14+
15+
## 02.里氏替换原则的两种含义:
16+
17+
### 1\).含义一
18+
19+
里氏替换原则是针对继承而言的,如果继承是为了实现代码重用,也就是为了共享方法,那么共享的父类方法就应该保持不变,不能被子类重新定义。子类只能通过新添加方法来扩展功能,父类和子类都可以实例化,而子类继承的方法和父类是一样的,父类调用方法的地方,子类也可以调用同一个继承得来的,逻辑和父类一致的方法,这时用子类对象将父类对象替换掉时,当然逻辑一致,相安无事。
20+
21+
### 2\).含义二
22+
23+
如果继承的目的是为了多态,而多态的前提就是子类覆盖并重新定义父类的方法,为了符合LSP,我们应该将父类定义为抽象类,并定义抽象方法,让子类重新定义这些方法,当父类是抽象类时,父类就是不能实例化,所以也不存在可实例化的父类对象在程序里。也就不存在子类替换父类实例(根本不存在父类实例了)时逻辑不一致的可能。
24+
25+
不符合LSP的最常见的情况是,父类和子类都是可实例化的非抽象类,且父类的方法被子类重新定义,这一类的实现继承会造成父类和子类间的强耦合,也就是实际上并不相关的属性和方法牵强附会在一起,不利于程序扩展和维护。
26+
27+
## **03.如何让软件设计符合里氏替换原则**
28+
29+
**总结一句话****** **就是尽量不要从可实例化的父类中继承,而是要使用基于抽象类和接口的继承”。**
230

6/6.1.4.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,8 @@
1-
# 陆.1.4 开放封闭原则
1+
# 陆.1.4 接口隔离原则
2+
3+
## 01.什么是接口隔离原则
4+
5+
6+
7+
## 02.示例
28

0 commit comments

Comments
 (0)