现在的位置: 首页 > 自动控制 > 工业·编程 > 正文

面向对象设计原则、模式开篇

2012-07-17 21:24 工业·编程 ⁄ 共 3781字 ⁄ 字号 暂无评论

    借用《设计模式精解-GoF 23种设计模式解析》的原文来表述一下:面向对象系统的分析和设计实际上追求的就是两点,一是高内聚(Cohesion),而是低耦合(Coupling)。这也是我们软件设计所准求的,因此无论是OO中的封装、继承、多态,还是我们的设计模式的原则和实例都是在为了这两个目标努力着、贡献着。设计模式体现的是一种思想,而思想则是指导行为的一切,理解和掌握了设计模式,并不是说记住了23种(或更多)设计场景和解决策略(实际上这也是很重要的一笔财富),实际接受的是一种思想的熏陶和洗礼,等这种思想融入到了你的思想中后,你就会不自觉地使用这种思想去进行你的设计和开发,这一切才是最重要的。

Rorbet C. Martin在《敏捷软件开发:原则、模式与实践》一书中提到:
个体和交互 胜过 过程和交互
可以工作的软件 胜过 面面俱到的文档
客户合作 胜过 合同谈判
响应变化 胜过 遵循计划

虽然右项也具有价值,但我们认为左项具有更大的价值

     设计模式是面向对象设计在实践中积累出来的,其追求的高内聚低耦合也必然需要遵循一些基本准则,所以总结一下ASD中提到的面向对象设计的原则:
SRP(Single Responsibility Principle):单一职责原则
对于一个类而言,应该仅有一个引起它变化的原因。
因为如果一个类承担了多于一个的职责,那么引起它变化的原因就会有多个。
如果一个类承担的职责过多,就等于把这些职责耦合在了一起。一个职责的变化可能会消弱或者会抑制这个类完成其他职责的能力。这种耦合会导致脆弱的设计,当变化发生时,设计会遭到意想不到的破坏。

OCP(Open-Closed Principle):开放封闭原则
软件实体(类、模块、函数等)应该是可以扩展的,但是不可以修改。
如果程序中的一处改动就会产生连锁反应,导致一系列相关模块的改动,那么设计就具有僵化性的臭味。如果正确的应用OCP,那么以后再进行同样的改动时,就只需要添加新的代码,而不比改动已经正常运行的代码。
遵循开放—封闭原则设计出的模块具有两个主要特征。他们是:
1. 对于扩展是开放的。
2. 对于更改是封闭的。

LSP(Liskov Substitution Principle):Liskov替换原则
子类型必须能够替换他们的基类。
LSP是OCP的基本保证。是在构建类的继承结构的过程中需要遵循的基本原则,什么时候该用,什么时候不能用,避免继承的滥用。
经典案例就是ASD中关于矩形和正方形的案例,反映了现实世界中概念和OO概念的区别,很多情况都需要在做OOD的时候仔细考虑,只有符合了LSP才可能实现OCP。因为OOD中的IS-A关系式就行为方式而言,行为方式是可以进行合理假设的,是客户程序所依赖的。

DIP(Dependency Inversion Principle):依赖倒置原则
抽象不应该依赖于细节,细节应该依赖于抽象。
高层模块不应该依赖于低层模块。二者都应该依赖于抽象。
如果高层模块独立于低层模块,那么高层模块就也可以非常容易地被重用。该原则是框架(framework)设计的核心原则。
根据这个启发式规则,可知:
1. 任何变量都不应该持有一个只想具体类的指针或者引用。
2. 任何类都不应该从具体类派生。
3. 任何方法都不应该覆写他的任何基类中的已经实现了的方法。

ISP(Interface Segregation Principle):接口隔离原则
不应该强迫客户依赖于他不是用的方法。接口属于客户,不属于他所在的类层次结构。
如果强迫客户程序依赖于那些他们不是用的方法,那么这些客户程序就面临着由于这些未是用的方法的改变所带来的变更。这无意中导致了所有客户程序之间的耦合。换种说法,如果一个客户程序依赖于一个含有他不是用的方法的类,但是其他客户程序却要使用该方法,那么当其他客户要求这个类改变时,就会影响到这个客户程序。我们希望尽可能地避免这种耦合,因此我们希望分离接口,其实就是说尽量让接口小,尽量让客户只接触到他需要的接口。

REP(Release Reuse Equivalency Principle):重用发布等价原则
重用的粒度就是发布的粒度。
重用的定义:可以重用的代码是指bug的改修和功能增加的改修的原因,代码版本要升级的场合,利用这些代码的系统不需要看具体的代码,只要适当的时机替换掉静态的库就能够正常工作。
包:是相关的类的集合,换言之一个类基本上都和其他的一些有依赖关系。因此 、发布的最小单位一般认为是一个包。

REP重用发布等价原则是针对包的设计来说的。
重用的单位和发布的单位等价
包里面包含的所有类都是可以重用的。可以重用的包中不能包含不可重用的类。因为不可重用的类参照了其他组件,包含这个类的这个包就变成不能重用了。

CCP(The Common Closure Principle):共同封闭原则
包中的所有类对于同一类性质的变化应该是共同封闭的。一个变化若对一个包产生影响,则将对该包中的所有类产生影响,而对于其他的包不造成任何影响。
即因为某个同样的原因而需要修改的所有类组合进一个包里。如果2个类从物理上或者从概念上联系得非常紧密,它们通常一起发生改变,那么它们应该属于同一个包。
CCP从软件功能的角度上为我们规范了包设计的一个原则:在设计包时,相互之间紧密关联的类应该放在同一包里。

CRP(Common Reuse Principle):共同重用原则
一个包中的所有类应该是共同重用的。如果重用了包中的一个类,那么就要重用包中的所有类。
CRP从用户观点的角度上为我们规范了包设计的原则:在设计包时,包中应该包含的元素要么都可以重用,要么都不可以重用。

ADP(Acyclic Dependencies Principle):无环依赖原则
在包的依赖关系图中不允许存在环。
包之间的依赖结构必须是一个直接的无环图形(DAG)。也就是说,在依赖结构中不允许出现环(循环依赖)。在C++中体现为头文件的循环包含。
比如A依赖B,B依赖C,C依赖A,我们修改了B并需要发布B的一个新的版本,因为B依赖C,所以发布时应该包含C,但C同时又依赖A,所以又应该把A也包含进发布版本里。也就是说,依赖结构中,出现在环内的所有包都不得不一起发布。它们形成了一个高耦合体,当项目的规模大到一定程度,包的数目变多时,包与包之间的关系便变得错综复杂,各种测试也将变得非常困难,常常会因为某个不相关的包中的错误而使得测试无法继续。而发布也变得复杂,需要把所有的包一起发布,无疑增加了发布后的验证难度。所以要避免出现环依赖。
有2种方法可以打破这种循环依赖关系:第一种方法是创建新的包,第二种方法是使用DIP(依赖倒置原则) 和ISP(接口分隔原则) 设计原则。

SDP(Stable Dependencies Principle):稳定依赖原则
朝着稳定的方向进行依赖。
一个包的抽象程度越高,它的稳定性就越高。反之,它的稳定性就越低。一个稳定的包必须是抽象的,反之,不稳定的包必须是具体的。
不稳定的(容易改变的)包处于上层 ,它们是具体的包实现
稳定的(不容易改变的)包处于下层 ,不容易改变,但容易扩展。接口比实现(具体的运行代码)在内在特性上更具有稳定性

SAP(Stable Abstractions Principle):稳定抽象原则
包的抽象程度应该和其稳定度一致。
稳定的包应该是抽象的(由抽象类或接口构成),不稳定的包应该是具体的(由具体的实现类构成)。

通常将设计模式做如下分类
1 创建型模式:创建型模式抽象了实例化过程。它们帮助一个系统独立于如何创建、组合和表示它的那些对象。一个类创建型模式使用继承改变被实例化的类,而一个对象创建型模式将实例化委托给另一个对象。
1.1 Factory模式
1.2 AbstactFactory模式
1.3 Singleton模式
1.4 Builder模式
1.5 Prototype模式

2 结构型模式:结构型模式涉及到如何组合类和对象以获得更大的结构。结构型类模式采用继承机制来组合接口或实现。结构型对象模式不是对接口和实现进行组合,而是描述了如何对一些对象进行组合,从而实现新功能的一些方法。因为可以在运行时刻改变对象组合关系,所以对象组合方式具有更大的灵活性,而这种机制用静态类组合是不可能实现的。
2.1 Bridge模式
2.2 Adapter模式
2.3 Decorator模式
2.4 Composite模式
2.5 Flyweight模式
2.6 Facade模式
2.7 Proxy模式

3 行为模式:行为模式涉及到算法和对象间职责的分配。行为模式不仅描述对象或类的模式,还描述它们之间的通信模式。这些模式刻划了在运行时难以跟踪的复杂的控制流。它们将你的注意力从控制流转移到对象间的联系方式上来。行为类模式使用继承机制在类间分派行为。行为对象模式使用对象复合而不是继承。一些行为对象模式描述了一组对等的对象怎样相互协作以完成其中任一个对象都无法单独完成的任务。
3.1 Template模式
3.2 Strategy模式
3.3 State模式
3.4 Observer模式
3.5 Memento模式
3.6 Mediator模式
3.7 Command模式
3.8 Visitor模式
3.9 Chain of Responsibility模式
3.10 Iterator模式
3.11 Interpreter模式

给我留言

留言无头像?