1、前言java
状态模式在某些场合中使用是很是方便的,什么叫作状态,若是你们学过《编译原理》就会明白DFA M和NFA M,在肯定有限状态机和非肯定有限状态机中,状态就是最小的单元,当知足某种条件的时候,状态就会发生改变,咱们能够把时间中的一个时刻当作一个状态,那么其实整个社会都是有状态组成的,前一时刻到下一时刻,整个社会上的物质(空间)发生了什么样的变化,所以状态能够很是的大也能够很是的小,天气变化状况是状态,白天和黑夜也是状态,人的生活做息等等都是状态,所以状态无处不在。那么状态模式就是将一个状态看作一个类,这与以往咱们对类的理解不同,以往咱们认为类是对对象的抽象,用来表示对象的,对象通常是具体的事物,而如今咱们将状态这种非具体的看不见的但又真实存在的事物当作类描述的东西,这一点可能须要你们理解。设计模式
那么为何必需要状态模式,不用状态模式能够吗?固然能够,可是仍是回到了代码的可维护性、可扩展性、可复用性这个层面上来考虑问题,好比咱们本例的内容,考虑一个银行系统,能够用来取款、打电话、报警、记录这四种功能,可是考虑以下需求:在白天若是咱们去取款是正常的,晚上取款就要发出警报;在白天打电话有人接,晚上打电话启动留言功能;白天和晚上按警铃都会报警。那么咱们应该如何设计这个程序呢,固然咱们能够对每个动做(做为一个函数),在这个函数内部,咱们进行判断是白天仍是黑夜,而后根据具体的状况作出反应。这样固然是能够的,可是假如咱们的状态(白天和黑夜)很是的多呢,好比将24小时分红24个时间段(24个状态),那么咱们对于每个函数就要判断24遍,这无疑是很是糟糕的代码,可读性很是的差,而且若是需求发生了改变,咱们很难去修改代码(很容易出现错误),可是若是咱们考虑将这些状态都做为一个类,在每个类内部进行处理、判断和相应的切换,这样思路就很是的清晰,若是再增长一种状态,代码须要修改的地方会很是的少,对于状态很是多的情景来讲很是的方便。app
2、代码函数
Context接口:this
package zyr.dp.state; public interface Context { public abstract void setClock(int hour); public abstract void changeState(State state); public abstract void callSecurity(String str); public abstract void recordLog(String msg); }
SafeFrame实现类:spa
package zyr.dp.state; import java.awt.*; import java.awt.Frame; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; public class SafeFrame extends Frame implements Context,ActionListener { private static final long serialVersionUID = 1676660221139225498L; private Button btnUse=new Button("使用"); private Button btnAlarm=new Button("警铃"); private Button btnPhone=new Button("打电话"); private Button btnExit=new Button("退出"); private TextField tfClock=new TextField(60); private TextArea taAlarm=new TextArea(10,60); private State state=DayState.getInstance(); public SafeFrame(String title){ super(title); setBackground(Color.BLUE); setLayout(new BorderLayout()); add(tfClock,BorderLayout.NORTH); tfClock.setEditable(false); add(taAlarm,BorderLayout.CENTER); taAlarm.setEditable(false); Panel panel=new Panel(); panel.add(btnUse); panel.add(btnAlarm); panel.add(btnPhone); panel.add(btnExit); add(panel,BorderLayout.SOUTH); pack(); show(); btnUse.addActionListener(this); btnAlarm.addActionListener(this); btnPhone.addActionListener(this); btnExit.addActionListener(this); } public void setClock(int hour) { tfClock.setText(hour<10 ? "如今时间是:" + "0"+hour : "如今时间是:" +hour); state.doClock(this, hour); } public void changeState(State state) { System.out.println("从状态"+this.state+"转变到了"+state); this.state=state; } public void callSecurity(String str) { taAlarm.append("Call..."+str+"\n"); } public void recordLog(String msg) { taAlarm.append("record..."+msg+"\n"); } public void actionPerformed(ActionEvent e) { if(e.getSource()==btnUse){ state.doUse(this); }else if(e.getSource()==btnAlarm){ state.doAlarm(this); }else if(e.getSource()==btnPhone){ state.doPhone(this); }else if(e.getSource()==btnExit){ System.exit(0); }else{ System.out.print("未预料错误!"); } } }
State接口:.net
package zyr.dp.state; public interface State { public abstract void doClock(Context context,int hour); public abstract void doUse(Context context); public abstract void doAlarm(Context context); public abstract void doPhone(Context context); }
NightState实现类:设计
package zyr.dp.state; public class NightState implements State { private NightState(){ } private static NightState nightState=new NightState(); public static NightState getInstance() { return nightState; } public void doClock(Context context, int hour) { if(hour>=6 && hour <18){ //白天 context.changeState(DayState.getInstance()); } } public void doUse(Context context) { context.callSecurity("晚上使用"); } public void doAlarm(Context context) { context.callSecurity("晚上警铃"); } public void doPhone(Context context) { context.recordLog("晚上打电话"); } }
DayState实现类:3d
package zyr.dp.state; public class DayState implements State { private DayState(){ } private static DayState dayState=new DayState(); public static DayState getInstance() { return dayState; } public void doClock(Context context, int hour) { if(hour<6 || hour >=18){ //晚上 context.changeState(NightState.getInstance()); } } public void doUse(Context context) { context.callSecurity("白天使用"); } public void doAlarm(Context context) { context.callSecurity("白天警铃"); } public void doPhone(Context context) { context.recordLog("白天打电话"); } }
Main类:代理
package zyr.dp.state; public class Main { public static void main(String[] args) { SafeFrame f=new SafeFrame("状态模式"); while(true){ for(int hour=1;hour<=24;hour++){ f.setClock(hour); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } }
运行结果:
3、总结
能够看到状态模式的强大威力,是用最简洁的代码经过接口、抽象类、普通类、继承、委托、代理模式等方式,将状态抽象为类,而后经过控制状态的逻辑委托不一样的状态去作不一样的事情,对于每个状态来讲又再次委托控制状态的逻辑做出相应的动做和修改,这样看起来比较复杂,其实仔细阅读就会发现由于接口的缘由,使得程序很是的简洁,各个状态分工明确,密切配合。
可是状态模式也有一些缺点,正是由于各个状态密切配合,在一个状态之中要知道其余状态的对象,这就形成了必定的关联,状态与状态之间是一种紧耦合的关系,这是状态模式的一点缺点,针对于这一点,咱们能够将状态迁移的代码统一交给SafeFrame来作,这样就要使用到了Mediator仲裁者模式了。
使用单例的缘由是若是一直创造新的对象会对内存产生浪费,由于单例便可。一样的使用状态模式经过接口使用state变量来表示相应的状态,不会产生混淆和矛盾,相比于使用多个变量来分区间表示状态来讲是很是清晰简练的。State模式便于增长新的状态(也须要修改其余状态的状态迁移代码),不便于增长新的“依赖于状态的处理”,好比doAlarm等,由于一旦增长了,实现了State接口的全部状态都要增长该部分代码。
同时咱们也看到了实例的多面性,好比SafeFrame实例实现了ActionListener接口和Context接口,那么就能够将new SafeFrame()对象传入fun1(ActionListener a)和fun2(Context context)这两个方法之中,以后这两个方法对该对象的使用是不一样的,权限也不同,所以多接口就会产生多面性。状态模式实际上是用了分而治之的思想,将不一样的状态分开来讨论,抽取共同性,从而使问题变得简单。