<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                ### 使用 enum 的狀態機 枚舉類型非常適合用來創建狀態機。一個狀態機可以具有有限個特定的狀態,它通常根據輸入,從一個狀態轉移到下一個狀態,不過也可能存在瞬時狀態(transient states),而一旦任務執行結束,狀態機就會立刻離開瞬時狀態。 每個狀態都具有某些可接受的輸入,不同的輸入會使狀態機從當前狀態轉移到不同的新狀態。由于 enum 對其實例有嚴格限制,非常適合用來表現不同的狀態和輸入。一般而言,每個狀態都具有一些相關的輸出。 自動售貸機是一個很好的狀態機的例子。首先,我們用一個 enum 定義各種輸入: ```java // enums/Input.java import java.util.*; public enum Input { NICKEL(5), DIME(10), QUARTER(25), DOLLAR(100), TOOTHPASTE(200), CHIPS(75), SODA(100), SOAP(50), ABORT_TRANSACTION { @Override public int amount() { // Disallow throw new RuntimeException("ABORT.amount()"); } }, STOP { // This must be the last instance. @Override public int amount() { // Disallow throw new RuntimeException("SHUT_DOWN.amount()"); } }; int value; // In cents Input(int value) { this.value = value; } Input() {} int amount() { return value; }; // In cents static Random rand = new Random(47); public static Input randomSelection() { // Don't include STOP: return values()[rand.nextInt(values().length - 1)]; } } ``` 注意,除了兩個特殊的 Input 實例之外,其他的 Input 都有相應的價格,因此在接口中定義了 amount(方法。然而,對那兩個特殊 Input 實例而言,調用 amount(方法并不合適,所以如果程序員調用它們的 amount)方法就會有異常拋出(在接口內定義了一個方法,然后在你調用該方法的某個實現時就會拋出異常),這似乎有點奇怪,但由于 enum 的限制,我們不得不采用這種方式。 VendingMachine 對輸入的第一個反應是將其歸類為 Category enum 中的某一個 enum 實例,這可以通過 switch 實現。下面的例子演示了 enum 是如何使代碼變得更加清晰且易于管理的: ```java // enums/VendingMachine.java // {java VendingMachine VendingMachineInput.txt} import java.util.*; import java.io.IOException; import java.util.function.*; import java.nio.file.*; import java.util.stream.*; enum Category { MONEY(Input.NICKEL, Input.DIME, Input.QUARTER, Input.DOLLAR), ITEM_SELECTION(Input.TOOTHPASTE, Input.CHIPS, Input.SODA, Input.SOAP), QUIT_TRANSACTION(Input.ABORT_TRANSACTION), SHUT_DOWN(Input.STOP); private Input[] values; Category(Input... types) { values = types; } private static EnumMap<Input,Category> categories = new EnumMap<>(Input.class); static { for(Category c : Category.class.getEnumConstants()) for(Input type : c.values) categories.put(type, c); } public static Category categorize(Input input) { return categories.get(input); } } public class VendingMachine { private static State state = State.RESTING; private static int amount = 0; private static Input selection = null; enum StateDuration { TRANSIENT } // Tagging enum enum State { RESTING { @Override void next(Input input) { switch(Category.categorize(input)) { case MONEY: amount += input.amount(); state = ADDING_MONEY; break; case SHUT_DOWN: state = TERMINAL; default: } } }, ADDING_MONEY { @Override void next(Input input) { switch(Category.categorize(input)) { case MONEY: amount += input.amount(); break; case ITEM_SELECTION: selection = input; if(amount < selection.amount()) System.out.println( "Insufficient money for " + selection); else state = DISPENSING; break; case QUIT_TRANSACTION: state = GIVING_CHANGE; break; case SHUT_DOWN: state = TERMINAL; default: } } }, DISPENSING(StateDuration.TRANSIENT) { @Override void next() { System.out.println("here is your " + selection); amount -= selection.amount(); state = GIVING_CHANGE; } }, GIVING_CHANGE(StateDuration.TRANSIENT) { @Override void next() { if(amount > 0) { System.out.println("Your change: " + amount); amount = 0; } state = RESTING; } }, TERMINAL {@Override void output() { System.out.println("Halted"); } }; private boolean isTransient = false; State() {} State(StateDuration trans) { isTransient = true; } void next(Input input) { throw new RuntimeException("Only call " + "next(Input input) for non-transient states"); } void next() { throw new RuntimeException( "Only call next() for " + "StateDuration.TRANSIENT states"); } void output() { System.out.println(amount); } } static void run(Supplier<Input> gen) { while(state != State.TERMINAL) { state.next(gen.get()); while(state.isTransient) state.next(); state.output(); } } public static void main(String[] args) { Supplier<Input> gen = new RandomInputSupplier(); if(args.length == 1) gen = new FileInputSupplier(args[0]); run(gen); } } // For a basic sanity check: class RandomInputSupplier implements Supplier<Input> { @Override public Input get() { return Input.randomSelection(); } } // Create Inputs from a file of ';'-separated strings: class FileInputSupplier implements Supplier<Input> { private Iterator<String> input; FileInputSupplier(String fileName) { try { input = Files.lines(Paths.get(fileName)) .skip(1) // Skip the comment line .flatMap(s -> Arrays.stream(s.split(";"))) .map(String::trim) .collect(Collectors.toList()) .iterator(); } catch(IOException e) { throw new RuntimeException(e); } } @Override public Input get() { if(!input.hasNext()) return null; return Enum.valueOf(Input.class, input.next().trim()); } } ``` 輸出為: ``` 25 50 75 here is your CHIPS 0 100 200 here is your TOOTHPASTE 0 25 35 Your change: 35 0 25 35 Insufficient money for SODA 35 60 70 75 Insufficient money for SODA 75 Your change: 75 0 Halted ``` 由于用 switch 語句從 enum 實例中進行選擇是最常見的一種方式(請注意,為了使 enum 在 switch 語句中的使用變得簡單,我們是需要付出其他代價的),所以,我們經常遇到這樣的問題:將多個 enum 進行分類時,“我們希望在什么 enum 中使用 switch 語句?”我們通過 VendingMachine 的例子來研究一下這個問題。對于每一個 State,我們都需要在輸入動作的基本分類中進行查找:用戶塞入鈔票,選擇了某個貨物,操作被取消,以及機器停止。然而,在這些基本分類之下,我們又可以塞人不同類型的鈔票,可以選擇不同的貨物。Category enum 將不同類型的 Input 進行分組,因而,可以使用 categorize0 方法為 switch 語句生成恰當的 Cateroy 實例。并且,該方法使用的 EnumMap 確保了在其中進行查詢時的效率與安全。 如果讀者仔細研究 VendingMachine 類,就會發現每種狀態的不同之處,以及對于輸入的不同響應,其中還有兩個瞬時狀態。在 run() 方法中,狀態機等待著下一個 Input,并一直在各個狀態中移動,直到它不再處于瞬時狀態。 通過兩種不同的 Generator 對象,我們可以使用不同的 Supplier 對象來測試 VendingMachine,首先是 RandomInputSupplier,它會不停地生成除了 SHUT-DOWN 之外的各種輸入。通過長時間地運行 RandomInputSupplier,可以起到健全測試(sanity test)的作用,能夠確保該狀態機不會進入一個錯誤狀態。另一個是 FileInputSupplier,使用文件以文本的方式來描述輸入,然后將它們轉換成 enum 實例,并創建對應的 Input 對象。上面的程序使用的正是如下的文本文件: ``` // enums/VendingMachineInput.txt QUARTER; QUARTER; QUARTER; CHIPS; DOLLAR; DOLLAR; TOOTHPASTE; QUARTER; DIME; ABORT_TRANSACTION; QUARTER; DIME; SODA; QUARTER; DIME; NICKEL; SODA; ABORT_TRANSACTION; STOP; ``` FileInputSupplier 構造函數將此文件轉換為流,并跳過注釋行。然后它使用 String.split() 以分號進行分割。這會生成一個 String 數組,并可以通過將其轉換為 Stream,然后應用 flatMap() 來將其輸入到流中。其輸出結果將去除所有空格空格,并轉換為 List\<String\>,且從中獲取 Iterator\<String\>。 這種設計有一個缺陷,它要求 enum State 實例訪問的 VendingMachine 屬性必須聲明為 static,這意味著,你只能有一個 VendingMachine 實例。不過如果我們思考一下實際的(嵌入式 Java)應用,這也許并不是一個大問題,因為在一臺機器上,我們可能只有一個應用程序。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看