JAVA設計模式:狀態模式 JAVA設計模式:狀態模式

 轉載:http://www.cnblogs.com/pony1223/p/7518226.htmlhtml

1、引出狀態模式java

假設咱們如今有一個糖果機項目,那麼咱們知道正常通常糖果機提供給用戶的行爲有這麼幾種:投入硬幣、轉動曲柄、退出硬幣幾種行爲;那麼糖果機呢通常有這幾中狀態,待機狀態、持有硬幣的準備狀態、運行狀態即正在售出狀態和初始狀態 這麼幾種正常狀態。 咱們發現處於不一樣狀態的時候,持有的行爲是不同的,圖以下:算法

 

 

若是咱們採用傳統的方法來寫代碼,那麼在投入硬幣這個行爲操做的時候,咱們會進行狀態的判斷,只有在處於待機狀態狀況下這種行爲是正常的,而其餘則非正常,那麼其餘行爲也同樣,都須要去先判斷下當前的狀態來進行操做。獲得的代碼則爲:設計模式

複製代碼
  1 package study.designmode.statemode;
  2 
  3 public class CandyMachine {
  4 
  5     final static int SoldOutState = 0; //初始狀態
  6     final static int OnReadyState = 1;  //待機狀態
  7     final static int HasCoin = 2;  //準備狀態
  8     final static int SoldState = 3;  //售出狀態
  9 
 10     private int state = SoldOutState; //變量,用於存放當前的狀態值
 11     private int count = 0; //糖果的數目
 12 
 13     public CandyMachine(int count) {
 14         this.count = count;
 15         if (count > 0) {
 16             state = OnReadyState;
 17         }
 18     }
 19 
 20     //投入硬幣行爲的時候,經過判斷當前的狀態來匹配全部的狀態.
 21     public void insertCoin() {
 22         switch (state) {
 23         case SoldOutState:
 24             System.out.println("you can't insert coin,the machine sold out!");
 25             break;
 26         case OnReadyState: //只有在待機狀態的時候,投入硬幣行爲正確,並將狀態改變爲準備狀態
 27             state = HasCoin;
 28             System.out
 29                     .println("you have inserted a coin,next,please turn crank!");
 30             break;
 31         case HasCoin:
 32             System.out.println("you can't insert another coin!");
 33 
 34             break;
 35         case SoldState:
 36             System.out.println("please wait!we are giving you a candy!");
 37 
 38             break;
 39         }
 40 
 41     }
 42 
 43     //回退硬幣
 44     public void returnCoin() {
 45         switch (state) {
 46         case SoldOutState:
 47             System.out
 48                     .println("you can't return,you haven't inserted a coin yet!");
 49             break;
 50         case OnReadyState:
 51             System.out.println("you haven't inserted a coin yet!");
 52             break;
 53         case HasCoin:
 54 
 55             System.out.println("coin return!");
 56             state = OnReadyState;
 57 
 58             break;
 59         case SoldState:
 60             System.out.println("sorry,you already have turned the crank!");
 61 
 62             break;
 63         }
 64 
 65     }
 66 
 67     //轉動曲柄
 68     public void turnCrank() {
 69         switch (state) {
 70         case SoldOutState:
 71             System.out.println("you turned,but there are no candies!");
 72             break;
 73         case OnReadyState:
 74             System.out.println("you turned,but you haven't inserted a coin!");
 75             break;
 76         case HasCoin:
 77             System.out.println("crank turn...!");
 78             state = SoldState;
 79             dispense();
 80             break;
 81         case SoldState:
 82             System.out
 83                     .println("we are giving you a candy,turning another get nothing,!");
 84             break;
 85         }
 86 
 87     }
 88 
 89     //觸發發放糖果行爲
 90     private void dispense() {
 91         count = count - 1;
 92         System.out.println("a candy rolling out!");
 93         if (count > 0) {
 94             state = OnReadyState;
 95         } else {
 96             System.out.println("Oo,out of candies");
 97             state = SoldOutState;
 98         }
 99 
100     }
101 
102     public void printstate() {
103 
104         switch (state) {
105         case SoldOutState:
106             System.out.println("***SoldOutState***");
107             break;
108         case OnReadyState:
109             System.out.println("***OnReadyState***");
110             break;
111         case HasCoin:
112 
113             System.out.println("***HasCoin***");
114 
115             break;
116         case SoldState:
117             System.out.println("***SoldState***");
118             break;
119         }
120 
121     }
122 }
複製代碼

那麼上面這種方式存在什麼問題呢?首先很直觀的感覺就是:架構

1.存在大量的switch case 語句  固然能夠用if  else 也是同樣的。dom

 

2.可擴展性差,而且一旦要加入一種新的狀態,那麼就會要修改全部的switch case  不符合開閉原則ide

3.沒有采用面向對象的方式去封裝post

好比,這個時候,新增長了一種狀態,贏家狀態,便可以獲取到兩粒糖果;那麼若是用上面的方式,確定是不符合開閉原則的,同時擴展性也是很差的;那麼咱們有什麼其它的方式來解決呢?測試

 

2、解決辦法this

爲了解決上面的問題,咱們首先分析項目中變化的部分和不變的部分,抽化出變化的部分,咱們發現糖果機提供的行爲通常是不變的,就是投入硬幣、轉動曲柄給、退回硬幣、機器發放糖果;而糖果機的狀態是能夠變化的,能夠新增出一種狀態來,好比咱們說的贏家狀態。那麼咱們這個抽出變化的部分,即咱們說的狀態,因而出現了下面的結構設計方案:

 

這個結構圖告訴咱們,提煉出狀態接口出來,而後將各個狀態抽出,並去實現接口,每一個狀態都持有投入硬幣,退回硬幣,轉動曲柄、售出糖果這幾種行爲對應的方法作出相應;而糖果機持有全部的狀態,並經過引用狀態接口來操做各個狀態;這種設計架構就是咱們說的狀態模式。

狀態模式定義:對象行爲的變化是因爲狀態的變化引入,那麼即當內部狀態發生變化的時候,就會改變對象的行爲,而這種改變視乎就改變了整個類。

那麼如今採用狀態模式來解決問題:

1.首先定義接口:

 

複製代碼
package study.designmode.statemode.state;

public interface State {
    public void insertCoin();
    public void returnCoin();
    public void turnCrank();
    public void dispense();
    public void printstate();
}
複製代碼

 

2.定義各個狀態的實現類

準備狀態:

複製代碼
package study.designmode.statemode.state;

import java.util.Random;

public class HasCoin implements State {
    private CandyMachine mCandyMachine;

    public HasCoin(CandyMachine mCandyMachine) {
        this.mCandyMachine = mCandyMachine;
    }

    @Override
    public void insertCoin() {
        // TODO Auto-generated method stub
        System.out.println("you can't insert another coin!");

    }

    @Override
    public void returnCoin() {
        // TODO Auto-generated method stub
        System.out.println("coin return!");
        mCandyMachine.setState(mCandyMachine.mOnReadyState);
    }

    @Override
    public void turnCrank() {
        // TODO Auto-generated method stub
        System.out.println("crank turn...!");
        Random ranwinner=new Random();
        int winner=ranwinner.nextInt(10);
        if(winner==0)
        {
            mCandyMachine.setState(mCandyMachine.mWinnerState);

        }else
        {
            mCandyMachine.setState(mCandyMachine.mSoldState);

        }
        
    }

    @Override
    public void dispense() {
    }

    @Override
    public void printstate() {
        // TODO Auto-generated method stub
        System.out.println("***HasCoin***");

    }

}
複製代碼

 

說明:咱們會發現裏面存在一個糖果機的屬性,而之因此存在這個屬性,就是由於糖果機中持有全部的狀態,而在準備狀態下,確定會因爲某種行爲發生狀態改變,而要改變的狀態都在糖果機中,因此持有一個糖果機屬性,下面也同樣,不在重複說明。

準備狀態:

複製代碼
package study.designmode.statemode.state;

public class OnReadyState implements State {
    private CandyMachine mCandyMachine;
    public OnReadyState(CandyMachine mCandyMachine)
    {
        this.mCandyMachine=mCandyMachine;
    }

    @Override
    public void insertCoin() {
        // TODO Auto-generated method stub
        System.out
        .println("you have inserted a coin,next,please turn crank!");
        mCandyMachine.setState(mCandyMachine.mHasCoin);
    }

    @Override
    public void returnCoin() {
        // TODO Auto-generated method stub
        System.out.println("you haven't inserted a coin yet!");
        
    }

    @Override
    public void turnCrank() {
        // TODO Auto-generated method stub
        System.out.println("you turned,but you haven't inserted a coin!");
        
    }

    @Override
    public void dispense() {
        // TODO Auto-generated method stub

    }

    @Override
    public void printstate() {
        // TODO Auto-generated method stub
        System.out.println("***OnReadyState***");
        
    }

}
複製代碼

 

初始狀態:

複製代碼
package study.designmode.statemode.state;

public class SoldOutState implements State {

    private CandyMachine mCandyMachine;
    public SoldOutState(CandyMachine mCandyMachine)
    {
        this.mCandyMachine=mCandyMachine;
    }

    @Override
    public void insertCoin() {
        // TODO Auto-generated method stub
        System.out.println("you can't insert coin,the machine sold out!");
        
    }

    @Override
    public void returnCoin() {
        // TODO Auto-generated method stub
        System.out
        .println("you can't return,you haven't inserted a coin yet!");

    }

    @Override
    public void turnCrank() {
        // TODO Auto-generated method stub
        System.out.println("you turned,but there are no candies!");
        
    }

    @Override
    public void dispense() {
        // TODO Auto-generated method stub

    }

    @Override
    public void printstate() {
        // TODO Auto-generated method stub
        System.out.println("***SoldOutState***");
    
    }

}
複製代碼

 

售出狀態:

 

複製代碼
package study.designmode.statemode.state;

public class SoldState implements State {
    private CandyMachine mCandyMachine;
    public SoldState(CandyMachine mCandyMachine)
    {
        this.mCandyMachine=mCandyMachine;
    }

    @Override
    public void insertCoin() {
        // TODO Auto-generated method stub
        System.out.println("please wait!we are giving you a candy!");

    }

    @Override
    public void returnCoin() {
        // TODO Auto-generated method stub
        System.out.println("you haven't inserted a coin yet!");
        
    }

    @Override
    public void turnCrank() {
        // TODO Auto-generated method stub
        System.out
        .println("we are giving you a candy,turning another get nothing,!");

    }

    @Override
    public void dispense() {
        // TODO Auto-generated method stub
        
        mCandyMachine.releaseCandy();
        if (mCandyMachine.getCount() > 0) {
            mCandyMachine.setState(mCandyMachine.mOnReadyState);
        } else {
            System.out.println("Oo,out of candies");
            mCandyMachine.setState(mCandyMachine.mSoldOutState);
        }

    
    
    }

    @Override
    public void printstate() {
        // TODO Auto-generated method stub
        System.out.println("***SoldState***");
        
    }

}
複製代碼

 

贏家狀態:

複製代碼
package study.designmode.statemode.state;

public class WinnerState implements State {

    private CandyMachine mCandyMachine;

    public WinnerState(CandyMachine mCandyMachine) {
        this.mCandyMachine = mCandyMachine;
    }

    @Override
    public void insertCoin() {
        // TODO Auto-generated method stub
        System.out.println("please wait!we are giving you a candy!");

    }

    @Override
    public void returnCoin() {
        // TODO Auto-generated method stub
        System.out.println("you haven't inserted a coin yet!");

    }

    @Override
    public void turnCrank() {
        // TODO Auto-generated method stub
        System.out
                .println("we are giving you a candy,turning another get nothing,!");

    }

    @Override
    public void dispense() {
        // TODO Auto-generated method stub

        
        mCandyMachine.releaseCandy();
        if (mCandyMachine.getCount() == 0) {
            mCandyMachine.setState(mCandyMachine.mSoldOutState);
        } else {
            System.out.println("you are a winner!you get another candy!");
            mCandyMachine.releaseCandy();
            if (mCandyMachine.getCount() > 0) {
                mCandyMachine.setState(mCandyMachine.mOnReadyState);
            } else {
                System.out.println("Oo,out of candies");
                mCandyMachine.setState(mCandyMachine.mSoldOutState);
            }
        }

    }

    @Override
    public void printstate() {
        // TODO Auto-generated method stub
        System.out.println("***WinnerState***");

    }

}
複製代碼

 

3.糖果機,糖果機要持有全部的狀態,並在初始化的時候,要設置其開始的狀態,而後糖果的各個行爲,就委託到了各個狀態中本身維護,代碼以下:

複製代碼
package study.designmode.statemode.state;

public class CandyMachine {

    State mSoldOutState;
    State mOnReadyState;
    State mHasCoin;
    State mSoldState;
    State mWinnerState;
    private State state;
    private int count = 0;

    public CandyMachine(int count) {
        this.count = count;
        mSoldOutState = new SoldOutState(this);
        mOnReadyState = new OnReadyState(this);
        mHasCoin = new HasCoin(this);
        mSoldState = new SoldState(this);
        mWinnerState = new WinnerState(this);
        if (count > 0) {
            state = mOnReadyState;
        } else {
            state = mSoldOutState;
        }
    }

    public void setState(State state) {
        this.state = state;
    }

    public void insertCoin() {
        state.insertCoin();
    }

    public void returnCoin() {
        state.returnCoin();
    }

    public void turnCrank() {
        state.turnCrank();
        state.dispense();
    }

    void releaseCandy() {

        // TODO Auto-generated method stub
        if (count > 0) {
            count = count - 1;
            System.out.println("a candy rolling out!");
        }

    }

    public int getCount() {
        return count;
    }

    public void printstate() {
        state.printstate();
    }
}
複製代碼

 

4.測試類

複製代碼
package study.designmode.statemode.state;

public class MainTest {
    public static void main(String[] args) {
        CandyMachine mCandyMachine = new CandyMachine(6);

        mCandyMachine.printstate();

        mCandyMachine.insertCoin();
        mCandyMachine.printstate();

        mCandyMachine.turnCrank();

        mCandyMachine.printstate();

        mCandyMachine.insertCoin();
        mCandyMachine.printstate();

        mCandyMachine.turnCrank();

        mCandyMachine.printstate();
    }
}
複製代碼

 

結果以下:

 

能夠和開始的傳統方案對比,結果是同樣的,可是具有了可擴展性。

 

3、總結

經過上面的例子,咱們已經對狀態模式有所瞭解,下面咱們作一個總結,來回顧咱們的狀態模式:

1.狀態模式容許對象在內部狀態改變時改變它的行爲,對象看起來好像修改了它的類。
   理解:這個模式將狀態封裝成獨立的類,並將動做委託到表明當前狀態的對象,這就是說行爲會隨着內部狀態而改變。
   「看起來好像修改了它的類」是什麼意思呢?從客戶的視角來看:若是說你使用的對象可以徹底改變它的行爲,那麼你會以爲,這個對象其實是從別的類實例化而來的。然而,實際上,你知道咱們是在使用組合經過簡單引用不一樣的狀態對象來形成類改變的假象

2.狀態模式要點

(1)客戶不會和狀態進行交互,全盤瞭解狀態是 context的工做
(2)在狀態模式中,每一個狀態經過持有Context的引用,來實現狀態轉移
(3)使用狀態模式老是會增長設計中類的數目,這是爲了要得到程序可擴展性,彈性的代價,若是你的代碼不是一次性的,後期可能會不斷加入不一樣的狀態,那麼狀態模式的設計是絕對值得的。【同時也是一個缺點】
(4)狀態類能夠被多個context實例共享

3.狀態模式和策略模式對比

首先讓咱們來看看它們之間更多的類似之處:
添加新的狀態或策略都很容易,並且不須要修改使用它們的Context對象。
它們都讓你的代碼符合OCP原則(軟件對擴展應該是開發的,對修改應該是關閉的)。在狀態模式和策略模式中,Context對象對修改是關閉的,添加新的狀態或策略,都不須要修改Context。
正如狀態模式中的Context會有初始狀態同樣,策略模式一樣有默認策略。
狀態模式以不一樣的狀態封裝不一樣的行爲,而策略模式以不一樣的策略封裝不一樣的行爲。
它們都依賴子類去實現相關行爲

兩個模式的差異在於它們的」意圖「不一樣:

狀態模式幫助對象管理狀態,咱們將一羣行爲封裝早狀態對象中,context的行爲隨時可委託到那些狀態中的一個.隨着時間的流逝,當前狀態在狀態對象集合中游走改變,以反映context內部狀態,所以,context的行爲也會跟着改變。當要添加新的狀態時,不須要修改原來代碼添加新的狀態類便可。 而策略模式容許Client選擇不一樣的行爲。經過封裝一組相關算法,爲Client提供運行時的靈活性。Client能夠在運行時,選擇任一算法,而不改變使用算法的Context。一些流行的策略模式的例子是寫那些使用算法的代碼,例如加密算法、壓縮算法、排序算法。客戶一般主動指定context所要組合的策略對象是哪個.

一句話:最根本的差別在於策略模式是在求解同一個問題的多種解法,這些不一樣解法之間毫無關聯;狀態模式則不一樣,狀態模式要求各個狀態之間有所關聯,以便實現狀態轉移。

 
相關文章
相關標籤/搜索