首页 > 代码库 > 面向对象设计的七大原则
面向对象设计的七大原则
在上一篇里我们谈了谈为何设计模式,那接下来我们再浅谈一下在面向对象设计中我们常常要遵循的一些原则。
这些原则是经过无数的前人总结出来的经验的结晶。
仅仅有遵循这些原则。你才有可能涉及出优秀的代码。今天我们要谈的原则有七大原则,即:单一职责。里氏替换。迪米特法则,依赖倒转,接口隔离,合成/聚合原则。开放-封闭 。
1. 开闭原则
定义:软件实体应当对扩展开放,对改动关闭。这句话说得有点专业。更通俗一点讲,也就是:软件系统中包括的各种组件,比如模块(Modules)、类(Classes)以及功能(Functions)等等。应该在不改动现有代码的基础上。去扩展新功能。开闭原则中“开”。是指对于组件功能的扩展是开放的。是同意对其进行功能扩展的。开闭原则中“闭”。是指对于原有代码的改动是封闭的,即不应该改动原有的代码。
问题由来:凡事的产生都有缘由。我们来看看。开闭原则的产生缘由。在软件的生命周期内,由于变化、升级和维护等原因须要对软件原有代码进行改动时。可能会给旧代码中引入错误,也可能会使我们不得不正确整个功能进行重构,而且须要原有代码经过又一次測试。
这就对我们的整个系统的影响特别大。这也充分展现出了系统的耦合性假设太高,会大大的添加后期的扩展。维护。为了解决问题,故人们总结出了开闭原则。解决开闭原则的根本事实上还是在解耦合。所以。我们面向对象的开发,我们最根本的任务就是解耦合。
解决方法:当软件须要变化时。尽量通过扩展软件实体的行为来实现变化。而不是通过改动已有的代码来实现变化。
小结:开闭原则具有理想主义的色彩。说的非常抽象,它是面向对象设计的终极目标。其它几条原则,则能够看做是开闭原则的实现。我们要用抽象构建框架,用实现扩展细节。
2. 单一职责原则(Single Responsibility Principle)
定义:一个类。仅仅有一个引起它变化的原因。即:应该仅仅有一个职责。
每个职责都是变化的一个轴线。假设一个类有一个以上的职责。这些职责就耦合在了一起。
这会导致脆弱的设计。当一个职责发生变化时,可能会影响其他的职责。另外,多个职责耦合在一起,会影响复用性。比如:要实现逻辑和界面的分离。须要说明的一点是单一职责原则不仅仅是面向对象编程思想所特有的,仅仅要是模块化的程序设计,都须要遵循这一重要原则。
问题由来:类T负责两个不同的职责:职责P1,职责P2。当因为职责P1需求发生改变而须要改动类T时。有可能会导致原本执行正常的职责P2功能发生问题。
解决方法:分别建立两个类T1、T2,使T1完毕职责P1功能,T2完毕职责P2功能。这样,当改动类T1时。不会使职责P2发生问题风险;同理,当改动T2时,也不会使职责P1发生问题风险。
3. 里氏替换原则(Liskov Substitution Principle)
定义:子类型必须可以替换掉它们的父类型。注意这里的可以两字。
有人也戏称老鼠的儿子会打洞原则。
问题由来:有一功能P1。由类A完毕。
现须要将功能P1进行扩展。扩展后的功能为P,当中P由原有功能P1与新功能P2组成。新功能P由类A的子类B来完毕。则子类B在完毕新功能P2的同一时候,有可能会导致原有功能P1发生问题。
解决方法:类B继承类A时。除加入新的方法完毕新增功能P2外,尽量不要重写父类A的方法,也尽量不要重载父类A的方法
小结:全部引用父类的地方必须能透明地使用其子类的对象。
子类能够扩展父类的功能,但不能改变父类原有的功能。即:子类能够实现父类的抽象方法,子类也中能够添加自己特有的方法。但不能覆盖父类的非抽象方法。当子类的方法重载父类的方法时。方法的前置条件(即方法的形參)要比父类方法的输入參数更宽松。
当子类的方法实现父类的抽象方法时。方法的后置条件(即方法的返回值)要比父类更严格。
4. 迪米特法则(Law Of Demeter)
定义:迪米特法则又叫最少知道原则,即:一个对象应该对其它对象保持最少的了解。假设两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。假设当中一个类须要调用还有一个类的某一个方法的话。能够通过第三者转发这个调用。简单定义为仅仅与直接的朋友通信。首先来解释一下什么是直接的朋友:每一个对象都会与其它对象有耦合关系,仅仅要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式非常多。依赖、关联、组合、聚合等。当中,我们称出现成员变量、方法參数、方法返回值中的类为直接的朋友。而出如今局部变量中的类则不是直接的朋友。也就是说。陌生的类最好不要作为局部变量的形式出如今类的内部。
问题由来:类与类之间的关系越密切。耦合度越大,当一个类发生改变时,对还有一个类的影响也越大。
最早是在1987年由美国Northeastern University的Ian Holland提出。通俗的来讲,就是一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类来说,不管逻辑多么复杂,都尽量地的将逻辑封装在类的内部,对外除了提供的public方法,不正确外泄漏不论什么信息。迪米特法则另一个更简单的定义:仅仅与直接的朋友通信。
解决方法:尽量减少类与类之间的耦合。 自从我们接触编程開始,就知道了软件编程的总的原则:低耦合,高内聚。不管是面向过程编程还是面向对象编程,仅仅有使各个模块之间的耦合尽量的低。才干提高代码的复用率。
迪米特法则的初衷是减少类之间的耦合,因为每一个类都减少了不必要的依赖,因此的确能够减少耦合关系。可是凡事都有度,尽管能够避免与非直接的类通信,可是要通信。必定会通过一个“中介”来发生联系。故过分的使用迪米特原则,会产生大量这种中介和传递类。导致系统复杂度变大。所以在採用迪米特法则时要重复权衡,既做到结构清晰,又要高内聚低耦合。
5. 依赖倒置原则(Dependence Inversion Principle)
定义:高层模块不应该依赖低层模块。二者都应该依赖其抽象。抽象不应该依赖细节;细节应该依赖抽象。中心思想是面向接口编程
问题由来:类A直接依赖类B,假如要将类A改为依赖类C。则必须通过改动类A的代码来达成。
这样的场景下,类A通常是高层模块,负责复杂的业务逻辑;类B和类C是低层模块,负责主要的原子操作;假如改动类A。会给程序带来不必要的风险。
解决方法:将类A改动为依赖接口I。类B和类C各自实现接口I,类A通过接口I间接与类B或者类C发生联系,则会大大减少改动类A的几率。
在实际编程中,我们一般须要做到例如以下3点:
1. 低层模块尽量都要有抽象类或接口。或者两者都有。
2. 变量的声明类型尽量是抽象类或接口。
3. 使用继承时遵循里氏替换原则。
採用依赖倒置原则尤其给多人合作开发带来了极大的便利,參与协作开发的人越多、项目越庞大,採用依赖导致原则的意义就越重大。
小结:依赖倒置原则就是要我们面向接口编程,理解了面向接口编程。也就理解了依赖倒置。
6. 接口隔离原则(Interface Segregation Principle)
定义:client不应该依赖它不须要的接口;一个类对还有一个类的依赖应该建立在最小的接口上。
问题由来:类A通过接口I依赖类B,类C通过接口I依赖类D,假设接口I对于类A和类B来说不是最小接口。则类B和类D必须去实现他们不须要的方法
解决方法:1、 使用托付分离接口。2、 使用多重继承分离接口。
3.将臃肿的接口I拆分为独立的几个接口,类A和类C分别与他们须要的接口建立依赖关系。也就是採用接口隔离原则。
举例说明:
以下我们来看张图,一切就一目了然了。
这个图的意思是:类A依赖接口I中的方法1、方法2、方法3,类B是对类A依赖的实现。类C依赖接口I中的方法1、方法4、方法5。类D是对类C依赖的实现。对于类B和类D来说,尽管他们都存在着用不到的方法(也就是图中红色字体标记的方法)。但因为实现了接口I,所以也必需要实现这些用不到的方法
改动后:
假设接口过于臃肿。仅仅要接口中出现的方法,无论对依赖于它的类有没实用处。实现类中都必须去实现这些方法。这显然不是好的设计。
假设将这个设计改动为符合接口隔离原则,就必须对接口I进行拆分。在这里我们将原有的接口I拆分为三个接口
小结:我们在代码编写过程中。运用接口隔离原则,一定要适度,接口设计的过大或过小都不好。
对接口进行细化能够提高程序设计灵活性是不挣的事实,可是假设过小,则会造成接口数量过多,使设计复杂化。
所以一定要适度。设计接口的时候。仅仅有多花些时间去思考和筹划。就能准确地实践这一原则。
7. 合成/聚合原则
1. 对象的继承关系在编译时就定义好了,所以无法在执行时改变从父类继承的子类的实现定义:也有人叫做合成复用原则,及尽量使用合成/聚合,尽量不要使用类继承。换句话说,就是能用合成/聚合的地方,绝不用继承。
为什么要尽量使用合成/聚合而不使用类继承?
2. 子类的实现和它的父类有很紧密的依赖关系。以至于父类实现中的不论什么变化必定会导致子类发生变化
3. 当你复用子类的时候,假设继承下来的实现不适合解决新的问题,则父类必须重写或者被其他更适合的类所替换,这样的依赖关系限制了灵活性。并终于限制了复用性。
总结:这些原则在设计模式中体现的淋淋尽致。设计模式就是实现了这些原则。从而达到了代码复用、增强了系统的扩展性。所以设计模式被非常多人奉为经典。
我们能够通过好好的研究设计模式,来慢慢的体会这些设计原则。
面向对象设计的七大原则