piątek, 20 września 2013

Enumy w PHPie? Oczywiście, że się da!

Ostatnio, przeglądając jedno z reviewew mój kolega z zespołu rozpoczął rozmowę na temat naszych pseudo-enumów w aplikacji.
Nie odkryliśmy tutaj koła na nowo i nasze "klasy enumeracyjne" wyglądały tak, jak w większości frameworków, bibliotek i aplikacji, które widziałem, a które zostały napisane w PHP:
interface PseudoEnum
{

    const SUCCESS = 1;
    const FAILURE = 2;
}

Dobre rozwiązanie? No cóż, osobiście od dawna bolałem nad tym, że takie konstrukcje nie pozwalają nam na jedną z jakże istotnych funkcjonalności, które w przypadku zwykłych obiektów PHP posiada, a które są bezproblemowe w językach, gdzie coś takiego jak enum istnieje. O czym mowa? Chodzi o typowanie.

Pomimo cudownych i deskryptywnych nazw, te stałe, jakby na to nie patrzyć, w gruncie rzeczy są int'ami i w chwili, gdy któraś metoda mogła przyjąć wartość tylko i wyłącznie taką, jaka została zadeklarowana wcześniej w naszym pseudo-enumie, to musieliśmy tworzyć do tego celu specjalne metody sprawdzające.
Nie mogę powiedzieć, że było to zadowalające rozwiązanie, ale jakoś z tym żyliśmy.

Jednak od słowa do słowa, zaczęliśmy rozmawiać nad tym, jak to jest rozwiązane w Javie i do czego cały enum tak naprawdę się sprowadza. I w pewnym momencie mój znajomy zadał jakże istotne pytanie - "To dlaczego my nie zrobimy tego w podobny sposób? Przecież da się."

Chwilę później już mieliśmy gotowy przykład pełnowartościowego enum'a:
final class Sex
{

    private static $_sexs = array();
    private $_value;

    public static function Male()
    {
        return self::_getSex('male');
    }

    public static function Female()
    {
        return self::_getSex('female');
    }

    public function getValue()
    {
        return $this->_value;
    }

    private static function _getSex($value)
    {
        if (!array_key_exists($value, self::$_sexs))
            self::$_sexs[$value] = new Sex($value);

        return self::$_sexs[$value];
    }

    private function __construct($value)
    {
        $this->_value = $value;
    }

    private function __clone() {}
}

Czyż nie jest lepiej? Zdaję sobie sprawę z tego, że kodu jest odrobinę więcej, ale za to możemy swobodnie używać typowania (więc znika nam kod za to odpowiedzialny) i w dodatku można dodawać do takich klas metody (co czasami bywa niezwykle pomocne).

A ja tylko zachodzę w głowę dlaczego sam na to wcześniej nie wpadłem?