说说设计原则

来源:互联网 发布:奇爱博士讲电影知乎 编辑:程序博客网 时间:2024/04/30 01:41

  面向对象的设计,核心是设计模式和设计原则。

  什么是设计模式?简单来说就是,针对反复出现的问题的经典解决方案。

设计模式小史

  在20世纪80年代中期,Kent Beck首先提出了软件命名模式的思想。

  大约在199410月,GOFGamma, Helm, JohnsonVlissides)根据前辈们的经验,总结出23OO设计模式,其中最常用和有效的模式大概有15个。

  2000年的时候Linda Rising写了一本书《Pattern Almanac 2000》,其中列出了大约500种设计模式。

  此外,每年都会举行“Pattern Languages Of Programs”(PLOP)会议,算上2011年,该会议已经举办了18次,会议出版模式的年度纲要《Pattern-Oriented SoftwareArchitecture》,包括卷1和卷2等。

  问题是什么?

  模式太多了,对有经验的设计者来说,详细了解和记住50种以上的模式非常重要,但要记住1000种模式,还是比较困难,如果把时间花在记住模式上面,我们都没时间去编程了。

  有人提出了解决方案,找到根本原则就行了,不必拘泥于模式。根据OO设计原则所创造出来的程序最终自然而然的趋向于模式。

设计原则总结

  Robert C.Martin在他的《AgileSoftware Development》中提到了11OO设计原则。

  S.O.L.I.D.原则:

  1.       SRP,单一职责:就一个类而言应该仅有一个引起它变化的原因。职责太多,类就不好理解,不容易维护,不利于重用,还会增加与其他类的耦合,容易受到其他类变化的影响。

  2.       OCP,开放封闭原则:软件实体(类,模块,函数等)应该对扩展开放,对修改封闭。实现OCP的关键就在于,依赖于抽象,而不是具体的类,抽象的类保持稳定不变,同时通过从这个抽象派生,可以扩展模块的功能。

  3.       LSPLiskov替换原则:子类型必须能够替换他们的基类型。OO设计经常遇到的问题是,新派生类的创建会导致我们改变基类,这样做虽然是情非得已,但它会给程序增加很多复杂度。解决的办法:

    a)       代码不多时,提取公共部分

    b)       DBCdesign by contract,大家约定一致的用法

    c)       在单元测试中指定契约

  4.       ISP,接口隔离原则:不应该强迫客户依赖于它们不用的方法。接口属于客户,不属于它们所在的类层次结构。可以用委托或者多重继承分离接口。

  5.       DIP,依赖倒置原则:抽象不应该依赖于细节,细节应该依赖于抽象。如果高层模块独立于底层模块,那么高层模块就非常容易被重用,该原则是框架设计的核心。办法是,接口所有权和依赖关系的倒置,让客户拥有抽象接口,而他们的服务者从这些接口派生。

  还有6个包的设计原则,前3个原则强调了包的内聚性,可重用性,可开发性:

  1.       REP,重用发布等价原则:重用的粒度就是发布的粒度。

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

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

  4.       ADP,无环依赖原则:包的依赖关系图中不允许出现环。依赖环会造成依赖关系成倍增加,从而给维护和开发造成负担。解决办法:

    a)       DIP

    b)       提取公共部分,会造成包结构变化,抖动和增加

  5.       SDP,稳定依赖原则:朝着稳定的方向进行依赖。越是被高度依赖的类,越是应该稳定,应该是抽象或者具体而不变的类。

  6.       SAP,稳定抽象原则:包的抽象程度应该和其稳定程度一致。依赖应该朝着抽象的方向进行。

  Craig Larman在他的《Applying UMLand Patterns》中提到的9个原则,称为GRASPGeneral Responsibility Assignment Software Patterns,通用职责分配软件模式),作者说大多数设计模式可以被视为少数几个GRASP基本原则的特化,这些原则有助于加速设计模式的学习,帮助我们透过大量细节发现应用设计技术的本质:

  1.       Information Expert,信息专家:将职责分配给信息专家,信息专家是指具有履行职责所需信息的类。

  2.       Creator,创建者:将创建类A实例的职责分配给类B

    a)       B容纳A

    b)       B聚集A

    c)       B具有A的初始化数据

    d)       B记录A

    e)       B紧密的使用A

  3.       Controller,控制器:UI层下面第一个接受和协调系统操作的对象是:

    a)       外观控制器,代表“系统”,“根对象”,运行的软件设备,或者是主要子系统

    b)       发生该系统操作的用例场景

  4.       Low Coupling,低耦合:减少变化产生的影响,利于重用和维护

  5.       High Cohesion,高内聚:保持对象有重点,可理解和可管理,同时还有低耦合的作用。

  6.       Polymorphism,多态:当行为基于类型变化时,使用多态将行为职责分配给行为所变化的类型。

  7.       Pure Fabrication,纯虚构:当你走投无路,但又不想破坏高内聚和低耦合时,将一组高内聚的职责分配给人为的或便利性的“行为”类,该类并不代表问题领域概念,而是虚构事物,以此来支持高内聚,低耦合和复用性。

  8.       Indirection,间接性:将职责分配给中介对象,由该对象来协调其他构件或服务,以避免直接耦合。有一句名言“计算机科学中的大多数问题都可以通过增加一层间接性来解决。”还有一句名言“大多数性能问题都可以通过取出一层间接性来解决!”

  9.       Protected Variation,防止变异:如何设计对象,子系统和系统,使其内部的变化或不稳定性不会对其他元素造成不良影响?识别预计变化或者不稳定之处,分配职责用以在这些变化之外创建稳定接口。很多技术都可以归结为这个原则:

    a)       Data-Driven Design

    b)       Interpreter-Driven Design

    c)       Reflective or Meta-Level Design

    d)       Uniform Access

    e)       Service Lookup

    f)        LSP

    g)       Law of Demeter迪米特原则,又叫最少知识原则(Least Knowledge PrincipleLKP),或者“不要和陌生人说话”:不要历经远距离的对象结构路径,去向远距离的间接对象发送消息。因为对象结构(连接)可能会发生变化,这种不稳定性会导致对象连接耦合的代码破裂,程序遍历的路径越长,也就越脆弱。有时可以用重构中的Remove Middle Man

  《Head First设计模式》中也总结了几个OO原则,去掉一些明显和前面重复的,它们是:

  1.       封装变化

  2.        多用组合,少用继承,又叫合成/ 聚合复用原则( Composite/Aggregate Reuse Principle CARP

  3.       针对接口编程,不针对实现编程

  4.       为交互对象之间的松耦合设计而努力

  5.       只和朋友交谈,也就是迪米特原则

  6.       Hollywood Principle,好莱坞原则:别找我,我会找你。由超类主控一切,当它们需要的时候,自然会去调用子类。

  还有几个在Neal Ford的《The productive programmer》中提到的:

  1.       YAGNI, you ain’t gonna need it:不要添加自认为将来用得到的代码,事到临头再添加。

  2.       DRY, don’t repeat yourself:去掉重复的代码。

  还有一个比较流行的:

  1.       KISS, keep it simple and stupid:用最简单的方法让程序工作。

想象中的更高境界

  这么多设计模式和原则要运用自如还是很困难,难道那些行业专家在OO设计的时候要考虑那么多问题?时时刻刻嘴巴里面念着模式,原则……

  我觉得不是这样的。

  先来说一说Dreyfus模型,这个模型是我在《Pragmatic Thinking and Learning》中看到的。它指出从新手到专家要经历5个阶段:

  1.        新手:在该技能领域经验很少,或者根本没有经验。一个开发人员声称有10年工作经验,但如果只是一年的经验重复9次,他还是新手。新手比较浮躁,遇到错误容易慌张,如果给新手提供与情境无关的规则去参照,他们就会变得很能干。

  2.        高级新手:或多或少能摆脱固定的规则,可以尝试独自完成任务,他们不想要全局思维,想快速的解决问题,但由于对情境没有全面的理解,仍难以完全解决问题。

  3.        胜任者:开始寻求和运用专家的意见,并有效利用。他们的工作更多是基于谨慎的计划和过去的经验。如果没有更多经验,在解决问题时,他们将难以确定关注哪些细节。

  4.        精通者:有全局思维,围绕这个技术,寻找并想了解更大的概念框架。他们能自我纠正,反思以前是如何做的,并修改其做法,期望下一次表现得更好。他们能向他人学习,能根据情境来解决具体的问题。

  5.        专家:是各个领域知识和信息的主要来源,不断寻找更好的方法和方式去做事,丰富的经验,在适当的情境中选取和应用这些经验。他们著书写文章,做巡回演讲,他们是当代的巫师。专家根据直觉工作,而不需要理由。一旦你在一个领域成为专家,在别的领域成为专家就变得容易。因为你已经有了现成的获取知识的技能和模型构建能力。

  好了,我想说的是什么呢?我想说的是,不要怕有那么多模式和原则,模式啊原则啊,这些具体的规则只能让你启程,不会让你走得更远。

  我们需要通过自我认知,在实践中,不断自我纠正,自我反思,主要是要转变思维,从依赖规则向依赖直觉转变;从关注各个方面,向关注相关方面转变;从问题的旁观者装变为问题涉及的系统本身的一部分。

  专家是靠直觉的,他们在情境中识别模式的能力已经超越了他们显性的知识。

  他们会独孤九剑,他们无招胜有招。

  最后希望大家都通过自己的努力成为专家。