面向对象的SOLID原则

简介

缩写 全称 中文
S The Single Responsibility Principle 单一责任原则
O The Open Closed Principle 开放封闭原则
L Liskov Substitution Principle 里氏替换原则
I The Interface Segregation Principle 接口分离原则
D The Dependency Inversion Principle 依赖倒置原则

单一职责原则

一个类只应承担一种责任。换句话说,让一个类只做一件事。如果需要承担更多的工作,那么分解这个类。

举例

订单和账单上都有流水号、业务时间等字段。如果只用一个类表达,赋予其双重职责,后果:

  1. 特有属性和共有属性相互掺杂,难以理解;
  2. 修改一个场景可能会影响另一个场景。

正确的做法是拆成两个独立的类。

开放封闭原则

实体应该对扩展是开放的,对修改是封闭的。即,可扩展(extension),不可修改(modification)。

举例

一个商户接入了多个付款方式,支付宝和微信支付,如果将调用支付API的类写成:

public class PayHandler {

	public Result<T> pay(Param param) {
if(param.getType() == "ALIPAY") {
// 支付宝付款调用
...
} else if(param.getType() == "WeChatPay") {
// 微信支付付款调用
...
}
}
}

那么每次新加一种支付方式,或者修改原有的其中一种支付方式,都要修改PayHandler这个类,可能会影响现有代码。

比较好的做法是将不同的行为(支付方式)抽象,如下:

public class PayHandler {

	private Map<String, PayProcessor> processors;

	public Result<T> pay(Param param) {
PayProcessor payProcessor = processors.get(param.getType());
// 异常处理略
return payProcessor.handle(param);
}
} interface PayProcessor {
Result<T> handle(Param param);
} public class AlipayProcessor implements PayProcessor {
...
} public class WeChatPayProcessor implements PayProcessor {
...
}

这样,新增支付方式只需要新增类,如果使用的是spring等容器,在xml配置对应key-value关系即可;修改已有的支付方式只需要修改对应的类。最大化地避免了对已有实体的修改。

里式替换原则

一个对象在其出现的任何地方,都可以用子类实例做替换,并且不会导致程序的错误。换句话说,当子类可以在任意地方替换基类且软件功能不受影响时,这种继承关系的建模才是合理的。

举例

经典的例子: 正方形不是长方形的子类。原因是正方形多了一个属性“长 == 宽”。这时,对正方形类设置不同的长和宽,计算面积的结果是最后设置那项的平方,而不是长*宽,从而发生了与长方形不一致的行为。如果程序依赖了长方形的面积计算方式,并使用正方形替换了长方形,实际表现与预期不符。

扩展

不能用继承关系(is-a),但可以用委派关系(has-a)表达。上例中,可以使用正方形类包装一个长方形类。或者,将正方形和长方形作进一步抽象,使用共有的抽象类。

逸闻

“里氏”指的是芭芭拉·利斯科夫(Barbara Liskov,1939年-),是美国第一个计算机科学女博士,图灵奖、冯诺依曼奖得主,参与设计并实现了OOP语言CLU,而CLU语言对现代主流语言C++/Java/Python/Ruby/C#都有深远影响。其项目中提炼出来的数据抽象思想,已成为软件工程中最重要的精髓之一。(来源: 互动百科

接口分离原则

客户(client)不应被强迫依赖它不使用的方法。即,一个类实现的接口中,包含了它不需要的方法。将接口拆分成更小和更具体的接口,有助于解耦,从而更容易重构、更改。

举例

仍以商家接入移动支付API的场景举例,支付宝支持收费和退费;微信接口只支持收费。

interface PayChannel {
void charge();
void refund();
} class AlipayChannel implements PayChannel {
public void charge() {
...
} public void refund() {
...
}
} class WeChatChannel implements payChannel {
public void charge() {
...
} public void refund() {
// 没有任何代码
}
}

第二种支付渠道,根本没有退款的功能,但是由于实现了PayChannel,又不得不将refund()实现成了空方法。那么,在调用中,这个方法是可以调用的,实际上什么都没有做!

改进

将PayChannel拆成各包含一个方法的两个接口PayableChannel和RefundableChannel。

依赖倒置原则

  1. 高层次的模块不应依赖低层次的模块,他们都应该依赖于抽象。
  2. 抽象不应依赖于具体实现,具体实现应依赖抽象。

实际上,依赖倒置是实现开闭原则的方法。

举例

开闭原则的场景仍然可以说明这个问题。以下换一种表现形式。

public class PayHandler {

	public Result<T> pay(Param param) {
if(param.getType() == "ALIPAY") {
AlipayProcessor processor = new AlipayProcessor();
processor.hander(param);
...
} else if(param.getType() == "WeChatPay") {
WeChatPayProcessor processor = new WeChatPayProcessor();
processor.hander(param);
...
}
}
} public class AlipayProcessor { ... } public class WeChatPayProcessor { ... }

这种实现方式,PayHandler的功能(高层次模块)依赖了两个支付Processor(低层次模块)的实现。

扩展:IOC和DI

控制反转(IOC)和依赖注入(DI)是Spring中最重要的核心概念之一,而两者实际上是一体两面的。

  • 依赖注入

    • 一个类依赖另一个类的功能,那么就通过注入,如构造器、setter方法等,将这个类的实例引入。
    • 侧重于实现。
  • 控制反转
    • 创建实例的控制权由一个实例的代码剥离到IOC容器控制,如xml配置中。
    • 侧重于原理。
    • 反转了什么:原先是由类本身去创建另一个类,控制反转后变成了被动等待这个类的注入。

后记

网络上很多文章中关于SOLID的介绍,语句都不通顺,徒增理解难度。如果对基本释义仍不能领会,可以参考 英文WIKI

面向对象的SOLID原则白话篇的更多相关文章

  1. 面向对象涉及SOLID原则

    S = Single Responsibility Principle 单一职责原则 O = Opened Closed Principle 开放闭合原则  L = Liscov Substituti ...

  2. 在net中json序列化与反序列化 面向对象六大原则 (第一篇) 一步一步带你了解linq to Object 10分钟浅谈泛型协变与逆变

    在net中json序列化与反序列化   准备好饮料,我们一起来玩玩JSON,什么是Json:一种数据表示形式,JSON:JavaScript Object Notation对象表示法 Json语法规则 ...

  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. 面向对象设计SOLID五大原则

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

  6. PHP 进阶篇:面向对象的设计原则,自动加载类,类型提示,traits,命名空间,spl的使用,反射的使用,php常用设计模式 (麦子学员 第三阶段)

    以下是进阶篇的内容:面向对象的设计原则,自动加载类,类型提示,traits,命名空间,spl的使用,反射的使用,php常用设计模式 ================================== ...

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

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

  8. 面向对象SOLID原则的自我理解

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

  9. 浅谈 SOLID 原则的具体使用

    SOLID 是面向对象设计5大重要原则的首字母缩写,当我们设计类和模块时,遵守 SOLID 原则可以让软件更加健壮和稳定.那么,什么是 SOLID 原则呢?本篇文章我将谈谈 SOLID 原则在软件开发 ...

随机推荐

  1. 04(1) 基于上下文相关的GMM-HMM声学模型1

    1.上下文对音素发音的语谱轨迹的影响 受到上下文的影响,同一个音素的发音语谱轨迹不同 为提高识别准确率,对音素建模时应将这种上下文影响考虑在内 2.基于上下文相关的音素建模 注意,非单音素建模中,每个 ...

  2. JQuery hover鼠标变换

    一般而言,我们为非按钮.链接等元素添加hover事件时,虽然能够处理悬停事件,但是鼠标却并没有变化,会造成悬停事件不明显的结果,为此,我们可以添加CSS样式cursor:pointer,使得该元素的悬 ...

  3. mui开发app之webview是什么

    WebView(网络视图)能加载显示网页,可以将其视为一个浏览器,webview被封装在html5+,plus对象中,底层由java,OC实现. 先来谈谈我对webview的理解: 使用mui开发的a ...

  4. MySQL AutoCommit带来的问题

    现象描述 测试中发现,服务A在得到了服务B的注册用户成功response以后,开始调用查询用户信息接口,却发现无法查询出任何结果.检查binlog发现,在查询请求之前,数据库确实已经完成了commit ...

  5. <java>枚举的简单介绍

    1.枚举,enum关键字,相当于public final static. 2.举例: 首先定义了一个名为spiciness的枚举类型. public enum Spiciness { NOT, MIL ...

  6. openresty源码剖析——lua代码的执行

    上一篇文章中我们讨论了openresty是如何加载lua代码的 那么加载完成之后的lua代码又是如何执行的呢 ##代码的执行  在init_by_lua等阶段  openresty是在主协程中通过lu ...

  7. zen coding一个牛的不行的html和css开发工具

    zen coding 是一种仿css选择器的语法来快速开发html和css的开源项目.现已更名为Emmet.可以到github上下载拜读.在这个都想偷懒的世界里,此方法可以极大的缩短开发人员的开发时间 ...

  8. 微信公众号开发《三》微信JS-SDK之地理位置的获取,集成百度地图实现在线地图搜索

    本次讲解微信开发第三篇:获取用户地址位置信息,是非常常用的功能,特别是服务行业公众号,尤为需要该功能,本次讲解的就是如何调用微信JS-SDK接口,获取用户位置信息,并结合百度地铁,实现在线地图搜索,与 ...

  9. Java7中的ForkJoin并发框架初探(中)——JDK中实现简要分析

    原文发表于 2013 年 8 月 28 日 由 三石 根据前文描述的Doug Lea的理论基础,在JDK1.7中已经给出了Fork Join的实现.在Java SE 7的API中,多了ForkJoin ...

  10. 非阻塞式线程安全列表-ConcurrentLinkedDeque

    一.ConcurrentLinkedDeque public class ConcurrentLinkedDeque<E> extends AbstractCollection<E& ...