面向对象设计有7个基本原则:
单一职责原则,开闭原则,里氏代换原则,依赖倒转原则,接口隔离原则,合成复用原则和迪米特法则。
以上就是7个常用的面向对象设计原则,这些原则并不是孤立存在的,它们相互依赖,相互补充。
在说面向对象设计原则之前,先引入面向对象中的一些重要概念。
软件的可维护性和可复用性:
软件的复用(Reuse)或重用拥有众多优点,如可以提高软件的开发效率,提高软件质量,节约开发成本,恰当的复用还可以改善系统的可维护性。多态就是软件重用的一种实现方式。
面向对象设计复用的目标在于实现支持可维护性的复用。
在面向对象的设计里面,可维护性复用都是以面向对象设计原则为基础的,这些设计原则首先都是复用的原则,遵循这些设计原则可以有效地提高系统的复用性,同时提高系统的可维护性。
传统的复用是指代码的复制,算法的复用和数据结构的复用。
面向对象设计的复用中,抽象化和继承是的概念和定义可以复用多态使得实现和应用可以复用,而抽象化和封装可以保持和促进系统的可维护性。
知名软件大师Robert C.Martin认为一个可维护性(Maintainability) 较低的软件设计,通常由于如下4个原因造成:
过于僵硬(Rigidity) 过于脆弱(Fragility) 复用率低(Immobility) 黏度过高(Viscosity) 。
重构(Refactoring)是在不改变软件现有功能的基础上,通过调整程序代码改善软件的质量、性能,使其程序的设计模式和架构更趋合理,提高软件的扩展性和维护性。
下面是面向对象设计基本原则的内容:
设计原则名称 | 设计原则简介 | 重要性 |
单一职责原则 | 类的职责要单一,不能将太多的职责放在一个类中 | ★★★★☆ |
开闭原则 | 软件实体对扩展是开放的,但对修改是关闭的,即在不修改一 | ★★★★★ |
里氏代换原则 | 在软件系统中,一个可以接受基类对象的地方必然可以接受一 | ★★★★☆ |
依赖倒转原则 | 要针对抽象层编程,而不要针对具体类编程 | ★★★★★ |
接口隔离原则 | 使用多个专门的接口来取代一个统一的接口 | ★★☆☆☆ |
合成复用原则 | 在系统中应该尽量多使用组合和聚合关联关系,尽量少使用甚 | ★★★★☆ |
迪米特法则 | 一个软件实体对其他实体的引用越少越好,或者说如果两个类 | ★★★☆☆ |
最简单但又最难运用的原则:
单一职责原则说明就一个类而言,应该仅有一个引起它变化的原因。因为一个类承担的职责越多,它被复用的可能性就越小,而且如果一个类承担的职责过多,就相当于将这些职责耦合在一起,当其中一个职责变化时,可能会影响其他职责的运作。单一职责原则是实现高内聚、低耦合的指导方针,多用于代码重构中。难点是发现类的职责(数据职责和行为职责)并将其分离。
面向对象设计中最重要的原则:
开闭原则要求一个软件实体应当对扩展开放,对修改关闭。也就是说在设计一个模块的时候,应当使这个模块可以在不被修改的前提下被扩展,即实现在不修改源代码的情况下改变这个模块的行为。这里的软件实体可以指一个软件模块、一个由多个类组成的局部结构或一个独立的类。抽象化是开闭原则的关键,并从抽象化中导出具体化的实现。它是一个对可变性封装的原则,重点是找到类的可变因素并将其封装起来。
里氏代换原则说明所有引用基类(父类)的地方必须能透明地使用其子类的对象,即将出现父类的地方全换成子类对象,程序将不会产生任何问题,反过来则不成立。它是实现开闭原则的重要方法之一。根据这个原则,我们在程序中应尽量使用基类类型来对对象进行定义,而在运行时再确定其子类类型,用子类对象来替换父类对象。
依赖倒转原则表明高层模块不应该依赖低层模块,它们都应该依赖抽象。抽象不应该依赖于细节,细节应该依赖于抽象。
如果说开闭原则是面向对象设计的目标的话,那么依赖倒转原则就是面向对象设计的主要手段。依赖倒转原则的常用实现方式之一是在代码中使用抽象类,而将具体类放在配置文件中。具体类的注入方式有三种:1.构造注入。通过构造函数注入实例变量。 2.设值注入。 通过Setter方法注入实例变量。 3.接口注入。 通过接口方法注入实例变量。
“将抽象放进代码,将细节放进元数据” ----《程序员修炼之道:从小工到专家》
依赖倒转原则要求客户端依赖于抽象耦合,以抽象方式耦合是依赖倒转原则的关键。
接口隔离原则说明客户端不应该依赖哪些它不需要的接口,一旦一个接口太大,则需要将它分割成一些更小的接口,使用该接口的客户端只需要知道与之相关的方法即可。每个接口应该承担一种相对独立的角色,不多不少,不干不该干的事,该干的事都要干。使用接口隔离原则拆分接口时,首先必须满足单一职责原则。
合成复用原则就是指在一个新的对象里通过关联关系(包括组合和聚合)来使用一些已有的对象,是指成为新的对象的一部分,新对象通过委派调用已有对象的方法达到复用其已有功能的目的。简言之:要尽量使用组合/聚合关系,少用继承。
通过继承实现的复用虽然简单易于扩展,但破坏了系统的封装性,从基类继承来的实现是静态的,不可能在运行时发生改变,没有足够的灵活性,只能在有限的环境中使用(白箱复用)。
组合/聚合复用:耦合度相对较低,使系统更加灵活。选择性地调用成员对象的操作;可以在运行时动态进行(“黑箱”复用 )。
迪米特法则又称为最少知识原则,即不要和陌生人说话,只与你的直接朋友通信。这样,当一个模块修改时,就会尽量少的影响其他的模块,扩展会相对容易,这是对软件实体之间通信的限制,它要求限制软件实体之间通信的宽度和深度。主要用途在于控制信息的过载。提倡创建松耦合的类,尽量降低其成员变量和成员方法的访问权限,只要有可能,一个类应当设计成不变类。一个对象对其他的对象的引用应降到最低。
在迪米特法则中,对于一个对象,其朋友包括以下几类:
(1) 当前对象本身(this);
(2) 以参数形式传入到当前对象方法中的对象;
(3) 当前对象的成员对象;
(4) 如果当前对象的成员对象是一个集合,那么集合中的元素也都是朋友;
(5) 当前对象所创建的对象。
任何一个对象,如果满足上面的条件之一,就是当前对象的“朋友”,否则就是“陌生人”。