面向对象的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. office web apps 部署-搭建域控服务器

    开始第一条先说注意事项:我所配置的环境是用了三台2012server虚拟机,三台虚拟机必须要加下域控,而且登录操作的时候必须以域账号登录,否则测试不通过!在笔记本上搭建了两个虚拟机(window se ...

  2. 《JavaScript权威指南》学习——js闭包

    序:闭包这个玩意啊~在很多没有代码块的语言中都会出现,已经成为大多程序员入门的一道坎,闭包让很多程序员觉得晦涩(事实上百度一下这个名词,真的说的很晦涩啊亲==|||),我第一次知道闭包这个名词是从&l ...

  3. 关于li标签之间的间隔如何消除!

    问题:li标签用了display:inline之后虽然成功的合并在一行,但是li标签之间出现了间距. 原因:按enter键换行之后li标签之间存在着空格,正是这些空格占据了li标签之间的空间. 解决方 ...

  4. 发送验证码(××s后重新发送)

    html: <input class="tel" type="text" name="tel" placeholder="手 ...

  5. 解决NSTimer循环引用Retain Cycle问题

    解决NSTimer循环引用Retain Cycle问题 iOS开发中以下的情况会产生循环引用 block delegate NSTimer 循环引用导致一些对象无法销毁,一定的情况下会对我们横须造成影 ...

  6. WebGIS开源解决方案之矢量数据导入

    前几篇介绍了开源WebGIS开发环境的搭建,本篇开始陆续介绍这些软件的使用,WebGIS的开发,首要的问题是解决数据来源,本篇主要介绍矢量数据在开源空间数据库PostgreSQL中的存储.后续篇幅中再 ...

  7. Java设计模式随笔

    大家都知道Java23种设计模式,大神总结如下: 创建型模式,共五种:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式. 结构型模式,共七种:适配器模式.装饰器模式.代理模式.外观模式.桥接 ...

  8. 用VsCode编辑TypeScript

    文地址:https://code.visualstudio.com/Docs/languages/typescript TypeScript是Javascript的超集,它提供了类.模块.接口来帮助你 ...

  9. 百度地图API,定位您的当前位置

    1.介绍 利用百度地图的API来定位您的所属位置,这个位置返回的是经纬度,而不是具体的汉字位置.利用经纬度,再显示在百度地图上的位置. 2.代码 <html> <head> & ...

  10. Linux下memcache的安装和启动测试

    memcache是一套分布式的高速缓存系统,MemCache的工作流程如下:先检查客户端的请求数据是否在memcached中,如有,直接把请求数据返回,不再对数据库进行任何操作:如果请求的数据不在me ...