注:以下图片均来自《如何向妻子解释OOD》译文链接:http://www.cnblogs.com/niyw/archive/2011/01/25/1940603.html
<How I explained OOD to my wife> 原文链接:http://www.codeproject.com/Articles/93369/How-I-explained-OOD-to-my-wife

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)

从面向对象角度解释这个原则为:"引起类变化的因素永远不要多于一个。" 或者说 "一个类有且仅有一个职责"。这似乎不太好理解,特别是"引起类变化的因素永远不要多于一个。"这句话更是有点虚,让人有点摸不着头脑。

我们通常都说“低耦合,高内聚”。在我看来,这里的"单一职责"就是我们通常所说的“高内聚”,即一个类只完成它应该完成的职责,不能推诿责任,也不可越殂代疱,不能成为无所不能的上帝类。如果你的团队中实施宽松的“代码集体所有权”,在编码的过程中出现许多人同时修改(维护)同一个类的现象,而且成员之间的沟通不够及时,主动和畅通的话,那么时间一长,就很可能出现“承担过多职责”的上帝类。这时,提炼基类/接口和提炼类重构将能帮助我们消除或减轻这种设计臭味。

看一个例子:

这是一个违反了“单一职责原则” 的类结构图。
这里,Rectangle类做了下面两件事:

  • 计算矩形面积;
  • 在界面(绘制设备)上绘制矩形;

并且,有两个应用使用了Rectangle类:

  • 计算几何应用程序(Computational Geometry Application)用这个类计算面积;
  • 图形程序(Graphical Application)用这个类在界面上绘制矩形;

这违反了SRP(单一职责原则)。因为Rectangle类做了两件事,在一个方法里它计算了面积,在另外一个方法了它返回一个表示矩形的GUI。这会带来一些有趣的问题:在计算几何应用程序中我们必须包含GUI。也就是在开发几何应用时,我们必须引用GUI库;图形应用程序中Rectangle类的变化可能导致计算几何应用程序的变化,编译和测试,反之亦然。那么,怎么修改才能让其符合单一职责原则呢?

答案是:拆分!拆分职责到两个不同的类中,如:

  • Rectangle: 这个类应该只定义Area()方法;
  • RectangleUI: 这个类应继承Rectangle类,并定义Draw()方法。

二、开放封闭原则 (OCP)

从面向对象设计角度看,这个原则可以这么理解:"软件实体(类,模块,函数等等)应当对扩展开放,对修改闭合。" 通俗来讲,它意味着你(或者类的客户)应当能在不修改一个类的前提下扩展这个类的行为。在OOD里,对扩展开放意味着类或模块的行为能够改变,在需求变化时我们能以新的,不同的方式让模块改变,或者在新的应用中满足需求。

也就是说,对扩展是开放的,而对修改是封闭的。我们通常都说:向系统中增加功能时应该只是添加新代码,而应该尽量少的修改原代码。在我看来,这就是遵循开放封闭原则所能带来的效果。曾经在网上看到过这样一句话“哪里变化,封装哪里”。这其实就是说,我们要将系统中可能变化的地方封装起来,即对修改封闭。同时,为了应对系统需求(功能)的扩展,需要抽象!

这里抽象是关键。《设计模式》中的state模式和strategy模式是这个原则的最好体现。

举一个例子:

违反了开放封闭原则的类结构图。

客户端代码直接面向服务器端的具体实现编程,缺乏灵活性。这样如果服务器因为某些原因被其他服务器替换了,那么客户端调用服务器的代码也必须做相应的修改或替换。这其实就是”面向实现编程“的设计臭味!

那么,如何修改才能得到正确灵活的设计?

答案是:抽象!为服务器端的代码(类型)抽象出一个抽象基类(定义一组完成服务职责的最小接口)。

下面是正确的设计:

遵循开放封闭原则的类结构图。

基本上,你抽象的东西是你系统的核心内容,如果你抽象得好,很可能增加一个新的服务器类型(扩展)只需要添加新类型(继承自AbstractServer即可)。因此代码要尽可能以抽象(这里的AbstractServer)为依据,这会允许你扩展抽象事物,定义一个新的实现而不需要修改任何客户端代码。即”面向接口编程,不要面向实现编程“!

三、Liskov's 替换原则(LSP)

Liskov's 替换原则意思是:"子类型必须能够替换它们的基类型。"或者换个说法:"使用基类引用的地方必须能使用继承类的对象而不必知道它。" 这个原则正是保证继承能够被正确使用的前提。通常我们都说,“优先使用组合(委托)而不是继承”或者说“只有在确定是 is-a 的关系时才能使用继承”,因为继承经常导致”紧耦合“的设计。
    在基本的面向对象原则里,"继承"通常是"is a"的关系。如果"Developer" 是一个"SoftwareProfessional",那么"Developer"类应当继承"SoftwareProfessional"类。在类设计中"Is a"关系非常重要,但它容易冲昏头脑,导致使用错误的继承造成错误设计。

看一个最最经典的例子:

遵循Liskov替换原则的类结构图。

注:这里,KingFisher(翠鸟)类扩展了Bird基类,并继承了Fly()方法,这没有问题。

但是下面这个类结构图就存在设计上的问题:

违反Liskov替换原则的类结构图。

Ostrich(鸵鸟)是一种鸟,这毋庸置疑,并从Bird类继承,这从概念上说没有问题。但是鸵鸟它能飞吗?不能,那么这个设计就违反了LSP。因为在使用Bird的地方不一定能用Ostrich代替。所以,即使在现实中看起来没问题,在类设计中,Ostrich不应该从Bird类继承,这里应该从Bird中分离一个不会飞的类NoFlyBrid,Ostrich应该继承这个不会飞的鸟类NoFlyBrid。

为什么LSP如此重要?

  • 如果没有LSP,类继承就会混乱;如果子类作为一个参数传递给方法,将会出现未知行为;
  • 如果没有LSP,适用与基类的单元测试将不能成功用于测试子类;

四、接口分离原则(ISP)

这个原则的意思是"客户端不应该被迫依赖于它们不用的接口。" 也就是说,一个接口或者类应该拥有尽可能少的行为(那么,什么叫尽可能少?就是少到恰好能完成它自身的职责),这也是保证“软件系统模块的粒度尽可能少,以达到高度可重用的目的。

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

接口分离原则确保实现的接口有它们共同的职责,它们是明确的,易理解的,可复用的.

下面这个例子充分的说明了”接口应该仅包含必要的方法,而不该包含其它的“。如果一个接口包含了过多的方法,应该通过分离接口将其拆分。

这是一个违反接口分离原则的胖接口。

注意到IBird接口包含很多鸟类的行为,包括Fly()行为.现在如果一个Bird类(如Ostrich)实现了这个接口,那么它需要实现不必要的Fly()行为(Ostrich不会飞)。因此,这个"胖接口"应该拆分成两个不同的接口,IBird和IFlyingBird, 而IFlyingBird继承自IBird。如下图所示:

这样的话,重用将变得非常灵活:如果一种鸟不会飞(如Ostrich),那它实现IBird接口。如果一种鸟会飞(如KingFisher),那么它实现IFlyingBird。

因此,如果我们想要获得可重用的方案,就应当遵循接口分离原则,把接口定义成仅包含必要的部分,以便在任何需要该接口功能的地方复用这个接口。

五、依赖倒置原则(DIP)

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

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

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

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

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

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

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

除SOLID原则外还有很多其它的面向对象原则。如:

  1. "组合替代继承":这是说相对于继承,要更倾向于使用组合;
  2. "笛米特法则":这是说"你的类对其它类知道的越少越好";
  3. "共同封闭原则":这是说"相关类应该打包在一起";
  4. "稳定抽象原则":这是说"类越稳定,越应该由抽象类组成";
当然,这些原则并不是孤立存在的,而是紧密联系的,遵循一个原则的同时也就遵循了另外一个或多个原则;反之,违反了其中一个原则也很可能同时就违反了另外一个或多个原则。 设计模式是这些原则在一些特定场景的应用结果。因此,可以把设计模式看作"框架",把OOD原则看作"规范"。 在学习设计模式的过程中,我们要经常性的反思,这个设计模式体现了面向对象设计原则中的哪个或哪一些原则。
特别是在重构实现模式,或重构趋向模式的过程中,我们更要结合SOLID原则思考代码在重构前后的区别,理解它的改进。我正在研读《重构与模式》一书,因此接下来我会结合例子记录自己的学习体会。

转载于:https://www.cnblogs.com/syxxlove/p/3714462.html

S.O.L.I.D 是面向对象设计(OOD)和面向对象编程(OOP)中的几个重要编码原则相关推荐

  1. 如何向妻子解释面向对象设计(OOD)

    前言 此文译自CodeProject上<How I explained OOD to my wife>一文,该文章在Top Articles上排名第3,读了之后觉得非常好,就翻译出来,供不 ...

  2. python面向对象设计管理系统_python面向对象之单例设计模型

    单例 目标 单例设计模式 `__new__` 方法 Python 中的单例 01. 单例设计模式 设计模式 设计模式 是 前人工作的总结和提炼,通常,被人们广泛流传的设计模式都是针对 某一特定问题 的 ...

  3. java五大原则_Java成长第五集--面向对象设计的五大原则

    S.O.L.I.D 是面向对象设计(OOD)和面向对象编程(OOP)中的几个重要编码原则(Programming Priciple)的首字母缩写.以下图说明: 下面就个人的理解来说说这五大原则的含义到 ...

  4. solid 设计原则 php,面向对象设计SOLID五大原则

    今天我给大家带来的是面向对象设计SOLID五大原则的经典解说. 我们知道,面向对象对于设计出高扩展性.高复用性.高可维护性的软件起到很大的作用.我们常说的SOLID五大设计原则指的就是: S = 单一 ...

  5. Java成长第五集--面向对象设计的五大原则

    S.O.L.I.D 是面向对象设计(OOD)和面向对象编程(OOP)中的几个重要编码原则(Programming Priciple)的首字母缩写.以下图说明: 下面就个人的理解来说说这五大原则的含义到 ...

  6. 编码原则总结:面向对象设计的SOLID原则

    S.O.L.I.D是 面向对象设计和编程(OOD&OOP)中几个重要的编码原则(Programming Priciple)的首字母缩写 缩写 全称 中文 SRP The Single Resp ...

  7. iOS 开发之 OOA (面向对象分析) OOD (面向对象设计) OOP (面向对象编程)

    OOA OOA 基本概念 OOA 全称 Object-Oriented Analysis 面向对象分析 指的是在一个系统的开发过程中进行了系统业务调查以后,按照面向对象的思想来分析问题.OOA 与结构 ...

  8. activexobject对象不能创建_面向对象设计方法(Object oriented)

    1.面向对象 (1)OOA(Object-oriented Analysis) 面向对象分析--事物的分类.命名.描述. (2)OOD(Object-oriented Design) 面向对象设计-- ...

  9. (软件工程复习核心重点)第十章面向对象设计-第一节:面向对象设计的基本概念与准则

    文章目录 一:面向对象设计概念 (1)定义 (2)设计与分析的关系 (3)分类 二:面向对象的设计准则 (1)模块化 (2)抽象 (3)信息隐藏 (4)低耦合 A:交互耦合 B:继承耦合 (5)高内聚 ...

最新文章

  1. 再见了Excel,我有Python了,不用天天加班了
  2. 个人所得税计算,计算器.
  3. 重写描述符(property)魔法方法时可能遇到的问题
  4. aliyun maven 添加jar_gradle添加阿里云maven库
  5. 代码谱写传奇,深度揭秘中国开发者现状!
  6. HTTP中常用响应头
  7. 斗地主案例的代码实现
  8. 1月份Web服务器份额:Microsoft涨2.32%居亚
  9. 动态规划——命运(hdu2571)
  10. 消息中间件学习总结(18)——MQ常见面试题总结
  11. 09-部署配置kubedns插件
  12. 基于python的人工智能的案例-Python在人工智能中的实例
  13. c语言头文件有哪些intr,有没有大神帮帮忙
  14. uni-app 动态获取元素wight/height/left/right/top/bottom/id
  15. 拓嘉辰丰:哪些会影响拼多多推广效果
  16. 交通仿真软件国内外详情分析及发展概述
  17. 【8月】100个HC3i优秀医疗信息化资源推荐
  18. 2011—那些人那些事
  19. Kingbase数据库实验五 PL/SQL程序设计
  20. 华大HC32L136--低功耗ADC功耗过高问题

热门文章

  1. vertica基本操作
  2. UG NX 10 草图重新附着
  3. springmvc自定义参数解析器
  4. 阿里和腾讯在泰国热战正酣,马云和马化腾要打世界大战?
  5. 这几款app疫情期间活跃度为何如此高?方法太可了吧
  6. k8s - service
  7. 第十五周 内部排序一(2)验证交换排序
  8. 希腊字母读音及科学方面应用
  9. Docker容器引擎
  10. MIT 18.01 Single Variable Calculus(单变量微积分)课堂笔记【6】——近似和求最值