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

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

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.1 策略模式:

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

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

小结:

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

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

原文地址:https://www.cnblogs.com/yssjun/p/11116652.html

时间: 2024-10-10 02:38:09

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

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

1.概述 行为类设计模式中,状态模式和策略模式是亲兄弟,两者非常相似,我们先看看两者的通用类图,把两者放在一起比较一下. 策略模式(左)和状态模式(右)的通用类图. 两个类图非常相似,都是通过Context类封装一个具体的行为,都提供了一个封装的方法,是高扩展性的设计模式.但根据两者的定义,我们发现两者的区别还是很明显的:策略模式封装的是不同的算法,算法之间没有交互,以达到算法可以自由切换的目的:而状态模式封装的是不同的状态,以达到状态切换行为随之发生改变的目的.这两种模式虽然都有变换的行为,但

设计模式(十五)状态模式

相关文章 设计模式系列 前言 建议在阅读本文前先阅读设计模式(十一)策略模式这篇文章,虽说状态模式和策略模式的结构几乎是相同的,但是它们所解决的问题是不同的,读完这两篇文章你就会有了答案. 1.状态模式定义 状态模式定义 定义:当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类. 状态模式UML图 在享元模式中有如下角色: Context:环境角色,定义客户端需要的接口,并且负责具体状态的切换. State:抽象状态角色,可以是抽象类或者接口,负责对象状态定义,并封装了环境角

【设计模式学习笔记】 之 状态模式

简介: 每种事物都有不同的状态,不同的状态会有不同的表现,通过更改状态从而改变表现的设计模式称为状态模式(state pattern) 下边会通过多个例子进行讲述,会有一些代码重用的类,请注意包名! 举例1: 人有多种心情,不同的心情会有不同的表现,这里先使用分支判断写个小例子 创建一个Person类,它持有一个表示心情的字符串,通过设置这个字符串并对这个字符串进行判断来决定产生不同的行为 1 package com.mi.state.state1; 2 3 /** 4 * 人类,拥有一个状态属

设计模式之第21章-状态模式(Java实现)

设计模式之第21章-状态模式(Java实现) “what are you 干啥了?怎么这么萎靡不振?”“昨晚又是补新番,又是补小笼包,睡得有点晚啊.话说杨过的那个雕兄真是太好了,每天给找蛇胆,又陪练武功的,想不无敌都难啊,还有那个blablabla”(作者已被拖走).咳咳,今天那个状态哥哥马不停蹄的赶过来,下面闪亮登场. 状态模式之自我介绍 今天不在状态,可能是由于宇宙差的原因,好了,先说下定义:Allow an object to alter its behavior when its int

职责链模式vs状态模式区别

状态模式在具体状态里设置了下一状态. 而职责链模式是在客户端代码里设置了下一状态的处理对象. 如果状态模式里的任何一环缺失,将导致事情无法进行下去.职责链模式的链式在客户端连接的,也就是说,如果我们请假,请假制度一旦改变,比如说我们不需要班长,或者是先请求老师后直接请求主任或者中间多了一个环节,都是很容易实现的,所以,职责链模式要比状态模式灵活很多. 小时候写日记都是这么写的:上午七点起床,八点之前洗脸刷牙吃早饭,十二点之前好好上课,中午一点,吃午饭,下午两点到六点,上课,下课,找请假,明天妈妈

23行为型模式之状态模式

概念 State模式也叫状态模式,是行为设计模式的一种.State模式允许通过改变对象的内部状态而改变对象的行为,这个对象表现得就好像修改了它的类一样. 状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况.把状态的判断逻辑转译到表现不同状态的一系列类当中,可以把复杂的判断逻辑简化. 角色和职责 Context:用户对象 拥有一个State类型的成员,以标识对象的当前状态: State:接口或基类 封装与Context的特定状态相关的行为: ConcreteState:接口实现

行为型模式之状态模式

概述 状态模式用于解决系统中复杂对象的状态转换以及不同状态下行为的封装问题.当系统中某个对象存在多个状态,这些状态之间可以进行转换,而且对象在不同状态下行为不相同时可以使用状态模式.状态模式将一个对象的状态从该对象中分离出来,封装到专门的状态类中,使得对象状态可以灵活变化,对于客户端而言,无须关心对象状态的转换以及对象所处的当前状态,无论对于何种状态的对象,客户端都可以一致处理. 定义 状态模式(State Pattern):允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类

【设计模式】策略模式 vs 状态模式

相同点:同为行为型策略模式,均通过context类封装一个具体行为,都提供一个封装的方法: 不同点: 1)从定义看:目的不同 策略模式:封装不同的算法,算法间没有交互,达到算法可以自由互换目的:  实际样例:人生不同阶段的主要工作:儿童->玩耍.成年->工作.比如加密算法的互换(AES->RSA) 状态模式:封装不同的状态,达到状态切换后行为随之发生改变的目的.      实际样例:从儿童 切换 成年,因为状态的变化(儿童到成年),主要工作内容发生了改变. 2)具体场景代码比对 场景:人

大话设计模式第十六章--状态模式

<?php abstract class State { abstract public function handle(Context $context); } class Concrete_state_A extends State { public function handle(Context $context) { echo "NOW IS IN STATE_A <br/>"; $context->state = new Concrete_state_