策略模式和状态模式

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

        平时我们可以从概念上理解各种设计模式(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、从理论上说,策略模式定义对象应该“怎么做”;状态模式定义了对象“是什么”,“什么时候做”。

### 状态模式策略模式区别 状态模式策略模式在结构意图上存在显著差异。状态模式聚焦于对象内部状态的管理,通过将状态行为紧密绑定,使对象在状态变化时能够自动调整其行为。例如,红绿灯系统中,不同状态(红、黄、绿)会触发不同的行为(停止、准备、通行),这种状态转换通常由对象内部状态决定,且遵循固定的规则[^1]。 策略模式则强调算法的封装动态替换。它允许客户端根据需求选择不同的算法实现,而不影响使用算法的客户端逻辑。例如,出行方式可以选择步行、骑车或公交,每种方式对应不同的实现逻辑,但接口保持一致,使客户端可以灵活切换而无需修改调用代码。 ### 结构交互差异 在状态模式中,状态转换通常由对象内部的状态变化触发。客户端对象交互时,无需关心具体的状态转换逻辑,仅需通过统一接口调用行为方法。状态类通常持有对象引用,以便在状态变化时能够操作对象的状态属性。 策略模式则要求客户端显式选择策略,策略类之间相互独立,不存在特定的转换关系。策略类通常不持有上下文对象的引用,仅负责执行具体的算法逻辑。这种设计使得策略模式更加灵活,但也要求客户端在运行时决定使用哪种策略[^3]。 ### 适用场景 状态模式适用于对象状态变化明显且状态转换规则固定的情况。例如,订单状态管理、游戏角色状态变化、线程状态转换等场景。状态模式能够有效避免在代码中硬编码大量的状态判断逻辑(如 `if (state == X) { ... }`),从而提高代码的可维护性扩展性[^3]。 策略模式适用于需要动态切换算法的场景。例如,支付方式选择、折扣策略应用、排序算法切换等。策略模式通过封装不同算法实现,使得客户端可以按需选择合适的策略,同时保持接口的一致性,降低耦合度[^3]。 ### 设计复杂度 尽管在某些情况下,状态模式策略模式可以互相转化,但两者在设计复杂度上有所不同。状态模式由于涉及状态的自动转换状态之间的关联,通常在类结构上比策略模式更复杂。这种复杂性并非指代码难以理解,而是从设计模式的角度来看,状态模式需要更多的类状态转换逻辑来支持自动的状态变化[^2]。 相比之下,策略模式的结构相对简单,主要关注算法的封装替换,不涉及状态的自动转换。因此,在仅需选择合适执行方案的情况下,策略模式更为适用[^2]。 ```python # 示例:策略模式的简单实现 class Strategy: def execute(self, data): raise NotImplementedError("Subclasses should implement this!") class ConcreteStrategyA(Strategy): def execute(self, data): return sorted(data) class ConcreteStrategyB(Strategy): def execute(self, data): return reversed(sorted(data)) class Context: def __init__(self, strategy: Strategy): self._strategy = strategy def set_strategy(self, strategy: Strategy): self._strategy = strategy def execute_strategy(self, data): return self._strategy.execute(data) # 使用策略模式 context = Context(ConcreteStrategyA()) result = context.execute_strategy([3, 1, 4, 1, 5]) print(result) # 输出:[1, 1, 3, 4, 5] context.set_strategy(ConcreteStrategyB()) result = context.execute_strategy([3, 1, 4, 1, 5]) print(result) # 输出:[5, 4, 3, 1, 1] ``` ### 总结 状态模式策略模式虽然在实现上可能相似,但它们的设计意图适用场景有明显区别状态模式强调状态的自动转换行为的动态变化,适合状态变化明显且有固定转换规则的场景;策略模式则注重算法的封装动态替换,适合需要灵活选择执行方案的场景。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值