设计模式:状态模式与策略模式
状态模式
在程序中,我们通常可以认为,对象在任一时刻的状态都是有限,可枚举的。且根据不同的状态,对象的行为也是不同的。对象可以瞬间从一个状态切换到另一个状态。
举一个例子,一个音乐播放器,具有以下三种状态:
初始化、播放、暂停、加载中
在播放的时候,我们可以按下暂停键,此时播放器的状态会转移到暂停。
在暂停的时候,按下播放键,此时会转移到播放状态。
而无论在播放还是暂停,如果按下“下一曲”按钮,一定会进入加载中状态,当加载中结束后,播放器又会自动变为播放状态。
传统实现方式,我们会这样写代码:
public enum State
{
Init = 0,
Pause = 1,
Play = 2,
Loading = 3,
}
public class Player
{
private State _state;
public Player()
{
_state = State.Init;
}
public void PlayButton()
{
if (_state == State.Init)
{
Load();
Play();
}
else if (_state == State.Pause)
{
Play();
}
}
public void PauseButton()
{
if (_state == State.Play)
{
Pause();
}
}
public void NextButton()
{
Load();
Play();
}
private void Load()
{
_state = State.Loading;
Thread.Sleep(1000);
}
private void Play()
{
_state = State.Play;
}
private void Pause()
{
_state = State.Pause;
}
}
可以看到,在按下不同的按钮的时候,我们需要根据当前的状态进行不同的处理,目前播放器的状态只有四种,如果状态更多一些,比如再增加一种“单曲循环状态”、“FM模式播放状态”,那么就会对代码产生侵入式的影响,我们需要和旧有的if-else产生大量的纠缠与斗争。
而通过状态模式,可以优雅的解决这个问题。
下面将用状态模式重新实现代码:
public interface IPlayerState
{
void SetPlayer(IPlayer player);
void OnNextButton();
void OnPlayButton();
void OnPauseButton();
}
public class InitState : IPlayerState
{
private IPlayer _player;
public void SetPlayer(IPlayer player)
{
_player = player;
}
public void OnNextButton()
{
if (_player == null)
throw new InvalidOperationException();
var loadingState = new LoadingState();
loadingState.SetPlayer(_player);
_player.ChangeState(loadingState);
_player.Loading();
_player.Play();
var playState = new PlayingState();
playState.SetPlayer(_player);
_player.ChangeState(playState);
}
public void OnPlayButton()
{
throw new System.NotImplementedException();
}
public void OnPauseButton()
{
throw new System.NotImplementedException();
}
}
public class PlayingState : IPlayerState
{
public void SetPlayer(IPlayer player)
{
throw new NotImplementedException();
}
public void OnNextButton()
{
throw new NotImplementedException();
}
public void OnPlayButton()
{
throw new NotImplementedException();
}
public void OnPauseButton()
{
throw new NotImplementedException();
}
}
public class PauseState : IPlayerState
{
public void SetPlayer(IPlayer player)
{
throw new NotImplementedException();
}
public void OnNextButton()
{
throw new NotImplementedException();
}
public void OnPlayButton()
{
throw new NotImplementedException();
}
public void OnPauseButton()
{
throw new NotImplementedException();
}
}
public class LoadingState : IPlayerState
{
public void SetPlayer(IPlayer player)
{
throw new NotImplementedException();
}
public void OnNextButton()
{
throw new NotImplementedException();
}
public void OnPlayButton()
{
throw new NotImplementedException();
}
public void OnPauseButton()
{
throw new NotImplementedException();
}
}
public interface IPlayer
{
void Play();
void Pause();
void Loading();
void ChangeState(IPlayerState state);
}
public class Player : IPlayer
{
private IPlayerState _state;
public Player(IPlayerState initState)
{
_state = initState;
}
public void PlayButton()
{
_state.OnPlayButton();
}
public void PauseButton()
{
_state.OnPauseButton();
}
public void NextButton()
{
_state.OnNextButton();
}
public void Play()
{
}
public void Pause()
{
}
public void Loading()
{
}
public void ChangeState(IPlayerState state)
{
_state = state;
}
}
由于篇幅过长,我仅实现了在初始化状态下,按下下一曲的状态改变。可以看到,我们完全干掉了if-else,但是代价确实增加了足足四个状态类,每个状态类实现了三个方法。代码量相比if-else写法增加了很多。但是这样做是完全值得的,它的可维护性,是if-else无可比拟的。
策略模式
策略模式允许我们将一系列算法进行封装,客户端可以自由选择所需的算法,而无需了解算法的细节。
例如最经典的排序算法。我们可以将排序算法抽象成一个接口,它输入一个无序数组,返回一个有序数组。我们可以使用各种排序方法实现这个接口,例如冒泡,快排等等。
策略模式的例子较为简单,就不再通过代码演示。
不过值得注意的是,许多语言支持函数对象(函数指针),可以通过函数对象来直接指向特定的算法,而无需过多的定义接口和类。例如C#完全可以通过委托来进行实现。
总结
状态模式与策略模式具有一定的相似之处,他们都将工作委托给了另一对象。但是在状态模式中,几乎每种状态类都知道其他状态的存在,并且会在合适的时候使状态发生转换。而策略模式中,每种策略之间不知道彼此的存在。他们之间没有依赖关系。
- 0
- 0
-
分享