行为类模式:策略模式VS状态模式。

在行为类设计模式中,状态模式和策略模式是亲兄弟,两者非常相似,都是通过Context类封装一个具体的行为,都提供了一个封装的方法,是高扩展性的设计模式。但根据两者的定义,我们发现两者的区别还是很明显的:策略模式封装的是不同的算法,算法之间没有交互,以达到算法可以自由切换的目的;而状态模式封装的是不同的状态,以达到状态切换行为随之发生改变的目的。这两种模式虽然都有变换的行为,但是两者的目标确实不同的。我们举例来说明两者的不同点。

人只要生下来就有工作可做,人在孩童时期的主要工作就是玩耍(学习知识);成人时期的主要工作是养活自己,然后为社会做贡献;老年时期的主要工作就是享受天伦之乐。按照策略模式来分析,这三种不同的工作方式就是三个不同的具体算法,随着时光的推移工作内容随之更替,这和对一堆数组的冒泡排序、快速排序、插入排序一样,都是一系列的算法;而按照状态模式进行设计,则认为人的状态(孩童、成人、老人)产生了不同的行为结果,这里的行为都相同,都是工作,但是他们的实现方式确实不同,也就是产生的结果不同,看起来就像是类改变了。

策略模式实现人生

定义了一个工作算法,然后有三个实现类:孩童工作、成年人工作和老年人工作。我们来看代码,首先看抽象工作算法,如下所示。

public abstract class WorkAlgorithm {
	/**
	 * 每个年龄段都必须完成的工作
	 */
	public abstract void work();
}

无论如何,每个算法都必须实现work方法,完成对工作内容的定义,三个具体的工作算法如下所示。

public class ChildWork extends WorkAlgorithm {

	@Override
	public void work() {
		System.out.println("儿童的工作是玩耍!");
	}

}
public class AdultWork extends WorkAlgorithm {

	@Override
	public void work() {
		System.out.println("成年人的工作就是先养活自己,然后为社会做贡献!");
	}

}
public class OldWork extends WorkAlgorithm {

	@Override
	public void work() {
		System.out.println("老年人的工作就是享受天伦之乐!");
	}

}

我们再来看环境角色,如下所示。

public class Context {
	private WorkAlgorithm workMethod;

	public WorkAlgorithm getWork() {
		return this.workMethod;
	}

	public void setWork(WorkAlgorithm work) {
		this.workMethod = work;
	}

	/**
	 * 每个算法都有必须具有的功能
	 */
	public void work() {
		this.workMethod.work();
	}
}

我们编写一个场景类来模拟该场景,如下所示。

public class Client {
	public static void main(String[] args) {
		// 定义一个环境角色
		Context context = new Context();
		System.out.println("====儿童的主要工作====");
		context.setWork(new ChildWork());
		context.work();
		System.out.println("\n====成年人的主要工作====");
		context.setWork(new AdultWork());
		context.work();
		System.out.println("\n====老年人的主要工作====");
		context.setWork(new OldWork());
		context.work();
	}
}

通过采用策略模式我们实现了“工作”这个策略的三种不同算法,算法可以自由切换,到底用哪个算法由调用者(高层模块)决定。策略模式的使用重点是算法的自由切换——老的算法退休,新的算法出台,对模块的整体功能没有非常大的改变,非常灵活。而如果想要增加一个新的算法,比如未出生婴儿的工作,只要继承WorkAlgorithm就可以了。

状态模式实现人生

我们再来看看使用状态模式是如何实现该需求的。随着时间的变化,人的状态变化了,同时引起了人的工作行为改变,完全符合状态模式。我们先来看抽象状态类,如下所示。

public abstract class HumanState {
	// 指向一个具体的人
	protected Human human;
	/**
	 * 设置一个具体的人
	 * @param human
	 */
	public void setHuman(Human human) {
		this.human = human;
	}
	/**
	 * 不管人是什么状态都要工作
	 */
	public abstract void work();
}

抽象状态定义了一个具体的人(human)必须进行工作(work),但是一个人在哪些状态完成哪些工作则是由子类来实现的。我们先来看孩童状态,如下所示。

public class ChildState extends HumanState {

	@Override
	public void work() {
		System.out.println("儿童的工作是玩耍!");
		super.human.setState(Human.ADULT_STATE);
	}

}

ChildState类代表孩童状态,在该状态下的工作就是玩耍。在work方法中为什么要设置下一个状态?因为我们的状态变化都是单方向的,从孩童到成年人,然后到老年人,每个状态转换到其他状态只有一个方向,因此会在这里看到work有两个职责:完成工作逻辑和定义下一个状态。

我们再来看成年人状态和老年人状态,如下所示。

public class AdultState extends HumanState {

	@Override
	public void work() {
		System.out.println("成年人的工作就是先养活自己,然后为社会做贡献!");
		super.human.setState(Human.OLD_STATE);
	}

}
public class OldState extends HumanState {

	@Override
	public void work() {
		System.out.println("老年人的工作就是享受天伦之乐!");
	}

}

每一个HumanState的子类都代表了一种状态,虽然实现的方法名work都相同,但是实现的内容却不同,也就是在不同的状态下行为随之改变。我们来看环境角色是如何处理行为随状态的改变而改变的,如下所示。

public class Human {
	// 定义人类都具备哪些状态
	public static final HumanState CHILD_STATE = new ChildState();
	public static final HumanState ADULT_STATE = new AdultState();
	public static final HumanState OLD_STATE = new OldState();
	// 定义一个人的状态
	private HumanState state;

	/**
	 * 设置一个状态
	 * 
	 * @param state
	 */
	public void setState(HumanState state) {
		this.state = state;
		this.state.setHuman(this);
	}

	/**
	 * 人类的工作
	 */
	public void work() {
		this.state.work();
	}
}

定义一个Human类代表人类,也就是状态模式中的环境角色,每个人都会经历从孩童到成年人再到老年人这样一个状态过渡(当然了,老顽童周伯通的情况我们就没有考虑进来),随着状态的改变,行为也改变。我们来看场景类,如下所示。

public class Client {
	public static void main(String[] args) {
		// 定义一个普通的人
		Human human = new Human();
		// 设置一个人的初始状态
		human.setState(new ChildState());
		System.out.println("====儿童的主要工作====");
		human.work();
		System.out.println("\n====成年人的主要工作====");
		human.work();
		System.out.println("\n====老年人的主要工作====");
		human.work();
	}
}

运行结果与策略模式相同,但是两者的分析角度是大相径庭的。策略模式的实现是通过分析每个人的工作方式的不同而得出三个不同的算法逻辑,状态模式则是从人的生长规律来分析,每个状态对应了不同的行为,状态改变后行为也随之改变。从以上示例中我们也可以看出,对于相同的业务需求,有很多种实现方法,问题的重点是业务关注的是什么,是人的生长规律还是工作逻辑?找准了业务的焦点,才能选择一个好的设计模式。

小结

从例子中我们可以看出策略模式和状态模式确实非常相似,称之为亲兄弟亦不为过,但是这两者还是存在着非常大的差别,而且也是很容易区分的。

  • 环境角色的职责不同

两者都有一个叫做Context环境角色的类,但是两者的区别很大,策略模式的环境角色只是一个委托作用,负责算法的替换;而状态模式的环境角色不仅仅是委托行为,他还具有登记状态变化的功能,与具体的状态类写作,共同完成状态切换行为随之切换的任务。

  • 解决问题的重点不同

策略模式旨在解决内部算法如何改变的问题,也就是将内部算法的改变对外部的影响降低到最小,他保证的是算法可以自由的切换;而状态模式旨在解决内在状态的改变而引起行为改变的问题,他的出发点是事物的状态,封装状态而暴露行为,一个对象的状态改变,从外界来看就好像是行为改变。

  • 解决问题的方法不同

策略模式只是确保算法可以自由切换,但是什么时候用什么算法他决定不了;而状态模式对外暴露的是行为,状态的变化一般是由环境角色和具体状态共同完成的,也就是说状态模式封装了状态的变化而暴露了不同的行为或行为结果。

  • 应用场景不同

两者都能实现前面例子中的场景,但并不表示两者的应用场景相同,这只是为了更好的展示出两者的不同而设计的一个场景。我们来想一下策略模式和状态模式的使用场景有什么不同,策略模式只是一个算法的封装,可以是一个有意义的对象,也可以是一个无意义的逻辑片段,比如MD5加密算法,他是一个有意义的对象吗?不是,他只是我们数学上的一个公式的相关实现,他是一个算法,同时DES算法、RSA算法等都是具体的算法,也就是说他们都是一个抽象算法的具体实现类,从这点来看策略模式是一系列平行的、可相互替换的算法封装后的结果,这就限定了他的应用场景:算法必须是平行的,否则策略模式就封装了一堆垃圾,产生了“坏味道”。状态模式则要求有一系列状态发生变化的场景,他要求的是有状态且有行为的而场景,也就是一个对象必须具有二维(状态和行为)描述才能采用状态模式,如果只有状态而没有行为,则状态的变化就失去了意义。

  • 复杂度不同

通常策略模式比较简单,这里的简单指的是结果简单,扩展比较容易,而且代码也容易阅读。当然,一个具体的算法也可以写的很复杂,只有具备很高深的数学、物理等知识的人才可以看懂,这也是允许的,我们只是从设计模式的角度来分析,他是很容易被看懂的。而状态模式则通常比较复杂,因为他要从两个角色看到一个对象状态和行为的改变,也就是说他封装的是变化,要知道变化是无穷尽的,因此相对来说状态模式通常都比较复杂,涉及面很多,虽然也很容易扩展,但是一般不会进行大规模的扩张和修正。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值