SOLID是5个设计原则的统称,它们分别是:单一职责原则、开闭原则、里式替换原则、接口隔离原则和依赖反转原则,依次对应SOLID中的S、O、L、I、D。

1、单一职责原则

单一职责原则,Single Responsibility Principle,SRP,英文描述是:A class or module should have a single responsibility。翻译成中文就是:一个类或者模块只负责完成一个职责(或者功能)。

如何理解单一职责原则

单一职责原则的定义描述非常简单:一个类(或模块,同理)只负责完成一个职责或功能。也就是说,不要设计大而全的类,要设计粒度小、功能单一的类。换个角度讲,一个类包含了两个或以上业务不相干的功能,我们就说它职责不够单一,应该拆分成多个功能更加单一、粒度更细的类。

如何判断类的职责是否足够单一

评价一个类的职责是否单一,是一件主观且见仁见智的事情,而且从不同业务层面看也会有不同结论。实际上我们在真正的软件开发中,没必要过度设计,可以先写一个粗粒度的类,满足业务要求,随着业务的发展,如果粗粒度的类越来越庞大、代码越来越繁杂,这时候我们就可以将这个粗粒度的类拆分成几个更细粒度的类。这就是所谓的持续重构

例如一个社交产品中,UserInfo类中既有用户名、邮箱等基本信息,又有省、市、区、详细地址等地址信息。如果地址信息和基本信息一样只是单纯用来展示,那么UserInfo类的设计就是符合单一职责原则的。但如果地址信息又用在了电商物流上,那么最好将地址信息拆分出来独立成地址类。

类的职责是不是越单一越好

答案是否定的,设计原则和设计模式的最终目的是提高代码的可读性、可扩展性、可复用性、可维护性等,这些才是评价应用某个设计原则是否合理时的最终考量标准。

比如一个Serialization类按照一个特定协议实现了序列化和反序列化的功能,如果我们想让类的职责更加单一,拆分成一个只负责序列化的Serializer类和另一个只负责反序列化的Deserializer类。虽然拆分后类的职责更单一了,但也随之带来了新的问题:如果我们修改了序列化协议(如序列化方式从JSON改为XML),那么Serializer类和Deserializer类都要做相应的修改,代码的内聚性没有原来Serialization高了。如果我们仅对Serializer类做了协议修改,而忘记修改Deserializer类,就会导致程序出错,也就是说,拆分后代码的可维护性变差了。

2、开闭原则

开闭原则,Open Closed Principle,OCP,英文描述是:software entities(modules,classes,functions,etc)should be open for extension,but closed for modification。翻译成中文就是:软件实体(模块、类、方法等)应该对扩展开放,对修改关闭。

换句话说,添加一个功能,应该是在已有代码基础上新增,而非修改已有代码。

如何理解“对扩展开放,对修改关闭”

首先,开闭原则并不是完全杜绝修改,而是以最小修改代价来完成新功能,让修改操作更少、更集中、更上层。其次,同样的代码改动,在粗代码粒度下,可能被认为“修改”,在细代码粒度下,有可能被认为“扩展”,例如给类中添加新的属性和方法,在类的层面是修改,在方法和属性的层面就是扩展。

如何做到“对扩展开放,对修改关闭”

时刻具备抽象意识,写代码的时候多思考一下这段代码未来可能有哪些需求变更,以便设计可扩展的代码结构。

很多设计思想、设计原则、设计模式,都是以提高代码扩展性为最终目的的。最常用用来提高代码扩展性的方法有:多态、依赖注入、基于接口而非实现编程,以及大部分的设计模式(装饰、策略、模板、职责链、状态等)。

3、里氏替换原则

里氏替换原则,Liskov Substitution Principle,英文描述是:If S is a subtype of T, then objects of T may be replaced with objects of type S, without breaking the program(子类对象可以替换父类对象而不破坏程序运行),或者:Functions that use pointers of references to base classes must be able to use objects of deried classes without knowing it(使用基类引用指针的方法,必须能够在无感知的情况下使用派生类对象)

一句话,这条原则要求:子类对象能够替换父类对象出现的任何地方,并且不破坏程序原有的逻辑行为。

里氏替换原则与多态的区别

多态是面向对象编程的一大特性,也是面向对象编程语言的一种语法,支持子类对象替换父类对象以实现不同效果。而里氏替换原则是在多态的基础上,指导子父类如何设计,要求子类对象在替换父类对象时不改变原有程序的逻辑行为,不破坏原有程序的正确性。(指导怎么更好的使用多态)

什么样的代码违背里氏替换原则

里氏替换原则还有另一个更有指导意义的描述:Design by Contract,按照协议来设计。即子类在设计的时候要遵守父类的协议(或叫行为约定),子类可以重写方法内部实现逻辑,但不能改变方法原有的协议,包括:方法声明要实现的功能;对输入输出、异常的约定;甚至注释中所罗列的任何特殊说明。

1、子类违背父类声明要实现的功能

父类orderByAge()方法是按照年龄排序,而子类重写该方法后按照身高排序,则子类的设计违背里氏替换原则。(这本是多态的寻常应用,但是违背里氏替换原则)

2、子类违背父类对输入输出、异常的约定

父类list()方法获取不到数据时返回空数组,而子类重写后,获取不到数据抛异常,则子类的设计违背里氏替换原则。(这本是多态的寻常应用,但是违背里氏替换原则)

3、子类违背父类注释中所罗列的任何特殊说明

父类withdraw()方法注释:用户提现金额不得超过账户余额,而子类重写后,可以支持透支提现,即提现金额可以大于账户余额,则子类的设计违背里氏替换原则。

4、接口隔离原则

接口隔离原则,Interface Segregation Principle,ISP,英文描述是:Clients should not be forced to depend upon interface that they do not use。翻译成中文就是:客户端不应该被强迫依赖它不需要的接口。其中“客户端”可以理解为接口的调用者或使用者。

把“接口”理解为API或函数

一个count()函数,功能是对输入的数据集求最小值、最大值、平均值、中位数、总和,并把结果封装成对象返回。如果在项目中,每个统计需求都需要求以上信息,那么count()函数的设计是合理的;相反,如果每个统计需求只涉及其中一个或几个信息,那么合理的设计应该是拆分成更小粒度的函数,否则每次调用count()函数都要把所有信息都算一遍,影响代码的性能,这就是强迫调用者依赖不需要的接口。

把“接口”理解为OOP中的接口概念

一个Config接口,包含了对某项配置的更新和查询,如果项目中有的需求我们只希望它更新配置,(因为某些原因)不希望它查询配置,而另外的需求我们只希望它查询配置,不希望它更新配置,那么更合理的设计是把Config接口拆分成Updater和Viewer两个接口,否则只更新的需求也要被迫实现查询功能,只查询的需求也要被迫实现更新功能。

5、依赖反转原则

依赖反转,Dependency Inversion Principle,DIP,英文描述是:High-level modules shoudn't depend on low-level modules. Both modules should depend on abstractions. In addition, abstraction shouldn't depend on details. Details depend on abstractions。翻译成中文就是:高层模块不要依赖低层模块。高层和低层模块都要依赖抽象。除此之外,抽象不要依赖具体实现,具体实现要依赖抽象。

如何理解依赖反转原则

高层:调用链上的调用者,低层:被调用者。在业务代码的开发中,高层模块依赖低层模块是没有问题的。实际上这条原则主要用来指导框架层面的设计。以Tomcat这个Servlet容器为例,Tomcat就是高层模块,我们写的Web应用程序代码就是低层模块,Tomcat和应用程序代码并无依赖关系,二者都依赖抽象——也就是Servlet规范。Servlet规范不依赖Tomcat和应用程序的具体实现,而Tomcat和应用程序依赖Servlet规范。

相似名词:控制反转(Inversion Of Control,IOC)

通常用在框架上,这里的“控制”指的是对程序执行流程的控制。“反转”是指没用框架之前,程序员自己控制整个程序的执行,使用框架后,框架来驱动整个程序的执行流程,程序员只需要在预留的扩展电上添加自己的业务代码即可,流程的控制权从程序员反转到了框架。

相似名词:依赖注入(Dependency Injection,DI)

一种具体的编程技巧,听起来高大上,实际很简单。一句话概括就是不通过new的方式在类内部创建所依赖的对象,而是通过构造函数、函数参数等方式传递(或叫注入)到类内部使用。好处就是提高了代码的扩展性,可以灵活地替换所依赖的类。(符合开闭原则)

设计原则——SOLID相关推荐

  1. 六大设计原则SOLID

    六大设计原则SOLID 一.SOLID 设计模式的六大原则 二.单一职责原则 (Single Responsibility Principle) 1. 单一职责原则定义 2. 问题由来 3. 解决方案 ...

  2. 流行20年的架构设计原则SOLID可能已经不适合微服务了

    点击上方"服务端思维",选择"设为星标" 回复"669"获取独家整理的精选资料集 回复"加群"加入全国服务端高端社群「后 ...

  3. 六大设计原则 (SOLID)

    设计原则概述 设计模式中主要有六大设计原则,简称为SOLID ,是由于各个原则的首字母简称合并的来(两个L算一个,solid 稳定的),六大设计原则分别如下: 1.单一职责原则(Single Resp ...

  4. 【六大设计原则-SOLID】

    SOLID简介: 历史:由Robert C·Martin汇总并推广 目标: 使软件更容易被改动 是软件更容易被理解 构建可以在多个软件系统中复用的组件 组成: 名称 简写 含义 单一职责原则 SRP ...

  5. 软件设计原则SOLID+组合聚合+迪米特原则(附代码讲解)

    SOLID是五大设计原则的首字母简写,最早出现于出自Robert Martin(罗伯特. 马丁)的<架构整洁之道>第三章设计原则.他们分别是 single Responsibility P ...

  6. 六大设计原则(SOLID)

    一.SOLID 设计模式的六大原则有: Single Responsibility Principle:单一职责原则 Open Closed Principle:开闭原则 Liskov Substit ...

  7. 设计原则—SOLID(LSP)

    文章目录 里氏替换原则 落地 测试 反例 子类违背父类声明要实现的功能 子类违背父类对输入.输出.异常的约定 异常 输入 子类违背父类注释中所罗列的任何特殊说明 意义 对比多态 改进已有实现 指导程序 ...

  8. 设计原则—SOLID(DIP)

    文章目录 依赖反转原则(DIP) 正确理解 优势 控制反转 例子 依赖注入(DI) 例子 依赖注入框架 对比基于接口编程 相同点 区别 总结 依赖反转原则(DIP) 高层模块(high-level m ...

  9. 设计原则—SOLID(SRP)

    文章目录 SOLID 单一职责原则 其他判断标准 过于单一的拆分 例子 序列化 例子 用户信息的例子 从实际场景的角度出发 从业务的角度出发 总结 SOLID 单一职责原则.开闭原则.里式替换原则.接 ...

最新文章

  1. 一个简单的程序来使用WiredTiger 存储引擎
  2. python中对多态和多态性的理解
  3. 方案一TCP 完成聊天室的编写
  4. Windows使用msi安装MySQL安装教程
  5. java虚拟机参数优化_JAVA虚拟机JVM参数优化(2):垃圾收集算法选择
  6. gradle是否可以编译c语言,build.gradle按条件编译与cmake配置
  7. 天池OCR大赛前排方案总结!
  8. 王学丹 确定测试原始需求
  9. Python单元测试框架——unittest
  10. 成功解决python.exe 无法找到入口 无法定位程序输入点
  11. 16种常用的数据分析方法-时间序列分析
  12. unity c#斗地主算法计算牌型
  13. xdb 服务_localhost 8080 XDB服务器需要用户名和密码的问题
  14. MT5 EA交易期货-获得持仓
  15. html5制作颜色的诗句,带有颜色的诗句(精选60句)
  16. RFID电子耳标识别棒,牦牛身份识别管理专用设备
  17. IPsec VPN 实验
  18. 使用tushare获取股票数据并计算历史概念板块的平均涨跌幅
  19. JAVA从菜鸟到架构师系列课程
  20. SDU_week4_A - DDL 的恐惧(贪心+作业调度问题)

热门文章

  1. 英伟达Jeston nano<3>使用Python实现三种方式串口通信
  2. [Visio] 如何导出Visio中的高清矢量图
  3. mysql报错 could not fetch initial value for incre...
  4. 裸跑2440启动MMC后LED全亮或者全灭问题
  5. CSS+html:魔兽世界网页仿制
  6. [Win32]一个调试器的实现(五)调试符号
  7. 轩辕剑五的乱码问题解决
  8. oracle dg redo日志,DG 配置standby redolog
  9. Java实战—POI操作Excel文档、读取、写入、合并单元格
  10. 习题 3.25 两个乒乓球队进行比赛,各出3人。甲队为A、B、C3人,乙队为X、Y、Z3人。已抽签决定比赛名单。有人向队员打听比赛的名单,A说他不和X比,C说他不和X、Z比,请编程序找出3对赛手的名单