三者的简介
- 策略模式:定义一组算法,将每个算法都封装起来,并且使它们之间可以相互转换
比如在执行一个排序算法的时候,排序的算turnOff冒泡、快排、堆排等,通过策略模式,则可以巧妙地在不同的算法之间进行切换。
- 状态模式:当一个对象内在状态改变时允许其改变行为,这个对象看起来像改变了其类
假设电视机有两种状态:开、关。如果电视机在开的时候,可以切换频道,电视机在关的时候,也可以切换频道。但是这两种切换频道所带来的结果不一样。
- 命令模式:将一个请求封装成一个对象,从而让使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能
主要是通过命令的发布者、命令、接收者,从而达到了发布者和接收者的解耦,并且能够更加精细的控制命令的执行状态。
策略模式
策略模式重在整个算法的替换,也就是策略的替换。
通过注入不同的实现对象来实现算法或者策略的动态替换。模式的可扩展性、可维护性更高。
策略模式中的行为是彼此独立的、可相互替换的。
使用场景
- 针对
同一类型
的多种处理方式,仅仅是具体行为有差别时。 - 需要安全地
封装多种同一类型的操作
时。 - 出现
同一抽象类
有多个子类,而又需要使用if-else或者switch-case来选择具体的子类时。
UML图
图片中
- Context是上下文,用来操作策略模式的上下文
- Stragety是一种策略的抽象
- ConcreteStragetyA和B是具体的策略的抽象的实现
总结
由此可见,在策略模式中,最主要看重的东西是策略。对于一件事情可以有不同的策略,当然,策略的结果可以是一样的也可以是不一样的。一个问题如果有多个解决方案的时候,最简单的当然是利用if-else。但如果是对于方案特别多的时候,这样就会显得耦合性特别高,代码也会特别的臃肿。
相对于if-else,策略模式将不同的策略构成一个具体的策略实现,通过不同的策略实现算法替换。
举个例子,在Android中,存在一个动画的东西。而各种动画,则是使用了策略模式。所有的Android中的动画都是实现了Interpolator这个接口。
状态模式
状态模式的行为是平行的、不可替换的。
状态模式把对象的行为包装在不同的状态对象里,每一个状态对象都有一个共同的抽象状态基类。状态模式的意图是让一个对象在其内部状态改变的时候,其行为也随之改变。
使用场景
- 一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为
- 代码中包含大量与对象状态有关的条件语句
电视机代码
简述:
电视机的状态有两种,一种是开一种是关,那么可以定义一个接口, 此时,对于两种状态,会有PowerOffState以及PowerOnState,如下
interface TvState {
fun nextChannel()
fun prevChannel()
fun turnOn()
fun turnUp()
}
class PowerOffState : TvState {
override fun nextChannel() {
TODO("No action because of power off")
}
override fun prevChannel() {
TODO("No action because of power off")
}
override fun turnUp() {
TODO("No action because of power off")
}
override fun turnOn() {
TODO("Can turn on")
}
}
class PowerOnState : TvState {
override fun nextChannel() {
TODO("nextChannel")
}
override fun prevChannel() {
TODO("prevChannel")
}
override fun turnUp() {
TODO("Turn off")
}
override fun turnOn() {
TODO("Do nothing because of power on")
}
}
上述便是电视的状态,那么对于电视遥控器的状态呢?假设它只能够进行开或者关,那么有如下的代码:
interface PowerController {
fun powerOn()
fun powerOff()
}
class TvController : PowerController {
private lateinit var tvState:TvState
private fun setTvState(tvState:TvState){
this.tvState=tvState
}
override fun powerOff() {
setTvState(PowerOffState()) // 电视遥控器设置电视开机了
}
override fun powerOn() {
setTvState(PowerOnState()) //电视遥控器设置电视关机了
}
fun nextChannel(){
tvState.nextChannel() //电视遥控器设置下一个频道
}
fun prevChannel(){
tvState.prevChannel()
}
fun turnUp(){
tvState.turnUp()
}
fun turnDown(){
tvState.turnDown()
}
}
以上,就是状态模式中例子的代码。
状态模式的UML图
- State:抽象的状态类或者接口
- ConcreteStateA、B是具体的状态类
- Context是环境类
总结
举个例子,在Android中WiFi的开启,使用了状态模式。在WiFi初始状态下,扫描的请求直接被忽略,在驱动加载状态中WiFi扫描的请求直接被添加到延迟处理的消息列表中,在驱动加载完成后扫描的WiFi将会被直接处理。
Android中的Wifi请求的行为表明了,在不同的状态下的不同行为,而这种模式就是状态模式。
State模式将所有与一个特定的状态相关的行为都放入一个状态对象中,它提供了一个更好的方法来组织与特定状态相关的代码,将繁琐的状态判断转换成结构清晰的状态类族,在避免代码膨胀的同时也保证了可扩展性与可维护性。
命令模式
将一个请求封装成一个对象,从而让用户使用不同的请求把客户端参数化;对请求排队或记录请求日志,以及支持可撤销的操作。
使用场景
- 需要抽象出待执行的动作,然后以参数的形式提供出来。
- 在不同的时刻制定、排列和执行请求。
- 需要支持取消操作。
- 需要支持事务操作。
代码例子
/**
* 接收者类,一般该类是一个执行具体逻辑的角色
*/
class Receiver {
fun action() {
TODO("Do something")
}
}
/**
* 抽象命令接口,命令角色
*/
interface Command {
fun execute()
}
/**
* 具体命令类,
*/
class ConcreteCommand(
private var receiver: Receiver
) : Command {
override fun execute() {
receiver.action()
}
}
/**
* 请求者类
*/
class Invoker(
private var command:Command
){
fun action(){
command.execute()
}
}
fun main(){
val receiver=Receiver()
val command=ConcreteCommand(receiver)
val invoker=Invoker(command)
invoker.action()
}
命令模式UML图
总结
命令模式中,类非常的膨胀,大量衍生类的创建。好处是:更弱的耦合性、更灵活的控制性以及更好的扩展性。