面向对象设计原则

面向对象设计原则的作用

可以提高软件的可维护性于可复用性,其中可维护性是指代码能够被理解、改正、适应扩展的难易程度,可复用性是指代码能够被重用的难易程度,可维护性和可复用性是用于衡量软件质量的两个非常重要的属性。

7个面向对象设计原则

设计原则名称 定义
单一职责原则 一个对象只应该包含单一的职责,并且该职责被完整地封装在一个类中
开闭原则 软件实体应该对扩展开放,对修改关闭
里氏代换原则 所有引用基类的地方必须都能使用基类的子类替换
依赖倒转原则 高层模块不应该依赖底层模块,它们都应该依赖抽象。抽象不应该依赖细节,细节应该依赖抽象
接口隔离原则 接口细分,不应该依赖不需要的接口
合成复用原则 优先使用对象组合,而不是使用继承来达到复用的目的
迪米特法则 每一个对象对其它的对象都只有最少的信息,而且局限于那些与本对象密切相关的信息
单一职责原则

单一职责原则是实现高内聚、低耦合的指导方针,对一个类来说,应该仅有一个引起它变化的原因。在软件系统中,一个类承担的职责越多,它被复用的可能性就越小,并且当承担过多的职责后,就相当于将这些职责耦合在一起,当其中一个职责发生变化时,极有可能会影响到其它职责的正常工作。因此要将这些职责进行分离,将不同的职责封装在不同的类中(将不同的变化原因封装在不同的类里面),如果多个职责总是会同时发生改变,那么则可以将它们封装在一个类中。

开闭原则

开闭原则是极其重要的设计原则,对扩展开发,对修改关闭,即尽量在不修改原有代码的情况下进行扩展。

当软件系统面临新的需求时,应该尽量保证系统的设计框架是稳定的,如果一个软件设计符合开闭原则,那么可以非常方便的对系统进行扩展,而且在扩展时无需修改现有代码,使系统同时具备适应性、灵活性和较好的稳定性以及延续性。

为了满足开闭原则,需要对系统进行抽象化设计,抽象化是开闭原则的关键。在C#、JAVA等编程语言中可以定义一个相对稳定的抽象层,而将不同的实现放在具体的实现层中完成。可以通过接口、抽象类等机制进行抽象化设计,定义系统抽象层,再通过具体类进行扩展,如果将来需求变动需要修改系统的行为,无须对抽象层进行改动,只要增加新的具体类来实现新的业务需求,在不修改原有代码的前提下扩展系统功能,即是开闭原则。

里氏代换原则

该原则表示,在软件中将一个基类对象替换成它的子类对象,程序可以正常运行,不会产生任何错误,反之则不成立。里氏代换原则是实现开闭原则的基础,由于使用基类的地方都可以使用子类代替,因此在程序中尽量使用基类类型定义对象,在运行时再确定其子类的具体类型,用子类对象替换父类对象。

在使用里氏代换原则时应将父类设计成抽象类或者接口,让子类继承父类或实现父类接口,并实现父类中声明的方法。运行时子类实例替换父类实例,可以很方便地扩展系统功能,无需修改原有子类代码,增加新的功能可以通过增加一个新的子类来实现。

依赖倒转原则

依赖倒转原则是面向对象设计的主要实现机制之一,它是系统抽象化的具体实现。依赖倒转原则要求:面向接口编程,不要针对实现编程。

在传递参数时或者在关联关系中尽量引用抽象层,即使用接口和抽象类进行变量类型声明、参数类型声明、方法返回类型声明和数据类型转换等,而不要用具体类来做这些事情。为了确保该原则的应用,一个具体类应当只实现接口或抽象类中声明过的方法,而不要给出多余的其它方法,否则基类将无法调用到子类中增加的新方法。

在引入抽象层后,系统将会具有很好的灵活性,在程序中应该尽量使用抽象层进行编程,而将具体类写在配置文件中,这样做的好处在于,如果将来需求发生变化,只需要对抽象层进行扩展,并修改配置文件,而无须修改原有的代码,满足开闭原则。

实现依赖倒转原则,需要针对抽象层进行编程,将具体类的对象通过依赖注入的方式注入其它对象中。依赖注入指的是:当一个对象需要与其它对象发生依赖关系时,通过方法参数来注入所依赖的对象。常用的注入方式:构造注入(通过构造函数传入具体类的对象)、设置注入(set方法传入具体类的对象)和接口注入(在接口中声明的业务方法传入具体类的对象)。

接口隔离原则

当一个接口太大时,需要将它分割成更细小的接口,每一个接口应该承担一种相对独立的角色,不做与之无关的事情。

接口细分是有好处的,如果提供的接口太大,包含的方法太多,使用起来反而不方便,因为实现接口就需要实现这个接口的所有方法,而并不是每一个方法都是所需要的。这时候为了使职责单一,需要将接口中的方法根据其职责分别放在不同的小接口中,确保接口使用起来方便,并且都承担单一的角色。接口应该尽量细化,每个接口中包含的方法应尽量的少,最好只包含一个模块所有要的方法即可。

合成复用原则

合成复用原则就是在一个新的对象里通过关联关系来使用一些已有的对象,使之成为新对象的一部分,复用时尽量使用组合,少用继承。

在面向对象中,可以使用组合或者继承的方式复用已有的设计和实现,但首先应该考虑组合,因为这种方式可以使系统更加灵活,降低类与类之间的耦合度,一个类的变化对其它类的影响较小。其次才考虑继承,使用继承时应该遵守里氏代换原则。

继承的局限性:如果基类发生改变,那么子类也不得不发生变化。同时继承的实现是静态的,不可能在运行时发生改变,缺少灵活性。

为什么使用组合:由于组合是将已有对象引入到新对象中,让它成为新对象的一部分,因此新对象可以调用已有对象的功能,而对象内部的实现对新对象来说并不可见。对比继承,组合的耦合度更低,成员对象的变化对新对象的影响不大,并且合成复用可以在运行时动态进行,新对象可以动态引用成员对象类型相同的其它对象。

什么时候用继承什么时候用组合?
如果两个类之间的关系是a-Has-b(a对象拥有b职责),那么应该使用组合;如果关系是a-Is-b(a对象属于b类型的某一种类),这是应该使用继承。

迪米特法则

每个模块应该尽量少地与其它模块相互引用产生关联,这样在模块变动时,对其它模块产生的影响也会最小,应用这个法则可降低类之间的耦合度。

对象只应该与自己所关心的成员通信,如:

  • 对象本身,即自己
  • 以参数形式传入到当前对象中的对象
  • 当前对象的成员对象
  • 当前对象所创建的对象

只要数据上述的某一个类型,便可与之直接交互。在程序设计的时候,如果两个对象没有必要彼此直接通信,那么这两个对象就不应该发生任何直接关联,如果一个对象需要调用另一个对象的方法,可以通过引入一个中间者的方式来实现。

应用迪米特法则时,应尽量创建松耦合的类,耦合度越低,越利于复用;在类的结构设计上,应当降低每一个类中成员变量和成员函数的访问权限;类型应当尽量设计成不变类;一个对象对其它对象的引用应当降到最低。

你可能感兴趣的:(oop设计模式编程技巧设计原则)