缩写 | 全称 | 中文 |
---|---|---|
S | The Single Responsibility Principle | 单一责任原则 |
O | The Open Closed Principle | 开放封闭原则 |
L | Liskov Substitution Principle | 里氏替换原则 |
I | The Interface Segregation Principle | 接口分离原则 |
D | The Dependency Inversion Principle | 依赖倒置原则 |
一个类只应承担一种责任。换句话说,让一个类只作一件事。若是须要承担更多的工做,那么分解这个类。java
订单和帐单上都有流水号、业务时间等字段。若是只用一个类表达,赋予其双重职责,后果:spring
正确的作法是拆成两个独立的类。微信
实体应该对扩展是开放的,对修改是封闭的。即,可扩展(extension),不可修改(modification)。网络
一个商户接入了多个付款方式,支付宝和微信支付,若是将调用支付API的类写成:微信支付
public class PayHandler { public Result<T> pay(Param param) { if(param.getType() == "ALIPAY") { // 支付宝付款调用 ... } else if(param.getType() == "WeChatPay") { // 微信支付付款调用 ... } } }
那么每次新加一种支付方式,或者修改原有的其中一种支付方式,都要修改PayHandler这个类,可能会影响现有代码。设计
比较好的作法是将不一样的行为(支付方式)抽象,以下:code
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关系便可;修改已有的支付方式只须要修改对应的类。最大化地避免了对已有实体的修改。xml
一个对象在其出现的任何地方,均可以用子类实例作替换,而且不会致使程序的错误。换句话说,当子类能够在任意地方替换基类且软件功能不受影响时,这种继承关系的建模才是合理的。对象
经典的例子: 正方形不是长方形的子类。缘由是正方形多了一个属性“长 == 宽”。这时,对正方形类设置不一样的长和宽,计算面积的结果是最后设置那项的平方,而不是长*宽,从而发生了与长方形不一致的行为。若是程序依赖了长方形的面积计算方式,并使用正方形替换了长方形,实际表现与预期不符。继承
不能用继承关系(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。
实际上,依赖倒置是实现开闭原则的方法。
开闭原则的场景仍然能够说明这个问题。如下换一种表现形式。
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)是Spring中最重要的核心概念之一,而二者其实是一体两面的。
网络上不少文章中关于SOLID的介绍,语句都不通顺,徒增理解难度。若是对基本释义仍不能领会,能够参考 英文WIKI。