中文字幕日韩精品一区二区免费_精品一区二区三区国产精品无卡在_国精品无码专区一区二区三区_国产αv三级中文在线

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

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

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

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

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

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

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

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

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

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

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

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

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

我們可以做得更好嗎?Sure!這個(gè)方法就是是使用類實(shí)例作為枚舉元素,類本身定義了一個(gè)新的類型。直到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)建枚舉元素的常量。枚舉元素是我們手動(dòng)構(gòu)造的對(duì)象,在這種情況下是常量值本身。我們可以將整型與對(duì)象進(jìn)行比較,這可能很奇怪。另外,正如文檔所述,這是一個(gè)仿真的枚舉。pHp本身并不支持枚舉類型,所以我們在這里探討的所有內(nèi)容都是仿真的。

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

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

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

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

第一個(gè)是 eloquent/enumeration . 它為每個(gè)元素創(chuàng)建一個(gè)定義類的實(shí)例. 請(qǐng)注意, 沒有我們的幫助, 枚舉的用戶仿真永遠(yuǎn)不能保證一個(gè)枚舉實(shí)例, 因?yàn)槲覀兿拗扑拿恳徊蕉加幸粋€(gè)方法去避免.

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

讓我們看些實(shí)例.

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

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

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

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

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

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

對(duì)于抽象類,我們知道我們不會(huì)意外地有一個(gè)枚舉的新元素,因?yàn)樗枰唧w的實(shí)現(xiàn)。通過遵循在enum本身中保持這些具體實(shí)現(xiàn)的規(guī)則,我們可以很容易地發(fā)現(xiàn)濫用。 匿名類 在這里很有用。

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

讓我們看一個(gè)簡單的例子。

<?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 并定義一個(gè)靜態(tài)方法 enumerate 。然后,在 enumerate 方法中,我們列出將被用來表示它們的元素名稱。

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

如果我們試圖通過構(gòu)造函數(shù)注入相反元素,在某一時(shí)刻,我們獲得一個(gè)循環(huán)引用,這意味著,我們需要相同元素的第二個(gè)實(shí)例。為了返回一個(gè)有效的相反世界,我們不得不用一個(gè) 代理對(duì)象 或者 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 方法,我們提供了每一個(gè)枚舉元素的實(shí)現(xiàn)。數(shù)組是用枚舉元素名稱來索引的。當(dāng)手動(dòng)的創(chuàng)建元素,我們定義我們元素名稱作為數(shù)據(jù)的鍵。

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

每個(gè)枚舉元素返回其相反的元素。

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

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

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

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

廣告

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

成都做網(wǎng)站
贵阳市| 石家庄市| 巴马| 崇仁县| 威远县| 民丰县| 卢湾区| 工布江达县| 绩溪县| 将乐县| 永城市| 弥勒县| 黄龙县| 铁岭县| 渝北区| 页游| 丹棱县| 青海省| 嘉峪关市| 体育| 瑞昌市| 北川| 沁阳市| 巴东县| 平顶山市| 三河市| 鲜城| 新野县| 龙游县| 屏东县| 鹤峰县| 台中市| 瑞安市| 房山区| 吉安县| 黄浦区| 靖安县| 安乡县| 龙海市| 太谷县| 永平县|