面向对象设计原则-《敏捷软件开发》笔记

yanglin 2011-04-14

1、单一职责原则(SRP):

     就一个类而言,应该仅有一个引起它变化的原因。

       我们在编程的时候,经常会给一个类添加各种各样的功能,如果一个类承担的职责过多,就相当于把这些职责耦合在了一起,一个职责的变化可能会削弱或者一直这个类完成其他职责的能力,这种耦合会导致脆弱的设计。在SRP中,将职责定义为“引起变化的原因”。如果有多于一个的动机改变一个类,这个类就有多于一个职责,这时候就应该考虑类的职责分离。

        在设计中为每一种职责设计一个类,SRP是最简单、也是需要考虑最多的原则,实际上,软件设计真正要做的许多内容,就是发现职责并把那些职责相互分离。

2、开放-封闭原则(OCP)

     软件实体(类、模块、函数等等)应该是可以扩展的,但是不可修改的。

     开放-封闭原则有两个主要的特征:

     对于扩展是开放的,这意味着模块的行为是可以扩展的。当应用的需求改变时,我们可以对模块进行扩展,使其具有满足那些改变的新行为。换句话说,我们可以改变模块的功能

        对于更改是封闭的,对模块行为扩展时,不必改动模块的源代码或者二进制代码。模块的二进制可执行版本,无论是可连接的库、DLL或者java的.jar文件,都无需改动。

         在C++、Java或者其他的OOPL中,可以创建出固定的却能够描述一组任意个可能行为的抽象体、这个抽象体就是抽象基类,而这一组任意个的行为则表现为可能的派生类。模块可以操作一个抽象体,由于模块依赖于一个固定的抽象体,所以它对于更改可以是关闭的。同时,通过从这个抽象体派生,也可以扩展此模块的行为。OCP的通常做法是,首先定义抽象实体,在面对需求的时候,增加新代码派生该实体,而不是更改现有的代码,这就是“开放-封闭原则”的精神所在。这样就不会引起连锁的改动,同时扩展了模块的行为。

         但是可能会有这样的疑问,那我如何定义抽象体来保证封闭呢。答案是,绝对的对修改关闭时不可能的。无论模块多么的“封闭”,都会存在一些无法对峙封闭的变化。既然不可能完全封闭,设计人员必须对于他设计的模块应该对哪种变化封闭做出选择。他必须先猜出最有可能发生的变化种类,然后构造抽象来隔离这些变化。创建正确的抽象是要花费开发时间和精力的。同时,这些抽象也增加了软件设计的复杂性。

         开发-封闭原则是面向对象设计的核心所在。遵循这个原则可以带来面向对象技术所声称的巨大好处,也就是可维护、可扩展、可复用、灵活性好。开发人员应该仅对程序中呈现出频繁变化的那些部分做出抽象,然而,对于应用程序中的每个部分都刻意地进行抽象同样不是一个好主意。拒绝不成熟的抽象和抽象本身一样重要。

3、依赖倒置原则(DIP)

       a、高层模块不应该依赖于底层模块,两个都应该依赖于抽象

       b、抽象不应该依赖于细节。细节应该依赖抽象

        在传统的软件开发中,比如结构化的分析和设计,总是倾向于创建一些高层模块依赖于底层模块、策略(policy)依赖于细节的软件结构。实际上这些方法的目的之一就是要定义子程序层次结构,该层次结构描述了高层模块怎样调用底层模块。一个设计良好的面向对象的程序,其依赖程序结构相对于传统的过程式方法设计的通常结构而言就是被“倒置”了。该原则是框架设计的核心原则。

       所有结构良好的面向对象架构都具有清晰的层次定义,每个层次通过一个定义良好的、受控的接口向外提供了一组内聚的服务。这里的倒置不仅仅是依赖关系的倒置,它是接口所有权的倒置。我们通常会认为底层应该拥有它们自己的接口。但是在DIP中,我们发现往往是高层拥有抽象接口,而底层则派生这些抽象接口。通过这种倒置的接口所有权,底层的任何改动都不会再影响到高层。

         依赖倒置其实可以说是面向对象设计的标志,用哪种语言来编写程序不重要,如果编写时考虑的都是如何针对抽象编程而不是针对细节编程,即程序中所有的依赖关系都是终止于抽象类或者接口,那就是面向对象的设计,反之那就是过程化的设计。

4、里氏代换原则(LSP)

     子类型必须能够替换它们的父类型

       一个软件实体如果使用的是一个父类的话,那么一定适用于其子类,而且它察觉不出父类对象和子类对象的区别。也就是说,在软件里面,把父类都替换成它的子类,程序的行为没有变化。

       正是里氏代换原则,使得继承复用成为了可能,只有当子类可以替换成父类,软件单位的功能不受到影响时,父类才能真正被复用,而子类也能够在父类的基础上增加新的行为。也正是由于子类型的可替换性才使得使用父类类型的模块

5、接口隔离原则(ISP)

       不应该强迫客户端依赖于他们不会使用的接口

       在设计应用程序的时候,如果一个模块包含多个子模块,那么我们应该小心对该模块做出抽象。设想该模块由一个类实现,我们可以把系统抽象成一个接口。但是当我们想要添加一个新的模块扩展程序时,如果要添加的模块只包含原系统的一些子模块,那么就会强迫我们实现接口中的所有方法,而且还要编写一些unsupported方法。这样的接口被称为胖接口或者叫污染的接口,使用这样的接口将会给系统引入不正确的行为。

       接口隔离原则则表明客户端不应该强迫实现一些他们不会使用的接口,应该把胖接口的方法分组,然后用多个接口代替它,每个接口服务于一个子模块。

相关推荐