题外
设计模式的内容看过不知道多少次了,然而,自己还是懵懵懂懂没有理解,所以看一次忘一次,另外还有很重要的一点是
读书不笔记,俩月忘光光
随着项目经验的增多,碰到一些问题让我不得不思考,是不是还有更好的代码设计结构,怎么才能更好的来实现接口以应对接下来的版本迭代以及逻辑修改,别人精简干练易维护可扩展的代码到底是怎么个思路,我知道我必须要好好研究一下设计模式了,这次买了几本相关的书,死磕到底。
一、什么是设计模式
设计模式是一套理论,由软件界的先辈们(The Gang of Four: Erich Gamma、Richard Helm、Ralph Johnson、John Vlissides)总结的一套可以反复使用的经验,它可以提高代码的可重用性,增强系统的可维护性,以及解决一系列复杂问题。
设计模式不是工具,它是软件开发的哲学,它能知道你如何去设计一个优秀的框架,编写一段健壮的代码,解决一个复杂的需求。
那么怎么去解决这一系列问题呢,专家们首先提出了6大设计原则,但这6大设计原则仅仅是一系列的“口号”,真正付诸行动还需要有详尽的指导方法,于是23种设计模式出现了。
二、6大设计原则
- 单一职责原则 Single Responsibility Principle
- 开闭原则 Open Close Principle
- 里氏替换原则 Liskov Substitution Principle
- 迪米特法则(最少知道原则) Law of Demeter
- 接口隔离原则 Interface Segregation Principle
- 依赖倒置原则 Dependence Inversion Principle
把这6个原则的首字母(里氏替换原则和迪米特法则的首字母重复,只取一个)联合起来就是SOLID(solid 稳定的),代表的含义就是结合6大设计原则建立稳定、灵活、健壮的设计。
1、单一职责原则 Single Responsibility Principle (SRP)
There should never be more than one reason for a class to change.
应该有且仅有一个原因引起类的变更。
单一职责原则的好处:类的复杂性降低,可读性提高,可维护性提高,变更引起的风险降低
对于接口,我们在设计的时候一定要做到单一,但是对于实现类就需要多方面的考虑了,生搬硬套单一职责原则会引起类的剧增,给维护带来非常多的麻烦,而且过分细分类的职责也会人为的增加系统的复杂性,本来一个类可以实现的行为硬要拆成两个类,然后再使用聚合或组合的方式耦合在一起,人为制造了系统的复杂性。所以原则是死的,人是活的。
2、里氏替换原则 Liskov Substitution Principle
Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.
所以引用基类的地方必须能透明地使用其子类的对象。
- 子类必须完全实现父类的方法
- 子类可以有自己的个性
- 覆盖或实现父类的方法时输入参数可以被放大
子类中方法的前置条件必须与超类中被覆写的方法的前置条件相同或者更宽松 - 覆写或实现父类的方法时输出结果可以被缩小
在项目中,采用里氏替换原则时,尽量避免子类的“个性”,一旦子类有“个性”,这个子类和父类之间的关系就很难调和了,把子类当父类使用,子类的“个性”被抹杀,把子类单独作为一个业务来使用,则会把代码间的耦合关系变得扑朔迷离——缺乏类替换的标准。
3、依赖倒置原则 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.
- 高层模块不应该依赖低层模块,两者应该依赖其抽象;
- 抽象不应该依赖细节
- 细节应该依赖抽象
这这这,什么鬼。。。
依赖倒置原则的核心——面向接口编程
依赖倒置原则在Java语言中的表现
- 模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或者抽象类产生的
- 接口或抽象类不依赖于实现类
- 实现类依赖接口或抽象类
什么是依赖倒置?
正常人的思维,我要开奔驰车就依赖奔驰车,我要使用笔记本电脑就直接依赖笔记本电脑,这个叫依赖正置,就是类间的依赖是实实在在的实现类间的依赖,也就是面向实现编程。
而编写程序需要的是对现实世界的事物进行抽象,抽象的结果就是有了抽象类和接口,然后我们根据系统设计的需要产生了抽象间的依赖,这个就是依赖倒置。
依赖倒置原则的本质就是通过抽象(接口或抽象类)使各个类或模块的实现彼此独立,不互相影响,实现模块机的松耦合:
- 每个类尽量都有接口或者抽象类
- 变量的表面类型尽量是接口或者抽象类
- 任何类都不应该从具体类派生(特殊情况下可能会不考虑)
- 尽量不要覆写基类的方法
- 结合里氏替换原则使用
4、接口隔离原则 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 interface.
类间的依赖关系应该建立在最小的接口上。
这个定义什么意思呢,概括一下大概是:建立单一接口,不要建立臃肿庞大的接口。再通俗一点讲:接口尽量细化,同事接口中的方法尽量少。看着和单一职责原则类似,其实不然,其一,单一职责原则原注重的是职责;而接口隔离原则注重对接口依赖的隔离。其二,单一职责原则主要是约束类,其次才是接口和方法,它针对的是程序中的实现和细节;而接口隔离原则主要约束接口接口,主要针对抽象,针对程序整体框架的构建。
网上找了图片来说明接口隔离原则:
图1 未使用接口隔离原则的设计 图2 使用了接口隔离原则的设计
图1:类A依赖接口I中的方法1、方法2、方法3,类B是对类A依赖的实现。类C依赖接口I中的方法1、方法4、方法5,类D是对类C依赖的实现。对于类B和类D来说,虽然他们都存在着用不到的方法(也就是图中红色字体标记的方法),但由于实现了接口I,所以也必须要实现这些用不到的方法。
图2:拆分接口I为三个接口
(图1图2来此https://blog.csdn.net/zhengzhb/article/details/7296921)
接口隔离原则是对接口进行规范约束,包含以下4层含义:
- 接口尽量小
这是接口隔离原则的核心定义,不出现臃肿的接口,但是“小”是有限度的,首先就是不能违反单一职责原则,一定要适度,不能过小导致接口数量过多导致程序复杂化。根据接口隔离原则拆分接口时,首先必须满足单一职责原则。 - 接口要高内聚
高内聚就是提高接口、类、模块的处理能力,减少对外的交互。接口是对外的承诺,承诺越少对系统的开发越有利,变更的风险也就越少,同时也有利于降低成本。 - 定制服务
定制服务就是单独为一个个体提供优良的服务:只提供访问者需要的方法,最小依赖,减少可能引起的风险。 - 接口设计是有限度的
接口的设计颗粒度大小问题:颗粒越小,系统越灵活,但同时也带来了结构复杂化,开发难度增加,可维护性降低,颗粒太大,灵活性不够,无法提供可定制服务,带来不可预估的风险,对于“度”的把握,只能根据经验和常识来判断,没有固化的标准。
怎么准确的实践接口隔离原则?实践、经验和领悟!(@_@)
5、迪米特法则 Law of Demeter
一个对象应该对其他对象有最少的了解。
通俗点讲,一个类应该对自己需要耦合或调用的类知道的最少,被耦合或调用的类内部是如何复杂都和要耦合的类没有关系,提供多少public方法,就调用多少,其他一概不关心。
迪米特法则的核心观念是类间解耦,弱耦合,只有弱耦合了之后,类的复用性才可以提高。其要求的结果就是产生了大量的中转或跳转类,导致系统的复杂性提高,同时也带来了维护难度。在采用迪米特法则时需要反复权衡,既做到让结构清晰,又做到高内聚低耦合。
迪米特法则对类的低耦合提出了明确的要求,包含如下4层含义:
- 只和朋友交流
Only talk to your immediate friends.
只与直接的朋友通信。
直接的朋友:每个对象都必然会与其他对象有耦合关系,两个对象之间的耦合就成为朋友关系,这种关系的类型有例如组合、聚合、依赖等。
一个类只和朋友交流,不与陌生类交流。
- 朋友间也是有距离的
类的内部尽量高内聚,暴露尽量少的接口低耦合。
一个类暴露的public属性和方法越多,修改时涉及的面也就越大,变更引起的风险也就越大。因此,为了保持朋友间的距离,在设计时应该尽量不要对外公布太多public,多使用private、package-private、protected等访问权限。 - 是自己的就是自己的
实际应用中经常会出现这样一个方法:放在本类中也可以,放在其他类中也没有错。可以坚持这个原则:如果一个方法放在本类中,既不增加类间关系,也不对本类产生负面影响 ,那就放置在本类中。 - 谨慎使用Serializable
6、开闭原则 Open Close Principle
Software entities like classes,modules and functions should be open for extension but closed for modificaitons.
一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。
一个软件产品只要在生命期内,都会发生变化,既然变化是一个既定的事实,我们就要在设计时尽量适应这些变化,以提高项目的稳定性和灵活性,真正实现“拥抱变化”。开闭原则告诉我们应尽量通过扩展软件实体(模块、抽象和类、方法)的行为来实现变化,而不是通过修改已有的代码来完成变化,它是为软件实体的未来事件而制定的对现行开发设计进行约束的一个原则。
开闭原则是一个非常虚的原则,前面5个原则则是对开闭原则的具体解释,但开闭原则并不局限于这么多。开闭原则类似于Java中的抽象类,其他五大原则是具体的实现类。
开闭原则:用抽象构建框架,用实现扩展细节
单一职责原则告诉我们实现类要职责单一;里氏替换原则告诉我们不要破坏继承体系;依赖倒置原则告诉我们要面向接口编程;接口隔离原则告诉我们在设计接口的时候要精简单一;迪米特法则告诉我们要降低耦合。而开闭原则是总纲,它告诉我们要对扩展开放,对修改关闭。
(这一段来自 https://blog.csdn.net/zhengzhb/article/details/7296944)
当其他五大原则都遵守的不错,那开闭原则自然也就不错,如果五大原则没有遵守好,自然开闭原则也有遵守的比较少。
网友评论