本文来讲解一下两个结构比较相似的行为设计模式:策略模式和状态模式。两者单独的理解和学习都是比较直观简单的,但是实际使用的时候却并不好实践,算是易学难用的设计模式吧。这也是把两者放在一起介绍的原因,经过对比和实例介绍,相信应该会一些比较深刻的感知。最后在结合个人的体会简单聊一下对这两个模式的一些看法。

1. 模式概念

1.1 策略模式

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

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

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

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

缺点: 对外暴露了类所有的行为和算法,行为过多导致策略类膨胀。

1.2 状态模式

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

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

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

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

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

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.2 状态模式:

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

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

小结:

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

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

设计模式之策略模式和状态模式(strategy pattern & state pattern)的更多相关文章

  1. Java设计模式之策略模式与状态模式

    版权声明:本文出自汪磊的博客,转载请务必注明出处. 一.策略模式定义 定义:策略模式定义了一系列的算法,并将每一个算法封装起来,而且使他们之间可以相互替换,策略模式可以在不影响客户端的情况下发生变化. ...

  2. 【设计模式】 模式PK:策略模式VS状态模式

    1.概述 行为类设计模式中,状态模式和策略模式是亲兄弟,两者非常相似,我们先看看两者的通用类图,把两者放在一起比较一下. 策略模式(左)和状态模式(右)的通用类图. 两个类图非常相似,都是通过Cont ...

  3. Java进阶篇设计模式之十二 ---- 备忘录模式和状态模式

    前言 在上一篇中我们学习了行为型模式的策略模式(Strategy Pattern)和模板模式(Template Pattern).本篇则来学习下行为型模式的两个模式,备忘录模式(Memento Pat ...

  4. java - 策略模式、状态模式、卫语句,避免多重if-else(转)

    前言 当代码中出现多重if-else语句或者switch语句时.弊端之一:如果这样的代码出现在多处,那么一旦出现需求变更,就需要把所有地方的if-else或者switch代码进行更改,要是遗漏了某一处 ...

  5. Java设计模式之十二 ---- 备忘录模式和状态模式

    前言 在上一篇中我们学习了行为型模式的策略模式(Strategy Pattern)和模板模式(Template Pattern).本篇则来学习下行为型模式的两个模式,备忘录模式(Memento Pat ...

  6. Java重构-策略模式、状态模式、卫语句

    前言 当代码中出现多重if-else语句或者switch语句时.弊端之一:如果这样的代码出现在多处,那么一旦出现需求变更,就需要把所有地方的if-else或者switch代码进行更改,要是遗漏了某一处 ...

  7. 【转】Java重构-策略模式、状态模式、卫语句

    前言 当代码中出现多重if-else语句或者switch语句时.弊端之一:如果这样的代码出现在多处,那么一旦出现需求变更,就需要把所有地方的if-else或者switch代码进行更改,要是遗漏了某一处 ...

  8. Java 策略模式和状态模式

    本文是转载的,转载地址:大白话解释Strategy模式和State模式的区别 先上图: 本质上讲,策略模式和状态模式做得是同一件事:去耦合.怎么去耦合?就是把干什么(语境类)和怎么干(策略接口)分开, ...

  9. 《大话》之 策略模式 Vs 状态模式

    一.简介: 策略模式: 背景:商店要打折销售,各种版本的销售方式,让小菜心烦意乱 内容:    定义算法家族,分别封装起来,让他们之间可以户型替换,此模式让算法的变化,不会影响到使用算法的用户. 图文 ...

随机推荐

  1. C#实现万年历(农历、节气、节日、星座、星宿、属相、生肖、闰年月、时辰)

    C# 万年历 农历 节气 节日 星座 星宿 属相 生肖 闰年月 时辰地址:http://www.cnblogs.com/txw1958/archive/2013/01/27/csharp-calend ...

  2. 实用的 Matlab

    activecontour:前景背景分离,Segment image into foreground and background using active contour 该函数返回的是一副 bin ...

  3. [WebGL入门]十一,着色器编译器和连接器

    注意:文章翻译http://wgld.org/,原作者杉本雅広(doxas).文章中假设有我的额外说明.我会加上[lufy:].另外.鄙人webgl研究还不够深入.一些专业词语,假设翻译有误.欢迎大家 ...

  4. 算法 Tricks(六)—— 判断一个数是否为完全平方数

    int(sqrt(n)) * int(sqrt(n)) == n ? 1:0; matlab 下判断一个数是否能开方的判断是: floor(sqrt(m))^2 == m

  5. Effection Go

    Introduction: 新语言, 新思维 Formatting Indentation: 默认tab Line Length: 无限制, 会自动换行 Parentheses: 圆括号, 无限制, ...

  6. WPF 4 开发Windows 7 任务栏(Overlay Icon、Thumbnail Toolbar、Progress Bar)

    原文:WPF 4 开发Windows 7 任务栏(Overlay Icon.Thumbnail Toolbar.Progress Bar)      在上一篇我们介绍了如何在WPF 4 中开发Wind ...

  7. WPF 寻找控件模板中的元素

    <Window x:Class="Wpf180706.Window10"        xmlns="http://schemas.microsoft.com/wi ...

  8. Delphi 接口使用中,对象生命周期管理,如何释放需要注意的问题

    网上有篇文章<Delphi接口编程的两大陷阱>,里面提到接口的生存期管理的问题.但该文章里面提到的两个问题,其实都是对 Delphi 不理解导致的.   先说该篇文章中提到的第一个问题为什 ...

  9. SQLServer 以备份初始化订阅

    原文:SQLServer 以备份初始化订阅 在创建事务复制时,如果发布数据库很大,使用快照初始化时,将等待很久,如果出现问题可能又得重新初始化.使用备份初始化会省很多时间,但是数据库在创建发布订阅期间 ...

  10. php 如何利用 soap调用.Net的WebService asmx文件

    原文:php 如何利用 soap调用.Net的WebService asmx文件 最近,帮一个同行测试用.net写的WebService接口,C#调用通过,现在需要测试一下php版本对它的调用,经过各 ...