设计模式——状态模式

更多内容,前往 IT-BLOG

在现实生活中,常常会出现这样的事例:一个人的情绪高兴时,会做出一些助人为乐的事情。情绪低落的时候,会做出一些伤天害理的事情。这里的情绪就是状态,对应做的事情就是行为。在软件开发中也是类似的,有些对象可能会根据不同的情况做出不同的行为,我们把这种对象称为有状态的对象,而把影响对象行为的一个或多个动态变化的属性称为状态。当有状态的对象与外部事件产生互动时,其内部状态会发生改变,从而使得其行为也随之发生改变。

一、基本介绍


【1】状态(State)模式的定义:对有状态的对象,把复杂的 “判断逻辑” 提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。
【2】状态模式(State Pattern):它主要用来解决对象在多种状态转换时,需要对外输出不同的行为问题。状态和行为是一一对应的,状态之间可以相互转换
【3】当一个对象的内在状态改变时,允许改变其行为,这个对象看起来像是改变了其类。
【4】这种类型的设计模式属于行为型模式。

二、状态模式的结构


状态模式把受环境改变的对象行为包装在不同的状态对象里,其意图是让一个对象在其内部状态改变的时候,其行为也随之改变。现在我们来分析其基本结构和实现方法。状态模式包含以下主要角色:
【1】环境(Context)角色:也称为上下文,它定义了客户感兴趣的接口,维护一个当前状态,并将与状态相关的操作委托给当前状态对象来处理。
【2】抽象状态(State)角色:定义一个接口,用以封装环境对象中的特定状态所对应的行为。
【3】具体状态(Concrete State)角色:实现抽象状态所对应的行为。
 

三、状态模式的应用案例


使用状态模式解决 APP 抽奖问题:根据如下流程中的状态,完成具体的业务操作。

【1】应用的结构类图:精华在RaffleActive(上下文类)和状态的子类中。两者之间相互组合,减少复杂的逻辑判断。

【2】State 接口的实现如下:

 1 /*
 2  *  状态对应的抽象行为
 3  */
 4 public interface State {
 5     //扣除积分
 6     public void deduceMoney();
 7     //是否中奖
 8     public boolean raffle();
 9     //发放奖品
10     public void dispensePrize();
11 }

【3】State 子类一:扣除积分类 NoRaffleState 的实现如下,扣除成功后,将 state 设置为抽奖状态。

 1 public class NoRaffleState implements State{
 2     
 3     //初始化时传入活动引用,扣除积分后改变其状态
 4     RaffleActivity active;
 5     
 6     //构造器
 7     public NoRaffleState(RaffleActivity active) {
 8         this.active = active;
 9     }
10 
11     // 当前状态可以扣分,扣分后修改状态
12     @Override
13     public void deduceMoney() {
14         System.out.println("扣除5个积分");
15         active.setState(active.getCanRaffeState());
16         
17     }
18     @Override
19     public boolean raffle() {
20         System.out.println("抽了积分才能抽奖");
21         return false;
22     }
23 
24     @Override
25     public void dispensePrize() {
26         System.out.println("不能方法奖品");
27     }
28 }

【4】 State 子类一:抽奖状态类 CanRaffeState,如果抽中设置状态为抽中状态,未抽中时设置状态为扣积分状态。

 1 public class CanRaffeState implements State{
 2     RaffleActivity active;
 3     
 4     //构造器
 5     public CanRaffeState(RaffleActivity active) {
 6         this.active = active;
 7     }
 8 
 9     //扣积分
10     @Override
11     public void deduceMoney() {
12         System.out.println("已扣除积分");
13     }
14 
15     @Override
16     public boolean raffle() {
17         System.out.println("正在抽奖,请稍等");
18         int num = new Random().nextInt(5);
19         //20% 的中奖机会,中了则返回true
20         if(num == 0) {
21             active.setState(active.getDispenseState());
22             return true;
23             
24         }else {
25             System.out.println("很遗憾没有抽中奖品");
26             active.setState(active.getNoRaffleState());
27             return false;
28         }
29     }
30 
31     @Override
32     public void dispensePrize() {
33         System.out.println("抽奖中,不能发送奖品");
34     }
35 }

【5】 State 子类一:抽中状态类 DispenseState,如果礼物未送完,则发送礼物,并设置状态为扣分状态。否则设置为礼物以发放完,且活动结束状态。

 1 public class DispenseState implements State{
 2     RaffleActivity active;
 3     
 4     //构造器
 5     public DispenseState(RaffleActivity active) {
 6         this.active = active;
 7     }
 8 
 9     @Override
10     public void deduceMoney() {
11         System.out.println("不能扣积分");
12     }
13 
14     @Override
15     public boolean raffle() {
16         System.out.println("不能抽奖");
17         return false;
18     }
19 
20     @Override
21     public void dispensePrize() {
22         if(active.getCount()>0) {
23             System.out.println("恭喜中奖了,奖品已发货");
24             active.setState(active.getNoRaffleState());
25         }else {
26             System.out.println("很遗憾,奖品已发完");
27             active.setState(active.getDispenseOutState());
28         }
29     }
30 }

【6】 State 子类一:礼物发放完,却活动结束状态类 DispenseOutState

 1 public class DispenseOutState implements State{
 2     
 3     @Override
 4     public void deduceMoney() {
 5         System.out.println("活动结束");
 6     }
 7 
 8     @Override
 9     public boolean raffle() {
10         System.out.println("活动结束");
11         return false;
12     }
13 
14     @Override
15     public void dispensePrize() {
16         System.out.println("活动结束");
17         System.exit(0);
18     }
19 
20 }

【7】上下文类 RaffleActivity,主要存储用户的状态和礼物的总记录数等重要信息。并组合所有的状态子类,传入自身对象。

 1 public class RaffleActivity {
 2     //状态
 3     private State state;
 4     //奖品记录数
 5     private int count;
 6     
 7     //构造器,初始化上述两个属性
 8     public RaffleActivity(int count) {
 9         //客户端创建的时候,说明开始是开始抽奖状态。
10         state = noRaffleState;
11         this.count = count;
12     }
13     
14     NoRaffleState noRaffleState = new NoRaffleState(this);
15     CanRaffeState canRaffeState = new CanRaffeState(this);
16     DispenseState dispenseState = new DispenseState(this);
17     DispenseOutState dispenseOutState = new DispenseOutState();
18     
19     // 当前状态可以扣分,扣分后修改状态
20     public void deduceMoney() {
21         state.deduceMoney();
22     }
23     public void raffle() {
24         if(state.raffle()) {
25             state.dispensePrize();
26         } 
27     }
28     
29     //需要注意,我们的数量应该是递减的
30     public int getCount() {
31         int countNum = count;
32         count--;
33         return countNum;
34     }
35 
36     public void setCount(int count) {
37         this.count = count;
38     }
39 
40     public State getState() {
41         return state;
42     }
43     
44     public void setState(State state) {
45         this.state = state;
46     }
47     
48     public NoRaffleState getNoRaffleState() {
49         return noRaffleState;
50     }
51 
52     public CanRaffeState getCanRaffeState() {
53         return canRaffeState;
54     }
55 
56     public DispenseState getDispenseState() {
57         return dispenseState;
58     }
59 
60     public DispenseOutState getDispenseOutState() {
61         return dispenseOutState;
62     }
63 }

【8】客户端端调用类 Client,只需要调用上下文类,便可实现客户端的需求。

 1 public class Client {
 2     
 3     public static void main(String[] args) {
 4         //为了演示方便,就定义只有一个奖品
 5         RaffleActivity activity = new RaffleActivity(1);
 6         for(int i=0;i<30;i++) {
 7             System.out.println("======第"+i+"次,抽取奖品========");
 8             //扣积分
 9             activity.deduceMoney();
10             //抽奖
11             activity.raffle();
12         }
13     }
14 }

四、状态模式的特点


状态模式的主要优点如下:
【1】状态模式将与特定状态相关的行为局部化到一个状态中,并且将不同状态的行为分割开来,满足“单一职责原则”。
【2】减少对象间的相互依赖。将不同的状态引入独立的对象中会使得状态转换变得更加明确,且减少对象间的相互依赖。
【3】有利于程序的扩展。通过定义新的子类很容易地增加新的状态和转换。

状态模式的主要缺点如下:
【1】状态模式的使用必然会增加系统的类与对象的个数。
【2】状态模式的结构与实现都较为复杂,如果使用不当会导致程序结构和代码的混乱。

posted @ 2020-11-19 10:45  Java程序员进阶  阅读(221)  评论(0编辑  收藏  举报