单一职责原则(Single Responsibility Principle)
单一职责原则简称 SRP ,顾名思义,就是一个类只负责一个职责。它的定义也很简单:
1、There should never be more than one reason for a class to change.
这句话很好理解,就是说,不能有多个导致类变更的原因。
那这个原则有什么用呢,它让类的职责更单一。这样的话,每个类只需要负责自己的那部分,类的复杂度就会降低。如果职责划分得很清楚,那么代码维护起来也更加容易。试想如果所有的功能都放在了一个类中,那么这个类就会变得非常臃肿,而且一旦出现bug,要在所有代码中去寻找;更改某一个地方,可能要改变整个代码的结构,想想都非常可怕。当然一般时候,没有人会去这么写的。
当然,这个原则不仅仅适用于类,对于接口和方法也适用,即一个接口/方法,只负责一件事,这样的话,接口就会变得简单,方法中的代码也会更少,易读,便于维护。
事实上,由于一些其他的因素影响,类的单一职责在项目中是很难保证的。通常,接口和方法的单一职责更容易实现。
单一原则的好处:
代码的粒度降低了,类的复杂度降低了。
可读性提高了,每个类的职责都很明确,可读性自然更好。
可维护性提高了,可读性提高了,一旦出现 bug ,自然更容易找到他问题所在。
改动代码所消耗的资源降低了,更改的风险也降低了。
翻译过来就是,所有引用基类的地方必须能透明地使用其子类的对象。
里氏替换原则的意思是,所有基类在的地方,都可以换成子类,程序还可以正常运行。这个原则是与面向对象语言的 继承 特性密切相关的。
由于面向对象语言的继承特性,子类拥有父类的所有方法,因此,将基类替换成具体的子类,子类也可以调用父类中的方法(其实是它自己的方法,继承于父类),
但是如果要保证完全可以调用,光名称相同不行,还需要满足下面两个条件:
子类中的方法的前置条件必须与超类中被覆写的方法的前置条件相同或更宽松。
子类中的方法的后置条件必须与超类中被覆写的方法的后置条件相同或更严格。
可以看到, LSP 原则最重要的一点就是:避免子类重写父类中已经实现的方法。这就是 LSP 原则的本质。这里由于 Fruit 父类已经实现了 introduce 方法,因此子类应该避免再对其进行重写,
如果需要增加个性化,就应该对父类进行扩展,而不是重写,否则也会违背开闭原则。
一般来讲,程序中父类大多是抽象类,因为父类只是一个框架,具体功能还需要子类来实现。因此很少直接去 new 一个父类。而如果出现这种情况,
那么就说明父类中实现的代码已经很好了,子类只需要对其进行扩展就会,尽量避免对其已经实现的方法再去重写。
依赖倒置原则(Dependence Inversion Principle)
依赖倒置原则,依赖就是类与类之间的依赖,主要是函数传参,上面的例子已经很明白地介绍了,参数要尽可能使用抽象类或接口,这就是“高层模块不应该依赖底层模块,两者都应该依赖其抽象” 的解释。那么如果要实现这个,就要求每个实现类都应该尽可能从抽象中派生,这就是上面的 “细节应该依赖抽象”。
简而言之,该原则主要有下面几点要求:
每个类都尽量要有接口或抽象类,或者两者都有
变量的表面类型尽量是接口或者抽象类(比如程序中的 Fruit apple = new Apple() Fruit 是表面类型, Apple 是实际类型)
高层模块不应该依赖底层模块,两者都应该依赖其抽象。
抽象不应该依赖细节。
细节应该依赖抽象。
在Java语言中的表现就是:
模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生的。
接口或抽象类不依赖于实现类。
实现类依赖于接口或抽象类。
简而言之,我们要尽可能使用接口或抽象类。也就是“面向接口编程” 或者说 “面向抽象编程” ,也就是说程序中要尽可能使用抽象类或是接口。
接口隔离原则(Interface Segregation Principle)
首先声明,该原则中的接口,是一个泛泛而言的接口,不仅仅指Java中的接口,还包括其中的抽象类。
首先,给出该原则的定义,该原则有两个定义:
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)
迪米特法则也叫最少知道原则(Least Knowledge Principle, LKP ),虽然名称不同,但都是同一个意思:一个对象应该对其他对象有最少的了解。
该原则也很好理解,我们在写一个类的时候,应该尽可能的少暴露自己的接口。什么意思呢?就是说,在写类的时候,能不 public 就不 public ,所有暴露的属性或是接口,都是不得不暴露的,这样的话,就能保证其他类对这个类有最少的了解了。
这个原则也没什么需要多讲的,调用者只需要知道被调用者公开的方法就好了,至于它内部是怎么实现的或是有其他别的方法,调用者并不关心,调用者只关心它需要用的。反而,如果被调用者暴露太多不需要暴露的属性或方法,那么就可能导致调用者滥用其中的方法,或是引起一些其他不必要的麻烦。
开闭原则(Open Closed Principle)
开闭原则所有设计模式原则中,最基础的那个原则。首先,还是先来看一下它的定义:
Software entities like classes, modules and functions should be open for extension but closed for modifications.
1
翻译过来就是:一个软件实体,如类、模块和函数应该对扩展开放,对修改关闭。
开闭原则之前也提到过,在 LSP 中,我们说,要避免子类重写父类中已经实现的方法。这个时候,继承父类就是对其进行扩展,但没有进行修改。这就是开闭原则一个很好的体现。
那是不是开闭原则与 LSP 原则混淆了呢?并不是, LSP 原则强调的是基类与子类的关系,只是其中的一种实现方式用到了开闭原则而已。
那么开闭原则具体是什么呢?可以说,开闭原则贯穿于以上五个设计模式原则。开闭原则中的对扩展开放,就是说,如果在项目中添加一个功能的时候,可以直接对代码进行扩展;如果要修改某一部分的功能时,我们应该做的是,尽量少做修改(完全不修改是不可能的),但是修改的时候,要保留原来的功能,只是在上面扩展出新的功能,就像版本更新一样,更新后,依然支持旧版本。
开闭原则是一个特别重要的原则,无论是在设计模式中还是在其他领域中,这都是一个非常基础的设计理念。
网友评论