设计模式之策略模式和状态模式(strategy pattern & state pattern)

本文来说解一下两个结构比较类似的行为设计模式:策略模式和状态模式。二者单独的理解和学习都是比较直观简单的,可是实际使用的时候却并很差实践,算是易学难用的设计模式吧。这也是把二者放在一块儿介绍的缘由,通过对比和实例介绍,相信应该会一些比较深入的感知。最后在结合我的的体会简单聊一下对这两个模式的一些见解。算法

1. 模式概念

1.1 策略模式

运行时更改类的行为或算法,从而达到修改其功能的目的;设计模式

使用场景: 一个系统须要动态地在几种算法中选择一种,而这些算法之间仅仅是他们的行为不一样。 此外决策过程当中过多的出现if else,也能够考虑使用该模式。api

实现:将这些算法封装成可单独运行的类,由使用者根据须要进行替换。ide

优势: 较为灵活,扩展性好,避免大量的if else结构。函数

缺点: 对外暴露了类全部的行为和算法,行为过多致使策略类膨胀。性能

1.2 状态模式

运行时类的行为由其状态决定;学习

使用场景: 对象依赖装填,行为随状态改变而改变的情景,或者存在大量的if else和分支结构等;this

实现:将对象的状态封装成单个的类,每一个状态处理该状态下的事务,并控制该状态到其余状态的转移;加密

优势: 容易新加状态,封装了状态转移规则,每一个状态能够被复用和共享,避免大量的if else结构。spa

缺点: 该模式结构和实现相对复杂,状态过多致使增长类和对象个数。同时因为由每一个状态控制向其余状态的转移,新加状态必需要修改现有的部分状态才能加入状态机中生效。

1.3 相同点

二者经过将行为和状态拆分红一系列小的组件,由条件和状态进行功能更替,这样符合开闭原则,便于扩展。此外都可做为if else或者分支的替换方案;支持的最大行为和状态均有限;

1.4 不一样点

  • 策略模式中,类的功能是根据当前条件主动更改;
  • 状态模式中,类的功能是被动由当前状态更改;
  • 策略模式中每一个行为或算法之间没有关联;
  • 状态模式中的状态之间有关联,而且状态自己控制着状态转移;

2. 原理

两种模式的结构很是类似,下面分别看一下两种设计模式的UML类图:

2.1 策略模式UML

描述:

Context:

使用了某种策略的类,其行为由其包含的具体的策略决定,该类能主动修改使用的策略从而改变其行为;

Strategy:

抽象策略类,用于定义全部支持的算法公共接口;

ConcreteStrategy:

可以被Context使用的具体的策略;

2.2 状态模式UML

描述:

Context:

带有某个状态标记的类,其行为由其当前的状态决定,类状态的转移由状态来控制;

State:

抽象状态类,用于定义Context全部状态的公共接口;

ConcreteState:

Context类的某种具体状态,包含了该状态下处理的事务并控制向他状态转移;

3. 实例——策略模式

举个压缩软件使用不一样压缩策略的例子。

 

抽象策略接口:Compression

public interface Compression {
    public void doCompression();
}

快速压缩算法:Rapid

public class Rapid implements Compression {
    @Override
    public void doCompression() {
        System.out.println("Use rapid compression strategy!");
    }
}

高效压缩算法:Efficient

public class Efficient implements Compression {
    @Override
    public void doCompression() {
        System.out.println("Use efficient compression strategy!");
    }
}

加密压缩算法:Encrypt

public class Encrypt implements Compression {
    @Override
    public void doCompression() {
        // TODO Auto-generated method stub
        System.out.println("Use encrypt compression strategy!");
    }
}

集成上面压缩算法的软件:WinRAR

public class WinRAR {
    
    private Compression compression = null;
    
    public WinRAR(Compression compression) {
        this.compression = compression;
    }
    
    public void setStrategy(Compression compression) {
        this.compression = compression;
    }
    
    public void compression() {
        if (compression != null) {
            compression.doCompression();
        }
    }
}

演示:

public class Demo {
    public static void main(String[] args) {
        WinRAR winrar = new WinRAR(new Rapid());
        winrar.compression();
        winrar.setStrategy(new Efficient());
        winrar.compression();
        winrar.setStrategy(new Encrypt());
        winrar.compression();
    }
}

结果:

Use rapid compression strategy!
Use efficient compression strategy!
Use encrypt compression strategy!

这个例子看着很直观,后面会给出一点分析和我的的理解。

4. 实例——状态模式

咱们经过自动洗衣机工做过程来描述一下状态模式使用。

简单起见,这里咱们仅仅考虑【开始】-> 【工做】-> 【结束】,这三个状态。

下面先来看一下其UML的类图:

抽象状态接口:State

public interface State {
    public void doJob(Washing washing);
}

开始状态:Start

public class Start implements State {
    @Override
    public void doJob(Washing washing) {
        System.out.println("Start Washing Clothes!");
        washing.setState(new Work());
        washing.request();
    }
}

工做状态:Work

public class Work implements State{
    @Override
    public void doJob(Washing washing) {
        System.out.println("Working Now!");
        washing.setState(new End());
        washing.request();
    }
}

结束状态:End

public class End implements State{
    @Override
    public void doJob(Washing washing) {
        System.out.println("All Finished!");
        washing.setState(null);
    }
}

洗衣机类:Washing

public class Washing {
    private State state = null;
    
    public void setState(State state) {
        this.state = state;
        if (state == null) {
            System.out.println("Current state: null!");
        }
        else {
            System.out.println("Current state: " + state.getClass().getName());
        }
    }
    
    public void request() {
        if (state != null) {
            state.doJob(this);
        }
    }
}

演示:

public class Demo {
    public static void main(String[] args) {
        Washing washing = new Washing();
        washing.setState(new Start());
        washing.request();
    }
}

结果:

Current state: state.Start
Start Washing Clothes!
Current state: state.Work
Working Now!
Current state: state.End
All Finished!
Current state: null!

washing中提供用户使用的主要接口。初始时,使用者使用一个状态来配置washing,而后即可对washing发送指令,后续不在须要用户直接于具体转态打交道。每一个状态会自动控制向下一个状态转移,直到运行结束。

5. 总结

谈一下我的对于策略设计模式和状态模式的一些理解(不必定对,仅仅是一些思考):

5.1 策略模式:

a)频繁使用if else 可能严重消耗性能

策略模式比较适用于,行为类常常在某一个模式下工做,而不是会根据随机条件进行切换。

举个例子,在APP开发过程当中,某一功能会依赖于横竖屏状态,那么咱们是否须要在每一帧都是使用if else进行判断当前是横屏仍是竖屏,而后进行下一步的处理?

显然这会严重消耗性能,正确的作法是将横竖屏处理拆分红两个策略,每次屏幕切换的时候,主动的切一下使用的模式;

b) 并非全部的if else 和 分支均可以使用策略模式来替代

对于上面的压缩软件的例子,用户会选用一种模式,而后进行下面的工做,这个没问题。

可是若是咱们提供的是一个压缩命令,该命令能够根据传递的参数,使用不一样的压缩方式,那么使用if else就是必要的,由于咱们不知道用户会输入什么参数,使用什么模式。

c) 策略模式没有策略

策略模式的核心是将一系列的操做拆分红可若干可单独重复使用的轮子,特定条件下直接选取其中一个使用,而不是传递条件,使用if else来进行条件判断以执行相应的操做。

从这个角度来看,策略模式名存实亡,其不只没有智能,合理的根据当前条件进行决策,还须要使用者主动的选取一种策略进行执行。这样作有好处,但同时其也变得更加没有策略。

实际开发过程当中,咱们都但愿对方提供的接口简单好用,最好一个接口能搞定全部的问题,由于对于调用者而言,我并不关心你的实现,我只关心简单使用这个接口完成个人需求。

根本缘由在于其破坏了封装性,暴露了具体的策略,这是其拆分组件便于扩展的同时带来的一个不可回避的问题,策略模式将决策由执行者提早到了调用者,代码灵活可扩展的同时带来的是使用的不便。

若是说策略模式主要是为了不大量的if else决策,那么语言支持的话彻底可使用hashtable,分别以条件和函数对象做为key,value来直接根据条件选取对应的操做。对于大量分支尤为适用。

所以实际开发过程当中须要根据本身的实际状况权衡利弊。

5.1 策略模式:

状态模式的核心是将对象每个状态作的事情分别交给每个单独的状态对象处理,而且由状态本身控制向其余状态的转移;行为类仅向外提供方便用户使用的接口;

对扩展状态不是特别友好,须要修改其余状态的转移。其次其实现比较灵活,用很差容易出错。

小结:

策略模式是经过 Context 自己的决策来主动更替使用的strategy对象达到改变行为的目的,状态模式经过状态转移来被动的更改当前的State对象,状态的改变发生在运行时。

策略模式提早封装一组能够互相替代的算法族,根据须要动态的选择合适的一个来处理问题,而状态模式处理不一样状态下, Context 对象行为不一样的问题;

相关文章
相关标签/搜索