本文我们要谈的七大原则,即:单一职责,里氏替换,迪米特法则,依赖倒转,接口隔离,合成/聚合原则,开放-封闭

1.   开闭原则(Open-Closed Principle, OCP)

定义:软件实体应当对扩展开放,对修改关闭。这句话说得有点专业,更通俗一点讲,也就是:软件系统中包含的各种组件,例如模块(Modules)、类(Classes)以及功能(Functions)等等,应该在不修改现有代码的基础上,去扩展新功能。开闭原则中原有“开”,是指对于组件功能的扩展是开放的,是允许对其进行功能扩展的;开闭原则中“闭”,是指对于代码的修改是封闭的,即不应该修改原有的代码。

问题由来:凡事的产生都有缘由。我们来看看,开闭原则的产生缘由。在软件的生命周期内,因为变化、升级和维护等原因需要对软件原有代码进行修改时,可能会给旧代码中引入错误,也可能会使我们不得不对整个功能进行重构,并且需要原有代码经过重新测试。这就对我们的整个系统的影响特别大,这也充分展现出了系统的耦合性如果太高,会大大的增加后期的扩展,维护。为了解决这个问题,故人们总结出了开闭原则。解决开闭原则的根本其实还是在解耦合。所以,我们面向对象的开发,我们最根本的任务就是解耦合。

解决方法:当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。

小结:开闭原则具有理想主义的色彩,说的很抽象,它是面向对象设计的终极目标。其他几条原则,则可以看做是开闭原则的实现。我们要用抽象构建框架,用实现扩展细节。

2.    单一职责原则(Single Responsibility Principle)

定义:一个类,只有一个引起它变化的原因。即:应该只有一个职责。

每一个职责都是变化的一个轴线,如果一个类有一个以上的职责,这些职责就耦合在了一起。这会导致脆弱的设计。当一个职责发生变化时,可能会影响其它的职责。另外,多个职责耦合在一起,会影响复用性。例如:要实现逻辑和界面的分离。需要说明的一点是单一职责原则不只是面向对象编程思想所特有的,只要是模块化的程序设计,都需要遵循这一重要原则。

问题由来:类T负责两个不同的职责:职责P1,职责P2。当由于职责P1需求发生改变而需要修改类T时,有可能会导致原本运行正常的职责P2功能发生故障。

解决方法:分别建立两个类T1、T2,使T1完成职责P1功能,T2完成职责P2功能。这样,当修改类T1时,不会使职责P2发生故障风险;同理,当修改T2时,也不会使职责P1发生故障风险。

3.    里氏替换原则(Liskov Substitution Principle)

定义:子类型必须能够替换掉它们的父类型。注意这里的能够两字。有人也戏称老鼠的儿子会打洞原则。

问题由来:有一功能P1,由类A完成。现需要将功能P1进行扩展,扩展后的功能为P,其中P由原有功能P1与新功能P2组成。新功能P由类A的子类B来完成,则子类B在完成新功能P2的同时,有可能会导致原有功能P1发生故障。

解决方法:类B继承类A时,除添加新的方法完成新增功能P2外,尽量不要重写父类A的方法,也尽量不要重载父类A的方法

小结:所有引用父类的地方必须能透明地使用其子类的对象。子类可以扩展父类的功能,但不能改变父类原有的功能,即:子类可以实现父类的抽象方法,子类也中可以增加自己特有的方法,但不能覆盖父类的非抽象方法。当子类的方法重载父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。

4.    迪米特法则(Law Of Demeter)

 定义:迪米特法则又叫最少知道原则,即:一个对象应该对其他对象保持最少的了解。如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。简单定义为只与直接的朋友通信。首先来解释一下什么是直接的朋友:每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式很多,依赖、关联、组合、聚合等。其中,我们称出现成员变量、方法参数、方法返回值中的类为直接的朋友,而出现在局部变量中的类则不是直接的朋友。也就是说,陌生的类最好不要作为局部变量的形式出现在类的内部。

问题由来:类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。

最早是在1987年由美国Northeastern University的Ian Holland提出。通俗的来讲,就是一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类来说,无论逻辑多么复杂,都尽量地的将逻辑封装在类的内部,对外除了提供的public方法,不对外泄漏任何信息。迪米特法则还有一个更简单的定义:只与直接的朋友通信。

解决方法:尽量降低类与类之间的耦合。 自从我们接触编程开始,就知道了软件编程的总的原则:低耦合,高内聚。无论是面向过程编程还是面向对象编程,只有使各个模块之间的耦合尽量的低,才能提高代码的复用率。

迪米特法则的初衷是降低类之间的耦合,由于每个类都减少了不必要的依赖,因此的确可以降低耦合关系。但是凡事都有度,虽然可以避免与非直接的类通信,但是要通信,必然会通过一个“中介”来发生联系。故过分的使用迪米特原则,会产生大量这样的中介和传递类,导致系统复杂度变大。所以在采用迪米特法则时要反复权衡,既做到结构清晰,又要高内聚低耦合。

5.    依赖倒置原则(Dependence Inversion Principle)

定义:高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。中心思想是面向接口编程

问题由来:类A直接依赖类B,假如要将类A改为依赖类C,则必须通过修改类A的代码来达成。这种场景下,类A一般是高层模块,负责复杂的业务逻辑;类B和类C是低层模块,负责基本的原子操作;假如修改类A,会给程序带来不必要的风险。

解决方法:将类A修改为依赖接口I,类B和类C各自实现接口I,类A通过接口I间接与类B或者类C发生联系,则会大大降低修改类A的几率。

在实际编程中,我们一般需要做到如下3点:

1). 低层模块尽量都要有抽象类或接口,或者两者都有。

2). 变量的声明类型尽量是抽象类或接口。

3). 使用继承时遵循里氏替换原则。

采用依赖倒置原则尤其给多人合作开发带来了极大的便利,参与协作开发的人越多、项目越庞大,采用依赖导致原则的意义就越重大。

小结:依赖倒置原则就是要我们面向接口编程,理解了面向接口编程,也就理解了依赖倒置。

6.    接口隔离原则(Interface Segregation Principle)

定义:客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。

问题由来:类A通过接口I依赖类B,类C通过接口I依赖类D,如果接口I对于类A和类B来说不是最小接口,则类B和类D必须去实现他们不需要的方法

解决方法:1、 使用委托分离接口。2、 使用多重继承分离接口。3.将臃肿的接口I拆分为独立的几个接口,类A和类C分别与他们需要的接口建立依赖关系。也就是采用接口隔离原则。

举例说明:

下面我们来看张图,一切就一目了然了。

这个图的意思是:类A依赖接口I中的方法1、方法2、方法3,类B是对类A依赖的实现。类C依赖接口I中的方法1、方法4、方法5,类D是对类C依赖的实现。对于类B和类D来说,虽然他们都存在着用不到的方法(也就是图中红色字体标记的方法),但由于实现了接口I,所以也必须要实现这些用不到的方法

修改后:

如果接口过于臃肿,只要接口中出现的方法,不管对依赖于它的类有没有用处,实现类中都必须去实现这些方法,这显然不是好的设计。如果将这个设计修改为符合接口隔离原则,就必须对接口I进行拆分。在这里我们将原有的接口I拆分为三个接口

小结:我们在代码编写过程中,运用接口隔离原则,一定要适度,接口设计的过大或过小都不好。对接口进行细化可以提高程序设计灵活性是不挣的事实,但是如果过小,则会造成接口数量过多,使设计复杂化。所以一定要适度。设计接口的时候,只有多花些时间去思考和筹划,就能准确地实践这一原则。

7.   合成/聚合原则(Composite/Aggregate Reuse Principle,CARP)

定义:也有人叫做合成复用原则,及尽量使用合成/聚合,尽量不要使用类继承。换句话说,就是能用合成/聚合的地方,绝不用继承。

为什么要尽量使用合成/聚合而不使用类继承?

1. 对象的继承关系在编译时就定义好了,所以无法在运行时改变从父类继承的子类的实现

2. 子类的实现和它的父类有非常紧密的依赖关系,以至于父类实现中的任何变化必然会导致子类发生变化

3. 当你复用子类的时候,如果继承下来的实现不适合解决新的问题,则父类必须重写或者被其它更适合的类所替换,这种依赖关系限制了灵活性,并最终限制了复用性。

总结:这些原则在设计模式中体现的淋淋尽致,设计模式就是实现了这些原则,从而达到了代码复用、增强了系统的扩展性。所以设计模式被很多人奉为经典。我们可以通过好好的研究设计模式,来慢慢的体会这些设计原则。

C#面向对象设计的七大原则的更多相关文章

  1. 面向对象设计SOLID五大原则

    转载自:码农社区,http://w3croom.com/read.php?tid-4522.html 今天我给大家带来的是面向对象设计SOLID五大原则的经典解说.       我们知道,面向对象对于 ...

  2. # 61条面向对象设计的经验原则-《OOD启思录》Arthur J.Riel

    61条面向对象设计的经验原则-<OOD启思录>Arthur J.Riel 原文 http://blog.csdn.net/cpluser/article/details/129291 61 ...

  3. 【转】面向对象设计的SOLID原则

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

  4. 面向对象设计的SOLID原则

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

  5. Delphi面向对象设计的经验原则(61条)

    (1)所有数据都应该隐藏在所在的类的内部. (2)类的使用者必须依赖类的共有接口,但类不能依赖它的使用者. (3)尽量减少类的协议中的消息. (4)实现所有类都理解的最基本公有接口[例如,拷贝操作(深 ...

  6. JAVA 设计的七大原则

    一.开闭原则 开闭原则(Open-Closed Principle, OCP)是指一个软件实体如类.模块和函数应该对 扩展开放,对修改关闭. 所谓的开闭,也正是对扩展和修改两个行为的一个原则.强调 的 ...

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

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

  8. OOD 面向对象面试干货分享| 面向对象设计的SOLID原则

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

  9. 面向对象设计的SOLID原则、迪米特法则

    SPR(The Single Responsibility Principle):单一责任原则 OCP(The Open Closed Principle):开放封闭原则 LSP(The Liskov ...

随机推荐

  1. B. Vova and Trophies

    链接 [https://codeforces.com/contest/1082/problem/B] 题意 给你一个包含GS的字符串,只允许交换一次任意不同位置的字符,问最长的连续G串是多少 分析 很 ...

  2. 结对项目——Core设计与实现

    写在前面:关于结对编程 结对编程我一直认为是一种非常好的合作方式,他的形式主要是由一个人负责代码编写,另一个人则在一旁即时对写下的代码进行审查,这样可以大大减少代码实现方面的错误. 这次我的结对伙伴是 ...

  3. Linux内核分析——第五章 系统调用

    第五章 系统调用 5.1 与内核通信 1.系统调用在用户空间进程和硬件设备之间添加了一个中间层,该层主要作用有三个: (1)为用户空间提供了一种硬件的抽象接口 (2)系统调用保证了系统的稳定和安全 ( ...

  4. SQL语句及5.7.2 mysql 用户管理

    一.用户的定义 1.1 用户名+主机域 此处为5.7.2版本的mysql当中password字段已改为authentication_string mysql> select user,host, ...

  5. ECSHOP后台登陆后一段时间不操作就超时的解决方法

    ECSHOP后台登陆后一段时间不操作就超时的解决方法 ECSHOP教程/ ecshop教程网(www.ecshop119.com) 2012-05-27   客户生意比较好,因此比较忙,常常不在电脑前 ...

  6. ping命令返回的TTL值判断操作系统

    通过简单的ping命令,查看返回的TTL值来判断对方的操作系统 生存时间(TTL)是IP分组中的一个值,网络中的路由器通过察看这个值就可以判断这个IP分组是不是已经在网络中停留了很久,进而决定是否要将 ...

  7. github使用指南(2015年3月23日更新了本地创建仓库再推送到remote仓库的使用方法)

    我是通过这个来学习的.个人愚笨,琢磨了半天,终于搞通了,醉了醉了,以前一直使用svn,用git确实有点水土不服.本文以如何使用git为主来展开,不涉及太多理论. git是分布式的版本管理.什么叫分布式 ...

  8. error eslint@5.12.0: The engine "node" is incompatible with this module.

    初始化 react项目时报错: error eslint@5.12.0: The engine "node" is incompatible with this module. E ...

  9. codeforces369A

    Valera and Plates CodeForces - 369A Valera is a lazy student. He has m clean bowls and k clean plate ...

  10. BZOJ3456 城市规划(多项式求逆)

    设f[i]为连通图的数量,g[i]为不连通图的数量,显然有f[i]=2i*(i-1)/2-g[i],g[i]通过枚举1所在连通块大小转移,有g[i]=Σf[j]*C(i-1,j-1)·2(i-j)*( ...