JAVA设计模式详解(六)----------状态模式
各位朋友,本次LZ分享的是状态模式,在这之前,恳请LZ解释一下,由于最近公司事情多,比较忙,所以导致更新速度稍微慢了些(哦,往后LZ会越来越忙=。=)。
状态模式,又称状态对象模式(Pattern of Objects for States),状态模式是对象的行为模式。
状态模式允许一个对象在其内部状态改变的时候改变其行为。这个对象看上去就像是改变了它的类一样。
这里LZ给大家举例子,海贼王中路飞在打多弗朗明哥的时候,首先是普通状态,然后发怒开启二挡状态,被多弗朗明哥嘲笑速度快,但是力量低,于是开启三挡状态,又被嘲笑力量够了,但是速度差远了,路飞被逼急,于是开启四挡,最终打败了多弗朗明哥。现在我们通过代码实现这样的一个过程。
这里我们发现路飞的状态经历了普通,二挡,三挡,四挡,共四个状态,我们创建一个实例变量state来持有目前的状态,然后定义每个状态的值
public final static int ORDINARY = 0;//普通状态
public final static int SECONDGEAR = 1;//二挡状态
public final static int THIRDGEAR = 2;//三挡状态
public final static int FOURTHGEAR = 3;//四挡状态
private int state = ORDINARY;//由于路飞一开始是普通状态,所以我们初始化state为ORDINARY
接下来我们实现LuFei这个类
public class LuFei {
public final static int ORDINARY = 0;//普通状态
public final static int SECONDGEAR = 1;//二挡状态
public final static int THIRDGEAR = 2;//三挡状态
public final static int FOURTHGEAR = 3;//四挡状态
private int state = ORDINARY;//由于路飞一开始是普通状态,所以我们初始化state为ORDINARY public void setstate(int state) {
this.state = state;
} public void change(){
if(state == SECONDGEAR){
System.out.println("路飞开启二挡战斗");
}else if(state == THIRDGEAR){
System.out.println("路飞开启三挡战斗");
}else if(state == FOURTHGEAR){
System.out.println("路飞开启四挡战斗");
}else{
System.out.println("路飞当前为普通状态战斗");
}
}
}
现在我们写个测试类
public class TestLuFei {
public static void main(String[] args) {
LuFei luFei = new LuFei();
luFei.setstate(luFei.SECONDGEAR);
luFei.change();
luFei.setstate(luFei.THIRDGEAR);
luFei.change();
luFei.setstate(luFei.FOURTHGEAR);
luFei.change();
luFei.setstate(luFei.ORDINARY);
luFei.change();
}
}
这里可以看到,通过状态的改变,路飞会以不同的状态进行战斗。
该来了总躲不过,那就是,变更需求!
这里,路飞在第一次变四挡后,并没有打败多弗朗明哥,而是因为四挡导致进入了第五个状态:虚弱状态,现在我们还要再加上一个if语句,把虚弱状态扔进去。这里就看出了问题,我们这个由于需求及其简单,所以代码特别简洁,但是倘若这是一个复杂的业务,我们每一次变更状态难道都要在类中增加if else语句或者是新的业务逻辑吗?这显然违背了我们的设计原则:封装变化原则。(这里各位可能会突然想起来策略模式,那里也用到了这个原则。)
现在,让我们重写代码,将每个状态对象封装到各自的类中,然后在动作发生时委托给当前对象。我们的步骤为:
1.首先,我们创建一个state的接口,我们把每个状态共有的change方法放在这个接口中
2.接下来,我们让每一个状态都实现状态类,这些类将负责在对应的状态下进行相应行为
3.最后,我们将动作委托到状态类中
interface LuFeiState{
public void change();
} class Ordinary implements LuFeiState{ @Override
public void change() {
System.out.println("路飞当前为普通状态战斗");
} } class SecondGear implements LuFeiState{ @Override
public void change() {
System.out.println("路飞开启三挡战斗");
} } class ThirdGear implements LuFeiState{
@Override
public void change() {
System.out.println("路飞开启三挡战斗");
}
} class FourthGear implements LuFeiState{
@Override
public void change() {
System.out.println("路飞开启四挡战斗");
}
}
由于LZ的State类在其他包下已经定义过了,所以这里LZ改名为LuFeiState.
接下来我们修改路飞类:
public class LuFei {
public static final LuFeiState Ordinary = new Ordinary();//普通状态
public static final LuFeiState SecondGear = new SecondGear();//二挡状态
public static final LuFeiState ThirdGear = new ThirdGear();//三挡状态
public static final LuFeiState FourthGear = new FourthGear();//四挡状态
private LuFeiState state = Ordinary;//由于路飞一开始是普通状态,所以我们初始化state为ORDINARY public void setstate(LuFeiState state) {
this.state = state;
} public void change(){
state.change();
}
}
可以看到,我们原本一大堆的if else语句消失了,代替的,是非常简洁的代码。看到这里,各位一定产生了一个问题,别急,忍住,LZ稍后会作解释。
接下来是我们的测试类:
public class TestLuFei {
public static void main(String[] args) {
LuFei luFei = new LuFei();
luFei.setstate(luFei.SecondGear);
luFei.change();
luFei.setstate(luFei.ThirdGear);
luFei.change();
luFei.setstate(luFei.FourthGear);
luFei.change();
luFei.setstate(luFei.Ordinary);
luFei.change();
}
}
这是我们重做之前的代码,可以看出,当有新的状态产生时,我们只需要写一个新的状态类实现State接口,在路飞类中提供一个变量即可,其他的都不需要动。对比原本的例子,我们明显的发现:
一:每个状态的行为局部化到它自己的类中
二、将容易产生问题的if else结构去掉,使得代码的可维护性更强,不易出错。
三、使用多态代替了条件判断,这样我们代码的扩展性更强,当要增加一些状态时,会非常的容易。
四、让每一个状态对修改关闭,对扩展开放
五、状态是可以被共享的,这个在上面的例子当中有体现,看下LuFei类当中的四个static final变量就知道了,因为状态类一般是没有自己的内部状态的,所有它只是一个具有行为的对象,因此是可以被共享的。
六、状态的转换更加简单安全,简单体现在状态的分割,因为我们把一堆if else分割成了若干个代码段分别放在几个具体的状态类当中,所以转换起来当然更简单,而且每次转换的时候我们只需要关注一个固定的状态到其他状态的转换。安全体现在类型安全,我们设置状态时,必须是状态接口的实现类,而不是原本的一个整数,这可以杜绝数不正确的状态码。
写到这里,不需要明说各位也看出了我们用到的是状态模式。
状态模式允许对象在内部状态改变时改变它的行为,对象看起来好像改变了它的类
1.Context(上下文)是一个类,它可以拥有一些内部状态,相当于我们例子中的LuFei。
2.state.handle() :不管什么时候,只要有人调用Context的request方法,它就会被委托到状态来处理。
3.state接口定义了一个所有具体状态的共同接口;任何状态都实现这个相同的接口,这样一来状态之间可以互相替换。
4.ConcreteState(具体状态)处理来自Context的请求,每一个ConcreteState都提供了它自己对于请求的实现。所以,当Context改变状态时行为也跟着改变。
状态模式适用于某一个对象的行为取决于该对象的状态,并且该对象的状态会在运行时转换,又或者有很多的if else判断,而这些判断只是因为状态不同而不断的切换行为。
到这里,相信各位早已按捺不住想问LZ:这个模式明明就是策略模式吗,无论是类图,还是结构,都跟策略模式一模一样啊!
下面LZ给出两者的区别:
状态模式:将一群行为封装到状态对象中,context的行为随时可委托到那些状态对象中的一个。随着时间的流逝,当前状态在状态对象集合中游走改变,以反映出context内部的状态,因此,context的行为也会跟着改变。但是context的客户对于状态对象了解不多,甚至根本是浑然不觉。
而以策略模式而言,客户通常主动指定Context所要组合的策略对象是哪一个。现在,固然策略模式让我们具有弹性,能够在运行时改变策略,但对于某个context对象来说,通常都只有一个最适当的策略对象。比方说LZ前面第一章所举的例子。
一般来说,我们把策略模式想成是除了继承之外的一种弹性替代方案。如果你使用继承定义了一个类的行为,你将被这个行为困住,甚至要修改它都很难。有了策略模式,你可以通过组合不同的对象来改变行为。
我们把状态模式想成是不用在context中放置许多条件判断的替代方案。通过将行为包装进状态对象中,你可以通过在context内简单地改变状态对象来改变context的行为。
下面我们总结一下状态模式的要点:
1.状态模式允许一个对象基于内部状态而拥有不同的行为。
2.状态模式用类代表状态
3.Context会将行为委托给当前状态对象
4.通过将每个状态封装进一个类,我们把以后需要做的任何改变局部化了
5.状态模式和策略模式有相同的类图,但是它们的意图不同
6.策略模式通常会用行为或算法来配置Context类
7.状态模式允许Context随着状态的改变而改变行为
8.状态转换可以由State类或Context类控制。
9.使用状态模式通常会导致设计中类的数目大量增加。
10.状态类可以被多个Context实例共享。
另外 ,状态模式在项目当中也算是较经常会碰到的一个设计模式,但是通常情况下,我们还是在看到if else的情况下,对项目进行重构时使用,又或者你十分确定要做的项目会朝着状态模式发展,一般情况下,LZ不建议在项目的初期使用。
本次LZ状态模式的分享就到此结束了,希望各位有所收获。
JAVA设计模式详解(六)----------状态模式的更多相关文章
- android java 设计模式详解 Demo
android java 设计模式详解 最近看了一篇设计模式的文章,深得体会,在此基础我将每种设计模式的案例都写成Demo的形式,方便读者研究学习, 首先先将文章分享给大家: 设计模式(Design ...
- javascript设计模式详解之命令模式
每种设计模式的出现都是为了弥补语言在某方面的不足,解决特定环境下的问题.思想是相通的.只不过不同的设计语言有其特定的实现.对javascript这种动态语言来说,弱类型的特性,与生俱来的多态性,导致某 ...
- javascript设计模式详解之策略模式
接上篇命令模式来继续看下js设计模式中另一种常用的模式,策略模式.策略模式也是js开发中常用的一种实例,不要被这么略显深邃的名字给迷惑了.接下来我们慢慢看一下. 一.基本概念与使用场景: 基本概念:定 ...
- Java设计模式(19)状态模式(State模式)
State的定义:不同的状态,不同的行为:或者说,每个状态有着相应的行为. 何时使用状态模式 State模式在实际使用中比较多,适合"状态的切换".因为我们经常会使用If else ...
- JAVA设计模式详解(五)----------适配器模式
各位朋友好,本章节我们继续讲第五个设计模式. 在生活中,我们都知道手机内存卡是无法直接接电脑的,因为内存卡的卡槽比较小,而电脑只有USB插孔,此时我们需要用到读卡器.这个读卡器就相当于是适配器.这是生 ...
- JAVA设计模式详解(三)----------装饰者模式
今天LZ带给大家的是装饰者模式,提起这个设计模式,LZ心里一阵激动,这是LZ学习JAVA以来接触的第一个设计模式,也许也是各位接触的第一个设计模式.记得当初老师在讲IO的时候就提到过它:“是你还有你, ...
- JAVA设计模式详解(一)----------策略模式
策略模式,顾名思义就是设计一个策略算法,然后与对象拆分开来将其单独封装到一系列策略类中,并且它们之间可以相互替换.首先LZ举一个例子为大家引出这一个模式. 例子:某公司的中秋节奖励制度为每个员工发放2 ...
- Java常用设计模式详解1--单例模式
单例模式:指一个类有且仅有一个实例 由于单例模式只允许有一个实例,所以单例类就不可通过new来创建,而所有对象都默认有一个无参的构造函数可以创建对象,所以单例类不仅不能提供public的构造方法,还需 ...
- JAVA设计模式详解(二)----------观察者模式
有一个模式可以帮助你的对象知悉现况,不会错过该对象感兴趣的事,对象甚至在运行时可以决定是否要继续被通知,如果一个对象状态的改变需要通知很多对这个对象关注的一系列对象,就可以使用观察者模式 .观察者模式 ...
随机推荐
- 面向对象总结、configparser配置文件模块、logging日志模块
面向对象总结 # 学习态度# python基础 2个月# html css js jq 1个月 # 上课困 # 学习方法 :# 列出知识点# 例子 写了哪些 # 面向对象学了哪些块# 为什么要讲面向对 ...
- [模板]最小割树(Gomory-Hu Tree)(luogu4897)
给定一个\(n\)个点\(m\)条边的无向连通图,多次询问两点之间的最小割 两点间的最小割是这样定义的:原图的每条边有一个割断它的代价,你需要用最小的代价使得这两个点不连通 Input 第一行两个数\ ...
- 「雅礼集训 2017 Day1」 解题报告
「雅礼集训 2017 Day1」市场 挺神仙的一题.涉及区间加.区间除.区间最小值和区间和.虽然标算就是暴力,但是复杂度是有保证的. 我们知道如果线段树上的一个结点,\(max=min\) 或者 \( ...
- cad2019卸载/安装失败/如何彻底卸载清除干净cad2019注册表和文件的方法
cad2019提示安装未完成,某些产品无法安装该怎样解决呢?一些朋友在win7或者win10系统下安装cad2019失败提示cad2019安装未完成,某些产品无法安装,也有时候想重新安装cad2019 ...
- <compilation debug="true" targetFramework="4.5"> 报错解决方案
在 VS2013 下开发的 MVC4 网站,基于 .net 4.5,服务器是一台 Windows 2008 R2,运行的时候就报错了 The 'targetFramework' attribute i ...
- CGI + FastCGI(PHP-FPM)联系与区别 【图解 + 注释】
〇.背景 参考了几篇文章,总结成 图解 + 注释 方便以后查阅. 参考资料: 1.https://www.zhihu.com/question/19582041 2.https://segmentfa ...
- 基于alpine用dockerfile创建的爬虫Scrapy镜像
一.下载alpine镜像 [root@DockerBrian ~]# docker pull alpine Using default tag: latest Trying to pull repos ...
- Matlab中常见的神经网络训练函数和学习函数
一.训练函数 1.traingd Name:Gradient descent backpropagation (梯度下降反向传播算法 ) Description:triangd is a networ ...
- mac下安装rzsz
1.先安装item2,item2 市类似mac风格的终端 item2 下载地址,http://iterm2.com/downloads.html,下载后解压缩就能运行 2.Install Homebr ...
- IntelliJ IDEA导入多个eclipse项目到同一个workspace下
IntelliJ IDEA 与eclipse在新建项目上工作区的叫法略有不同,区别见下图. 我们在eclipse都是在新建的workspace目录下新建我们的项目,但是在IDEA中没有workspac ...