1、常见面向对象设计原则,引言,设计原则是思想上的指导 设计模式是实现上的手段 设计模式是设计原则的具体体现 在实际开发中,很少做到完全遵守,总是在有意无意的违反一些或者部分原则 设计是一种危险的平衡艺术,单一职责原则(SRP) Single Responsibility Principle,拍摄UFO,单一职责原则,就一个类而言,应该仅有一个引起它变化的原因(职责)。 如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力。这种耦合会导致脆弱的设计,当变化发生时,设计会遭受到意想不到的破坏 难点在于如何区分职责、职责的粒度问题,软件设计真正
2、要做的内容,就是发现职责并把那些职责相互分离。如果你能够想到多于一个的动机去改变一个类,那么这个类就具有多于一个的职责,就应该考虑类的职责分离。,开放-封闭原则(OCP原则) Open-Closed Principle,开放-封闭原则,Open-Closed Principle原则讲的是:一个软件实体应当对扩展开放,对修改关闭。 需要考虑: 怎样的设计才能面对需求的改变却可以保持相对稳定,从而使得系统可以在第一个版本以后不断推出新的版本。面对需求,对程序的改动是通过增加新的代码进行的,而不是更改现有代码。 例如第一章程序,关键,合理地抽象、分离出变化与不变化的部分,为变化的部分预留下可扩展的方
3、式。例如:钩子方法或是动态组合对象等要完全遵守开闭原则是不可能的,也没这个必要。适当的抽象可以提高系统的灵活性、使其可扩展、可维护;过度抽象,会大大增加系统的复杂程度。,例子:,招安,招安之法的关键便是不允许更改现有的秩序,但允许将被招安者纳入现有秩序中,从而扩展了这一秩序。 用面向对象的语言来讲,不允许更改的是系统的抽象层,而允许更改的是系统的实现层。,里氏代换原则 Liskov Substitution Principle,使得开放-封闭成为可能,里氏代换原则,里氏代换原则子类型(subtype)必须能够替换它们的基(父)类型。(子类可以以父类的身份出现) 如果鸟是会飞的,企鹅不会飞,企鹅
4、是鸟吗?,由于子类型的可替换性才使得使用父类型的模块在无需修改的情况下就可以扩展。因此是实现开闭原则的前提之一,依赖倒转(置)原则(DIP) Dependence Inversion Principle,依赖倒转原则,依赖倒转(置)(Dependence Inversion Principle)原则讲的是:要依赖于抽象,不要依赖于具体。 简单的说,依赖倒转原则要求客户端依赖于抽象耦合。原则表述: 抽象不应当依赖于具体实现;具体实现应当依赖于抽象; 高层模块不应当依赖于底层模块,二者都应该依赖于抽象 要针对接口编程,不针对实现编程。,修电脑得到的启示,强内聚、松耦合 由于PC易插拨的方式,那么不
5、管哪一个出问题,都可以在不影响别的部件的前题下进行修改或替换。” 依赖倒转原则 要针对接口编程,不要对实现编程,无论主板、CPU、内存、硬盘都是在针对接口编程,如果针对实现编程,那就会出现换内存需要把主板也换了的尴尬,常见错误,层次化调用的时候,应该是高层调用“底层所拥有的接口”,这是一典型的误解。 一般高层包含对业务功能的处理和业务策略选择,应该被重用,是高层模块去影响底层的具体实现。 这个底层的接口应该是由高层提出的,然后由底层实现,即底层的接口的所有权在高层模块,是一种所有权的倒置,反面例子,缺点: 耦合太紧密,Light发生变化将影响ToggleSwitch。,解决办法一:,将Ligh
6、t作成Abstract,然后具体类继承自Light。,优点: ToggleSwitch依赖于抽象类Light,具有更高的稳定性,而BulbLight与TubeLight继承自Light,可以根据“开放封闭”原则进行扩展。只要Light不发生变化,BulbLight与TubeLight的变化就不会波及ToggleSwitch。 缺点: 如果用ToggleSwitch控制一台电视就很困难了。总不能让TV继承自Light吧。,解决方法二:,优点:更为通用、更为稳定。,结论:,使用传统过程化程序设计所创建的依赖关系,策略依赖于细节,这是糟糕的,因为策略受到细节改变的影响。依赖倒置原则使细节和策略都依赖
7、于抽象,抽象的稳定性决定了系统的稳定性。 Hollywood Principle:dont call us, well call you,合成/聚合复用原则,要尽量使用合成/聚合,而不是继承关系达到复用的目的。 合成/聚合原则就是在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分;新的对象通过向这些对象的委派达到复用已有功能的目的。Favor Composition Over Inheritance,接口隔离原则(ISP),接口隔离原则,接口隔离原则(Interface Segregation Principle)讲的是:使用多个专门的接口比使用单一的总接口要好。换而言之,从一个客户
8、类的角度来讲:一个类对另外一个类的依赖性应当是建立在最小接口上的。 过于臃肿的接口是对接口的污染。不应该强迫客户依赖于它们不用的方法。,实现方法:,使用多重继承分离接口,迪米特法则(LoD),迪米特法则(Law of Demeter或简写LoD)又叫最少知识原则(Least Knowledge Principle或简写为LKP) 如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。 也就是说,一个对象应当对其它对象有尽可能少的了解。,其它表述,只与你直接的朋友们通信 不要跟“陌生人”说话 每一个软件单位
9、对其它的单位都只有最少的知识,而且局限于那些与本单位密切相关的软件单位。,迪米特法则其根本思想,是强调了类之间的松耦合。 类之间的耦合越弱,越有利于复用,一个处于弱耦合的类被修改,不会对有关系的类造成波及。 信息的隐藏促进了软件的复用。,以史为鉴,使民无知 老子第三章曰:“是以圣人之治,虚其心,实其腹,弱其志,常使民无知无欲。”使被“统治”的对象“愚昧”化,处于“无知”的状态,可以使“统治”的成本降低。 所谓“最少知识“原则,实际上便是老子的“使民无知“的统治之术。 不相往来 老子云:“小国寡民邻国相望,鸡犬之声相闻,民至老死,不相往来。”将被统治的对象隔离开来,使它们没有直接的通信,可以达到分化瓦解,继而分而治之的效果。 迪米特法则与老子的“不相往来“的统治之术不谋而合。,单一职责原则 就一个类而言,应该仅有一个引起它变化的原因。 开放-封闭原则 一个软件实体应当对扩展开放,对修改关闭 里氏代换原则 子类型(subtype)必须能够替换它们的基(父)类型 依赖倒转(置)原则 要依赖于抽象,不要依赖于具体 接口隔离原则 使用多个专门的接口比使用单一的总接口总要好,合成/聚合复用原则 要尽量使用合成/聚合,而不是继承关系达到复用的目的 迪米特法则 一个软件实体应当尽可能少的与其他实体发生相互作用,