• 设计模式之状态模式


    情景:有一个自动投币的糖果机。它有以下几种状态(每个圆圈代表一个状态):

    如果用常量值来定义几个变量

    final static int SOLD_OUT = 0;
    final static int NO_QUARTER = 1;
    final static int HAS_QUARTER = 2;
    final static int SOLD = 3;
    
    int state = SOLD_OUT;

    这样状态变化就可以写成:

    public void insertQuarter() {
        if (state == HAS_QUARTER) {
            ...
        } else if (state == ...) {
            ...
        } ...
    }

    每一个动作都要先判断状态,改后再决定反馈以及下一个状态是什么。

    虽然可以实现要求,但是当出现新的状态的时候,很不利于扩展。

    我们把状态变成一个类,把不同的状态封装在各自的类中,然后在动作发生时委托给当前状态。

    首先,定义状态接口

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

    然后定义各种状态

    public class NoQuarterState implements State {
        
        GumballMachine gumballMachine;
        
        public NoQuarterState(GumballMachine gumballMachine) {
            this.gumballMachine = gumballMachine; 
        }
    
        @Override
        public void insertQuarter() {
            System.out.println("You insert a quarter");
            gumballMachine.setState(gumballMachine.getHasQuarterState());
        }
    
        @Override
        public void ejectQuarter() {
            System.out.println("You haven't inserted a quarter");
        }
    
        @Override
        public void turnCrank() {
            System.out.println("You turned, but there's no quarter");
        }
    
        @Override
        public void dispense() {
            System.out.println("You need to pay first");
        }
    
    }
    NoQuarterState.java
    public class HasQuarterState implements State {
        
        GumballMachine gumballMachine;
        
        public HasQuarterState(GumballMachine gumballMachine) {
            this.gumballMachine = gumballMachine;
        }
    
        @Override
        public void insertQuarter() {
            System.out.println("You can't insert another quater");        
        }
    
        @Override
        public void ejectQuarter() {
            System.out.println("Quarter returned");
            gumballMachine.setState(gumballMachine.getNoQuarterState());
        }
    
        @Override
        public void turnCrank() {
            System.out.println("You turned...");
            gumballMachine.setState(gumballMachine.getSoldState());
        }
    
        @Override
        public void dispense() {
            System.out.println("No gumball dispensed");
        }
    
    }
    HasQuarterState.java
    public class SoldOutState implements State {
        
        GumballMachine gumballMachine;
        
        public SoldOutState(GumballMachine gumballMachine) {
            this.gumballMachine = gumballMachine;
        }
    
        @Override
        public void insertQuarter() {
            System.out.println("You can't insert a quarter, the machine is sold out");
        }
    
        @Override
        public void ejectQuarter() {
            System.out.println("You can't eject, you haven't inserted a quarter yet");
        }
    
        @Override
        public void turnCrank() {
            System.out.println("You turned, but there are no gumballs");
        }
    
        @Override
        public void dispense() {
            System.out.println("No gumball dispensed");
        }
    
    }
    SoldOutState.java
    public class SoldState implements State {
        
        GumballMachine gumballMachine;
        
        public SoldState(GumballMachine gumballMachine) {
            this.gumballMachine = gumballMachine;
        }
    
        @Override
        public void insertQuarter() {
            System.out.println("Please wait, we're already giving you a gumball");
        }
    
        @Override
        public void ejectQuarter() {
            System.out.println("Sorry, you already turned the crank");
        }
    
        @Override
        public void turnCrank() {
            System.out.println("Turning twice doesn't get you another gumball!");
        }
    
        @Override
        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());
            }
        }
    
    }
    SoldState.java

    最后写一下糖果机

    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);
            
            count = numberGumballs;
            if (numberGumballs > 0) {
                state = noQuarterState;
            }
        }
        
        public void insertQuarter() {
            state.insertQuarter();
        }
        
        public void ejectQuarter() {
            state.ejectQuarter();
        }
        
        public void turnCrank() {
            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 -= 1;
            }
        }
        
        public int getCount() {
            return count; 
        }
        
        public State getSoldOutState() {
            return soldOutState;
        }
    
        public State getNoQuarterState() {
            return noQuarterState;
        }
    
        public State getHasQuarterState() {
            return hasQuarterState;
        }
    
        public State getSoldState() {
            return soldState;
        }
        
        public String toString() {    
            return "
    Mighty Gumball, Inc
    " + "Inventory: " + 
                    count + " gumballs
    ";
        }
    }

    测试类:

    public class GumballMachineTestDriver {
        public static void main(String[] args) {
            GumballMachine gumballMachine = new GumballMachine(5);
            
            System.out.println(gumballMachine);
            
            gumballMachine.insertQuarter();
            gumballMachine.turnCrank();
            
            System.out.println(gumballMachine);
            
            gumballMachine.insertQuarter();
            gumballMachine.ejectQuarter();
            gumballMachine.turnCrank();
            
            System.out.println(gumballMachine);
            
            gumballMachine.insertQuarter();
            gumballMachine.turnCrank();
            gumballMachine.insertQuarter();
            gumballMachine.turnCrank();
            gumballMachine.ejectQuarter();
            
            System.out.println(gumballMachine);
            
            gumballMachine.insertQuarter();
            gumballMachine.insertQuarter();
            gumballMachine.turnCrank();
            gumballMachine.insertQuarter();
            gumballMachine.turnCrank();
            gumballMachine.insertQuarter();
            gumballMachine.turnCrank();
            
            System.out.println(gumballMachine);
            
        }
    }
    GumballMachineTestDriver.java

    将每一个状态局部化到它自己的类中。将容易产生问题的if语句删除,方便日后的维护。

    这样,每一个状态“对修改关闭”,糖果机“对扩展开放”

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

    类图:

    现在我们改变需求,希望有10%的概率中奖,也就是投一个币,获得两个糖果。

    我们只需要添加一个新的状态类。

    public class WinnerState implements State {
        
        GumballMachine gumballMachine;
        
        public WinnerState(GumballMachine gumballMachine) {
            this.gumballMachine = gumballMachine;
        }
    
        @Override
        public void insertQuarter() {
            System.out.println("Please wait, we're already giving you a gumball");
        }
    
        @Override
        public void ejectQuarter() {
            System.out.println("Sorry, you already turned the crank");
        }
    
        @Override
        public void turnCrank() {
            System.out.println("Turning twice doesn't get you another gumball!");
        }
    
        @Override
        public void dispense() {
            System.out.println("YOU'RE A WINNER! YOU GET TWO GUMBALLS FOR YOUR QUARTERS");
            gumballMachine.releaseBall();
            if (gumballMachine.getCount() == 0) {
                gumballMachine.setState(gumballMachine.getSoldOutState());
            } else {
                gumballMachine.releaseBall();
                if (gumballMachine.getCount() > 0) {
                    gumballMachine.setState(gumballMachine.getNoQuarterState());
                } else {
                    System.out.println("Oops, out of gumballs!");
                    gumballMachine.setState(gumballMachine.getSoldOutState());
                }
            }
        }
    
    }

    在HasQuarterState中有几率跳到WinnerState状态:

    public class HasQuarterState implements State {
        
        GumballMachine gumballMachine;
        Random randomWinner = new Random(System.currentTimeMillis());
    
        // ....
    
        @Override
        public void turnCrank() {
            System.out.println("You turned...");
            int winner = randomWinner.nextInt(10);
            if (winner == 0 && (gumballMachine.getCount() > 1)) {
                gumballMachine.setState(gumballMachine.getWinnerState());
            } else {
                gumballMachine.setState(gumballMachine.getSoldState());
            }
        }
    
        // ....
    
    }

    记得在糖果机类中添加一个WinnerState状态类,这个新的需求就实现了。

    • 状态模式允许Context随着状态的改变而改变行为。
    • 状态装换可以由State类或者Context类控制。
    • 使用状态模式通常会导致设计中的类数目大量增加。
    • 状态类可以被多个Context实例共享。
  • 相关阅读:
    netty的ChannelPipeline执行顺序对inBound和outBound执行器造成的影响
    【转载,并做少量修改整合】Java 双亲委派模型与应用:SPI(Service Provider Interface)
    JDK1.8 论ConcurrentHashMap是如何扩容的
    如何解决Vue.js里面noVNC的截图问题之后篇——用web虚拟终端作为替代功能
    hihocoder 1036 Trie图
    Codeforces#390
    Codeforces#386
    codeforces 743D Chloe and pleasant prizes
    codeforces 742E (二分图着色)
    洛谷 P1280 尼克的任务题解
  • 原文地址:https://www.cnblogs.com/wenruo/p/6558448.html
Copyright © 2020-2023  润新知