面向对象编程五大原则(转)

单一职责原则SRPSingle Responsibility Principle
开放封闭原则OCPOpenClose Principle
Liskov
替换原则LSPLiskov Substitution Principle
依赖倒置原则DIPDependency Invertion Principle
接口隔离原则ISPInterface Separate Principle

  在面向对象设计中,如何通过很小的设计改变就可以应对设计需求的变化,这是令设计者极为关注的问题。为此不少OO先驱提出了很多有关面向对象的设计原则用于指导OO的设计和开发。下面是几条与类设计相关的设计原则。

1. 
开闭原则(the Open Closed Principle OCP
   一个模块在扩展性方面应该是开放的而在更改性方面应该是封闭的。因此在进行面向对象设计时要尽量考虑接口封装机制、抽象机制和多态技术。该原则同样适合 于非面向对象设计的方法,是软件工程设计方法的重要原则之一。我们以收音机的例子为例,讲述面向对象的开闭原则。我们收听节目时需要打开收音机电源,对准 电台频率和进行音量调节。但是对于不同的收音机,实现这三个步骤的细节往往有所不同。比如自动收缩电台的收音机和按钮式收缩在操作细节上并不相同。因此, 我们不太可能针对每种不同类型的收音机通过一个收音机类来实现(通过重载)这些不同的操作方式。但是我们可以定义一个收音机接口,提供开机、关机、增加频 率、降低频率、增加音量、降低音量六个抽象方法。不同的收音机继承并实现这六个抽象方法。这样新增收音机类型不会影响其它原有的收音机类型,收音机类型扩 展极为方便。此外,已存在的收音机类型在修改其操作方法时也不会影响到其它类型的收音机。 
 
2. 
替换原则 (the Liskov Substitution Principle LSP)
  子类应当可以替换父类并出现在父类能够出现的任何地方。这个原则是Liskov1987年提出的设计原则。它同样可以从Bertrand Meyer DBC (Design by Contract) 的概念推出。
  我们以学生为例,夜校生为学生的子类,因此在任何学生可以出现的地方,夜校生均可出现。这个例子有些牵强,一个能够反映这个原则的例子时圆和椭圆,圆是椭圆的一个特殊子类。因此任何出现椭圆的地方,圆均可以出现。但反过来就可能行不通。 
  运用替换原则时,我们尽量把类B设计为抽象类或者接口,让C类继承类B(接口B)并实现操作A和操作B,运行时,类C实例替换B,这样我们即可进行新类的扩展(继承类B或接口B),同时无须对类A进行修改。

3. 
依赖原则 (the Dependency Inversion Principle DIP)
  在进行业务设计时,与特定业务有关的依赖关系应该尽量依赖接口和抽象类,而不是依赖于具体类。具体类只负责相关业务的实现,修改具体类不影响与特定业务有关的依赖关系。
  在结构化设计中,我们可以看到底层的模块是对高层抽象模块的实现(高层抽象模块通过调用底层模块),这说明,抽象的模块要依赖具体实现相关的模块,底层模块的具体实现发生变动时将会严重影响高层抽象的模块,显然这是结构化方法的一个"硬伤"
  面向对象方法的依赖关系刚好相反,具体实现类依赖于抽象类和接口。
  为此,我们在进行业务设计时,应尽量在接口或抽象类中定义业务方法的原型,并通过具体的实现类(子类)来实现该业务方法,业务方法内容的修改将不会影响到运行时业务方法的调用。 

4. 
接口分离原则(the Interface Segregation Principle ISP
    
采用多个与特定客户类有关的接口比采用一个通用的涵盖多个业务方法的接口要好。
  ISP原则是另外一个支持诸如COM等组件化的使能技术。缺少ISP,组件、类的可用性和移植性将大打折扣。
  这个原则的本质相当简单。如果你拥有一个针对多个客户的类,为每一个客户创建特定业务接口,然后使该客户类继承多个特定业务接口将比直接加载客户所需所有方法有效。

上四个原则是面向对象中常常用到的原则。此外,除上述四原则外,还有一些常用的经验诸如类结构层次以三到四层为宜、类的职责明确化(一个类对应一个具体职 责)等可供我们在进行面向对象设计参考。但就上面的几个原则看来,我们看到这些类在几何分布上呈现树型拓扑的关系,这是一种良好、开放式的线性关系、具有 较低的设计复杂度。一般说来,在软件设计中我们应当尽量避免出现带有闭包、循环的设计关系,它们反映的是较大的耦合度和设计复杂化。 

 

 

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

什么是设计原则? 

设计原则是基本的工具,应用这些规则可以使你的代码更加灵活、更容易维护,更容易扩展。

基本原则

 

·         封装变化Encapsulate what varies.

·         面向接口编程而非实现 Code to an interface rather than to an implementation.

·         优先使用组合而非继承 Favor Composition Over Inheritance

SRP: The single responsibility principle 单一职责

系统中的每一个对象都应该只有一个单独的职责,而所有对象所关注的就是自身职责的完成。

Every object in your system should have a single responsibility ,and all the object s services should be focused on carrying out that single responsibility .

 

1.       每一个职责都是一个设计的变因,需求变化的时候,需求变化反映为类职责的变化。当你系统里面的对象都只有一个变化的原因的时候,你就已经很好的遵循了SRP原则。

2.       如果一个类承担的职责过多,就等于把这些职责耦合在了一起。一个职责的变化就可能削弱或者抑制这个类其它职责的能力。这种设计会导致脆弱的设计。当变化发生的时候,设计会遭到意想不到的破坏。

·         SRP 让这个系统更容易管理维护,因为不是所有的问题都搅在一起。

·         内聚Cohesion 其实是SRP原则的另外一个名字.你写了高内聚的软件其实就是说你很好的应用了SRP原则。

·         怎么判断一个职责是不是一个对象的呢?你试着让这个对象自己来完成这个职责,比如:“书自己阅读内容”,阅读的职责显然不是书自己的。

·         仅当变化发生时,变化的轴线才具有实际的意义,如果没有征兆,那么应用SRP或者任何其它的原则都是不明智的。

DRY : Don't repeat yourself Principle

通过抽取公共部分放置在一个地方避免代码重复.

Avoid duplicate code by abstracting out things that are common and placing those thing in a single location .

 

1.       DRY 很简单,但却是确保我们代码容易维护和复用的关键。

2.       你尽力避免重复代码候实际上在做一件什么事情呢?是在确保每一个需求和功能在你的系统中只实现一次,否则就存在浪费!系统用例不存在交集,所以我们的代码更不应该重复,从这个角度看DRY可就不只是在说代码了。

·         DRY 关注的是系统内的信息和行为都放在一个单一的,明显的位置。就像你可以猜到正则表达式在.net中的位置一样,因为合理所以可以猜到。

·         DRY 原则:如何对系统职能进行良好的分割!职责清晰的界限一定程度上保证了代码的单一性。

OCP : Open-Close Principle开闭原则

类应该对修改关闭,对扩展打开;

Classes should be open for extension ,and closed for modification .

 

1.       OCP 关注的是灵活性,改动是通过增加代码进行的,而不是改动现有的代码;

2.       OCP的应用限定在可能会发生的变化上,通过创建抽象来隔离以后发生的同类变化

·         OCP原则传递出来这样一个思想:一旦你写出来了可以工作的代码,就要努力保证这段代码一直可以工作。这可以说是一个底线。稍微提高一点要求,一旦我们的代码质量到了一个水平,我们要尽最大努力保证代码质量不回退。这样的要求使我们面对一个问题的时候不会使用凑活的方法来解决,或者说是放任自流的方式来解决一个问题;比如代码添加了无数对特定数据的处理,特化的代码越来越多,代码意图开始含混不清,开始退化。

·         OCP 背后的机制:封装和抽象;封闭是建立在抽象基础上的,使用抽象获得显示的封闭;继承是OCP最简单的例子。除了子类化和方法重载我们还有一些更优雅的方法来实现比如组合;

怎样在不改变源代码(关闭修改)的情况下更改它的行为呢?答案就是抽象,OCP背后的机制就是抽象和多态

5.       没有一个可以适应所有情况的贴切的模型!一定会有变化,不可能完全封闭.对程序中的每一个部分都肆意的抽象不是一个好主意,正确的做法是开发人员仅仅对频繁变化的部分做出抽象。拒绝不成熟的抽象和抽象本身一样重要。

·         OCPOOD很多说法的核心,如果这个原则有效应用,我们就可以获更强的可维护性 可重用 灵活性 健壮性 LSPOCP成为可能的主要原则之一

LSP: The Liskov substitution principle

子类必须能够替换基类。

Subtypes must be substitutable for their base types.

 

1.       LSP关注的是怎样良好的使用继承.

2.       必须要清楚是使用一个Method还是要扩展它,但是绝对不是改变它。

·         LSP清晰的指出,OODIS-A关系是就行为方式而言,行为方式是可以进行合理假设的,是客户程序所依赖的。

·         LSP让我们得出一个重要的结论:一个模型如果孤立的看,并不具有真正意义的有效性。模型的有效性只能通过它的客户程序来表现。必须根据设计的使用者做出的合理假设来审视它。而假设是难以预测的,直到设计臭味出现的时候才处理它们。

·         对于LSP的违反也潜在的违反了OCP

DIP:依赖倒置原则

高层模块不应该依赖于底层模块 二者都应该依赖于抽象

抽象不应该依赖于细节 细节应该依赖于抽象

1. 什么是高层模块?高层模块包含了应用程序中重要的策略选择和业务模型。这些高层模块使其所在的应用程序区别于其它。

2. 如果高层模块依赖于底层模块,那么在不同的上下文中重用高层模块就会变得十分困难。然而,如果高层模块独立于底层模块,那么高层模块就可以非常容易的被重用。该原则就是框架设计的核心原则。

·         这里的倒置不仅仅是依赖关系的倒置也是接口所有权的倒置。应用了DIP我们会发现往往是客户拥有抽象的接口,而服务者从这些抽象接口派生。

·         这就是著名的Hollywood原则:"Don't call us we'll call you."底层模块实现了在高层模块声明并被高层模块调用的接口。

·         通过倒置我们创建了更灵活 更持久更容易改变的结构

·         DIP的简单的启发规则:依赖于抽象;这是一个简单的陈述,该规则建议不应该依赖于具体的类,也就是说程序汇总所有的依赖都应该种植于抽象类或者接口。

·         如果一个类很稳定,那么依赖于它不会造成伤害。然而我们自己的具体类大多是不稳定的,通过把他们隐藏在抽象接口后面可以隔离不稳定性。

·         依赖倒置可以应用于任何存在一个类向另一个类发送消息的地方

·         依赖倒置原则是实现许多面向对象技术多宣称的好处的基本底层机制,是面向对象的标志所在。

ISP:接口隔离原则

不应该强迫客户程序依赖它们不需要的使用的方法。

 

1. 接口不是高内聚的,一个接口可以分成N组方法,那么这个接口就需要使用ISP处理一下。

2. 接口的划分是由使用它的客户程序决定的,客户程序是分离的接口也应该是分离的。

·         一个接口中包含太多行为时候,导致它们的客户程序之间产生不正常的依赖关系,我们要做的就是分离接口,实现解耦。

·         应用了ISP之后,客户程序看到的是多个内聚的接口。

posted @ 2012-09-14 16:40  海燕一家  阅读(385)  评论(0编辑  收藏  举报