文章目录
- 一、单一职责原则
- 二、里式替换原则
- 三、依赖倒置原则
- 四、接口隔离原则
- 五、迪米特法则(最少知识原则)
- 六、开闭原则
一、单一职责原则
定义:应该有且仅有一个原因引起类的变更
好处:
- 降低类的复杂度;
- 提高类的可读性;
- 提高系统的可维护性;
- 变更引起的风险降低;
个人理解:就是应该把需求归类、细化。例如:现在要做一道菜,猪肉白菜炖粉条。
我们把任务细分,确保每个人都是单一职责(即每个人负责一件事),张三负责买猪肉、李四负责买白菜、王五负责买粉条、赵六复杂洗菜、吴老七负责做菜。这个每个人的任务都是明确的,哪个环节出了问题或者哪个环节需要做出细微的调整,都不会影响我们吃上猪肉白菜炖粉条。
对于类而言:我们在设计之初就应该归好类,某个类就负责处理某一类对象的行为,例如你设计了一个计算类,那它就应该只负责各式各样的计算,而不应该出现背诵《滕王阁序》、渲染图形等其它超出它范围的行为;
对于函数而言:函数尽量做到单一功能模块,不要将所有功能耦合在一起。例如需要修改图元的属性:颜色、样式、宽度等;最好每个函数负责修改其中的一项,而不是通过标志来鉴别此处修改的内容是什么;
对于接口而言:同样需要细分好接口的职责,例如一个表示人的类,它有生活接口、工作接口、学习接口,这个有条不紊就不容易出错;
二、里式替换原则
在面向对象语言中,继承是非常优秀的语言机制,它有很多语言优点:
- 代码共享;
- 减少创建类的工作量;
- 提升代码的可重用性、可扩展性和开放性;
可它也有很多弊端:
- 降低了代码的灵活性;
- 增加了父类与子类间的耦合性;
在程序设计时,如何才能让这种“利”的因素发挥到最大,同时减少“弊”的因素呢?解决方案是引入里式替换原则。
定义:所有引用基类的地方必须能透明地使用其子类对象,即程序中使用父类的地方可以替换成子类而不产生任何错误或异常,使用者根本不用知道替换的地方是子类还是父类,但是反过来不成立,有子类出现的地方,父类不一定能适应。
里式替换原则为良好的继承定义了一个规范,它主要包含四层含义:
- 子类必须完全实现父类的方法;若子类不能完整地实现父类的方法,或者父类中的某些方法在子类中发生了变化,则应该取消这种继承关系,而用依赖、聚合、组合等关系取代继承;
- 子类可以有自己的方法和属性;因为里式替换原则要求,在子类出现的地方,父类不一样能适应;
- 覆写或实现父类的方法时,输入参数可以方法范围,这样替换时不会对程序造成影响;
- 覆写或实现父类的方法时,输出结果的范围可以缩小,这样替换时也不会对程序造成影响;
三、依赖倒置原则
依赖倒置原则(Dependence Inversion Principle)包含三层含义:
- 上层模块不能依赖底层模块,两者都应该依赖其抽象;
- 抽象不能依赖细节;
- 细节应该依赖抽象。
采用依赖倒置原则:
- 可以有效减少各个类间的耦合性;
- 提高系统的稳定性
- 降低并行开发的风险
- 提升代码的可读性和可维护性。
依赖倒置原则的本质就是通过抽象使各个类或模块的实现彼此独立,不相互影响,实现模块间的松耦合,我们在项目实现的过程中,遵守以下几个规范即可:
每个类都尽量有接口或抽象类,或者两者都具备,有了抽象才可能依赖倒置;
变量的表面类型尽量是抽象类,即定义成Parent obj = new Child()的形式;
任何类型都不要从具体实现类中派生;
尽量不覆写父类的方法。若父类是一个抽象类,方法已实现好,子类尽量不要覆写,覆写了抽象方法,会对依赖的稳定性产生一定的影响;
结合里氏替换原则使用。
依赖倒置原则是六个原则中最难以实现的原则,它是实现开闭原则的重要途径。在项目开发过程中,我们要时刻谨记“面向接口编程”,这就基本抓住了依赖倒置原则的核心。
四、接口隔离原则
接口隔离原则要求接口尽量细化,接口中的方法尽量少,提供给每个模块的接口都应该是单一的,几个模块就应该有几个接口,而不是建立一个庞大臃肿的接口,让众多的模块访问。接口是我们开发设计时对外提供的契约,通过分散定义多个接口,可以预防未来变更的扩散,提高系统的灵活性和可维护性。
接口隔离原则是对接口进行规范约束,其包含以下四方面的含义:
接口要尽量”小“,这是隔离原则的核心,但”小“是有限度的,不能违背单一职责原则,这是在业务逻辑上的限定;
- 接口要高内聚,即提高类、接口、模块的处理能力,减少对外的交互。这就要求在接口中尽量少用public方法,接口是对外的承诺,承诺越少对系统的开发越有利,变更的风险也就越少,同时也有利于降低成本;
- 定制服务,即单独为一个个体提供优良的服务。我们在进行系统设计时,也需要在系统间或模块间考虑采用定制服务,即只提供访问者需要的方法,其它方法隐藏,单独为其定制服务,减少可能引起的风险。
- 接口设计是有限度的。接口设计的粒度越小,系统越灵活,这是不争的事实,可灵活的同时也带来了结构的复杂化,开发难度的增加,可维护性降低,因此接口设计一定要注意适度。
接口隔离原则是对接口和类的定义,接口和类尽量使用原子接口或原子类来组装,因此在开发实践中,我们应遵守一个接口只服务于一个子模块或业务逻辑的规则。
五、迪米特法则(最少知识原则)
最少知识原则(Least Knowledge Principle)定义为:一个对象应该对其他对象有最少的了解。更明确地说,一个类应该对自己需要耦合或调用的类知道的最少,被调用类中如何复杂与我无关,我只知道你提供的public方法。
最少知识原则对类的耦合提出了明确的要求,其包含以下四导含义:
- 只与直接耦合类沟通。直接耦合类是指出现在成员变量、成员方法的输入输出参数中的类,不包含方法体中的类。类与类之间的关系是建立在类间的,不是方法间的,因此在一个成员方法中,尽量不引入一个类中不存在的对象;
- 控制直接耦合的程度。最少知识原则要求类尽量不要对外公布太多的public方法和非静态的public变量,尽量内敛,多使用private、protected等访问权限。一个类公开的public属性或方法越多,修改时涉及的面也越大,变更时引起的风险扩散也越大,因此在开发设计时,需要反复衡量,是否可以再减少公有属性和方法,是否可以添加final关键字等;
- 自己的方法放置在自己的类。若一个方法放在本类中,既不增加类间关系,也不对本类产生负面影响,那就放在本类中;
最小知识原则的核心观念就是类间解耦,只有弱耦合了以后,类的复用率才会提高,但是解耦是有限度的,除非是计算机的最小单元——二进制才是完全解耦。在实际项目中,在使用这个原则时要反复权衡,既要做到结构清晰,又要做到高内聚低耦合。原则只是用来参考的,不遵循是不对的,严格执行就会过犹不及。
六、开闭原则
开闭原则(Open Closed Principle)定义为:一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。更明确地说,就是一个软件实体应该通过扩展来实现变化,而不是通过修改已有的代码来实现变化。开闭原则是为软件实体的未来事件而制定的对现行开发设计进行约束的一个原则。
开闭原则是一个非常虚的原则,前面5个原则都是对开闭原则的具体解释,但是开闭原则并不仅局限于此。它就像”好好学习,天天向上“的口号一样,告诉我们要好好学习,但学习什么,如何学习,需要我们自己去把握。
以上就是软件系统设计的六大原则,在软件设计的过程中,将这六大原则结合起来,才可以设计出稳定、灵活、健壮的系统。其中开闭原则是最核心、最基础的原则,它是我们系统设计的终级目标,不断朝着这个方向努力,可以非常显著地改善一个系统的架构,真正地做到”拥抱未知变化“。