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

记得毕业后刚上班不久,一个同学打电话给我求救,说他正在做笔试题,要写几个常见的Design Pattern,然后问我什么是Design Pattern,叫我赶紧告诉他几个。身为菜鸟的我,要能回答如此问题还能叫菜鸟?因此要他自己搞定,结果可想而知,他面试杯具了。我也慢慢忘了这档事。然而不久后的一天,我面试时也被问到了这个Design Pattern,考官要我说下对于Design Pattern的理解,然后讲几个常见的Pattern,我除了明白这两个单词翻译出来叫“设计模式”之外,对它一无所知,面试当然同样杯具了。在一个坑里看别人摔了一次,自己也摔了一次,我才关注这个Design Pattern是神马呢?于是百度了一下,了解了这个原来是面向对象中的一门大学问。于是马上从当当上买了号称圣经的GOF的《设计模式:可复用面向对象软件的基础》,开始啃起来。当时的水平是C++的语法还得借助教科书才说得清楚,写代码基本上是师傅手把手的教才能开笔(开键盘?),可想而知,那对我来说就是天书。于是,继续求助于网络,下了诸如《设计模式精解-GoF 23种设计模式解析附C++实现源码》,《常见设计模式的解析和实现》,《Head First设计模式》,《大话设计模式》慢慢研究起来(CSDN真是好地方,大部分计算机的电子档都可以在上面找到)。网上对于《Head First设计模式》的评价非常高,可篇幅实在太长了,我也不习惯那图文并茂的方式,可能是对老外思维模式不习惯吧,所以这本书没读几页。但对《大话设计模式》还是仔细拜读了的,感觉作者把抽象的理论用轻松诙谐的言语表达出来了,出于对作者的敬意,我还特意买了本纸版的。

 

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

俗话说,好记心不如烂笔头,曾经对设计模式的一些学习笔记和心得也做过文字记录,在以前的公司电脑硬盘中,由于信息安全,没法带出来,离职后数以万字的一些记录都随着离职而丢失了,我觉得随着丢失的不仅仅是记录本身,还有个人的精神财富(这是真心话,感觉四五年时间就积累了一点笔记)。因为零零碎碎的记录了好多东西,包括好几年的学习笔记,遇到的一些问题的解决方法,一些较常用的有用的源码。好在现在可以上网了,对于以前学过的很多东西都快忘得差不多了,于是再重温一下,并记录下来。

 

Rorbet C. Martin在《敏捷软件开发:原则、模式与实践》一书中提到:

个体和交互    胜过     过程和交互

可以工作的软件胜过     面面俱到的文档

客户合作       胜过     合同谈判

响应变化       胜过     遵循计划

虽然右项也具有价值

但我们认为左项具有更大的价值

 

设计模式是面向对象设计在实践中积累出来的,其追求的高内聚低耦合也必然需要遵循一些基本准则,所以总结一下ASD中提到的面向对象设计的原则:

1SRPSingle Responsibility Principle):单一职责原则

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

因为如果一个类承担了多于一个的职责,那么引起它变化的原因就会有多个。

如果一个类承担的职责过多,就等于把这些职责耦合在了一起。一个职责的变化可能会消弱或者会抑制这个类完成其他职责的能力。这种耦合会导致脆弱的设计,当变化发生时,设计会遭到意想不到的破坏。

 

2OCPOpen-Closed Principle):开放封闭原则

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

如果程序中的一处改动就会产生连锁反应,导致一系列相关模块的改动,那么设计就具有僵化性的臭味。如果正确的应用OCP,那么以后再进行同样的改动时,就只需要添加新的代码,而不比改动已经正常运行的代码。

遵循开放封闭原则设计出的模块具有两个主要特征。他们是:

1.       对于扩展是开放的。

2.       对于更改是封闭的。

 

3LSPLiskov Substitution Principle):Liskov替换原则

子类型必须能够替换他们的基类。

LSPOCP的基本保证。是在构建类的继承结构的过程中需要遵循的基本原则,什么时候该用,什么时候不能用,避免继承的滥用。

经典案例就是ASD中关于矩形和正方形的案例,反映了现实世界中概念和OO概念的区别,很多情况都需要在做OOD的时候仔细考虑,只有符合了LSP才可能实现OCP。因为OOD中的IS-A关系式就行为方式而言,行为方式是可以进行合理假设的,是客户程序所依赖的。

 

4DIPDependency Inversion Principle):依赖倒置原则

抽象不应该依赖于细节,细节应该依赖于抽象。

高层模块不应该依赖于低层模块。二者都应该依赖于抽象。

如果高层模块独立于低层模块,那么高层模块就也可以非常容易地被重用。该原则是框架(framework)设计的核心原则。

根据这个启发式规则,可知:

1.       任何变量都不应该持有一个只想具体类的指针或者引用。

2.       任何类都不应该从具体类派生。

3.       任何方法都不应该覆写他的任何基类中的已经实现了的方法。

 

5ISPInterface Segregation Principle):接口隔离原则

不应该强迫客户依赖于他不是用的方法。接口属于客户,不属于他所在的类层次结构。

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

 

6REPRelease Reuse Equivalency Principle):重用发布等价原则

重用的粒度就是发布的粒度。

重用的定义:可以重用的代码是指bug的改修和功能增加的改修的原因,代码版本要升级的场合,利用这些代码的系统不需要看具体的代码,只要适当的时机替换掉静态的库就能够正常工作。

包:是相关的类的集合,换言之一个类基本上都和其他的一些有依赖关系。因此、发布的最小单位一般认为是一个包。

 

REP重用发布等价原则是针对包的设计来说的。

重用的单位和发布的单位等价

包里面包含的所有类都是可以重用的。可以重用的包中不能包含不可重用的类。因为不可重用的类参照了其他组件,包含这个类的这个包就变成不能重用了。

 

7CCPThe Common Closure Principle):共同封闭原则

包中的所有类对于同一类性质的变化应该是共同封闭的。一个变化若对一个包产生影响,则将对该包中的所有类产生影响,而对于其他的包不造成任何影响。

即因为某个同样的原因而需要修改的所有类组合进一个包里。如果2个类从物理上或者从概念上联系得非常紧密,它们通常一起发生改变,那么它们应该属于同一个包。

CCP从软件功能的角度上为我们规范了包设计的一个原则:在设计包时,相互之间紧密关联的类应该放在同一包里。

 

8CRPCommon Reuse Principle):共同重用原则

一个包中的所有类应该是共同重用的。如果重用了包中的一个类,那么就要重用包中的所有类。

CRP从用户观点的角度上为我们规范了包设计的原则:在设计包时,包中应该包含的元素要么都可以重用,要么都不可以重用。

 

9ADPAcyclic Dependencies Principle):无环依赖原则

在包的依赖关系图中不允许存在环。

包之间的依赖结构必须是一个直接的无环图形(DAG)。也就是说,在依赖结构中不允许出现环(循环依赖)。在C++中体现为头文件的循环包含。

比如A依赖BB依赖CC依赖A,我们修改了B并需要发布B的一个新的版本,因为B依赖C,所以发布时应该包含C,但C同时又依赖A,所以又应该把A也包含进发布版本里。也就是说,依赖结构中,出现在环内的所有包都不得不一起发布。它们形成了一个高耦合体,当项目的规模大到一定程度,包的数目变多时,包与包之间的关系便变得错综复杂,各种测试也将变得非常困难,常常会因为某个不相关的包中的错误而使得测试无法继续。而发布也变得复杂,需要把所有的包一起发布,无疑增加了发布后的验证难度。所以要避免出现环依赖。

2种方法可以打破这种循环依赖关系:第一种方法是创建新的包,第二种方法是使用DIP(依赖倒置原则)和ISP(接口分隔原则)设计原则。

 

10SDPStable Dependencies Principle):稳定依赖原则

朝着稳定的方向进行依赖。

一个包的抽象程度越高,它的稳定性就越高。反之,它的稳定性就越低。一个稳定的包必须是抽象的,反之,不稳定的包必须是具体的。

不稳定的(容易改变的)包处于上层,它们是具体的包实现

稳定的(不容易改变的)包处于下层,不容易改变,但容易扩展。接口比实现(具体的运行代码)在内在特性上更具有稳定性

 

11SAPStable Abstractions Principle):稳定抽象原则

包的抽象程度应该和其稳定度一致。

稳定的包应该是抽象的(由抽象类或接口构成),不稳定的包应该是具体的(由具体的实现类构成)。

 

12CARPComposite/Aggregate Reuse Principle):尽量使用组合/聚合,尽量不要使用类继承。

原因:对象的继承关系是在编译时就定义好了,所以无法再运行时改变从父类继承的实现。子类的实现与它的父类有非常紧密的依赖关系,以至于父类实现中的任何变化必然会导致子类发生变化。当你需要复用子类时,如果继承袭来的实现不适合解决新的问题,则父类必须重写或被其他更适合的类替换。这种依赖关系限制了灵活性并最终限制了复用性。

有限使用对象的组合/聚合将有助于你保持每个类被封装,并被集中在单个任务上。这样类和类继承层次会保持较小规模,并且不太可能增长为不可控制的庞然大物。

 

13Lod:迪米特法则,也叫最少知识原则

如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。

迪米特法则首先强调的前提是在类的结构设计上,每一个类都应当尽量降低成员的访问权限。

 

通常将设计模式做如下分类:

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模式

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值