设计模式见解
设计模式
什么是设计模式?
设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。
本意是对具体功能代码得预先设计。比如基类、接口得设计。
但这里不仅限于此,也包含了实际编码过程中需要遵守得一些规则。
设计模式可以帮助我们编写质量更高得程序。
开闭原则(Open Close Principle)
原文:
"software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification"
这里有两种:
- 梅耶开闭原则
- A module will be said to be open if it is still available for extension. For example, it should be possible to add fields to the data structures it contains, or new elements to the set of functions it performs.
- A module will be said to be closed if [it] is available for use by other modules. This assumes that the module has been given a well-defined, stable description (the interface in the sense of information hiding).[3]
- 多态开闭原则
A class is closed, since it may be compiled, stored in a library, baselined, and used by client classes. But it is also open, since any new class may use it as parent, adding new features. When a descendant class is defined, there is no need to change the original or to disturb its clients.
理解:
我们可以去扩展一个软件实体(类,模块,方法等)得功能,但是不应该改变原有功能。
- 梅耶开闭原则:
如果要对一个类添加新功能,应该新建一个类继承它,而不是直接修改它。 - 多态开闭原则:
使用抽象类,接口。
个人认为:
梅耶开闭原则指的是在产品上线以后,需要添加新的功能时。不能够去修改原有得类或方法。而是通过继承复用原有得基类,在此基础之上再增加新的功能。
在设计阶段时,多态开闭原则倡导使用抽象类,接口。这么做目的是实现多态,而多态则不用修改基类就可以实现新功能。
总的来说,开闭原则都指向一个方向:不变动原有代码,通过一系列手段增加新功能。当然,修改bug不算增加新功能。
开闭原则能带来什么好处?
不变动已经工作得代码。可以降低风险,降低代码审查得难度。
里氏替换原则(Liskov Substitution Principle )
原文:
What is wanted here is something like the following substitution property: If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T.
理解:
提及这个规则时,往往伴随着一句话:子类可以替换父类。
子类可以替换父类得前提是父类得功能,子类中都有,并且功能一样。
然而开闭原则得多态,比如重写父类方法(你都重写了,子类当然不能替换父类)。这似乎有些冲突。
这里需要提一个概念。区分开“可以实例化得类”和“抽象类、接口”。
多态开闭原则是针对抽象类和接口得,并不是指所有的类。梅耶开闭原则更多得是指复用父类代码产生新类,而不是重写。
里氏替换原则针对得是继承于可以实例化得父类。
对于可以实例化得父类,遵守里氏替换原则就是:不重写父类已经拥有具体实现得方法。
所以开闭原则和里氏替换原则并不冲突。合起来就是,开闭原则不提倡修改父类来提供新功能,里氏替换原则不提倡重写父类已经拥有具体实现得方法。
里氏替换原则能带来什么好处?
首先要清楚得一点就是,子类可以替换父类,不是说我们要用父类时去使用子类。这里是对代码得一种约束。
换个角度去论证,你需要去重写父类方法时,为什么继承于可以实例化得类而不是抽象类呢?
可以实例化得类是用来做事情得,是用来代码复用得。不是拿来当抽象类使用。
以上都是从某一角度出发得论证,意在理解。
从狭义得论证再回到广义就是:子类可以替换父类。
单一职责原则(Single Responsibility Principle)
原文:
There should never be more than one reason for a class to change.
理解:
不要让一个类乱七八糟得做了所有事情。
拓展到方法上也是如此。
好处:
职责明确,可读性高,易于维护
依赖倒置原则(Dependence Inversion Principle)
原文:
- High level modules should not depend upon low level modules.
- Both should depend upon abstractions.
- Abstractions should not depend upon details. Details should depend upon abstractions.
理解:
- 高层模块不应该依赖底层模块。
- 两者都应该依赖其抽象。
- 抽象不应该依赖细节,细节应该依赖抽象。
个人理解:
- 面向对象替代面向过程
- 不同职责类之间不相互依赖,尽可能使用抽象类、接口。
举例:
面向对象得实现男人跳,女人跳。
一种实现方式:
男人类,女人类。分别实现跳方法。
控制类中,男人跳方法:实例化男人类,调用方法。
控制类中,女人跳方法:实例化女人类,调用方法。
执行时,实例控制类,调用男人跳方法,女人跳方法。
另外一种实现方式:
接口类,定义跳方法。
男人类继承接口类,实现跳方法。
女人类继承接口类,实现跳方法。
控制类定义跳方法,参数为接口类。
执行时,实例控制类,传入男人类、女人类,调用统一得跳方法。
这么看来,似乎第二种方法更复杂了。然而第二种才符合依赖倒置原则。
为什么呢?
此时世界上出现了新人种--矮人。那我得程序中需要加上矮人跳。
第一种方案情况下:
增加矮人类,实现跳方法。
控制类中,矮人跳方法:实例化矮人类,调用方法。
执行时,实例控制类,调用矮人跳方法。
而第二种方案情况下:
增加矮人类继承接口,实现跳方法
执行时,实例控制类,传入矮人类,调用统一得跳方法。
这两种方案得不同点在于,第二种方案在面对新的需求时无需改动控制类。而第一种方案则需要具体得修改控制类。在扩展性上第二种方案远胜于第一种方案。
依赖倒置,个人理解为:模块之间得松耦合,不同职责类之间通过接口、抽象类解耦。
接口隔离原则(Interface Segregation Principle)
原文:
- Clients should not be forced to depend upon interfaces that they don`t use.
- The dependency of one class to another one should depend on the smallest possible.
理解:
- 客户端不应该依赖它不需要的接口。
- 类间的依赖关系应该建立在最小的接口上。
个人更倾向于理解为 接口分离原则,强调一种细分得感觉。
类在使用接口时,不应当去被迫得实现类不需要得方法。也就是接口中定义得方法要尽可能得精简,不应当出现冗余得情况,否则就需重新设计接口,比如接口得拆分,哪怕一个接口拆的只剩一个方法。
迪米特法则(Law of Demeter)
原文:
- Each unit should have only limited knowledge about other units: only units "closely" related to the current unit.
- Each unit should only talk to its friends; don't talk to strangers.
- Only talk to your immediate friends.
理解:
引用百度的一段话,很直白的一段话。
迪米特法则(Law of Demeter)又叫作最少知识原则(Least Knowledge Principle 简写LKP)
迪米特法则的初衷在于降低类之间的耦合。由于每个类尽量减少对其他类的依赖,因此,很容易使得系统的功能模块功能独立,相互之间不存在(或很少有)依赖关系。
迪米特法则不希望类之间建立直接的联系。如果真的有需要建立联系,也希望能通过它的友元类来转达。因此,应用迪米特法则有可能造成的一个后果就是:系统中存在大量的中介类,这些类之所以存在完全是为了传递类之间的相互调用关系——这在一定程度上增加了系统的复杂度。
狭义的迪米特法则的缺点:
在系统里造出大量的小方法,这些方法仅仅是传递间接的调用,与系统的业务逻辑无关。
遵循类之间的迪米特法则会是一个系统的局部设计简化,因为每一个局部都不会和远距离的对象有直接的关联。但是,这也会造成系统的不同模块之间的通信效率降低,也会使系统的不同模块之间不容易协调。
广义的迪米特法则在类的设计上的体现:
1. 优先考虑将一个类设置成不变类。
2. 尽量降低一个类的访问权限。
3. 谨慎使用Serializable。
4. 尽量降低成员的访问权限。
写在最后
这六大原则,首字母可以组成一个单词 SOLID(稳定)。
S: Single Responsibility Principle:单一职责原则
O:Open Closed Principle:开闭原则
L: Liskov Substitution Principle:里氏替换原则, Law of Demeter:迪米特法则
I: Interface Segregation Principle:接口隔离原则
D:Dependence Inversion Principle:依赖倒置原则