状态模式
状态模式:当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。
本质:根据状态来分离和选择行为。实现了状态逻辑和动态实现的分离。
就举个例子来说明该模式的作用:
当快到期末考试周了,你(实际上是我)还有一堆科门没复习完,你要整天的复习,但是你的学习状态会根据每天的时间段的不同而变化,要求根据不同的时间段输出对应时间段的状态。
什么嘛,这简单到掉牙了啊,啊sir,有没有搞错啊。
package ch11.sjms.state.study;
public class state {
public void dis(int hour){
if(hour<12 && hour>6){
System.out.println("当前时间为上午"+hour+"点,精神抖擞,好好复习!");
}else
if(hour<13){
System.out.println("当前时间为"+hour+"点,饿了,开始犯困...午睡!");
}else
if(hour<17){
System.out.println("当前时间为下午"+hour+"点,状态还不错,继续复习!");
}else
if(hour<21){
System.out.println("当前时间为"+hour+"点,好累啊,不想复习!我要睡觉!");
}else{
System.out.println("睡觉中.....");
}
}
}
package ch11.sjms.state.study;
public class test {
public static void main(String[] args) {
int hour=15;
state state=new state();
state.dis(hour);
}
}
你看,写完了吧
确实可以,但是如果当我们给每天定制了一个任务,规定完成不了任务就要一直学。又或者今天晚上6点要和女朋友出去逛街,所以不管完成与否,6点都要结束复习。如果代码像上面这样写,那么我们需要改的地方就太多了,而且使用了大量的if else语句,导致了方法过长,这并不利于维护。面向对象设计其实就是希望做到代码的责任分解。
不会写没关系,先来看看状态模式的UML类图:
看完UML类图就发现,这个状态模式跟策略模式好像啊
和策略模式的简单对比
我们来看一下策略模式的UML类图
是不是很像,但是区别也显而易见,策略模式里面上下文类的方法只执行了一次,而且参数只是普通类型。而在状态模式里面,为了保证可以根据时间段来查看状态,所以在上下文类里面的方法调用了自身,而且还增加了时间段hour属性。
状态模式的好处是将与特定状态相关的行为局部化,并且将不同状态的行为分割开来。将特定的状态相关的行为都放入一个对象中,由于所有与状态相关的代码都存在于某个ConcreteState中,所以通过定义新的子类可以很容易的增加新的状态和转换。
就是说,状态模式通过把各种状态转移逻辑分布到State的子类之间,来减少相互间的依赖。
来看看状态模式完成例子的示例性代码:
package ch11.sjms.state.study;
public abstract class State {
public abstract void StudyForReview(Study s);
}
package ch11.sjms.state.study;
public class Study {
private State current;
private double hour;
private boolean finish=false;
public Study(){
current=new ForenoonState();//学习初始化上午的学习状态
}
public State getCurrent() {
return current;
}
public void setCurrent(State current) {
this.current = current;
}
public double getHour() {
return hour;
}
public void setHour(double hour) {
this.hour = hour;
}
public boolean isFinish() {
return finish;
}
public void setFinish(boolean finish) {
this.finish = finish;
}
public void StudyForReview(){
current.StudyForReview(this);
}
}
package ch11.sjms.state.study;
public class ForenoonState extends State{
@Override
public void StudyForReview(Study s) {
if(s.getHour()<12){
System.out.println("当前时间为上午"+s.getHour()+"点,精神抖擞,好好复习!");
}else{
s.setCurrent(new NoonState());
s.StudyForReview();
}
}
}
package ch11.sjms.state.study;
public class NoonState extends State{
@Override
public void StudyForReview(Study s) {
if(s.getHour()<13){
System.out.println("当前时间为中午"+s.getHour()+"点,饿了,开始犯困...午睡!");
}else{
s.setCurrent(new AfternoonState());
s.StudyForReview();
}
}
}
package ch11.sjms.state.study;
public class AfternoonState extends State{
@Override
public void StudyForReview(Study s) {
if(s.getHour()<17){
System.out.println("当前时间为下午"+s.getHour()+"点,状态还不错,继续复习!");
}else{
s.setCurrent(new EveningState());
s.StudyForReview();
}
}
}
package ch11.sjms.state.study;
public class EveningState extends State{
@Override
public void StudyForReview(Study s) {
if(s.isFinish()){
s.setCurrent(new RestState());
s.StudyForReview();
}else{
if(s.getHour()<21){
System.out.println("当前时间为"+s.getHour()+"点,好累啊,不想复习!");
}else{
s.setCurrent(new SleepingState());
s.StudyForReview();
}
}
}
}
package ch11.sjms.state.study;
public class RestState extends State{
@Override
public void StudyForReview(Study s) {
System.out.println("当前时间"+s.getHour()+",打打游戏,刷刷视频,累了就睡觉。");
}
}
package ch11.sjms.state.study;
public class SleepingState extends State{
@Override
public void StudyForReview(Study s) {
System.out.println("当前时间"+s.getHour()+"点,不行了,睡着了...");
}
}
package ch11.sjms.state.study;
public class client {
public static void main(String[] args) {
Study s=new Study();
s.setHour(9);
s.StudyForReview();
s.setHour(10);
s.StudyForReview();
s.setHour(12);
s.StudyForReview();
s.setHour(14);
s.StudyForReview();
/*s.setFinish(true);*/
s.setHour(19);
s.StudyForReview();
s.setHour(22);
s.StudyForReview();
}
}
/*输出结果:
当前时间为上午9.0点,精神抖擞,好好复习!
当前时间为上午10.0点,精神抖擞,好好复习!
当前时间为中午12.0点,饿了,开始犯困...午睡!
当前时间为下午14.0点,状态还不错,继续复习!
当前时间为19.0点,好累啊,不想复习!
当前时间22.0点,不行了,睡着了...*/
再看此时的代码,如果要完成刚才的要求:今天晚上6点要和女朋友出去逛街,所以不管完成与否,6点都要结束复习。我们只要增加一个强制停止学习的类,并改动一下傍晚工作状态的判断就可以了,而且不影响其他状态的代码。
那什么时候可以用到状态模式呢?
当一个对象的行为取决于它的状态,并且必须在运行时刻根据状态改变它的行为时,就可以考虑使用状态模式了。
职责链模式
职责链模式:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
本质:分离职责,动态组合。
模拟公司的请假系统:当你请假的时候,请假请求会先给项目经理,项目经理批准后,如果天数大于3天会再交给部门经理进行批准,直到都批准通过后,请假才成功。
我们先用状态模式写
UML类图:
代码:
package ch11.sjms.state;
public interface state {
public void approve(work w);
}
package ch11.sjms.state;
public class state_xmjl implements state{
@Override
public void approve(work w) {
System.out.println("项目经理审批中...");
w.setFinish(true);
//w.setFinish(false);
if(!w.isFinish()){
System.out.println("项目经理审批不通过...");
w.setState(new state_res());w.approve();
}
else{
System.out.println("项目经理审批通过...");
if(w.getDay()<=3){
w.setState(new state_res());w.approve();
}
else{
w.setState(new state_bmjl());w.approve();
}
}
}
}
package ch11.sjms.state;
public class state_bmjl implements state{
@Override
public void approve(work w) {
System.out.println("部门经理审批中...");
//w.setFinish(true);
w.setFinish(false);
if(!w.isFinish()){
System.out.println("部门经理审批不通过...");
w.setState(new state_res());w.approve();
}
else{
System.out.println("部门经理审批通过...");
w.setState(new state_res());w.approve();
}
}
}
package ch11.sjms.state;
public class state_res implements state {
@Override
public void approve(work w) {
if(w.isFinish()){
System.out.println("审批结果:通过");
}
else{
System.out.println("审批结果:不通过");
}
}
}
package ch11.sjms.state;
public class work {
private state current;
public work(){
current=new state_xmjl();
}
private int day;
private boolean finish=false;
public int getDay() {
return day;
}
public void setDay(int day) {
this.day = day;
}
public boolean isFinish() {
return finish;
}
public void setFinish(boolean finish) {
this.finish = finish;
}
public void setState(state s){
current=s;
}
public void approve(){
current.approve(this);
}
}
package ch11.sjms.state;
public class client {
public static void main(String[] args) {
work leave = new work();
leave.setDay(2);
leave.approve();
work leave1 = new work();
leave1.setDay(5);
leave1.approve();
}
}
我们写完之后发现,如果此时部门经理的权限转移到了人事部,而人事部批准之后还要经过董事长的批准才能请下来假。又或者董事长出差了,不用董事长批准也能请假了。那么对上述代码进行修改,就比较麻烦了。
不要紧,我们还有职责链模式来供使用
先来看一下职责链模式的UML图:
可以看出来职责链模式相对于状态模式将上下文类和状态类合并为了一个Handler处理者类,而处理者类又有很多具体的处理者子类。
我们可以看到处理者类中有一个SetSuccessor的方法,这个方法的作用是可以设置某一个处理者的下一个处理者。
当客户提交一个请求时,请求时沿链传递直到有一个具体的处理者对象负责处理它。使得处理者和发送者双方都没有对方明确的信息,且链中的对象自己也并不知道链的结构。结果是职责链可简化对象的相互连接,它们仅需保持一个指向其后继者的引用,且不需要保持它所有的候选处理者的引用。
实现了请求者和处理者的松耦合。动态组合职责。 比状态模式耦合度更低,更加灵活。可以随时地增加或修改处理一个请求地结构。增强了给对象指派职责地灵活性。但是一个请求也极有可能到了链的末端都得不到处理,或者因为没有正确配置而得不到处理,这就很糟糕了。
来看看职责链模式的请假代码吧:
package ch15.sjms.chain;
public abstract class Handler {
protected Handler successor;
public void setSuccessor(Handler successor){
this.successor=successor;
}
public abstract void HandleRequest();
}
package ch15.sjms.chain;
public class HandlerChairman extends Handler{
@Override
public void HandleRequest() {
boolean flag=false;
System.out.println("董事长正在审批...");
flag=true;
if(flag) {
System.out.println("董事长审批通过...");
if (successor != null) {
successor.HandleRequest();
}else{
System.out.println("审批通过!");
}
}
else System.out.println("董事长审批不通过...");
}
}
package ch15.sjms.chain;
public class HandlerDepartment extends Handler{
@Override
public void HandleRequest() {
boolean flag=false;
System.out.println("人事部正在审批...");
flag=true;
if(flag) {
System.out.println("人事部审批通过...");
if (successor != null) {
successor.HandleRequest();
}else{
System.out.println("审批通过!");
}
}
else System.out.println("人事部审批不通过...");
}
}
package ch15.sjms.chain;
public class HandlerManager extends Handler{
@Override
public void HandleRequest() {
boolean flag=false;
System.out.println("经理正在审批...");
flag=true;
if(flag) {
System.out.println("经理审批通过...");
if (successor != null) {
successor.HandleRequest();
}else{
System.out.println("审批通过!");
}
}
else System.out.println("经理审批不通过...");
}
}
package ch15.sjms.chain;
public class client {
public static void main(String[] args) {
Handler h1=new HandlerManager();
Handler h2=new HandlerDepartment();
Handler h3=new HandlerChairman();
h1.setSuccessor(h2);
h2.setSuccessor(h3);
h1.HandleRequest();
System.out.println("-----------------------");
h1.setSuccessor(h2);
h1.HandleRequest();
}
}
/*输出结果:
经理正在审批...
经理审批通过...
人事部正在审批...
人事部审批通过...
董事长正在审批...
董事长审批通过...
审批通过!
-----------------------
经理正在审批...
经理审批通过...
人事部正在审批...
人事部审批通过...
董事长正在审批...
董事长审批通过...
审批通过!*/
而职责链模式的优点和缺点刚才也说了。
优点:实现了请求者和处理者的松耦合。动态组合职责。比状态模式耦合度更低,更加灵活。可以随时地增加或修改处理一个请求地结构。增强了给对象指派职责地灵活性。
缺点:产生了很多细粒性的对象。一个请求也极有可能到了链的末端都得不到处理,或者因为没有正确配置而得不到处理,这就很糟糕了,所以需要提供默认处理方式。