设计模式----状态模式

状态模式:

允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。

状态模式的结构:

  • 环境(Context)角色,也称上下文:定义客户端所感兴趣的接口,并且保留一个具体状态类的实例。这个具体状态类的实例给出此环境对象的现有状态。
  • 抽象状态(State)角色:定义一个接口,用以封装环境(Context)对象的一个特定的状态所对应的行为。
  • 具体状态(ConcreteState)角色:每一个具体状态类都实现了环境(Context)的一个状态所对应的行为。

源代码:

环境角色类:

public class Context {
    //持有一个State类型的对象实例
    private State state;
    public void setState(State state) { this.state = state; }
    
    //用户感兴趣的接口方法
    public void request(String s) {
        //转调state来处理
        state.handle(s);
    }
}

抽象状态角色:

public interface State {
    //状态对应的处理
    public void handle(String s);
}

具体状态角色:

//具体状态A
public class ConStateA implements State {
    public void handle(String s) {
        System.out.println("ConcreteStateA :" + s);
    }

}
//具体状态B
public class ConStateB implements State {
    public void handle(String s) {
        System.out.println("ConcreteStateB :" + s);
    }

}

测试方法:

public class Client {
    public static void main(String[] args){
        //创建状态
        State state = new ConcreteStateB();
        //创建环境
        Context context = new Context();
        //将状态设置到环境中
        context.setState(state);
        //请求
        context.request("test");
    }
}

示例:糖果机

糖果机工作如上图所示,这个例子中,糖果机是环境,每一个圆圈都是一个具体状态,而每一个箭头都是状态之间的转换。

我们使用状态模式来重写代码:

  1. 首先定义一个state接口。这个接口内糖果机每个动作都有一个对应的方法。
  2. 然后为机器中的每个状态实现状态类。这些类将负责在对应的状态下进行机器的行为。
  3. 最后将动作委托到状态类。

实现state接口,每个状态类都要实现该接口:

public interface State {
	public void insertQuarter();
	public void ejectQuarter();
	public void turnCrank();
	public void dispense();
}

实现糖果机类:

public class GumballMachine {
    //所有的状态都在这里
	State soldOutState;
	State noQuarterState;
	State hasQuarterState;
	State soldState;
 
	State state = soldOutState;
	int count = 0;
    //构造器取得糖果初始数目,并为每个状态创建一个状态实例
	public GumballMachine(int numberGumballs) {
		soldOutState = new SoldOutState(this);
		noQuarterState = new NoQuarterState(this);
		hasQuarterState = new HasQuarterState(this);
		soldState = new SoldState(this);
		this.count = numberGumballs;
 		if (numberGumballs > 0) {
			state = noQuarterState;
		} 
	}
 
	public void insertQuarter() {//委托当前状态
		state.insertQuarter();
	}
	public void ejectQuarter() {//委托当前状态
		state.ejectQuarter();
	}
	public void turnCrank() {//注意这里和其他两个的区别。dispense是一个内部动作,用户不能直接要求发放糖果。用户转动手柄turnCrack()方法调用dispense()
		state.turnCrank();
		state.dispense();
	}
	void setState(State state) {//允许其他对象将机器的状态转换到不同状态
		this.state = state;
	}
 
	void releaseBall() {
		System.out.println("A gumball comes rolling out the slot...");
		if (count != 0) {
			count = count - 1;
		}
	}
 
	int getCount() {
		return count;
	}
 
	void refill(int count) {
		this.count = count;
		state = noQuarterState;
	}

    public State getState() {
        return state;
    }

    public State getSoldOutState() {
        return soldOutState;
    }
    public State getNoQuarterState() {
        return noQuarterState;
    }
    public State getHasQuarterState() {
        return hasQuarterState;
    }
    public State getSoldState() {
        return soldState;
    }
 //更多方法
}

实现状态类:

import java.util.Random;

public class HasQuarterState implements State {
	GumballMachine gumballMachine;
	public HasQuarterState(GumballMachine gumballMachine) {
		this.gumballMachine = gumballMachine;
	}
	public void insertQuarter() {
		System.out.println("You can't insert another quarter");
	}
	public void ejectQuarter() {
		System.out.println("Quarter returned");
		gumballMachine.setState(gumballMachine.getNoQuarterState());
	}
	public void turnCrank() {
		System.out.println("You turned...");
		gumballMachine.setState(gumballMachine.getSoldState());
	}
    public void dispense() {
        System.out.println("No gumball dispensed");
    }
	public String toString() {
		return "waiting for turn of crank";
	}
}

public class NoQuarterState implements State {
    GumballMachine gumballMachine;
    public NoQuarterState(GumballMachine gumballMachine) {
        this.gumballMachine = gumballMachine;
    }
	public void insertQuarter() {
		System.out.println("You inserted a quarter");
		gumballMachine.setState(gumballMachine.getHasQuarterState());
	}
	public void ejectQuarter() {
		System.out.println("You haven't inserted a quarter");
	}
	public void turnCrank() {
		System.out.println("You turned, but there's no quarter");
	 }
	public void dispense() {
		System.out.println("You need to pay first");
	} 
	public String toString() {
		return "waiting for quarter";
	}
}


public class SoldOutState implements State {
    GumballMachine gumballMachine;
    public SoldOutState(GumballMachine gumballMachine) {
        this.gumballMachine = gumballMachine;
    }
	public void insertQuarter() {
		System.out.println("You can't insert a quarter, the machine is sold out");
	}
	public void ejectQuarter() {
		System.out.println("You can't eject, you haven't inserted a quarter yet");
	}
	public void turnCrank() {
		System.out.println("You turned, but there are no gumballs");
	}
	public void dispense() {
		System.out.println("No gumball dispensed");
	}
	public String toString() {
		return "sold out";
	}
}


public class SoldState implements State {
    GumballMachine gumballMachine;
    public SoldState(GumballMachine gumballMachine) {
        this.gumballMachine = gumballMachine;
    }
	public void insertQuarter() {
		System.out.println("Please wait, we're already giving you a gumball");
	}
	public void ejectQuarter() {
		System.out.println("Sorry, you already turned the crank");
	}
	public void turnCrank() {
		System.out.println("Turning twice doesn't get you another gumball!");
	}
	public void dispense() {
		gumballMachine.releaseBall();
		if (gumballMachine.getCount() > 0) {
			gumballMachine.setState(gumballMachine.getNoQuarterState());
		} else {
			System.out.println("Oops, out of gumballs!");
			gumballMachine.setState(gumballMachine.getSoldOutState());
		}
	}
	public String toString() {
		return "dispensing a gumball";
	}
}

要点:

  • 状态模式允许一个对象基于内部状态而拥有不同的行为。
  • 和程序状态机(PSM)不同,状态模式用类代表状态。
  • Context会将行为委托给当前状态对象。
  • 通过将每个状态封装进一个类,我们把以后需要做的任何改变局部化了。
  • 状态模式和策略模式有相同的类图,但他们的意图不同。
  • 使用状态模式通常会导致设计中类的数目大量增加。
  • 状态类可以被多个Context实例共享。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏用户画像

浅析JAVA堆内存和栈内存的区别

堆内存:https://baike.baidu.com/item/%E5%A0%86%E5%86%85%E5%AD%98/7270805?fr=aladdin

20610
来自专栏Python研发

Memcached·Redis缓存的基本操作

Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载。它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态、...

18240
来自专栏向治洪

java造成内存泄露原因

一、Java内存回收机制  不论哪种语言的内存分配方式,都需要返回所分配内存的真实地址,也就是返回一个指针到内存块的首地址。Java中对象是采用new或者反射的...

344100
来自专栏转载gongluck的CSDN博客

Lua学习笔记

--Lua笔记-- --0.Lua开篇-- --http://www.cnblogs.com/stephen-liu74/archive/2012/06/11/...

67760
来自专栏DT乱“码”

java asm 框架 浅析

什么是asm呢?asm是assembly的缩写,是汇编的称号,对于java而言,asm就是字节码级别的编程。   而这里说到的asm是指objectweb as...

27990
来自专栏yl 成长笔记

深刻理解反射(Reflection)

最近公司在搞自动化测试,由于版权问题,无法用 ’录制脚本‘ 进行,也就没法用 VS 自带的 UITest 框架(蛋疼), 所以只能开源的 FlaUI 框架来搞了...

15940
来自专栏Java 源码分析

Java 虚拟机运行时数据区

运行时数据区: Java 虚拟机的运行时数据区按照大的可以分为线程独立使用的数据区,和所有线程共享的数据区。 一.线程独立使用数据区 1.程序计数器 程序计数器...

28050
来自专栏IT技术精选文摘

阿里架构师带你深入浅出jvm

27820
来自专栏Android开发指南

7:多线程

30880
来自专栏技术小站

编程填空:第i位替换 编程填空:第i位取反 编程填空:左边i位取反

写出函数中缺失的部分,使得函数返回值为一个整数,该整数的第i位和m的第i位相同,其他位和n相同。

24210

扫码关注云+社区

领取腾讯云代金券