设计模式学习之遵循原则

来源:互联网 发布:mmd美腿战队数据 编辑:程序博客网 时间:2024/06/05 01:10
设计模式对理解面向对象的三大特征有很好的启发,不看设计模式,很难深层地体会到面向对象开发带来的好处

在刚开始学习中,很难做到将这些模式融汇贯通,所以这个需要我们在编码前多思考,等想充分了,在开始实践编码,就像一篇作文一样,如果思维信马由缰,想到哪写到哪,那将很容易偏离主题,显得整篇文章混乱不堪。可以看到现在很多考试,真正让考生去写的并不多,重点都放到考察考生的思维过程中去了。所以在做任何一件事情前,总体设计(即作文构思)要充分考虑,细节可以先不用管,就像盗梦空间,刚开始规划时,就是三层梦境,每一层做哪些事,什么时候进入第二层梦境,由哪些人进入,这些总体构思是不变的,当意外出现时(如投资者中了枪,这是事先入梦境前无法遇知的),就像软件过程中遇到客户提的新的需求,此时设计要能灵活应变,动态扩展,程序才算是健壮的。

1.单一职责原则
定义:就一个类而言,应该仅有一个引起它变化的原因
软件设计真正要做的许多内容,就是发现职责并把那些职责相互分离。如果你能够想到多于一个的动机去改变一个类,那么这个类就具有多于一个的职责,就应该考虑类的职责分离。如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或者抑止这个类完成其他职责的能力,这种耦合会导致脆弱的设计,当变化发生时,设计会遭受到意不到的破坏

2.开放-封闭原则

我们在做任何系统的时候,都不要指望系统一开始时需求确定,就再也不会变化,这是不现实也不科学的想法,既然需求是一定会变化的,那么如何在面对需求的变化时,设计的软件可以相对容易修改,不至于说,新需求一来,就要把整个程序推倒重来。怎样的设计才能面对需求的改变却可以保持相对稳定,从而使得系统可以在第一个版本以后不断推出新的版本呢?开放-封闭原则给我们答案

开放-封闭原则的意思就是说,你设计的时候,时刻要考虑,尽量让这个类是足够好,写好了就不要去修改了,如果新需求来,我们增加一些类就完事了,原来的代码能不动则不动。这个原则有两个特性,一个是说“对于扩展是开放的”,另一个是说“对于更改是封闭的”。面对需求,对程序的改动是通过增加新代码进行的,而不是更改现有的代码。这就是“开放-封闭原则”的精神所在

比如,刚开始需求只是写加法程序,很快在client类中完成后,此时变化没有发生,需求让再添加一个减法功能,此时会发现增加功能需要修改原来这个类,这就违背了开放-封闭原则,于是你就应该考虑重构程序,增加一个抽象的运算类,通过一些面向对象的手段,如继承、动态等来隔离具体加法、减法与client耦合,需求依然可以满足,还能应对变化。此时需求要添加乘除法功能,就不需要再去更改client及加减法类,而是增加乘法和除法子类即可。


绝对的修改关闭是不可能的,无论模块是多么的‘封闭‘,都会存在一些无法对之封闭的变化,既然不可能完全封闭,设计人员必须对于他设计的模块应该对哪种变化封闭做出选择。他必须先猜测出最有可能发生的变化种类,然后构造抽象来隔离那些变化。在我们最初编写代码时,假设变化不会发生,当变化发生时,我们就创建抽象来隔离以后发生同类的变化。

我们希望的是在开发工作展开不久就知道可能发生的变化,查明可能发生的变化所等待的时候越长,要创建正确的抽象就越困难。开放-封闭原则是面向对象设计的核心所在,遵循这个原则可以带来面向对象技术所声称的巨大好处,也就是可维护、可扩展、可复用、灵活性好。开发人员应该仅对程序中呈现出现频繁变化的那些部分做出抽象,然而对于应用程序中的每个部分都刻意地进行抽象同样不是一个好主意,拒绝不成熟的抽象和抽象本身一样重要。

开放-封闭原则,可以保证以前代码的正确性,因为没有修改以前代码,所以可以保证开发人员专注于将设计放在新扩展的代码上。

开放-封闭原则,其实在实际生活中也得到了体现。过去的事已成历史,是不可修改的,因为时光不可倒流,但现在或明天计划做什么,是可以自己决定(即扩展)的。学习任何一个东西,要做到理论联系实际,实际反馈理论,举一反三,这样才能不自觉地在生活中灵活运用它们。解决问题没有万能的方法,因为每种模式都有其优缺点,有其适应场合,切忌生搬硬套,问题是在动态中产生的,所以解决方案也是动态的产生这样才能适应,这其实也是哲学领域的一个辩证思维,所以要经常打破一成不变的思维,结合具体实际问题分析采用何种模式

3.依赖倒转原则
定义:抽象不应该依赖细节,细节应该依赖抽象,高层模块不能依赖低层模块,两者都应该依赖抽象,即要针对接口编程,不要对实现编程。
例子:无论主板、CPU、内存、硬盘都是针对接口设计的,如果针对实现来设计,内存就要对应到具体的某个品牌的主板,那就会出现换内存需要把主板也换了的尴尬。CPU的对外都是针脚式或触点式等标准的接口,CPU只需要把接口定义好,内部再复杂也不让外界知道,而主板只需要预留与CPU针脚的插槽就可以了。这样如果CPU坏了,只需要换CPU就行了,和其它硬件没什么关系,这就是使用接口带来的最大好处。PC电脑里的热插拔可以看作是开放-封闭原则的体现,所以说,PC电脑硬件的发展,和面向对象思想发展是完全类似的。这也说明世间万物都是遵循某种类似的规律,谁先把握了这些规律,谁就最早成为了强者。

依赖倒转其实就是谁也不要依靠谁,除了约定的接口,大家都可以灵活自如。依赖倒转可以说是面向对象设计的标志,用哪种语言来编写程序不重要,如果编写时考虑的都是如何针对抽象编程而不是针对细节编程,即程序中所有的依赖关系都是终止于抽象类或者接口,那就是面向对象的设计,反之那就是过程化的设计了。如果设计的各个部件或类相互依赖,这样就是耦合度高,难以维护和扩展,这也就体现不出面向对象的好处了

依赖倒转原则,好比一个团队,有需求组,开发组,测试组,开发组和测试组都是面对同样的需求后,做自己相应的工作,而不应该是测试组按照开发组理解的需求去做测试用例,也就是说开发组和测试组都是直接面向需求组工作,大家的目的是一样的,保证产品按时上线,需求是不依赖于开发和测试的。

4.里氏代换原则
第三点提到用依赖倒转原则,那为什么依赖了抽象的接口或抽象类,就不怕更改呢?这里就要由里氏代换原则来做保证

定义:子类型必须能够替换掉它们的父类型。
描述:一个软件实体如果使用的是一个父类的话,那么一定适用于其子类,而且它察觉不出父类对象和子类对象的区别,也就是说,在软件里面,把父类都替换成它的子类,程序的行为没有变化
例子:在生物学分类上,企鹅是一种鸟,但在编程世界里,企鹅却不能继承鸟。在面向对象设计时,子类拥有父类所有非private的行为和属性,鸟会飞,但企鹅不会飞,所以企鹅不能继承鸟类。

只有当子类可以替换掉父类,软件单位的功能不受影响时,父类才能真正被复用,而子类也能够在父类的基础上增加新的行为,正是有里氏代换原则,使得继承复用成为了可能。正是由于子类型的可替换性才使得使用父类类型的模块在无需修改的情况下就可以扩展,不然还谈什么扩展开放,修改关闭呢

5.迪米特原则
如果两个类不直接通信,那么这两个类就不应当发生直接的相互作用,如果一个类需要调用另一个类的某个方法的话,可以通过第三个类转发这个调用。这个原则是依赖倒转原则的一个体现,即面向接口编程
在类的结构设计上,每一个类都应该尽量降低成员的访问权限。迪米特法则其根本思想,是强调了类之间的松耦合,类之间的耦合越弱,越有利于复用,一个处在弱耦合的类被修改,不会对有关系的类造成影响,也就是说,信息的隐藏促进了软件的复用。