那些容易混淆的设计模式,了解一下~
Posted 涂程
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了那些容易混淆的设计模式,了解一下~相关的知识,希望对你有一定的参考价值。
好文推荐:
作者:RicardoMJiang
前言
了解过设计模式的同学都知道,设计模式家族成员非常庞大,具体可以分为3类共23种设计模式
对我们来说,设计模式在种类上实在是有些多了,而且很多设计模式非常类似,让人傻傻分不清
本文主要介绍一些容易混淆的设计模式,以加深对设计模式的理解
本文主要包括以下内容
- 六大设计原则的介绍
- 简单工厂、工厂方法与抽象工厂模式的区别
- 代理,装饰与适配器模式的区别
- 策略、状态与命令模式的区别
设计原则介绍
众所周知,5大设计原则一般有6个
- 单一职责原则(
Single Responsibility Principle, SRP
) - 开闭原则(
Open Close Principle, OCP
) - 里氏替换原则(
Liskov Substitution Principle, LSP
) - 依赖倒置原则(
Dependence Inversion Principle, DIP
) - 接口隔离原则(
Interface Segregation Principle, ISP
) - 迪米特法则(
Law of Demeter, LoD
),又称最少知识原则(Principle of Least Knowledge
)
其中,前5个原则称为
SOLID
原则
单一职责原则
单一职责原则很简单也很好理解:类的职责应该单一,一个方法只做一件事,这里就不多说了
开闭原则
开闭原则是说程序对扩展开放,对修改关闭:当你的程序需要扩展时,不应该修改原来的代码,而是可以添加一个类来扩展。
比如商品打折,可以更改原来的商品类的getPrice
方法,也可以增加一个子类,重写getPrice
方法,通过高层模块,在打折时使用子类。
开闭原则的思想其实是将不变的地方封装起来,将可变的部分暴露出去,这样它们就不会混淆,还可以复用不变的部分,增加扩展性
开闭原则的优点就在于:原来的代码不用更改,而且还可以复用原来的代码
里氏替换原则
里氏替换原则用一句话描述就是:所有引用基类的地方,必须能够使用其子类直接替换,换句话说,所有引用基类的地方必须透明地使用其子类对象。
但是这看起来有点奇怪,因为在Java
中,引用基类的地方,本来就可以用子类替换,为什么要提出个原则?
这是因为子类可以重写父类的方法,如果父类中有个加法方法,在子类中重写成减法后,如果使用子类替换父类,会导致意料之外的问题
里氏替换原则告诉我们,继承实际上让两个类耦合性增强了,在子类中尽量不要重写父类得方法,在适当的情况下,可以通过聚合,组合,依赖来解决问题
通用的做法是:如果子类需要重写父类的方法,则可以将原有的继承关系去掉,原来的父类A
和子类B
都继承一个更通俗的基类,如果B
需要使用到A
的方法,可以使用组合的方式实现
具体示例可参见:设计模式之里氏替换原则
依赖倒置原则
所谓依赖倒置就是:高层次的模块不应该依赖于低层次的模块,两者都应该依赖于抽象接口
比如一个Person
类需要实现收取信息,需要依赖于Message
类,这里的Person
就是高层次模块,Message
就是低层次模块
依赖倒置其实是说,Person
不应该依赖于Message
的具体实现,而是应该依赖于一个抽象的接口,方便后续扩展
所以说所谓依赖倒置就是面向接口编程,但这个名字的确不是很好理解
接口隔离原则
接口隔离原则简单来说就是:类不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上
这与单一职责原则比较像,单一职责原则针对职责,从业务逻辑划分,而接口隔离原则则要求接口的方法尽量少
感觉这跟最小知识原则又比较像,一个类不应该知道它不需要的东西
最小知识原则
最小知识原则也很容易理解:一个对象应该对其他对象有最少的了解。最小知识原则对类的低耦合提出了明确的要求
简单工厂、工厂方法与抽象工厂模式的区别
工厂模式是我们经常用的模式之一,但是由于它可以细分为3种,很容易让人混淆
我们首先列出他们的定义:
- 简单工厂模式:又称为静态方法工厂模式,是由一个工厂对象决定创建哪一个产品类的实例。
- 工厂方法模式:创建一个用户创建对象的接口,让子类决定实例化哪个类。工厂方法使一个类的初始化延迟到其子类。
- 抽象工厂模式:为创建一组相关或者是相互依赖的对象提供一个接口,而不需要指定它们的具体类。
三种模式的UML
图如下
可以看出它们的主要区别就在于
- 简单工厂模式:一个工厂方法创建不同类型的对象
- 工厂方法模式:一个具体的工厂类负责创建一个具体对象类型
- 抽象工厂模式:一个具体的工厂类负责创建一系列相关的对象
代理,装饰与适配器模式的区别
在结构型设计模式中,结构相似且比较容易混淆的模式有代理、装饰、适配器模式。首先,我们还是列出它们的定义以及UML
图。
- 代理模式:为其他对象提供一种代理以控制对这个对象的访问
- 装饰模式:动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式相比生成子类更加灵活
- 适配器模式:把一个类的接口变换成客户端所期待的另一个接口,从而使原本因接口不匹配而无法工作的两个类能够在一起工作
三种模式的UML
图分别如下:
它们的主要区别在于:
- 代理模式的特点在于隔离,隔离调用类和被调用类的关系,通过一个代理类去调用,因此代理模式不需要传入原有的对象,内部会持有原有对象的实现
- 装饰器模式特点在于增强,他的特点是被装饰类和所有的装饰类必须实现同一个接口,而且必须持有被装饰的对象,可以无限装饰,被装饰对象通过构造函数传入
- 适配器的特点在于兼容, 适配器模式需要实现新的接口,代理,装饰器模式是与原对象实现同一个接口,而适配器类则是匹配新接口
总的来说就是如下三句话:
- 代理模式是将一个类(
a
)转换成具体的操作类(b
). - 装饰模式是在一个原有类(
a
)的基础之上增加了某些新的功能变成另一个类(b
). - 适配器模式是将一个类(
a
)通过某种方式转换成另一个类(b
).
策略、状态与命令模式的区别
策略,状态与命令模式,这三种设计模式都是行为型设计模式,在结构上又都很像,容易让人混淆,我们也首先来看下它们的定义与UML
图
- 策略模式:定义一组算法,将每个算法都封装起来,并且使它们之间可以互换
- 状态模式:状态模式中的行为是由状态来决定的,不同的状态下有不同的行为。允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。
- 命令模式:将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或记录请求日志,可以提供命令的撤销和恢复功能
其UML
图如下所示
我们可以看到,策略与状态模式,它们的类图居然是一样的.虽然它们类型接口一样,但是它们的本质不一样。
区分这三种模式不要关注在结构上,这三种模式最主要是在使用意图上有区别:
- 策略模式:策略模式关注的是算法替换的问题,用一个新的算法替换旧算法,或者提供多种算法由调用者选择,算法的自由替换是它实现的重点
- 状态模式:状态模式策略模式很相似,也是将类的"状态"封装了起来,在执行动作时进行相应的替换,从而实现,类在不同状态下的同一动作显示出不同结果。它与策略模式的区别在于,这种转换是"自动","无意识"的。策略模式会控制对象使用什么策略,而状态模式会自动改变状态。状态模式内部维护一个状态,会随着
public api
的调用进行相应的状态转移。外界不需要知道状态及其变化情况。 - 命令模式:命令模式则关注的是解耦问题,如何让请求者和执行者解耦是它首先需要解决的,解耦的要求就是把请求的内容封装为一个个命令,由接收者执行。由于封装成了命令,就同时可以对命令进行多种处理,例如通过统一的
execute
接口执行命令,或者将命令存储起来,后续做撤销或者恢复功能
关于策略模式与状态模式对比的具体示例,可参见:策略模式 VS 状态模式
总结
本文主要介绍了设计模式六大原则,以及几种类似的容易让人混淆的设计模式。
希望可以通过分析比较这几种容易混淆的设计模式,更加深入地了解它们的异同与适用场景,加深对设计模式的了解
以上是关于那些容易混淆的设计模式,了解一下~的主要内容,如果未能解决你的问题,请参考以下文章