創(chuàng)新互聯(lián)教你如何處理pHp代碼中的枚舉類型Enum的

2023-12-02    分類: 網(wǎng)站建設(shè)

本文旨在提供一些更好的理解什么是枚舉,什么時候使用它們以及如何在php中使用它們.

我們在某些時候使用了常量來定義代碼中的一些常數(shù)值.他們被用來避免 魔法值 .用一個象征性的名字代替一些 魔法值 ,我們可以給它一些意義.然后我們在代碼中引用這個符號名稱.因為我們定義了一次并使用了很多次,所以搜索它并稍后重命名或更改一個值會更容易.

這就是為什么看到類似于下面的代碼并不罕見.

<?php
class User {
    const GENDER_MALE = 0;
    const GENDER_FEMALE = 1;
    const STATUS_INACTIVE = 0;
    const STATUS_ACTIVE = 1;
}

以上常量表示了兩組屬性,GEDNER_* 和 STATUS_*。他們表示一組性別和一組用戶狀態(tài)。每一組都是一個 枚舉 。枚舉是一組元素(也叫做成員)的集合,每一個枚舉都定義了一種新類型。這個類型,和它的值一樣,可以包含任意屬于該枚舉的元素。

在上面的例子中,枚舉借助于常量,每一個常量的值都是一個成員。注意,這樣做的話,我們只能在常量包含的類型中取值。因此,我們在寫這些值的時候不會有類型提示,不知道詳細(xì)的枚舉類型。

來看一個簡短的例子, 但我們假定例子中有更多的代碼

<?php
interface UserFactory {
    public function create(
        string $email,
        int $gender,
        int $status
    ): User;
}
$factory->create(
    $email,
    User::STATUS_ACTIVE,
    User::GENDER_FEMALE
);

第一眼看上去代碼很好,但是他只是碰巧正確運行了!因為兩個不同的枚舉成員實際上是同一個值,調(diào)用create方法成功,是因為這最后兩個參數(shù)被互換了不影響結(jié)果。盡管我們檢查方法接受的值是否有效,運行界面也不會警告我們,測試也會通過。有人能正確的發(fā)現(xiàn)這些bug,但是它也很可能被忽視掉。之后一些情況,比如合并沖突的時候,如果它的值改變了,它可能會引起系統(tǒng)異常。

如果使用標(biāo)量類型,我們會受限于這種類型,無法辨別這兩個值是是不是屬于兩個不同的枚舉。

另一個問題是這個代碼描述的的不是很好。想象一下 create 方法沒有引用常量。 $gender 被別人看作為一個枚舉元素將是有多么困難?看這些元素在哪里被定義又有多么困難?我們之后將會閱讀那些代碼,因此我們應(yīng)該盡可能是讓代碼易于閱讀以及和通過。

我們可以做得更好嗎?Sure!這個方法就是是使用類實例作為枚舉元素,類本身定義了一個新的類型。直到pHp 7,我們可以安裝 SpL類 pECL擴(kuò)展并且使用 SplEnum 。

<?php
class YesNo extends \SplEnum
{
    const __default =  self::YES;
    const NO = 0;
    const YES = 1;
}
$no = new YesNo(YesNo::NO);
var_dump($no == YesNo::NO); //true
var_dump(new YesNo(YesNo::NO) == YesNo::NO); //true

我們擴(kuò)展 SplEnum 并且定義用于創(chuàng)建枚舉元素的常量。枚舉元素是我們手動構(gòu)造的對象,在這種情況下是常量值本身。我們可以將整型與對象進(jìn)行比較,這可能很奇怪。另外,正如文檔所述,這是一個仿真的枚舉。pHp本身并不支持枚舉類型,所以我們在這里探討的所有內(nèi)容都是仿真的。

我們用這種方法得到了什么?我們可以輸入提示我們的參數(shù),并讓pHp引擎在發(fā)生錯誤時提醒我們。我們還可以在枚舉類中包含一些邏輯,并使用 switch 語句來模擬多態(tài)行為。

但也有一些缺點. 例如, 在大多數(shù)情況下, 有些你可以用枚舉元素而不能用標(biāo)識檢查. 這不是不可能的,我們不得不非常小心. 由于我們手動創(chuàng)建枚舉成員, 所以許多成員應(yīng)該是同一個成員, 但這一點手動很難確定.

利用 SplEnum 我們解決枚舉類型問題, 但是當(dāng)我們用標(biāo)識檢查的時候不得不非常小心. 我們需要一個方法限制可以創(chuàng)建的多個元素, 例如  multiton (multiple  singleton objects ).

現(xiàn)在我們將看到由 Java Enum 啟發(fā)并實現(xiàn) multiton 的兩個不同的庫.

第一個是 eloquent/enumeration . 它為每個元素創(chuàng)建一個定義類的實例. 請注意, 沒有我們的幫助, 枚舉的用戶仿真永遠(yuǎn)不能保證一個枚舉實例, 因為我們限制它的每一步都有一個方法去避免.

這個庫可以讓我們用錯誤的方式去嘗試, 例如用反射創(chuàng)建一個實例, 在這一點上我們可以問我們自己是否做了正確的事. 它也可以在代碼的評審過程中有所幫助,因為這樣的實現(xiàn)可以定義幾個應(yīng)該被遵循的規(guī)則. 如果這些規(guī)則比較簡單很容易發(fā)現(xiàn)代碼中存在的問題.

讓我們看些實例.

<?php
final class YesNo extends \Eloquent\Enumeration\AbstractEnumeration {
    const NO = 0;
    const YES = 1;
}
var_dump(YesNo::YES()->key()); // YES

我們定義了一個繼承 \Eloquent\Enumeration\AbstractEnumeration 的新類  YesNo . 接下來我們定義一個定義元素名和創(chuàng)建表現(xiàn)這些元素的對象的庫的常量.

還有一些情況我們需要謹(jǐn)記,用 serialize / deserialize 在其中創(chuàng)建自定義對象 .

我們可以在GitHub頁面上找到更多的例子和很完善的文檔。

我們要展示的第二個庫是 zlikavac32/php-enum . 與  eloquent/enumeration 不同,這個庫面向允許真正的多態(tài)行為的抽象類。所以,我們可以用每個方法都定義一個枚舉元素來實現(xiàn),而不是使用 switch 的方法。通過嚴(yán)格的規(guī)則來定義枚舉,也可以相當(dāng)可靠地確保每個元素只有一個實例。

這個庫面向抽象類,以便將每個成員的許多實例限制為一個。這個想法是,每個枚舉必須被定義為抽象的,并枚舉它的元素。請注意,你可以通過擴(kuò)展類,然后構(gòu)造一個元素來濫用,但是如果你這么用了,這些是會在代碼審查過程中標(biāo)紅的。

對于抽象類,我們知道我們不會意外地有一個枚舉的新元素,因為它需要具體的實現(xiàn)。通過遵循在enum本身中保持這些具體實現(xiàn)的規(guī)則,我們可以很容易地發(fā)現(xiàn)濫用。 匿名類 在這里很有用。

庫強制抽象枚舉類,但不能強制創(chuàng)建有效的元素。這是這個庫的用戶的責(zé)任。圖書館照顧其余的。

讓我們看一個簡單的例子。

<?php
/**
 * @method static YesNo YES
 * @method static YesNo NO
 */
abstract class YesNo extends \Zlikavac32\Enum\Enum
{
    protected static function enumerate(): array
    {
        return [
            'YES', 'NO'
        ];
    }
}
var_dump(YesNo::YES()->name()); // YES

pHpDoc注釋定義了返回枚舉元素的現(xiàn)有靜態(tài)方法。這有助于搜索和重構(gòu)代碼。接下來,我們將枚舉 YesNo 定義為抽象,并擴(kuò)展 \Zlikavac32\Enum\Enum 并定義一個靜態(tài)方法 enumerate 。然后,在 enumerate 方法中,我們列出將被用來表示它們的元素名稱。

剛剛我們提到了多態(tài)行為,那么為什么我們會使用它呢?當(dāng)我們試圖限制同一個枚舉元素的多個實例時會發(fā)生一件事,那就是我們不能有循環(huán)引用。讓我們想象一下,我們想擁有由 NORTH , SOUTH , EAST 和 WEST 組成的 WorldSide 枚舉。我們還想有一個方法 opposite():WorldSide ,它返回代表相反的元素。

如果我們試圖通過構(gòu)造函數(shù)注入相反元素,在某一時刻,我們獲得一個循環(huán)引用,這意味著,我們需要相同元素的第二個實例。為了返回一個有效的相反世界,我們不得不用一個 代理對象 或者 switch 語句破解。

隨著多態(tài)行為,我們能做的就是讓我們看到我們可定義我們需要的 WorldSide 枚舉。

<?php
/**
 * @method static WorldSide NORTH
 * @method static WorldSide SOUTH
 * @method static WorldSide EAST
 * @method static WorldSide WEST
 */
abstract class WorldSide extends \Zlikavac32\Enum\Enum
{
    protected static function enumerate(): array
    {
        return [
            'NORTH' => new class extends WorldSide {
                public function opposite(): WorldSide {
                    return WorldSide::SOUTH();
                }
            },
            'SOUTH' => new class extends WorldSide {
                public function opposite(): WorldSide {
                    return WorldSide::NORTH();
                }
            },
            'EAST' => new class extends WorldSide {
                public function opposite(): WorldSide {
                    return WorldSide::WEST();
                }
            },
            'WEST' => new class extends WorldSide {
                public function opposite(): WorldSide {
                    return WorldSide::EAST();
                }
            }
        ];
    }
    abstract public function opposite(): WorldSide;
}
foreach (WorldSide::iterator() as $worldSide) {
    var_dump(sprintf(
        'Opposite of %s is %s',
        (string) $worldSide,
        (string) $worldSide->opposite()
    ));
}

在 enumerate 方法,我們提供了每一個枚舉元素的實現(xiàn)。數(shù)組是用枚舉元素名稱來索引的。當(dāng)手動的創(chuàng)建元素,我們定義我們元素名稱作為數(shù)據(jù)的鍵。

我們可以用 WorldSide::iterator() 獲取枚舉元素的順序迭代器,來定義和遍歷他們。每一個枚舉元素都有一個默認(rèn)的  __toString(): string 實現(xiàn)返回元素的名稱。

每個枚舉元素返回其相反的元素。

回顧一下,常量不是枚舉,枚舉不是常量。每個枚舉定義一個類型。如果我們有一些常數(shù)的值對我們很重要,但名字沒有,我們應(yīng)該堅持常數(shù)。如果我們有一些常量的價值對我們無關(guān)緊要,但是與同一群體中的其他所有人有所不同則是重要的,請使用枚舉

枚舉為代碼提供了更多的上下文,也可以將某些檢查委托給引擎本身。如果pHp有一個本地的枚舉支持,這將是非常好的。語法更改可以使代碼更具可讀性。引擎可以為我們執(zhí)行檢查,并執(zhí)行一些不能從用戶區(qū)執(zhí)行的規(guī)則。

當(dāng)前標(biāo)題:創(chuàng)新互聯(lián)教你如何處理pHp代碼中的枚舉類型Enum的
新聞來源:http://www.muchs.cn/news30/298180.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站設(shè)計公司、網(wǎng)站改版、云服務(wù)器、網(wǎng)站排名、電子商務(wù)網(wǎng)站策劃

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

成都做網(wǎng)站