关闭

面向对象的设计原则

711人阅读 评论(0) 收藏 举报

单一职责原则(SRP)——分离耦合的职责

就一个类而言,应该仅有一个引起它变化的原因。
如果一个类承担的职责过多,就等于把这些职责耦合到一起,一个职责的变化可能会影响这个类完成其他职责的能力。这种耦合会导致脆弱的设计,当变化发生的时候,会引起意想不到的破坏。

 

我们把职责定义为“变化的原因”。如果你能想到多于一个动机去改变一个类,那么这个类就有多于一个职责。

 

“开-闭”原则(OCP)——对可变性封装

软件实体(类、模块、函数等)应该是可以扩展的,但是不可修改。
任何系统在其生命周期中都会发生变化。如果我们希望开发出的系统不会在第一版本后就被抛弃,那么我们就必须牢牢记住这一点。

 

OCP特征

可扩展(对扩展是开放的)
模块的行为功能可以被扩展,在应用需求改变或需要满足新的应用需求时,我们可以让模块以不同的方式工作
不可更改(对更改是封闭的)
对模块进行扩展时,不必改动模块的源代码或者二进制代码。

 

关键是抽象

  • 抽象是实现OCP的主要方式
  • 模块可以操作一个抽象体。由于模块依赖于一个固定的抽象体,因此它可以是不允许修改(closed for modification)的;同时,通过从这个抽象体派生,也可扩展此模块的行为功能。
  • 符合OCP原则的程序只通过增加代码来变化而不是通过更改现有代码来变化,因此这样的程序就不会引起象非开放―封闭(open-closed)的程序那样的连锁反应的变化。

 

OO(once and only once)

  • 在最初编码时,假设变化不会发生。当变化发生时,就创建抽象来隔离以后会发生的同类变化。简而言之,可以犯一次错误,以后不会犯同样的错误。

 

对可变性的封装

  • 考虑系统中什么可能会发生变化
  • 一种可变性不应当散落在代码的很多角落里,而应当被封装到一个对象里
正确理解继承
  • 一种可变性不应当与另一个可变性混合在一起

 

选择性的封闭(Strategic Closure)没有任何一个大的程序能够做到100%的封闭。一般来讲,无论模块是多么的“封闭”,都会存在一些无法对之封闭的变化。既然不可能完全封闭,因此就必须选择性地对待这个问题。也就是说,设计者必须对于他(她)设计的模块应该对何种变化封闭做出选择。

 

里氏替换原则(LSP)——如何进行继承

The Liskov Substitution Principle
子类型必须能够替换掉它们的基类型。
OCP原则背后的主要机制是抽象和多态。支持抽象和多态的关键机制是继承。

 

 

LSP的定义
若对于每一个类型S的对象o1,都存在一个类型T的对象o2,使得在所有针对T编写的程序P中,用o1替换o2后,程序P的行为功能不变,则S是T的子类型。

 

LSP原则清楚地指出,OOD中Is-A关系是就行为功能而言。行为功能(behavior)不是内在的、私有的,而是外在、公开的,是客户程序所依赖的。行为功能(behavior)才是软件所关注的问题!所有派生类的行为功能必须和客户程序对其基类所期望的保持一致。

LSP和DBC

  • 基于契约的设计,简称DBC(Design by Contract)定义,把类和其客户之间的关系看作是一个正式的协议,明确各方的权利和义务。DBC对类的要求类的方法声明为先决条件(precondition)和后续条件(postcondition)。为了让方法得以执行,先决条件必须为真。完成后,方法保证后续条件为真。DBC对派生类的要求当重新定义派生类中的例行程序时,我们只能用更弱的先决条件和更强的后续条件替换之。

 

LSP-结论

  • LSP原则是符合OCP原则应用程序的一项重要特性。仅当派生类能完全替换基类时,我们才能放心地重用那些使用基类的函数和修改派生类型。

依赖倒转原则(DIP)——针对接口编程

a.高层模块不应该依赖于低层模块。二者都应该依赖于抽象。
b.抽象不应该依赖于细节。细节应该依赖于抽象。
如果高层模块依赖于低层模块,那么在不同的上下文中重用高层模块就会变得非常困难。而如果高层模块独立于低层模块,高层模块就可以非常容易得被复用。该原则是框架(framework)设计的核心原则。

 

倒置接口的所有权

  • 这就是著名的好莱坞原则:“Don't call us,we'll call you”(不要调用我们,我们会调用你)。低层模块实现了在高层模块中声明并被高层模块调用的接口。

 

依赖于抽象

  • 一个简单但是有效的对于DIP的解释,是这样一个简单的启发式规则“依赖于抽象”。
  • 该启发式规则建议不应该依赖于具体类,也就是说,程序中所有的依赖关系都应该中止于抽象类或者接口。
根据这个启发式规则,可知:
  1. 任何变量都不应该持有一个指向具体类的指针或者引用。
  2. 任何类都不应该从具体类派生。
  3. 任何方法都不应该覆写它的任何基类中的已经实现了的方法。

 

实际情况中,每个程序都会有违反该启发规则的情况,总会在必要时创建具体类的实例。另外,该启发式规则对于那些虽然是具体但是稳定的类似乎不太合理。如果一个类不太会改变,并且也不会创建其他类似的派生类,那么依赖于这个具体类并不会有什么损害。

实施重点

  • 从问题的具体细节中分离出抽象,以抽象方式对类进行耦合

 

DIP与设计模式

  • DIP以LSP为基础,是实现OCP的主要手段,是设计模式研究和应用的主要指导原则

 

 

接口隔离原则(ISP)——恰当的划分角色和接口

不应该强迫客户依赖于他们不用的方法。接口属于客户,不属于它所在的类层次结构。
接口的污染(Interface Contamination):一个没有经验的设计师往往想节省接口的数目,将一些功能相近或功能相关的接口合并,并将这看成是代码优化的一部分。

 

从一个客户类的角度来讲:一个类对另外一个类的依赖性应当是建立在最小的接口上的。使用多个专门的接口比使用单一的总接口要好

ISP承认存在一些对象,它们确实不需要内聚的接口;但是ISP建议客户程序不应该看到它们作为单一的类存在。相反,客户程序看到的应该是多个具有内聚接口的抽象基类。

 

合成/聚合复用原则(CARP)——尽量使用合成/聚合、尽量不使用继承

在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分;新的对象通过向这些对象的委派达到复用这些对象的目的
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:67704次
    • 积分:989
    • 等级:
    • 排名:千里之外
    • 原创:32篇
    • 转载:6篇
    • 译文:0篇
    • 评论:6条
    最新评论