状态模式:允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类
当控制一个对象状态转换的条件表达式过于复杂时,把状态的判断逻辑转移至表示不同状态的一系列类中
我们来看一下自动售票机的使用场景:
圆圈表示自动售票机所处的状态
箭头线表示乘客的操作(也是自动售票机从一个状态切换到另一个状态所需的操作)
状态抽象为自动售票机类的参数,操作抽象为自动售票机类的方法
public class AutoTicketVendor {
private int noCoin = 0;
private int hasCoin = 1;
private int ticketOut = 2;
private int state = noCoin;
public void insertCoin(){
if(state == noCoin){
System.out.println("投币成功,请选择线路");
this.state = hasCoin;
}else if(state == hasCoin){
System.out.println("已投币,无需再次投币");
}else if(state == ticketOut){
System.out.println("已出票,请先拿走您的车票");
}
}
public void cancel(){
if(state == noCoin){
System.out.println("未投币,请先投币");
}else if(state == hasCoin){
System.out.println("退币成功");
this.state = noCoin;
}else if(state == ticketOut){
System.out.println("已出票,不能退币");
}
}
public void chooseRoute(){
if(state == noCoin){
System.out.println("请先投币");
}else if(state == hasCoin){
System.out.println("路线选择成功,正在出票");
this.state = ticketOut;
}else if(state == ticketOut){
System.out.println("已出票,不能更改线路");
}
}
public void getTicket(){
if(state == noCoin){
System.out.println("请先投币");
}else if(state == hasCoin){
System.out.println("请先选择线路");
}else if(state == ticketOut){
System.out.println("已出票,请拿走您的车票");
this.state = noCoin;
}
}
}
public class Passenger {
public void buyTicket(AutoTicketVendor atv) {
atv.insertCoin();
atv.cancel();
atv.insertCoin();
atv.chooseRoute();
atv.getTicket();
}
public static void main(String[] args){
Passenger passenger = new Passenger();
AutoTicketVendor atc = new AutoTicketVendor();
passenger.buyTicket(atc);
}
}
执行结果如下
投币成功,请选择线路
退币成功
投币成功,请选择线路
路线选择成功,正在出票
已出票,请拿走您的车票
现在需求有所变动,在选择好线路后,不希望直接出票,而是待用户确认后再出票,如果用户取消,可以重新选择线路
public class AutoTicketVendor {
private int noCoin = 0;
private int hasCoin = 1;
private int ticketOut = 2;
private int route2Confirm = 3;
private int state = noCoin;
public void insertCoin(){
if(state == noCoin){
System.out.println("投币成功,请选择线路");
this.state = hasCoin;
}else if(state == hasCoin){
System.out.println("已投币,无需再次投币");
}else if(state == ticketOut){
System.out.println("已出票,请先拿走您的车票");
}else if(state == route2Confirm){
System.out.println("请先确认线路");
}
}
public void cancel(){
if(state == noCoin){
System.out.println("未投币,请先投币");
}else if(state == hasCoin){
System.out.println("退币成功");
this.state = noCoin;
}else if(state == ticketOut){
System.out.println("已出票,不能退币");
}else if(state == route2Confirm){
System.out.println("请重新选择线路");
this.state = hasCoin;
}
}
public void chooseRoute(){
if(state == noCoin){
System.out.println("请先投币");
}else if(state == hasCoin){
System.out.println("路线选择成功,请确认");
this.state = route2Confirm;
}else if(state == ticketOut){
System.out.println("已出票,不能更改线路");
}else if(state == route2Confirm){
System.out.println("已选择线路,如需更改,请先取消当前选择");
}
}
public void confirmRoute(){
if(state == noCoin){
System.out.println("请先投币");
}else if(state == hasCoin){
System.out.println("请先选择线路");
}else if(state == ticketOut){
System.out.println("已出票,请拿走您的车票");
}else if(state == route2Confirm){
System.out.println("线路已确认,正在出票中");
this.state = ticketOut;
}
}
public void getTicket(){
if(state == noCoin){
System.out.println("请先投币");
}else if(state == hasCoin){
System.out.println("请先选择线路");
}else if(state == ticketOut){
System.out.println("已出票,请拿走您的车票");
this.state = noCoin;
}else if(state == route2Confirm){
System.out.println("请先确认线路");
}
}
}
public class Passenger {
public void buyTicket(AutoTicketVendor atv) {
atv.insertCoin();
atv.cancel();
atv.insertCoin();
atv.chooseRoute();
atv.cancel();
atv.chooseRoute();
atv.confirmRoute();
atv.getTicket();
}
public static void main(String[] args){
Passenger passenger = new Passenger();
AutoTicketVendor atc = new AutoTicketVendor();
passenger.buyTicket(atc);
}
}
执行结果如下
投币成功,请选择线路
退币成功
投币成功,请选择线路
路线选择成功,请确认
请重新选择线路
路线选择成功,请确认
线路已确认,正在出票中
已出票,请拿走您的车票
考虑到如下几点问题:
1,改动量很大,自动售票机类的每个方法,都要增加对新增状态的判断
2,每个方法糅合了大量的if else以便对所有状态提供正确的响应,在修改的过程中极易出错,能否将不同状态的行为进行分割?
3,状态经常会发生变动,如果状态发生变动,能否只修改状态相关类?而不需要修改自动售票机类
4,自动售票机所处的状态为私有属性,对外是不可见的,乘客类仅关心可以进行的操作,并不关心其内部所处状态以及状态之间的变换
因此我们可以将自动售票机的状态从自动售票机类中提取出来单独进行封装
public interface State {
public void insertCoin(AutoTicketVendor atv);
public void cancel(AutoTicketVendor atv);
public void chooseRoute(AutoTicketVendor atv);
public void confirmRoute(AutoTicketVendor atv);
public void getTicket(AutoTicketVendor atv);
}
public class NoCoin implements State {
public void insertCoin(AutoTicketVendor atv) {
System.out.println("投币成功,请选择线路");
atv.setState(new HasCoin());
}
public void cancel(AutoTicketVendor atv) {
System.out.println("未投币,请先投币");
}
public void chooseRoute(AutoTicketVendor atv) {
System.out.println("请先投币");
}
public void confirmRoute(AutoTicketVendor atv) {
System.out.println("请先投币");
}
public void getTicket(AutoTicketVendor atv) {
System.out.println("请先投币");
}
}
public class HasCoin implements State {
public void insertCoin(AutoTicketVendor atv) {
System.out.println("已投币,无需再次投币");
}
public void cancel(AutoTicketVendor atv) {
System.out.println("退币成功");
atv.setState(new NoCoin());
}
public void chooseRoute(AutoTicketVendor atv) {
System.out.println("路线选择成功,请确认");
atv.setState(new Route2Confirm());
}
public void confirmRoute(AutoTicketVendor atv) {
System.out.println("请先选择线路");
}
public void getTicket(AutoTicketVendor atv) {
System.out.println("请先选择线路");
}
}
public class Route2Confirm implements State {
public void insertCoin(AutoTicketVendor atv) {
System.out.println("请先确认线路");
}
public void cancel(AutoTicketVendor atv) {
System.out.println("请重新选择线路");
atv.setState(new HasCoin());
}
public void chooseRoute(AutoTicketVendor atv) {
System.out.println("已选择线路,如需更改,请先取消当前选择");
}
public void confirmRoute(AutoTicketVendor atv) {
System.out.println("线路已确认,正在出票中");
atv.setState(new TicketOut());
}
public void getTicket(AutoTicketVendor atv) {
System.out.println("请先确认线路");
}
}
public class TicketOut implements State {
public void insertCoin(AutoTicketVendor atv) {
System.out.println("已出票,请先拿走您的车票");
}
public void cancel(AutoTicketVendor atv) {
System.out.println("已出票,不能退币");
}
public void chooseRoute(AutoTicketVendor atv) {
System.out.println("已出票,不能更改线路");
}
public void confirmRoute(AutoTicketVendor atv) {
System.out.println("已出票,请拿走您的车票");
}
public void getTicket(AutoTicketVendor atv) {
System.out.println("已出票,请拿走您的车票");
atv.setState(new NoCoin());
}
}
public class AutoTicketVendor {
private State state = new NoCoin();
public void insertCoin(){
state.insertCoin(this);
}
public void cancel(){
state.cancel(this);
}
public void chooseRoute(){
state.chooseRoute(this);
}
public void confirmRoute(){
state.confirmRoute(this);
}
public void getTicket(){
state.getTicket(this);
}
public State getState() {
return state;
}
public void setState(State state) {
this.state = state;
}
}
消除了庞大的条件分支语句,通过把各种状态转移逻辑分散到State接口的具体实现类中,来降低耦合度,每个方法的具体实现将十分精简
类的数量变多,这是为了获得扩展性而付出的代价,其实真正重要的是暴漏给用户的类的数量
看一下状态模式的类图:
状态模式和策略模式从类图上来看没有区别,本质上真的有什么区别么?
策略模式:除非告诉对象使用另一个策略,否则对象会一直使用其初始化时被赋予的策略
状态模式:初始化时告诉类从哪个状态开始,随着时间的变化,状态也会自动发生变化,大量条件判断语句的替代方式