对比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对象,而状态模式则是用来管理状态迁移 的。