设计模式之SOLID原则

设计模式之SOLID原则

在程序设计领域, SOLID(单一功能、开闭原则、里氏替换、接口隔离以及依赖反转)是由罗伯特·C·马丁在21世纪早期引入,指代了面向对象编程和面向对象设计的五个基本原则。当这些原则被一起应用时,它们使得一个程序员开发一个容易进行软件维护和扩展的系统变得更加可能。

1 单一职责原则(SRP)

一个对象应该只包含单一的职责,并且该职责被完整地封装在一个类中,即又定义有且仅有一个原因使类变更。(甲类负责两个不同的职责:职责A,职责B。当由于职责A需求发生改变而需要修改类T时,有可能会导致原本运行正常的职责B功能发生故障。也就是说职责A和B被耦合在了一起”)。

2 开放封闭原则(OCP)

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

eg:

原代码,不同用户类型进行不同服务,但是后续每新增不同的用户类型,只能在下面继续加判断代码。

修改后代码,用户实现统一的接口,后续新增用户类型,只需要新增对应实现类。

3 里氏替换原则(LSP)

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

4 接口隔离原则(ISP)

接口隔离原则表明客户端不应该被强迫实现一些他们不会使用的接口,应该把胖接口中的方法分组,然后用多个接口替代它,每个接口服务于一个子模块。简单地说,就是使用多个专门的接口比使用单个接口要好很多。

ISP的主要观点如下:

1)一个类对另外一个类的依赖性应当是建立在最小的接口上的。

ISP可以达到不强迫客户(接口的使用方法)依赖于他们不用的方法,接口的实现类应该只呈现为单一职责的角色(遵循SRP原则)

ISP还可以降低客户之间的相互影响---当某个客户要求提供新的职责(需要变化)而迫使接口发生改变时,影响到其他客户程序的可能性最小。

2)客户端程序不应该依赖它不需要的接口方法(功能)。

客户端程序就应该依赖于它不需要的接口方法(功能),那依赖于什么?依赖它所需要的接口。客户端需要什么接口就是提供什么接口,把不需要的接口剔除,这就要求对接口进行细化,保证其纯洁性。

5 依赖倒置原则(DIP)

抽象不应该依赖于细节,细节应当依赖于抽象。换言之,要针对抽象(接口)编程,而不是针对实现细节编程。

开闭原则(OCP)是面向对象设计原则的基础也是整个设计的一个终极目标,而依赖倒置原则(DIP )则是实现OCP原则的一个基础,换句话说开闭原则(OCP)是你盖一栋大楼的设计蓝图,那么依赖倒置原则就是盖这栋大楼的一个钢构框架。

来看一个例子假设我们在开发一个软件产品需要一个日志系统,要将系统产生的一些重要事情记录在记事本上。通常我们的实现如下:

但是随着时间的推移,产品做的好买了很多客户,产品变得越来越大,使用Logger 类的地方成千上万处,可怕的事情终于发生了:

A 客户提出来我想把日志存在数据库中便于做统计分析。

B 客户说我想把日志打印在一个控制台上便于我时时监测系统运行情况。

C 客户说我要把日志存到Windows Azure Storage上。

怎么办呢? 回过头来看看我们的这个日志系统的设计才恍然大悟:没有遵守面向对象设计原则的依赖倒置原则和开闭原则了。知道就好,找到法门了, 我们将日志这一块的设计重构一下让其符合OCP和DIP应该就可以了。 那么我们就要首先抽象写日志的接口ILog, 让实际调用的地方调用高层抽象(ILog),具体的实现类TextLogger,ConsoleLogger,DatabaseLogger,AzureStorageLogger都继承自ILog接口,然后我们在利用反射加配置,不同的用户配置不同的具体实现类,这样问题就迎任而解了。

发布于 2019-10-08 14:42