系统设计原则--SRP OCP LSP DIP ISP

来源:互联网 发布:数据库应用程序 编辑:程序博客网 时间:2024/05/22 06:33
一、单一职责原则(SRP)
      每个类应当只负责单一内聚的职责,每一个职责都是变化的一个轴线,当需求变化时,该变化会反映为类的职责的变化。一个类应当仅有一个引起它变化的原因,如果一个类承担了多于一个的职责,那么引起它变化的原因就会有多个,等于把这些职责耦合在了一起。违反SRP的设计通常可以采用门面模式或代理模式进行重构,分离业务的职责。
二、开闭原则(OCP)
      软件实体(类、包、模块等)应该是可以扩展的,但是不可修改的,即对于扩展是开放的,对于修改是封闭的,对实体的行为扩展时,无需改动源代码或二进制代码,即开闭原则(The Open-Closed Principle)。开闭原则的关键是抽象设计,对于完全的OCP是不可能的,应当对最有可能、最经常的变化进行抽象,遵循开闭原则,拒绝不成熟的抽象和抽象本身一样重要。
三、Liskov替换原则(LSP)LSPLiskov Substitution Principle
      子类型必须能够完全替换掉它们的基类型。满足Liskov原则的类型满足如下性质:若对每个类型S的对象O1,都存在一个类型T的对象O2,使得在所有针对T编写的程序中,用O1替换掉O2后,程序的行为功能不变,则S是T的子类型。OOD中IS-A关系是就行为方式而言的,行为方式是可以进行合理假设的,是客户程序所依赖的。模型的有效性只能通过它的客户程序来体现。一些违反了LSP的继承设计可以用提取公共部分的方法代替继承。
四、依赖倒置原则(DIP)Dependence Inversion Principle
      1、高层模块不应该依赖于低层模块,二者都应该依赖于抽象;
      每个较高层次都为它所需要的服务声明一个抽象接口,较低的层次实现这些抽象接口,每个高层类都通过该抽象接口使用下一层,这样高层就不依赖于低层,低层反而依赖于高层声明的抽象服务接口。
      2、抽象不应该依赖于细节,细节应该依赖于抽象;
      程序中所有的依赖关系都应该终止于抽象类或者接口,即:
      任何变量都不应该持有一个指向具体类的指针或引用;
      任何类都不应该从具体类派生;
      任何方法都不应该覆写它的任何基类中已经实现的方法;
      如果一个具体类不太会改变,并且也不会创建其他类似的派生类,那么依赖于它不会造成损害。例如:java.lang.String。
五、接口隔离原则(ISP) Interface Segregation Principle
      不应该强迫客户依赖于它们不用的方法。
      胖接口会导致他们的客户程序产生不正常并且有害的耦合关系,当一个客户程序要求该胖接口进行一个改动时,会影响到其他的客户程序,因此,客户程序应当仅仅依赖于他们实际调用的方法,通过把胖接口分离为多个特定的客户程序的接口,可以实现这个目标。每个特定于客户程序的接口仅仅声明它的客户程序需要调用的方法,实现类实现所有特定于客户程序的接口,解除这些耦合关系,使客户程序之间互不依赖。
      分离接口的常用方式包括使用委托分离和使用多重继承分离。分离接口的关键在于对于接口的客户进行分组。




敏捷开发-面向对象设计的11原则
"你不必严格遵守这些原则,违背它们也不会被处以宗教刑罚.
但你应当把这些原则看成警铃,若违背了其中的一条,那么警铃就会响起."
1.SRP单一职责原则[适用于类功能]
  (就一个类而言,应该仅有一个引起它变化的原因.)
  详细说明:
  如果一个类承担的职责过多,就等于把这些职责耦合在一起.
  一个职责的变化可能会削弱或者抑制这个类完成其它职责的能力.
  这种耦合会导致脆弱的设计,当变化发生时,设计会遭受到意想不到的破坏.
  结论:
  它是所有类设计原则最简单的,也是最难正确使用的.
  我们会自然的把职责结合在一起,软件设计真正要做的内容就是发现职责并把那些职责相互分离.
2.OCP开放-封闭原则[适用于类抽象]
  (软件实体(类,模块,函数...)应该是可以扩展的,但是不可以修改.)
  详细说明:
  OCP=对于扩展是开放的,对于修改是封闭的.
  如果程序中的一处改动就会产生连锁反应,导致一系列相关模块的改动,那么设计就有臭味.
  OCP建议我们如果要对系统进行重构,就只需要添加新的代码,而不必改动已经正常运行的代码.
  结论:
  在许多方面,OCP都是面向对象设计的核心.
  尊循它可以带来巨大的好处(程序的灵活性,可重用性,可维护性).
  在代码中肆意使用OCP也不是一个好主意.
  正确的做法是:开发人员仅仅对程序中呈现频繁变化的部分做出抽象!拒绝不成熟的抽象和抽象本身一样重要!
 
3.LSP Liskov替换原则[适用于类层次]
  (子类型必须能够替换掉它们的基类型.)
  详细说明:
  Barbara Liskov在1988年说道:
  Liskov替换性质:若对每个类型S的对象O1,都存在一个类型T的对象O2,
  在所有针对类型T编写的程序P中,用O1代换O2后,程序P行为功能不变,则类型S是类型T的子对象.
  结论:
  LSP是使用OCP开放-封闭原则成为可能的主要原则之一,
  正是子类型的可替换性才能用基类类型(基类引用或者指针)的模块在无需修改的情况下就可以扩展.
  这种可替换性是开发人员可以隐式依赖的东西.
  因此,如果没有显示的强制基类类型的契约,那么代码就必须良好并明显的表达出这一点.
  术语"IS-A"不能作为子类型的定义,
  子类型的正确定义是"可替换性","可替换性"可以通过显式或者隐式的(动态绑定必须用基类类型)契约.
 
4.DIP依赖倒置原则[适用于类层次]
  (抽象不应该依赖细节.细节应该依赖抽象.)
  详细说明:
  a.高层模块不应该依赖于低层模块,二者都应该依赖抽象(使用接口或者虚类来连接).
  b.抽象不应该依赖于细节,细节应该依赖于抽象.
  结论:
  使用传统的过程化程序设计方法所创建出来的依赖关系结构和策略是依赖于细节.
  DIP使得细节和策略都依赖于抽象,并且常常为客户定制服务接口.
  事实上,这种依赖关系的倒置是好的面向对象的程序设计的标记.
  DIP正确应用对于可重用框架是必须的,对于构建在变化面前富有弹性的代码也是非常重要的.
  由于抽象和细节被DIP彼此隔离,所以代码也非常容易维护.

5.ISP接口隔离原则[适用于类的接口]
  不应该强迫客户程序依赖于它们不用的方法.
  接口属于客户,不属于它所在的类层次结构.
  详细说明:
  分离客户就是分离接口.分离接口有2种方法:委托和多重继承
  接口隔离原则是用来处理胖接口所具有的缺点.
  如果类接口不是内聚的,就表示该类的接口是胖的,需要减肥.
  减肥的原则是接口分成多组方法,每一组方法都服务于一组不同的客户程序!
  客户程序面对的就是多个具有内聚接口的抽象基类.
  结论:
  胖类会导致它们的客户程序之间产生不正常的有害的耦合关系.
  当客户程序要求胖类进行一个改动时,会影响到所有其它户程序.
  因此,程序应该仅仅依赖于它们实际调用的方法.
  通过把胖类的接口分解为多个特定的客户程序的接口,可以实现这个目标.
  每个特定于客户程序的接口仅仅声明它自己调用的函数.
  解除了类的客户程序之间依赖关系,使它们互不依赖.
 
 
6.REP重用发布等价原则[适用于包]
  (重用的粒度就是发布的粒度)
  详细说明:
  当你重用别人一个类库时,你的期望是什么?
  当然是好的文档,可以工作的代码,规格清晰的接口!
  你希望作者会一直维护类库代码,当作者都把类库的接口和功能进行任何改变时,你希望得到通知.
  代码的作者把它们的软件组织到一个包中(dll,jar,...),所以我们重用的粒度就是包的发布粒度.
  结论: 
  一个包的重用粒度和和发布粒度一样大,由于重用性是基于包的,所以可重用的包必须包含可重用的类.
 
7.CCP共同封闭原则[适用于包]
  (包中的所有类对于同一类性质的变化应该是共同封闭的.
  一个变化若对一个包产生影响,则将对该包中的所有类产生影响,而对于其它包不造成任何影响.)
  详细说明:
  这是SRP单一职责原则对包的重新规定.这规定了一个包不应该包含多个引用包变化的原因.
  在大多数应用中,可维护性超过可重用性.
  代码更改:如果代码要更改,原意更改都集中在一个包中,而不是分布于多个包中.
  代码发布:我们也只发布更改中的包!
  结论:   
  CCP鼓励我们把可以由于同样的原因而更改的所有类共同聚集在同一个包中.
8.CRP共同重用原则[适用于包]
  (一个包中的所有类应该是共同重用的.
  如果重用了包中的一个类,那么就要重用包中的所有类.)
  详细说明:
  一个包中的所有类应该是共同重用的.
  结论:   
  如果重用了包中的一个类,那么就要重用包中的所有类.
  这个原则可以帮助我们决定哪些类应该放进同一个包中.
9.ADP无环依赖原则[适用于包]
  (在包的依赖关系图中不允许存在环.)
  详细说明:
  如果开发环境中有许多开发人员都在更改相同的源代码文件集合的情况,
  因为有人比你走的晚,且改了你所依赖一些东西(类或者方法),第二天来上班,
  你昨天完成的功能,今天不能正常工作,那么就会发生"晨后综合症"!
  针对此问题有两个解决方案:"每周构建"和"消除依赖环" 
  每周构建:应用于中等规模的项目中,它的工作方式为:每周1-4,开发人员各自工作在私人的代码空间,周5-6联合调试!
  消除依赖环:通过把开发环境划分成可发布的包,可以解决依赖环.
  结论:
  解决包之间的依赖环有两个主要方法:
  1.使用依赖倒置原则,在类和依赖类之前添加一个依赖的接口或者抽象类,解除依赖环.
  2.添加新类,把类和依赖类之间的依赖移到一个新的类,解除依赖环.

10.SDP稳定依赖原则[适用于包]
  (朝着稳定的方向进行依赖.)
  详细说明:
  设计不是完全固定的,要使设计可维护,某种程序的易变性是必要的.
  使用这个原则,我们可以创建对某些变化类型敏感的包.
  其它的包不要依赖这个要变的包.
  软件包就可以分为稳定包和可变包!
  如何识别稳定包和可变包?如果许多其它的包都依赖此包,那么它就是稳定包,否则就是可变包!
  把包放在不同的位置,它的稳定性是不同的.
  如何计算一个包的不稳定性?(输入耦合度Ca,输出耦合度Ce)
  不稳定值=Ce/(Ca+ce),此值越低越稳定!
  结论:
  把可变包不稳定值降低的方法是:为它加上一个抽象外衣(interface/抽象类),其它包调用抽象外衣!
  可变包为抽象外衣的实现!

11.SAP稳定抽象原则[适用于包]
  (包的抽象程序应该和其它稳定程序一致.)
  详细说明:  
  此原则把包的稳定性和抽象性联系到一起.
  一个稳定的包应该是抽象的,这样它的稳定性就不会使其无法扩展;
  一个不稳定的包应该具体的, 这样它的不稳定性使代码易于修改.
  结论:
  它指出一个包有时候应该达到部分是可抽象的,部分是不稳定的原则