OOP设计原则

OOP的全称是Object Oriented Programming,即面向对象编程。它的设计原则对于指导开发有实际意义。
S.O.L.I.D  是面向对象设计(OOD)和面向对象编程(OOP)中的几个重要编码原则(Programming Priciple)的首字母缩写。

面向对象设计的原则
SRP The Single Responsibility Principle单一职责原则
OCP  The Open Closed Principle开放封闭原则
LSP The Liskov Substitution Principle里氏替换原则
ISP  The Interface Segregation Principle接口分离原则
DIP  The Dependency Inversion Principle依赖倒置原则


一、单一职责原则(SRP)

      从面向对象角度解释这个原则为:"引起类变化的因素永远不要多于一个。" 或者说 "一个类有且仅有一个职责"。

  我们通常都说“低耦合,高内聚”。在我看来,这里的"单一职责"就是我们通常所说的“高内聚”,即一个类只完成它应该完成的职责,不能推诿责任,也不可越殂代疱,不能成为无所不能的上帝类。如果你的团队中实施宽松的“代码集体所有权”,在编码的过程中出现许多人同时修改(维护)同一个类的现象,而且成员之间的沟通不够及时,主动和畅通的话,那么时间一长,就很可能出现“承担过多职责”的上帝类。如果你有一个类承担了多项职责,那么你应该重新考虑下这个类,将它进行拆分。
class Student { 
public $studentName = '';
public $studentRegNo = '';
// Responsibility 1
function insertStudent($aStudent) {
// Logic for inserting a student to database.
}
function selectStudent($aStudent){
//logic
}
function deleteStudent($aStudent){
//logic
}
// Responsibility 2
function generateMarkSheet($aStudent) {
//logic for generating marks.
}
}
注意一下你会发现"Student"这个类有多个职责。它会处理所有的CRUD操作,同时还会为学生打印成绩单。问题就在于类中的方法是互相依赖的。
解决方案:

我们将这个类拆分开来,这样每个类就都满足单一职责了。

类1:Student 类2:StudentDB 类3:StudentReport

Student 负责get\set name,

StudentDB 负责:selectStudent、deleteStudent 

StudentReport负责:generateMarkSheet

二、开放封闭原则 (OCP)
软件实体(类,模块,函数等等)应当对扩展开放,对修改闭合。" 通俗来讲,它意味着你(或者类的客户)应当能在不修改一个类的前提下扩展这个类的行为。就是说,我们要将系统中可能变化的地方封装起来,即对修改封闭。同时,为了应对系统需求(功能)的扩展,需要抽象!

举例说明:

让我们看一张图:

想必大家都写过服务器与客户端。他们之间的联系是很紧密的,若服务器端发生变化,很可能使客户端也要做修改,这就导致了无谓的麻烦。因为这样做违背了”开放封闭原则“的设计,导致了 他们之间的高耦合度。

若是改成下图:  

                                                  

这样子做添加了一个抽象服务类,而在客户端类中包含了一个抽象服务类的引用,具体的服务类实现了抽象服务类。这样,当具体的服务类发生修改时,抽象服务类没有改动,进而包含抽象服务类的引用的客户端类就不用发生改动!

在这里,抽象是关键!我觉得之前梳理的java动态代理就是基于这种接口模式。

三、Liskov's 替换原则(LSP)
Liskov's 替换原则意思是:"子类型必须能够替换它们的基类型。"或者换个说法:"使用基类引用的地方必须能使用继承类的对象而不必知道它。" 这个原则正是保证继承能够被正确使用的前提。通常我们都说,“优先使用组合(委托)而不是继承”或者说“只有在确定是 is-a 的关系时才能使用继承”,因为继承经常导致”紧耦合“的设计。
下面是经典的“鸵鸟会飞”的例子
OOP设计原则 - bohu83 - bohu83的博客
 Ostrich(鸵鸟)是一种鸟,这毋庸置疑,并从Bird类继承,这从概念上说没有问题。但是鸵鸟它能飞吗?不能,那么这个设计就违反了LSP。因为在使用Bird的地方不一定能用Ostrich代替。
OOP设计原则 - bohu83 - bohu83的博客
 修改原始的Bird接口中只有Eat()和Sing()两个方法,然后用FlyBird接口去继承Bird类,这样做,就避免了“会飞的鸵鸟”发生了!
四、接口分离原则(ISP)

接口分离原则的核心思想就是:不应该强迫客户程序依赖它们不需要使用的方法。 也就是说,一个接口或者类应该拥有尽可能少的行为(就是少到恰好能完成它自身的职责),这也是保证“软件系统模块的粒度尽可能少,以达到高度可重用的目的。

   接口包含太多的方法会降低其可用性,像这种包含了无用方法的"胖接口"会增加类之间的耦合。如果一个类想实现该接口,那么它需要实现所有的方法,尽管有些对它来说可能完全没用,所以这样做会在系统中引入不必要的复杂度,降低代码的可维护性或鲁棒性。

例子可以参见上面的拆分fly接口

五、依赖倒置原则(DIP)

  这个原则的意思是:高层模块不应该依赖底层模块,两者都应该依赖其抽象。其实又是”面向接口编程,不要面向实现编程“的内在要求。

们考虑一个现实中的例子,来看看依赖倒置原则给我们软件带来的好处。 

你的汽车是由很多如引擎,车轮,空调和其它等部件组成,对吗?


注意:这里的 Car 就是高层模块;它依赖于抽象接口IToyotaEngine 和 IEighteenInchWheel.

而具体的引擎FifteenHundredCCEngine 属于底层模块,也依赖于抽象接口IToyotaEngine ;

具体的车轮 EighteenInchWheelWithAlloy同样属于底层模块,也依赖于抽象接口IEighteenInchWheel。

上面Car类有两个属性(引擎和车轮列表),它们都是抽象类型(接口)。引擎和车轮是可插拔的,因为汽车能接受任何实现了声明接口的对象,并且Car类不需要做任何改动。

再举个例子:门禁(第一步:必须先建立连接;第二部:发送传输命令,让门磁打开,或是关闭。第三步:关闭连接。)

这里,我会将门禁的连接、断开封装在一个接口中。将发送命令封装在另外一个接口中。   

这里是连接接口:

interface IConnect{

 Connect();

Disconnect();
          }
这里是通讯接口:

interface ISendCommand{

    sendcommand();

}

这里是我们的门禁接口:

interface IDoor Extends IConnect,ISendCommand{}

我们可以用的Door来实现Idoor接口。如果这个时候硬件工程师告诉我们。通讯方式变了。这个时候。我们只需要添加一个新的类叫NewDoor用不同实现idoor接口。
//旧设备
IDoor dr =  new  Door();
dr.Connect();
dr.SendCommand();
dr.DisConnect();
//新设备
IDoor ndr =  new  NewDoor();
ndr.Connect();
ndr.SendCommand();
ndr.DisConnect();
当然,这些原则并不是孤立存在的,而是紧密联系的,遵循一个原则的同时也就遵循了另外一个或多个原则;反之,违反了其中一个原则也很可能同时就违反了另外一个或多个原则。 设计模式是这些原则在一些特定场景的应用结果。因此,可以把设计模式看作"框架",把OOD原则看作"规范"。 在学习设计模式的过程中,我们要经常性的反思,这个设计模式体现了面向对象设计原则中的哪个或哪一些原则。

还有两个常被提到的原则:

六、合成/聚合复用原则(Composite/Aggregate Reuse Principle,CARP)

合成复用原则(CARP),在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分;新的对象通过这些向对象的委派达到复用已有功能的目的.这个设计原则有另一个简短的表述:要尽量使用合成/聚合,尽量不要使用继承.

意思就是:在复用对象的时候,要优先考虑使用组合,而不是继承,这是因为在使用继承时,父类的任何改变都可能影响子类的行为,而在使用组合时,是通过获得对其他对象的引用而在运行时刻动态定义的,有助于保持每个类的单一职责原则。

七、迪米特法则(Law of Demeter LoD)又叫做最少知识原则(Least Knowledge Principle,LKP),
就是说,一个对象应当对其他对象有尽可能少的了了解.
还有别的 说法,比如:只与朋友类交流
OOP设计原则 - bohu83 - bohu83的博客

朋友类就是出现在成员变量和方法的输入输出参数中的类,而出现在方法体内部的类则不是朋友类。

该Teacher类中,GroupLeader类是朋友类,而List<Girl>类和Gril类不是朋友类。这就违反了"迪米特法则"

拆分如下:

OOP设计原则 - bohu83 - bohu83的博客  
OOP设计原则 - bohu83 - bohu83的博客

 这样做就把他们拆分开来了,如果需要修改其中任何一个类,都不用对其他的类做太大改动。

 
参考:
http://blog.csdn.net/e5max/article/details/8872182
http://blog.csdn.net/anders_zhuo/article/details/8949566
http://www.cnblogs.com/LearningC/p/3629140.html
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值