欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java使用枚舉實現(xiàn)狀態(tài)機的方法詳解

 更新時間:2023年11月09日 10:30:25   作者:一只小熊貓呀  
這篇文章主要介紹了Java使用枚舉實現(xiàn)狀態(tài)機的方法詳解,枚舉類型很適合用來實現(xiàn)狀態(tài)機,狀態(tài)機可以處于有限數(shù)量的特定狀態(tài),它們通常根據(jù)輸入,從一個狀態(tài)移動到下一個狀態(tài),但同時也會存在瞬態(tài),需要的朋友可以參考下

Java枚舉實現(xiàn)狀態(tài)機

枚舉類型很適合用來實現(xiàn)狀態(tài)機。狀態(tài)機可以處于有限數(shù)量的特定狀態(tài)。它們通常根據(jù)輸入,從一個狀態(tài)移動到下一個狀態(tài),但同時也會存在瞬態(tài)。當(dāng)任務(wù)執(zhí)行完畢后,狀態(tài)機會立即跳出所有狀態(tài)。

每個狀態(tài)都有某些可接受的輸入,不同的輸入會使?fàn)顟B(tài)機從當(dāng)前狀態(tài)切換到新的狀態(tài)。由于枚舉限制了可能出現(xiàn)的狀態(tài)集大?。礌顟B(tài)數(shù)量),因此很適合表達(dá)(枚舉)不同的狀態(tài)和輸入。

每種狀態(tài)一般也會有某種對應(yīng)的輸出。

自動售貨機是個很好的狀態(tài)機應(yīng)用的例子。首先,在一個枚舉中定義一系列輸入:

Input.java

import java.util.Random;

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 { // 這必須是最后一個實例
        @Override
        public int amount() { // 不允許
            throw new RuntimeException("SHUT_DOWN.amount()");
        }
    };
    int value; // 單位為美分(cents)

    Input(int value) {
        this.value = value;
    }

    Input() {
    }

    int amount() {
        return value;
    }

    ; // In cents
    static Random rand = new Random(47);

    public static Input randomSelection() {
        //不包括 STOP:
        return values()[rand.nextInt(values().length - 1)];
    }
}

注意其中兩個 Input 有著對應(yīng)的金額,所以在接口中定義了 amount() 方法。然而,對另外兩個 Input 調(diào)用 amount() 是不合適的,如果調(diào)用就會拋出異常。盡管這是個有點奇怪的機制(在接口中定義一個方法,然后如果在某些具體實現(xiàn)中調(diào)用它的話就會拋出異常),但這是枚舉的限制所導(dǎo)致的。

VendingMachine(自動售貨機)接收到輸入后,首先通過 Category(類別) 枚舉來對這些輸入進(jìn)行分類,這樣就可以在各個類別間切換了。下例演示了枚舉是如何使代碼變得更清晰、更易于管理的。

VendingMachine.java

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.function.Supplier;
import java.util.stream.Collectors;

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} // 標(biāo)識 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);
    }
}

// 基本的穩(wěn)健性檢查:
class RandomInputSupplier implements Supplier<Input> {
    @Override
    public Input get() {
        return Input.randomSelection();
    }
}

// 從以“;”分割的字符串的文件創(chuàng)建輸入
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());
    }
}

下面是用于生成輸出的文本文件:

VendingMachine.txt

QUARTER;QUARTER;QUARTER;CHIPS;
DOLLAR;DOLLAR;TOOTHPASTE;
QUARTER;DIME;ABORT_TRANSACTION;
QUARTER;DIME;SODA;
QUARTER;DIME;NICKEL;SODA;
ABORT_TRANSACTION;
STOP;

以下是運行參數(shù)配置:

在這里插入圖片描述

運行結(jié)果如下:

在這里插入圖片描述

因為通過 switch 語句在枚舉實例中進(jìn)行選擇操作是最常見的方式(注意,為了使 switch 便于操作枚舉,語言層面需要付出額外的代價),所以在組織多個枚舉類型時,最常問的問題之一就是“我需要什么東西之上(即以什么粒度)進(jìn)行 switch”。這里最簡單的辦法是,回頭梳理一遍 VendingMachine,就會發(fā)現(xiàn)在每種 State 下,你需要針對輸入操作的基本類別進(jìn)行 switch 操作:投入錢幣、選擇商品、退出交易、關(guān)閉機器。并且在這些類別內(nèi),你還可以投入不同類別的貨幣,選擇不同類別的商品。Category 枚舉會對不同的 Input 類型進(jìn)行分類,因此 categorize() 方法可以在 switch 中生成恰當(dāng)?shù)?Category。這種方法用一個 EnumMap 實現(xiàn)了高效且安全的查詢。

如果你研究一下 VendingMachine 類,便會發(fā)現(xiàn)每個狀態(tài)的區(qū)別,以及對輸入的響應(yīng)區(qū)別。同時還要注意那兩個瞬態(tài):在 run() 方法中,售貨機等待一個 Input,并且會一直在狀態(tài)間移動,直到它不再處于某個瞬態(tài)中。

VendingMachine 可以通過兩種不同的 Supplier 對象,以兩種方法來測試。RandomInputSupplier 只需要持續(xù)生成除 SHUT_DOWN 以外的任何輸入。通過一段較長時間的運行后,就相當(dāng)于做了一次健康檢查,以確定售貨機不會偏離到某些無效狀態(tài)。FileInputSupplier 接收文本形式的輸入描述文件,并將它們轉(zhuǎn)換為 enum 實例,然后創(chuàng)建 Input 對象。下面是用于生成以上輸出的文本文件:

FileInputSupplier 的構(gòu)造器將這個文件轉(zhuǎn)換為行級的 Stream 流,并忽略注釋行。然后它通過 String.split() 方法將每一行都根據(jù)分號拆開。這樣就能生成一個字符串?dāng)?shù)組,可以通過先將該數(shù)組轉(zhuǎn)化為 Stream,然后執(zhí)行 flatMap(),來將其注入(前面 FileInputSupplier 中生成的)Stream 中。結(jié)果將刪除所有的空格,并轉(zhuǎn)換為 List,并從中得到 Iterator。

上述設(shè)計有個限制:VendingMachine 中會被 State 枚舉實例訪問到的字段都必須是靜態(tài)的,這意味著只能存在一個 VendingMachine 實例。這可能不會是個大問題——你可以想想一個實際的(嵌入式Java)實現(xiàn),每臺機器可能就只有一個應(yīng)用程序。

到此這篇關(guān)于Java使用枚舉實現(xiàn)狀態(tài)機的方法詳解的文章就介紹到這了,更多相關(guān)Java枚舉實現(xiàn)狀態(tài)機內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 實體類或?qū)ο笮蛄谢瘯r,忽略為空屬性的操作

    實體類或?qū)ο笮蛄谢瘯r,忽略為空屬性的操作

    這篇文章主要介紹了實體類或?qū)ο笮蛄谢瘯r,忽略為空屬性的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • Java詳細(xì)分析連接數(shù)據(jù)庫的流程

    Java詳細(xì)分析連接數(shù)據(jù)庫的流程

    Java數(shù)據(jù)庫連接,JDBC是Java語言中用來規(guī)范客戶端程序如何來訪問數(shù)據(jù)庫的應(yīng)用程序接口,提供了諸如查詢和更新數(shù)據(jù)庫中數(shù)據(jù)的方法。JDBC也是Sun Microsystems的商標(biāo)。我們通常說的JDBC是面向關(guān)系型數(shù)據(jù)庫的
    2022-05-05
  • Spring MVC 中 短信驗證碼功能的實現(xiàn)方法

    Spring MVC 中 短信驗證碼功能的實現(xiàn)方法

    短信驗證功能在各個網(wǎng)站應(yīng)用都非常廣泛,那么在springmvc中如何實現(xiàn)短信驗證碼功能呢?今天小編抽時間給大家介紹下Spring MVC 中 短信驗證碼功能的實現(xiàn)方法,一起看看吧
    2016-09-09
  • Java實現(xiàn)Consul/Nacos根據(jù)GPU型號、顯存余量執(zhí)行負(fù)載均衡的步驟詳解

    Java實現(xiàn)Consul/Nacos根據(jù)GPU型號、顯存余量執(zhí)行負(fù)載均衡的步驟詳解

    這篇文章主要介紹了Java實現(xiàn)Consul/Nacos根據(jù)GPU型號、顯存余量執(zhí)行負(fù)載均衡的步驟詳解,本文分步驟結(jié)合實例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2025-04-04
  • 初識JAVA數(shù)組

    初識JAVA數(shù)組

    java語言中,數(shù)組是一種最簡單的復(fù)合數(shù)據(jù)類型。數(shù)組是有序數(shù)據(jù)的集合,數(shù)組中的每個元素具有相同的數(shù)據(jù)類型,可以用一個統(tǒng)一的數(shù)組名和下標(biāo)來唯一地確定數(shù)組中的元素。數(shù)組有一維數(shù)組和多維數(shù)組。
    2014-08-08
  • Java中的方法、常量、變量、參數(shù)用例詳解

    Java中的方法、常量、變量、參數(shù)用例詳解

    在JVM的運轉(zhuǎn)中,承載的是數(shù)據(jù),而數(shù)據(jù)的一種變現(xiàn)形式就是“量”,量分為:常量與變量,我們在數(shù)學(xué)和物理學(xué)中已經(jīng)接觸過變量的概念了,在Java中的變量就是在程序運行過程中可以改變其值的量,這篇文章主要介紹了Java中的方法、常量、變量、參數(shù),需要的朋友可以參考下
    2024-01-01
  • Java獲取路徑的6種方式代碼示例

    Java獲取路徑的6種方式代碼示例

    在Java中獲取路徑的方法有多種,每種方法適用于不同的場景,這篇文章主要介紹了Java獲取路徑的6種方式,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2025-02-02
  • java8新特性教程之time包使用總結(jié)

    java8新特性教程之time包使用總結(jié)

    Java8新增了date和time的util包,下面這篇文章主要給大家介紹了關(guān)于java8新特性教程之time包使用的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-01-01
  • Java進(jìn)行反編譯生成.java文件方式(javap、jad下載安裝使用)

    Java進(jìn)行反編譯生成.java文件方式(javap、jad下載安裝使用)

    這篇文章主要介紹了Java進(jìn)行反編譯生成.java文件方式(javap、jad下載安裝使用),具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • 使用dom4j遞歸解析節(jié)點內(nèi)還含有多個節(jié)點的xml

    使用dom4j遞歸解析節(jié)點內(nèi)還含有多個節(jié)點的xml

    這篇文章主要介紹了使用dom4j遞歸解析節(jié)點內(nèi)還含有多個節(jié)點的xml,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-09-09

最新評論