策略模式和状态模式的代码结构非常相似,其UML类图更是一致,容易让人困惑。究其原因,是没有理解两种模式的设计目的,以至于明明设计了状态模式的代码结构,仍以策略模式的形式使用这些代码。
策略模式
策略模式比较简单,分析应用类,将类中用于完成特定任务的不同操作抽离成一组独立的类,称之为策略类。
由于不同操作的是服务同一任务的,其功能是一致的,只是具体实现不同,我们将相同的功能抽象为策略接口,所有策略类都通过实现这些接口来实现具体的操作;当然也可以抽离为抽象类,实现一些相同的操作,只抽象那些不同的操作。
这时候原来的应用类,只需要使用一致的策略接口就能完成特定任务,不需要维护具体的策略及其实现。
在客户端使用该类的时候,根据需求传入与需求对应的策略对象即可。在新增需求时,只需要新增对应的策略实现就能完成拓展,无需修改应用类和既有的策略类。
状态模式
状态模式的目的是让一个对象的内部状态变化时改变其行为,使其看上去就像改变了自身所属的类一样。
那些适用状态模式的对象,其操作所发生的行为由当前状态决定,且行为的发生可能会使对象的状态从当前状态转移到另一种状态。这就是有限状态机的概念。
简单地实现一个状态机,我们可能会使用 if/else
或 switch/case
这样的条件运算符来判断状态、切换行为、转换状态。但是这会随着功能的膨胀使得代码难以维护。
状态的多寡与状态对应的行为会根据客户端需求的变化而变化,这也是造成代码维护困难的原因,将其分离出来即可解决。
策略模式建议将对象所有可能的状态都新建一个类,然后将状态所对应的行为抽离到该状态对应的类中。
状态模式包含以下主要角色:
- 环境类(Context)角色:也称为上下文,它定义了客户端需要的接口,内部维护一个当前状态,并负责具体状态的切换。
- 抽象状态(State)角色:定义一个接口或抽象类,用以封装环境对象中的特定状态所对应的行为,可以有一个或多个行为。
- 具体状态(Concrete State)角色:实现抽象状态所对应的行为,并且在需要的情况下通过持有环境类对象的引用来进行状态切换。
通过将原始对象中的的状态和状态对应的行为进行抽离,改写其所属的类,我们得到了环境类角色;
抽离的状态类实现了与其状态对应的行为,它们就是具体状态角色。
为了实现在环境类对象中对当前状态实现转换以对应不同的具体状态对象,需要对具体的状态类进一步抽离,将行为的定义抽离出来,作为一个接口(或者抽象类),所有具体状态类都通过实现该接口(或继承抽象类)来实现具体的行为。这个抽离出来的接口就是抽象状态角色。
环境类角色只需要定义供客户端使用的功能接口,并维护自身状态,不需要关心状态对应的具体行为,而是将所有与状态相关的工作都委派给该状态对象。
当我们需要拓展状态时,就新增一个状态类;当特定状态的行为发生改动时,只需要修改对应的状态类。环境类不需要进行改动。
但是,状态模式的弊端也很明显,它使类变多了。如果只是维护既有功能,它确实让代码变得清晰明了,易于维护。但是当我们的环境类新增了功能,且新增的功能受状态影响,那我们不得不修改抽象状态接口和所有的具体状态类。
理解策略模式与状态模式的区别
二者的代码组织方式非常相似,都有环境类,而代表行为的策略接口、策略类以及状态接口、状态类更是一一对应。但是其核心区别在于环境类与行为、行为与行为之间的关系。
虽然两种模式中的行为都发生在环境对象中,但是策略模式是由客户端指定策略对象来决定环境对象的行为,客户端对具体行为是有感知的,而环境类对具体的行为是没有感知的;
而在状态模式中,状态的变化决定了行为的变化,而状态对象是环境对象的一部分,那么环境对象的行为就是由其自身决定的,环境类对具体的行为是有感知的。
虽然状态模式并未规定在哪里定义初始状态、在哪里转换状态。但是我觉得为了解耦,客户端不应对环境对象进行任何状态对象的注入操作;初始的状态应在环境类内部创建并定义,而状态的转换可以在环境类或状态类中进行操作。
客户端调用环境类对象的功能接口,可能间接地导致环境类对象内部状态的转换,但客户端对这些事的发生是没有感知的。
也就是说状态模式下,客户端对具体的行为不应有感知。
如果非要在客户端进行状态的转换,这时候状态就变成了策略。不能理解状态和环境是一个整体,写出来的状态模式的代码就和策略模式一样了。
标签:状态,二者,策略,对象,差异,模式,行为,客户端 From: https://www.cnblogs.com/chaihuibin/p/18011456