OO设计原则:
在程序设计领域,面向对象设计的设计原则有封装变化,面向接口而不是实现,优先使用组合而非继承,具体分为五个基本原则:单一功能原则SRP,开闭原则OCP,里氏替换原则LSP,接口隔离原则ISP,依赖反转原则DIP.
1>单一功能原则SRP:
单一功能原则SRP规定每个类都应该有一个单一的功能,并且该功能应该有这个类完全封装起来,所以这个类的服务都应该严密的和该功能平行(功能平行意味着没有依赖).
2>开闭原则OCP:
开闭原则OCP规定软件中的对象(类,模块,函数等)应该对于扩展是开放的,但是对于修改是封闭的,这意味着一个实体是允许在不改变它的源代码的前提下变更它的行为. 开闭原则的命名被应用在两种方式上.这两种方式都是用了继承来解决问题,但是它们的目的,技术以及结果是不同的.
2.1>梅耶开闭原则:
该原则规定一个类的实现只应该因错误而修改,新的或者改变的特性应该通过新建不同的类实现.新建的类可以通过继承的方式来重用原类的代码.衍生的子类可以或不可以拥有和原类相同的接口.
梅耶的定义提倡实现继承.具体实现可以通过继承的方式重用,但是接口规格不必如此.已存在的实现对于修改是封闭的,但是新的实现不必实现原有的接口.
2.2>多态开闭原则:
该原则用于抽象化接口的使用,在这中间实现可以被改变,多种实现可以被创建,并且多态化得替换不同的实现.
相比梅耶的使用方式,多态开闭原则的定义倡导对抽象基类的继承.接口规约可以通过继承来重用,但是实现不必重用.与存在的接口对于修改时封闭的,并且新的实现
必须,至少,实现那个接口.
3>里氏替换原则LSP:
该原则是对子类型的特别定义,派生类对象能够替换其基类对象被使用.
4>接口隔离原则ISP:
多个专门的接口优于单一的总接口.
如果一个类向多个客户端提供服务,与其在这个类中加载所有的服务方法,不如为每个特定的客户端创建一个特定的接口,然后将该类多继承与这写接口.
图4.1展示了传统的设计所有一个服务类加载的客户端所需要的所有方法.这样做破坏了OCP原则,因为如果要条件新的方法,必须重新修改这个服务类.
图4.1
图4.2展示了符合OO设计原则的设计,如果有新的类型的客户端,那么只需要为该类型的客户端派生新的接口即可.
图4.2
ISP原则不是要求每个使用某种服务的类创建一个特定的接口然后服务类继承与这个接口,而是客户端应该按照它们的类型被分类,然后对于每个类型创建接口.如果两个或者多个客户端类型需要同一个方法,那么这个方法应该被加入到每一个接口之中.这是合理的.
5>依赖反转DIP:
该原则指定了一种特定的解耦(传统的依赖关系建立在高层次上,而具体的策略设置则应用在低层次的模块上)形式.在这种形式下,为了使得高层次的模块不依赖与低层次的模块的实现细节的目的,依赖模块被颠倒了.该原则规定:
1.高层次的模块不应该依赖于低层次的模块,两者都应该依赖于抽象接口.
2.抽象接口不应该依赖于具体实现.而具体实现则应该依赖于抽象接口.
抽象不应该依赖于细节,细节应该依赖于抽象,另一种表述:要针对接口编程,不要针对实现编程.依赖倒置式依赖于接口或者抽象函数和类而不是具体的函数和类.
DIP原则的动机之一就是阻止依赖于可变的模块,DIP原则假设具体的实现都是可变的,而抽象的接口很少变化.而且抽象的接口代表了设计之中能够被扩展并且是不可修改的部分(OCP原则).
传统的应用架构中(图5.1),低层次的组件设计被高层次的组件使用,这一点提供了逐步构建一个复杂系统的可能.在这种结构下,高层次的组件直接依赖于低层次的组件去实现一些任务.这种对于低层次组件的依赖限制了重用高层次组件的可能性.
图5.1
依赖反转原则(图5.2所示结构)的目的在于把高层次组件从低层次组件中解耦出来,这样使得重用不同低层组件实现变得可能.把高层组件和低层组件分划到不同的包/库(这些包/库中定义了高层组件所必须的行为和服务接口,并且存在高层组件的包)中的方式促进了这种解耦.由低层组件对于高层组件接口的具体实现要求低层组件包的编译是依赖于高层组件的,因此颠倒了传统的依赖关系.众多的设计模式,比如,插件,服务定位器或者依赖反转,则被用来在运行时把指定的低层组件实现提供给高层组件.
图5.2
应用依赖反转原则同样被认为是应用了适配器模式,例如:高层的类定义了它自己的适配器接口(高层类所依赖的抽象接口).被适配的对象同样依赖于适配器接口的抽象,同时它的实现则可以使用它自身所在低层模块的代码.通过这种方式,高层组件则不依赖于低层组件,因为它(高层组件)仅间接地通过调用适配器接口多态方法的方式使用了底层组件通过适配器接口,在这些多态方法则是被适配对象以及它的低层模块所实现的.