面向对象设计原则
1. 单一职责原则(Single Responsibility Principle,SRP)
单一职责原则:一个对象应该只包含单一的职责,并且该职责被完整地封装在一个类中。就一个类而言,应该仅有一个引起它变化的原因。
优点:
- 类的复杂性降低,实现什么职责都有清晰明确的定义;
- 可读性提高;
- 可维护性提高;变更引起的风险降低;
- 单一职责适用于接口设计、类的设计、方法的设计。
2. 开闭原则(Open-Closed Principle,OCP)
开闭原则:是面向对象设计的可复用设计的第一块基石,是最重要的面向对象设计原则,开闭原则应该对软件实体(类、模块、函数等)可以扩展,但是不能修改。
任何系统一开始的需求都做不到考虑以后的任何变化,怎样的设计才能面对需求的改变却可以保持相对稳定,从而使得系统可以在第一个版本后不断推出新的版本呢?
3. 里氏代换原则(Liskov Substitution Principle,LSP)
继承优点:
- 代码共享、减少创建类的工作量,每个子类拥有父类的方法和属性;
- 提高代码复用; 子类可以形似父类,但又异于父类;
- 提高代码可扩展性,很多开源框架的扩展接口都是通过继承父类完成;
- 提高产品或者项目的开放性。
继承缺点(鸡蛋挑骨头):
- 继承是侵入式的。只要继承,就必须拥有父类的所有属性和方法;
- 降低代码的灵活性。子类必须拥有父类的属性和方法,让子类多了些约束。
- 增强了耦合性。当父类的常量、变量和方法被修改时,需要考虑子类的修改。
- Java单一继承,C++多重继承。
里氏代换原则:如果对每一个类型为S的对象o1,都有类型为T的对象o2,使得以T定义的所有程序P在所有的对象o1都代换成o2时,程序P的行为没有发生变化,那么类型S是类型T的子类型。所有引用基类的地方必须能透明地使用其子类的对象。
运用时应该将父类设计为抽象类或者接口,让子类继承父类或者实现父接口,并实现父类中的方法。在程序中尽量使用基类类型来对对象进行定义,而在运行时再确定其子类类型,用子类对象替换父类对象。
4. 依赖倒转原则(Dependency Inversion Principle,DIP)
依赖倒转原则:抽象不应该依赖于细节,细节应该依赖于抽象。(Java中,抽象就是接口或者抽象类,两者都是不能实例化的;细节就是实现类,可以直接实例化,就是new产生一个对象。)
Java中依赖倒置原则就是:
- 模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或者抽象类产生的;
- 接口或者抽象类不依赖于实现类;
- 实现类依赖接口或者抽象类。
依赖正转就是类间的依赖是实实在在的实现类之间的依赖,也就是面向实现编程,也是传统的思维方式。
注意:依赖倒转原则:针对接口编程,不要针对实现编程在程序代码中传递参数时或在关联关系中,尽量引用层次高的抽象层类,即使用接口和抽象类进行变量类型声明、参数类型声明、方法返回类型声明,以及数据类型的转换等,不要用具体类来做。
在程序中尽量使用抽象层进行编程,而将具体类写在配置文件中
针对抽象层编程,将具体类的对象通过依赖注入(Dependency Injection, DI)的方式注入到其他对象
- 构造注入
- 设值注入(Setter注入)
- 接口注入
5. 接口隔离原则(Interface Segregation Principle,ISP)
接口隔离原则:客户端不应该依赖那些它不需要的接口。类间的依赖关系应该建立在最小的接口上。
接口细化:当一个接口太大时,需要将它分割成一些更细小的接口;
使用该接口的客户端仅需知道与之相关的方法即可;
每一个接口中只包含一个客户端所需的方法,承担一种相对独立的角色,不干不该干的事,该干的事都要干;
与单一职责角度不同:
- 单一职责要求类和接口职责单一,注重业务逻辑划分的职责
- 接口隔离要求接口的方法尽量少
接口隔离原则是对接口进行规范:4层含义
- 接口要尽量小,不臃肿。但是拆分接口时,首先还得满足单一职责。比如业务逻辑上不可分割的。
- 接口要高内聚。提高接口、类、模块的处理能力,减少对外的交互。
- 定制服务。系统设计时考虑为访问者(客户端),提供单独的服务。
- 接口的设计是有限度的。设计粒度越小,系统越灵活。但是也带来结构复杂化,开发难度增加等。把握好“度”。
6. 合成复用原则(Composite Reuse Principle,CRP)
合成复用原则:优先使用对象组合,而不是通过继承来达到复用的目的。
合成复用原则就是在一个新的对象里通过关联关系(包括组合关系和聚合关系)来使用一些已有的对象,使之成为新对象的一部分
新对象通过委派调用已有对象的方法达到复用功能的目的
复用时要尽量使用组合/聚合关系(关联关系),少用继承
聚合(Aggregation):是关联关系的一种,用来表示一种整体和部分的拥有关系。整体持有对部分的引用,可以调用部分的能够被访问的方法和属性等,这种访问往往是对接口和抽象类的访问。作为部分可以同时被多个新的对象引用,同时为多个新的对象提供服务。代码中通过实例化变量实现。
组合(Composition):也是关联关系的一种,比聚合关联程度更强。合成关系中,部分和整体的生命周期是一样的。作为整体的新对象完全拥有对部分的支配权,包括负责和支配部分的创建和销毁等,即要负责部分的内存分配的释放等。代码中也通过实例化变量实现,通过语义与聚合区分。
OOP设计中,两种基本方法在不同的环境中复用已有的设计和实现:组合/聚合关系或者继承。
- 继承复用:实现简单,易于扩展。破坏系统的封装性;从基类继承而来的实现是静态的,不可能在运行时发生改变,没有足够的灵活性;只能在有限的环境中使用。(“白箱”复用 )
- 组合/聚合复用:耦合度相对较低,有选择性地调用成员对象的操作;可以在运行时动态进行,新对象可以动态地引用与成员对象类型相同的其他对象。(“黑箱”复用 )
合成/聚合复用的优点:
- 该复用支持封装;
- 该复用所需的依赖较少;
- 每个新的任务可将焦点集中在一个任务上。
合成/聚合复用的缺点:
- 通过这种复用建造的系统会有较多的对象需要管理;
- 为了能将多个不同的对象作为组合块来使用,必须仔细地对接口进行定义。
要正确的选择合成/复用和继承,必须透彻地理解里氏替换原则和Coad法则。(Peter Coad提出的Coad法则:只有当以下Coad条件全部被满足时,才应当使用继承关系进行复用)
- 子类是基类的一个特殊种类,而不是基类的一个角色。区分“Has-A”和“Is-A”。只有“Is-A”关系才符合继承关系,“Has-A”关系应当用聚合来描述。
- 永远不会出现需要将子类换成另外一个类的子类的情况。如果不能肯定将来是否会变成另外一个子类的话,就不要使用继承。
- 子类具有扩展基类的责任,而不是具有置换掉(override)或注销掉(Nullify)基类的责任。如果一个子类需要大量的置换掉基类的行为,那么这个类就不应该是这个基类的子类。
- 只有在分类学角度上有意义时,才可以使用继承。不要从工具类继承。
7. 迪米特法则(Law of Demter,LoD)
迪米特法则: 每一个软件单位对其他的单位都只有最少的知识,而且局限于那些与本单位密切相关的软件单位。
- 迪米特法则要求一个软件实体应当尽可能少地与其他实体发生相互作用
- 一个对象应该对其他对象有最少的了解。
- 一个类对其他自己需要耦合或者调用的类知道的尽量少,只管调用其方法即可。
- 应用迪米特法则可降低系统的耦合度,使类与类之间保持松散的耦合关系
- 迪米特法则根本思想强调类之间的松耦合。
- 类的划分应该尽量创建松耦合的类,类之间耦合度越低,越有利于复用。
- 松耦合中的类被修改时,不会对被关联的类造成太多影响。
- 类的结构设计上,尽量降低其成员变量和成员函数的访问权限。
本文来自博客园,作者:yuxin!!,转载请注明原文链接:https://www.cnblogs.com/yuxin6924/p/17298653.html

浙公网安备 33010602011771号