策略模式和状态模式

        设计模式,个人理解是一种微妙的东西,它的存在,就像是衣服搭配一样,要么就是根据形象搭配,要么就是根据环境搭配,要么就是根据其他穿着搭配。美好的搭配,可以奠稳形象,可以锦上添花。

        平时我们可以从概念上理解各种设计模式(32种设计模式)的概念以及使用场景,当我们在真实项目中遇到类似场景时,就能有联想到适合的设计模式并加以使用。

        在这里,我们讨论一下策略模式和状态模式,因为刚巧在最近的项目中有用到这两种设计模式。

一、策略模式

项目背景:我司产品是智能音箱,该音箱的手势交互是一块触摸板,可以想象为一块手机屏幕,只是没有显示,仅仅响应用户的手势输入,例如,上下滑动表示音量增减,单击表示暂停播放,左右滑动表示切换上下首歌曲等。问题在于,音箱有两个场景,一个作为智能音箱响应云端指令和音乐,一个作为蓝牙音箱响应蓝牙控制端的指令和音乐,场景抽象为,多个类只表现在行为不同,可以在运行时动态切换要执行的行为;

1、音箱的触摸板可以对当前播放进行控制,比如上一首,下一首,播放,暂停,音量增减等;

2、但是由于当前播放的音频分为在线播放和蓝牙播放,如果没有一个好的设计的话,每次滑动触摸板的时候,需要判断当前的音频是来自蓝牙的还是来自网络在线的,会特别的繁琐。

所以,在这里,我想到了使用策略模式去套用这个场景。

使用方式:

1、抽象一个策略角色(Strategy角色),通过一个接口来实现,在接口里面封装具体策略类实现的方法,我们项目在里面封装了play(),stop(),next(),pre(),volume()等方法;

2、环境角色(Context),持有策略(Strategy)的引用,在里面调用策略的方法;

3、具体策略角色(ConcreteStrategy),包含了具体的算法或者行为。

具体实现:1、定义策略接口

public interface MusicStrategy {

         void play();

         void stop();

         void next();

         void pre();

         void volume()

}

2、定义环境角色

public class MusicControl {

      private MusicStrategy musicStrategy;

      //传入具体的策略

      public MusicControl (MusicStrategy musicStrategy) {

      this.musicStrategy = musicStrategy;

}

//具体的策略实现

public void play() {

      musicStrategy.play();

}

。。。。。。

}

 

3、定义具体的策略角色

public class BTStrategy extends MusicStrategy {

@Override

public void play() {

。。。

}

。。。。。。

}

public class OnlineStrategy extends MusicStrategy {

@Override

public void play() {

。。。

}

。。。。。。

}

4、在触摸板调用处使用

MusicStrategy musicStrategy = new BTStrategy();

      MusicControl musicControl = new MusicControl(musicStrategy);

      musicControl.play()

整个实现流程大概如此(纯手敲的代码,格式有点丑,见谅),这样,就可以省却了每次在调用行为方法时需要一系列的if...else...的判断,结构和代码都有了清晰的展现

二、状态模式

项目背景:整个智能音箱的链路涉及到唤醒--->倾听----->识别---->播报,大概如下图所示

其中,1,2 的过程是串行过程,3过程可并行执行,意思是,必须从唤醒--->倾听--->识别这个流式串行过程,而且一旦唤醒了,必须这个流式执行完才能进行下一个唤醒,不能同时存在两个串行。其实很多语音方案都是如此设计,如果我们不遵循这个原则,可能会导致一些问题(多线程问题,内存泄露等),场景抽象为对象的行为依赖于其所在的状态,当其状态改变时,所有依赖这个对象都得到更新。

基于此状态的场景,我们可以套用状态模式优化这个过程;

使用方式:

1、抽象一个语音的行为接口VoiceState,我们这里使用抽象类表示,在里面定义所有的方法wakeup(),listening(),thinking(),speaking()

2、定义一个环境类Context,其中持有VoiceState的引用,通过该VoiceState的引用调起相应的方法;

3、具体的状态子类,继承语音行为接口VoiceState,进行具体的操作,并且进行状态的转化,该状态的变化体现需体现在Context环境类中

具体实现:

1、定义语音接口VoiceState;

public abstract class VoiceState{

        protected Context context;

        public void setContext(Context context) {

          this.context = context;

     }

         public abstract void wakeup();

         public abstract void listening();

         public abstract void thinking();

         public abstract void speaking(); 

}

2、定义环境类Context

public class Context{

//定义所有的状态

public final static WakeUpState wakeupstate = new WakeUpState();

public final static ThinkingState thinkingState = new ThinkingState();

public final static ListeningState listeningState = new ListeningState();

public final static SpeakingState speakingState = new SpeakingState();

   private VoiceState voiceState;

   public VoiceState getState(){

      return this.voiceState;

   } 

    public void setVoiceState(VoiceState voiceState){

      this.voiceState = voiceState;   

      this.voiceState.setContext(this);

}

   public void wakeup() {

    this.voiceState.wakeup();

}

  public void listening() {

   this.voiceState.listening();

}

  public void thinking() {

   this.voiceState.thinking();

}

  public void speaking() {

   this.voiceState.speaking();

}

}

3、具体的状态子类

public class WakeUp extends VoiceState {

    @Override

     public void wakeup(){

       //不能再次执行唤醒

  }

     @Override

    public void thinking() {

            //未到识别阶段

   }

     public void listening(){

            super.context.setVoiceState(Context.listenState);

            super.context.getVoiceState().listening();

}

。。。。。。

}

余下的子类省略。。。。。。

4、调用端

Context context = new Context();

//在唤醒成功的回调中,调用

context.setVoliceState(new WakeUpState());

context.wake();

//然后依次在每个回调中(listening,thinking,speaking)通过context调用相应的方法,让状态在其内部转化,这样可以防止在调用的时候由于状态不一致导致的错误

通过这个状态流程,可以很自然的在识别完了之后,可同时进入唤醒状态和播报状态,防止了在某些异常情况下,无法进入唤醒状态的重大问题。

三、策略模式和状态模式的异同

     看了上诉两个例子的使用方式,我们会有一种疑惑,觉得策略模式和状态模式非常的相像;确实,他们拥有很多相似点,甚至可以说UML图也差不多是一样的,但是本质上是不同的。

1、策略模式封装了一组行为或者算法,它允许Client在运行时动态的切换;状态模式是帮助一个类在不同的状态下显示不同的行为,依赖于内部的状态;

2、策略模式不持有Context的引用,而是被Context所使用;状态模式的每个状态都持有Context的引用,从而在Context中实现状态的转移;

3、从理论上说,策略模式定义对象应该“怎么做”;状态模式定义了对象“是什么”,“什么时候做”。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值