2023-12-02 分類: 網站建設
本文旨在提供一些更好的理解什么是枚舉,什么時候使用它們以及如何在php中使用它們.
我們在某些時候使用了常量來定義代碼中的一些常數值.他們被用來避免 魔法值 .用一個象征性的名字代替一些 魔法值 ,我們可以給它一些意義.然后我們在代碼中引用這個符號名稱.因為我們定義了一次并使用了很多次,所以搜索它并稍后重命名或更改一個值會更容易.
這就是為什么看到類似于下面的代碼并不罕見.
<?php class User { const GENDER_MALE = 0; const GENDER_FEMALE = 1; const STATUS_INACTIVE = 0; const STATUS_ACTIVE = 1; }
以上常量表示了兩組屬性,GEDNER_* 和 STATUS_*。他們表示一組性別和一組用戶狀態。每一組都是一個 枚舉 。枚舉是一組元素(也叫做成員)的集合,每一個枚舉都定義了一種新類型。這個類型,和它的值一樣,可以包含任意屬于該枚舉的元素。
在上面的例子中,枚舉借助于常量,每一個常量的值都是一個成員。注意,這樣做的話,我們只能在常量包含的類型中取值。因此,我們在寫這些值的時候不會有類型提示,不知道詳細的枚舉類型。
來看一個簡短的例子, 但我們假定例子中有更多的代碼
<?php interface UserFactory { public function create( string $email, int $gender, int $status ): User; } $factory->create( $email, User::STATUS_ACTIVE, User::GENDER_FEMALE );
第一眼看上去代碼很好,但是他只是碰巧正確運行了!因為兩個不同的枚舉成員實際上是同一個值,調用create方法成功,是因為這最后兩個參數被互換了不影響結果。盡管我們檢查方法接受的值是否有效,運行界面也不會警告我們,測試也會通過。有人能正確的發現這些bug,但是它也很可能被忽視掉。之后一些情況,比如合并沖突的時候,如果它的值改變了,它可能會引起系統異常。
如果使用標量類型,我們會受限于這種類型,無法辨別這兩個值是是不是屬于兩個不同的枚舉。
另一個問題是這個代碼描述的的不是很好。想象一下 create
方法沒有引用常量。 $gender
被別人看作為一個枚舉元素將是有多么困難?看這些元素在哪里被定義又有多么困難?我們之后將會閱讀那些代碼,因此我們應該盡可能是讓代碼易于閱讀以及和通過。
我們可以做得更好嗎?Sure!這個方法就是是使用類實例作為枚舉元素,類本身定義了一個新的類型。直到pHp 7,我們可以安裝 SpL類 pECL擴展并且使用 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
我們擴展 SplEnum
并且定義用于創建枚舉元素的常量。枚舉元素是我們手動構造的對象,在這種情況下是常量值本身。我們可以將整型與對象進行比較,這可能很奇怪。另外,正如文檔所述,這是一個仿真的枚舉。pHp本身并不支持枚舉類型,所以我們在這里探討的所有內容都是仿真的。
我們用這種方法得到了什么?我們可以輸入提示我們的參數,并讓pHp引擎在發生錯誤時提醒我們。我們還可以在枚舉類中包含一些邏輯,并使用 switch
語句來模擬多態行為。
但也有一些缺點. 例如, 在大多數情況下, 有些你可以用枚舉元素而不能用標識檢查. 這不是不可能的,我們不得不非常小心. 由于我們手動創建枚舉成員, 所以許多成員應該是同一個成員, 但這一點手動很難確定.
利用 SplEnum
我們解決枚舉類型問題, 但是當我們用標識檢查的時候不得不非常小心. 我們需要一個方法限制可以創建的多個元素, 例如 multiton (multiple singleton objects ).
現在我們將看到由 Java Enum 啟發并實現 multiton
的兩個不同的庫.
第一個是 eloquent/enumeration . 它為每個元素創建一個定義類的實例. 請注意, 沒有我們的幫助, 枚舉的用戶仿真永遠不能保證一個枚舉實例, 因為我們限制它的每一步都有一個方法去避免.
這個庫可以讓我們用錯誤的方式去嘗試, 例如用反射創建一個實例, 在這一點上我們可以問我們自己是否做了正確的事. 它也可以在代碼的評審過程中有所幫助,因為這樣的實現可以定義幾個應該被遵循的規則. 如果這些規則比較簡單很容易發現代碼中存在的問題.
讓我們看些實例.
<?php final class YesNo extends \Eloquent\Enumeration\AbstractEnumeration { const NO = 0; const YES = 1; } var_dump(YesNo::YES()->key()); // YES
我們定義了一個繼承 \Eloquent\Enumeration\AbstractEnumeration
的新類 YesNo
. 接下來我們定義一個定義元素名和創建表現這些元素的對象的庫的常量.
還有一些情況我們需要謹記,用 serialize
/ deserialize
在其中創建自定義對象 .
我們可以在GitHub頁面上找到更多的例子和很完善的文檔。
我們要展示的第二個庫是 zlikavac32/php-enum . 與 eloquent/enumeration
不同,這個庫面向允許真正的多態行為的抽象類。所以,我們可以用每個方法都定義一個枚舉元素來實現,而不是使用 switch
的方法。通過嚴格的規則來定義枚舉,也可以相當可靠地確保每個元素只有一個實例。
這個庫面向抽象類,以便將每個成員的許多實例限制為一個。這個想法是,每個枚舉必須被定義為抽象的,并枚舉它的元素。請注意,你可以通過擴展類,然后構造一個元素來濫用,但是如果你這么用了,這些是會在代碼審查過程中標紅的。
對于抽象類,我們知道我們不會意外地有一個枚舉的新元素,因為它需要具體的實現。通過遵循在enum本身中保持這些具體實現的規則,我們可以很容易地發現濫用。 匿名類 在這里很有用。
庫強制抽象枚舉類,但不能強制創建有效的元素。這是這個庫的用戶的責任。圖書館照顧其余的。
讓我們看一個簡單的例子。
<?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注釋定義了返回枚舉元素的現有靜態方法。這有助于搜索和重構代碼。接下來,我們將枚舉 YesNo
定義為抽象,并擴展 \Zlikavac32\Enum\Enum
并定義一個靜態方法 enumerate
。然后,在 enumerate
方法中,我們列出將被用來表示它們的元素名稱。
剛剛我們提到了多態行為,那么為什么我們會使用它呢?當我們試圖限制同一個枚舉元素的多個實例時會發生一件事,那就是我們不能有循環引用。讓我們想象一下,我們想擁有由 NORTH
, SOUTH
, EAST
和 WEST
組成的 WorldSide
枚舉。我們還想有一個方法 opposite():WorldSide
,它返回代表相反的元素。
如果我們試圖通過構造函數注入相反元素,在某一時刻,我們獲得一個循環引用,這意味著,我們需要相同元素的第二個實例。為了返回一個有效的相反世界,我們不得不用一個 代理對象 或者 switch
語句破解。
隨著多態行為,我們能做的就是讓我們看到我們可定義我們需要的 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
方法,我們提供了每一個枚舉元素的實現。數組是用枚舉元素名稱來索引的。當手動的創建元素,我們定義我們元素名稱作為數據的鍵。
我們可以用 WorldSide::iterator()
獲取枚舉元素的順序迭代器,來定義和遍歷他們。每一個枚舉元素都有一個默認的 __toString(): string
實現返回元素的名稱。
每個枚舉元素返回其相反的元素。
回顧一下,常量不是枚舉,枚舉不是常量。每個枚舉定義一個類型。如果我們有一些常數的值對我們很重要,但名字沒有,我們應該堅持常數。如果我們有一些常量的價值對我們無關緊要,但是與同一群體中的其他所有人有所不同則是重要的,請使用枚舉
枚舉為代碼提供了更多的上下文,也可以將某些檢查委托給引擎本身。如果pHp有一個本地的枚舉支持,這將是非常好的。語法更改可以使代碼更具可讀性。引擎可以為我們執行檢查,并執行一些不能從用戶區執行的規則。
當前標題:創新互聯教你如何處理pHp代碼中的枚舉類型Enum的
新聞來源:http://m.newbst.com/news30/298180.html
成都網站建設公司_創新互聯,為您提供網站設計公司、網站改版、云服務器、網站排名、電子商務、網站策劃
聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯
猜你還喜歡下面的內容