对比Java设计模式编程中的状态模式和策略模式

为了能在Java应用程序中正确的使用状态模式和策略模式,开发人员需要清楚地知道这两种模式之间的区别。尽管状态模式和策略模式的结构非常相似,它们又同样遵循开闭原则,都代表着SOLID设计原则的'O',但它们的意图是完全不同的。Java中的策略模式是对一组相关的算法进行封装,给调用方提供了运行时的灵活性。调用方可以在运行时选择不同的算法,而不用修改使用策略的那个Context类。使用策略模式的经典例子包括实现加密算法,压缩算法,以及排序算法。另一方面,状态模式使用一个对象可以在不同的状态下表现出不同的行为。真实世界里的对象也是有状态的,并且它们会随着状态的不同而有不同的表现,比方说自动售货机,它只会在hasCoin状态下才能出售物品,如果你不塞硬币进去它是不会售货的。现在你可以很清楚地看到策略模式和状态模式的区别了,它们的目的是不一样的。状态模式可以帮助对象来管理它的状态,而策略模式使得客户端可以选择不同的行为。还有一个不太容易看到的区别是,谁去驱动行为的改变。在策略模式中,是客户端驱动的,它给上下文信息提供了不同的策略,而在状态模式中,状态的迁移是由Context或者State对象自己来管理的。同样的,如果你在State对象里面进行状态的修改,它必须持有Context的引用,也就是说对自动售货机而言,它可以调用setState方法来修改当前Context的状态。另一方面,策略对象不会持有Context的引用 ,它的客户端将选中的策略传递给Context。策略模式和状态模式是最容易碰见的关于Java设计模式的面试题,在这篇关于Java设计模式的文章里,我们将会对这点进行详细的介绍。我们会探索这两种模式的相同点与不同点,这有助于提高你对这两种模式的理解。

状态模式和策略模式的相似点:
如果你看下策略模式和状态模式的UML图,它们看起来非常相似。在状态模式中,使用State对象来改变行为的的对象叫Context对象,类似的在策略模式中,使用Strategy对象来改变行为的对象也是Context对象。记住,客户端是和Context对象交互的。在状态模式中,Context代理了状态对象的方法调用,Context中的当前对象就是具体的状态对象,而在策略模式中,Context操作的也是策略对象,这个对象要么作为参数传入进来,要么是在创建Context对象的时候就已经提供了。

我们再来看一下这两种核心的Java设计模式的一些相似点:

状态模式和策略模式都很容易新增新的状态或者策略,而不会影响到使用它们的Context对象
两种模式都遵循开闭的设计原则,也就是说你的设计对扩展开放而对修改关闭。在这两个模式里,Context对修改是封闭的,新增状态或者策略,你不需要修改其它状态的Context对象,或者只需要很小的改动
正如状态模式中Context对象会有一个初始状态一样,策略模式中的Context通常也有一个默认的策略。
状态模式以不同的状态对象的方式来封装不同的行为,而策略模式以不同的策略对象来封装不同的行为。
这两种模式都依赖具体的子类来实现具体的行为。每一个具体的策略都扩展自一个抽象的策略类,每个状态也都是用来表示状态的接口或者抽象类的子类。

状态模式实例

public class WindowState {
  private String stateValue; 

  public WindowState(String stateValue) {
    this.stateValue = stateValue;
  } 

  public String getStateValue() {
    return stateValue;
  } 

  public void setStateValue(String stateValue) {
    this.stateValue = stateValue;
  } 

  public void handle() {
    /*
     * 根据不同状态做不同操作, 再切换状态
     */
    if ("窗口".equals(stateValue)) {
      switchWindow();
      this.stateValue = "全屏";
    } else if ("全屏".equals(stateValue)) {
      switchFullscreen();
      this.stateValue = "窗口";
    }
  } 

  private void switchWindow() {
    System.out.println("切换为窗口状态");
  } 

  private void switchFullscreen() {
    System.out.println("切换为全屏状态");
  } 

}
/**
 * 状态的使用
 */
public class WindowContext {
  private WindowState state; 

  public WindowContext(WindowState state) {
    this.state = state;
  } 

  public WindowState getState() {
    return state;
  } 

  public void setState(WindowState state) {
    this.state = state;
  } 

  public void switchState() {
    this.state.handle();
  }
}
/*
 * 状态(State)模式 行为型模式
 * 既改变对象的状态,又改变对象的行为
 * 根据状态,改变行为
 */
public class Test {
  public static void main(String[] args) {
    /*
     * 本例的 状态值只有两个,由状态类自身控制
     * 也可以把状态值的控制,交由客户端来设置
     */
    WindowContext context = new WindowContext(new WindowState("窗口"));
    context.switchState();
    context.switchState();
    context.switchState();
    context.switchState(); 

  }
}

打印

切换为窗口状态
切换为全屏状态
切换为窗口状态
切换为全屏状态

策略模式实例

/**
 * 商品促销
 * 本类为:收取现金的类
 */
public interface ICashSuper {
   double acceptCash(double money);
}
/**
 * 正常收取现金
 * @author stone
 *
 */
public class CashNormal implements ICashSuper { 

  @Override
  public double acceptCash(double money) {
    return money;
  } 

}
/**
 * 打折收取现金
 * @author stone
 *
 */
public class CashRebate implements ICashSuper {
  private double rebate; //折扣
  public CashRebate (double rebate) {
    this.rebate = rebate;
  } 

  @Override
  public double acceptCash(double money) {
    return new BigDecimal(money * rebate / 10).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
  } 

}
/**
 * 让利返现 收取现金
 * @author stone
 *
 */
public class CashReturn implements ICashSuper {
  private double moneyCondition; //返现底限金额
  private double returnMoney; //返还金额
  public CashReturn(double moneyCondition, double returnMoney) {
    this.moneyCondition = moneyCondition;
    this.returnMoney = returnMoney;
  } 

  @Override
  public double acceptCash(double money) {//多重返利
    if (money >= moneyCondition) {
      return money - Math.floor(money / moneyCondition) * returnMoney;
    } else {
      return money;
    }
  } 

}
/**
 * 根据传递的的策略类,执行相应的行为
 */
public class CashContext {
  private ICashSuper casher; 

  public CashContext() { 

  } 

  public CashContext(ICashSuper casher) {
    this.casher = casher;
  } 

  public void setCasher(ICashSuper casher) {
    this.casher = casher;
  } 

  //根据具体的策略对象,调用它的算法行为
  public double acceptCash(double money) {
    return this.casher.acceptCash(money);
  } 

}
public class Test {
  public static void main(String[] args) {
    double money = 998; //原价
    CashContext cashContext = new CashContext(new CashNormal());
    System.out.println("原价:" + cashContext.acceptCash(money)); //通常 策略 

    cashContext.setCasher(new CashRebate(8.5));
    System.out.println("打85折:" + cashContext.acceptCash(money)); //折扣  策略  85折 

    cashContext.setCasher(new CashReturn(300, 50));
    System.out.println("满300 返50:" + cashContext.acceptCash(money)); //返现 策略  满300 返50 

  }
}

打印

原价:998.0
打85折:848.3
满300 返50:848.0

策略模式和状态模式的区别
我们已经了解到这两个模式在结构上非常相似,但它们仍有不同的地方。下面来看下它们之间一些关键的不同点。

  • 策略模式封装了一系列的相关的算法,使用客户端可以在运行时通过组合和委托来使用不同的行为,而状态模式使得对象可以在不同的状态下展现出不同的行为。
  • 这两个模式的另一个不同之处在于状态模式封装的是对象的状态,而策略模式封装的是一个算法或者策略。由于状态是和对象耦合在一起的,它无法重用,而通过策略或者算法独立于它的上下文,使得它们可以重复使用。
  • 状态模式中,状态本身会包含Context的引用,从而实现状态迁移 ,但策略模式则没有Context的引用
  • 具体的策略可以作为一个参数传递给使用它们的对象,比如说Collections.sort()接受一个Comparator,这是一个策略。另状态本身 是 Context对象的一部分,随着时间的迁移,Context对象会从一个状态迁移迁移到另一个状态下。
  • 尽管两种模式都遵循了开闭原则,策略模式还遵循了单一职责原则,因为每个策略都 封装的是独立 的算法,不同的策略独立于其它策略。改变一个策略并不会影响到另一个策略的实现。
  • 从理论上说,策略模式和状态模式还有一个不同,前者定义的是一个对象“如何”去做一件事情,比如说如何对数据进行排序,而另一方面,状态模式定义的是“什么”以及“何时“,比如说一个对象能做什么,某个时间点它处于哪个状态。
  • 状态的迁移顺序在状态模式中是定义好的,而策略模式则没有这样的要求。客户端可以随便选择使用哪个策略。
  • 常见的策略模式的例子都是封装算法,比如说排序算法,加密算法,或者压缩算法。如果你发现代码中需要使用到不同的算法,那么你可以考虑使用策略模式。而如果你需要管理状态进行状态间的迁移,而不希望嵌套许多条件语句,那么状态模式就是你的首选,因为它非常简单.
  • 最后也是最重要的一个区别在于,策略模式是由客户端进行处理的,而状态的改变Context或者State对象都可以进行。

这就是关于Java中策略模式和状态模式的所有区别。正如我所说的,它们在UML图中看起来非常类似,两者都遵循了开闭原则,并且封装了行为。策略模式是用来封装算法或者策略的,它会在运行时作为参数或者组合对象来提供给Context对象,而状态模式则是用来管理状态迁移 的。

(0)

相关推荐

  • Java设计模式之策略模式详解

    本文实例为大家分享了Java策略模式,供大家参考,具体内容如下 1.策略模式(Strategy Pattern)是一种比较简单的模式,也叫做政策模式(PolicyPattern). 定义如下: Define a family of algorithms,encapsulate each one,and make them interchangeable.    (定义一组算法,将每个算法都封装起来,并且使它们之间可以互换.) 策略模式的通用类图如下所示: 策略模式的三个角色: ● Context

  • Java设计模式之策略模式(Strategy模式)介绍

    Strategy是属于设计模式中 对象行为型模式,主要是定义一系列的算法,把这些算法一个个封装成单独的类. Stratrgy应用比较广泛,比如,公司经营业务变化图,可能有两种实现方式,一个是线条曲线,一个是框图(bar),这是两种算法,可以使用Strategy实现. 这里以字符串替代为例,有一个文件,我们需要读取后,希望替代其中相应的变量,然后输出.关于替代其中变量的方法可能有多种方法,这取决于用户的要求,所以我们要准备几套变量字符替代方案. 首先,我们建立一个抽象类RepTempRule 定义

  • java设计模式学习之策略模式

    策略模式:策略模式是一种定义一系列算法的方法,算法完成的工作都是相同的工作,但是实现不同,它可以以相同的方式调用所有的算法,减少了各种算法类与使用算法类之间的耦合. Java实现一个策略模式: 需求:商场收银系统,收银方式为正常收费,打八折,满300返100,这三种收费方式. 1:创建一个超类.即收费的抽象方法. public abstract class CashSuper { public abstract double acceptCash(double money); } 2:创建实现此

  • Java设计模式之策略模式_动力节点Java学院整理

    定义:定义一组算法,将每个算法都封装起来,并且使他们之间可以互换. 类型:行为类模式 类图: 策略模式是对算法的封装,把一系列的算法分别封装到对应的类中,并且这些类实现相同的接口,相互之间可以替换.在前面说过的行为类模式中,有一种模式也是关注对算法的封装--模版方法模式,对照类图可以看到,策略模式与模版方法模式的区别仅仅是多了一个单独的封装类Context,它与模版方法模式的区别在于:在模版方法模式中,调用算法的主体在抽象的父类中,而在策略模式中,调用算法的主体则是封装到了封装类Context中

  • 详解Java设计模式编程中的策略模式

    定义:定义一组算法,将每个算法都封装起来,并且使他们之间可以互换. 类型:行为类模式 类图: 策略模式是对算法的封装,把一系列的算法分别封装到对应的类中,并且这些类实现相同的接口,相互之间可以替换.在前面说过的行为类模式中,有一种模式也是关注对算法的封装--模版方法模式,对照类图可以看到,策略模式与模版方法模式的区别仅仅是多了一个单独的封装类Context,它与模版方法模式的区别在于:在模版方法模式中,调用算法的主体在抽象的父类中,而在策略模式中,调用算法的主体则是封装到了封装类Context中

  • Java经典设计模式之策略模式原理与用法详解

    本文实例讲述了Java经典设计模式之策略模式.分享给大家供大家参考,具体如下: 策略模式指:策略模式指将程序中可变部分抽象分离成一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换.策略模式让算法独立于使用它的客户而独立变化. 策略模式一般由下面三部分组成: 1. 抽象策略角色: 策略类,通常由一个接口或者抽象类实现. 2. 具体策略角色:包装了相关的算法和行为. 3. 环境角色:持有某一个策略类的引用,客户端调用. 策略模式设计原则: 1. 把程序中需要变化的部分抽离出来,独立于不变

  • 对比Java设计模式编程中的状态模式和策略模式

    为了能在Java应用程序中正确的使用状态模式和策略模式,开发人员需要清楚地知道这两种模式之间的区别.尽管状态模式和策略模式的结构非常相似,它们又同样遵循开闭原则,都代表着SOLID设计原则的'O',但它们的意图是完全不同的.Java中的策略模式是对一组相关的算法进行封装,给调用方提供了运行时的灵活性.调用方可以在运行时选择不同的算法,而不用修改使用策略的那个Context类.使用策略模式的经典例子包括实现加密算法,压缩算法,以及排序算法.另一方面,状态模式使用一个对象可以在不同的状态下表现出不同

  • 浅析Java设计模式编程中的单例模式和简单工厂模式

    单例模式 动机 有时候只有一个类的实例是很重要的.比如,一个系统应该只有一个窗口管理实例. 单例模式是最简单设计模式:类负责实例化自己,确保只有一个实例,并且提供一个访问这个实例的入口. 目的 1. 确保只有一个实例被创建. 2. 提供访问这个实例的入口. 使用final确保被创建一次,private的构造函数确保不被实例化.public的getInstance方法确保外部能够访问.下面是饿汉模式: public class Singleton { private static final Si

  • Java设计模式编程中简单工厂与抽象工厂模式的使用实例

    简单工厂模式 类图 通过一个工厂类,以一个条件来创建对应的对象 //业务功能 public interface ICalculation { double getResult(double numA, double numB); } public class CalcAdd implements ICalculation { @Override public double getResult(double numA, double numB) { System.out.println("加法&q

  • 详解C++设计模式编程中对状态模式的运用

    状态模式:当一个对象的内在状态发生变化时,允许改变其行为,这个对象看来像是改变了其类. 状态模式与策略模式的UML图几乎一模一样,下面列举了两者的不同: (1)可以通过环境类状态的个数来决定是使用策略模式还是状态模式. (2)策略模式的环境类自己选择一个具体策略类,具体策略类无须关心环境类:而状态模式的环境类由于外在因素需要放进一个具体状态中,以便通过其方法实现状态的切换,因此环境类和状态类之间存在一种双向的关联关系. (3)使用策略模式时,客户端需要知道所选的具体策略是哪一个,而使用状态模式时

  • 详解Java设计模式编程中的Flyweight享元模式的开发结构

    享元(Flyweight)模式:通过共享技术以便有效的支持大量细粒度的对象. 享元模式在阎宏的<java与模式>中分为单纯享元模式和复合享元模式,复合模式的复合享元是不可以共享的,享元对象能做到共享的关键是区分内蕴态(Internal State)和外蕴态( External State).这两个"蕴态"翻译的太难懂,我不是说翻译的不好,可能是我理解能力差,还是<Design Pattern Elements of Reusable Object-Oriented S

  • 详解Java设计模式编程中的访问者模式

    定义:封装某些作用于某种数据结构中各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作. 类型:行为类模式 类图: 例子: 例如,思考一下添加不同类型商品的购物车,当点击结算的时候,它计算出所有不同商品需付的费用.现在,计算逻辑即为计算这些不同类型商品的价格.或者说通过访问者模式我们把此逻辑转移到了另外一个类上面.让我们实现这个访问者模式的例子. 为了实现访问者模式,最先需要做的是创建能够被添加到购物车中代表不同类型商品(itemElement)的类. ItemElement

  • 讲解Java设计模式编程中的建造者模式与原型模式

    建造者模式 定义 又叫生成器模式,它可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象. 当创建复杂对象的算法应该独立于该对象的组成部分时,而且构造过程必须允许被构造的对象有不同的表示时.我们可以考虑使用建造者模式. 实现 1. Builder为创建一个Product对象的各个部件指定抽象接口.通常包含创建产品和返回产品的抽象方法,也可以是具体方法,把创建过程放到ConcreteBuilder类中. 2. ConcreteBuilder 实

  • 实例解析Java设计模式编程中的适配器模式使用

    平时我们会经常碰到这样的情况,有了两个现成的类,它们之间没有什么联系,但是我们现在既想用其中一个类的方法,同时也想用另外一个类的方法.有一个解决方法是,修改它们各自的接口,但是这是我们最不愿意看到的.这个时候Adapter模式就会派上用场了. Adapter模式也叫适配器模式,是由GoF提出的23种设计模式的一种.Adapter模式是构造型模式之一,通过Adapter模式,可以改变已有类(或外部类)的接口形式. 适配器 模式 有三种方式,一种是对象适配器,一种是类适配器, 一种是接口适配器 以下

  • 详解Java设计模式编程中命令模式的项目结构实现

    正论: 命令模式把一个请求或者操作封装到一个对象中.命令模式运行系统使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能. 通俗: 其实很好理解.命令模式,关心的就是命令(或者称为操作).打个比方.在一个公司里面,整个运作就像一个系统.某个boss发布了一个命令,中层领导接到这个命令,然后指派给具体负责这个员工.整个流程很清晰吧.有一个需求,如何将这个流程固定下来,形成一个系统.我们只要抓住了重点:命令.将它抽取出来,其他的都迎刃而解了.抽取出命令,封装成一个独

随机推荐