软件设计原则5+1
1. 单一职责
定义:Single Responsibility Principle,SRP,单一职责原则规定一个类应该有且仅有一个引起它变化的原因,否则类应该被拆分(There should never be more than one reason for a class to change)
作用:控制类的粒度大小、将对象解耦、提高其内聚性;降低类的复杂度,提高可读性;降低风险,提到可维护性;
实现方法:分析类的不同职责并将其分离,再封装到不同的类或者模块中。
类的划分原则同样适用于方法,即C语言中的函数定义。
2. 开闭原则
定义:Open Closed Principle,OCP,软件实体应当对扩展开放,对修改关闭(Software entities should be open for extension,but closed for modification)。
作用:使软件实体在拥有一定的适应性和灵活性的同时具备稳定性和灵活性;减少测试任务,仅测试新增代码即可;踢桃代码的可复用性;易于扩展和维护。
实现方法:抽象约束,封装变化。
(1)通过接口或者抽象类为软件实体定义一个相对稳定的抽象层,将相同的可变因素封装在相同的具体实现类中。
(2)C语言中常见的函数指针数组,可以作为典型实例。通过抽象接口实现上层功能后,在函数指正数组中确认具体的回调函数;后续扩展新功能,不需要修改抽象层,仅需要在数组中增加相关实现即可。
3. 里氏替换
定义:Liskov Substitution Principle,LSP,继承必须确保超类所拥有的性质在子类中仍然成立(Inheritance should ensure that any property proved about supertype objects also holds for subtype objects)。
作用:实现开闭原则的必要条件之一;动作正确性的保证,继承时不会引入新错误,降低需求引入的风向;加强程序的健壮性和兼容性。
实现:子类可以扩展父类的功能,但不能改变父类原有的功能。
(1)阐述继承的使用场景,子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法;
(2)C语言中的继承特性不明显,可以使用结构体嵌套定义实现,但是调用不方便;使用宏定义实现父类和子类,可以较友好的实现继承特性;
4. 接口隔离
定义:Interface Segregation Principle,ISP,一个类对另一个类的依赖应该建立在最小的接口上(The dependency of one class to another one should depend on the smallest possible interface)。
要求尽量将臃肿庞大的接口拆分成更小的和更具体的接口,让接口中只包含客户感兴趣的方法;要为各个类建立它们需要的专用接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。单一职责注重的是函数功能,接口隔离注重的是对接口依赖的隔离;单一职责约束类中方法实现和细节,接口隔离原则约束接口本身,针对抽象和程序整体框架的构建。
作用:高内聚,低耦合;提到系统的灵活性和可维护性;接口粒度大小合理,保证系统的稳定性;专门的接口可以提箱对象的层次,减少工程中的代码冗余(过大的接口里面放置许多不用的方法)
实现:接口大小设计合理,过小使设计复杂化,过大降低灵活性;定制服务,仅提供需要使用的方法,屏蔽不需要使用的方法;了解业务环境;提高内聚,减少对外交互。
(1)C语言中,针对这个部分,可以体现在对外接口功能和结构体的设计上,需要注意对外接口功能的独立性;结构体的设计也不宜过大,否则会造成较多冗余。
5. 依赖倒置原则
定义:Dependence Inversion Principle,DIP, 高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象(High level modules shouldnot depend upon low level modules.Both should depend upon abstractions.Abstractions should not depend upon details. Details should depend upon abstractions)。抽象指的是接口或者抽象类,而细节是指具体的实现类。核心思想是:要面向接口编程,不要面向实现编程。
作用:降低类间的耦合性,提高系统的稳定性,降低并行开发风险,提高代码的可读性和可维护性。
实现:要面向接口的编程
(1)变量的声明类型尽量是接口或抽象类,任何类不应从具体的类派生;
(2)在C中,该原则同样适用,可见其定义。利用抽象层,将高层设计和低层时间进行解耦。
6. 迪米特法则
定义:Law of Demeter,LoD或者Least Knowledge Principle,LKP,只与你的直接朋友交谈,不跟“陌生人”说话(Talk only to your immediate friends and not to strangers)。如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。其目的是降低类之间的耦合度,提高模块的相对独立性。
作用:限制软件实体之间通信的宽度和深度;降低类之间的耦合性,提高模块的独立性;提高类的可复用率和系统的扩展性。
实现:依赖最少的对象,暴露最少的方法。——少即是多。
(1)类的设计上,降低类成员的访问权限,优先考虑设计成不变类;
(2)降低对其他类对象的引用次数;也避免暴露类的属性成员;
(3)在C中,类实际上更模块库类似,仅提供有限的方法,也极少的依赖其他模块的接口,而模块库的引用,基本上就类似于类的继承和封装了,只不过颗粒比较大而已。
7. 总结
(1)设计原则,是通用设计方法的基本准则,不能仅局限与面对对象,在主要面向过程设计的C语言中,也基本上都有对应部分,甚至单个函数的设计,也尽量满足以上原则;
(2)面对对象的特性:继承与封装,C语言中小颗粒的可以用结构体和宏定义实现,大颗粒的可以用头文件和动态库实现。
(3)开闭原则是其核心,其他原则基本上是为其服务。
(4)另外还有个原则,也被提到:合成复用原则,Composite Reuse Principle,CRP,要求在软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。