2

グローバル状態を使用してクラス メタデータに関する実行時情報を PHP に格納するのに苦労しています。明らかな理由により、グローバル状態は「悪い」と見なされるため、できるだけ避けたいと思います。とはいえ、どの状況でそれが本当に受け入れられ、どの状況で受け入れられないのかはまだわかりません. 具体的な例を提供するために、PHP での Enum 型の実装を見てみましょう。各具体的な Enum 型は抽象 Enum 型を拡張し、リフレクションを使用してサブクラスの定数を有効な Enum 値として読み取ります。

<?php

abstract class AbstractEnum
{
    /**
     * The constants currently resolved.
     *
     * Constants are resolved as flyweights into global state,
     * therefore they have to be distinguished by the class name they
     * refer to in this array.
     *
     * E.g.
     *
     * array(
     *     'My\Full\Qualified\Enum\Class' => array(
     *         'CONST1' => 'value1',
     *         'CONST2' => 'value2'
     *     ),
     *     'My\Other\Full\Qualified\Enum\Class' => array(
     *         'CONST1' => 'value1',
     *         'CONST2' => 'value2'
     *     )
     * )
     *
     * @var array
     */
    private static $constants = array();

    /**
     * The enumeration value of this instance.
     *
     * @var mixed
     */
    private $value;

    /**
     * Constructor.
     *
     * @param mixed $value The enumeration value of this instance.
     *
     * @throws \UnexpectedEnumValueException if given value is not defined
     *                                                                        in the enumeration class.
     */
    final public function __construct($value)
    {
        $values = static::getValues();

        if (!in_array($value, $values, true)) {
            throw new \UnexpectedEnumValueException($value, $values, get_called_class());
        }

        $this->value = $value;
    }

    /**
     * Returns the enumeration value of this instance as string representation.
     *
     * @return string
     */
    final public function __toString()
    {
        return (string) $this->value;
    }

    /**
     * Returns the enumeration value of this instance.
     *
     * @return mixed
     */
    final public function getValue()
    {
        return $this->value;
    }

    /**
     * Returns all enumeration values defined in this class.
     *
     * @return array
     */
    final public static function getValues()
    {
        $class = get_called_class();

        // Resolve class constants as flyweights.
        if (!isset(self::$constants[$class])) {
            self::resolveConstants($class);
        }

        return self::$constants[$class];
    }

    /**
     * Resolves the constants of a given full qualified class name.
     *
     * @param string $class The full qualified class name to resolve the constants for.
     */
    private static function resolveConstants($class)
    {
        $reflectionClass = new \ReflectionClass($class);
        self::$constants[$class] = $reflectionClass->getConstants();
    }
}

class Month extends AbstractEnum
{
    const JANUARY = 1;
    const FEBRUARY = 2;
    // ...
}

$month = new Month(Month::JANUARY);

ここで考えられる問題は、リフレクションを使用してサブクラスの定数を解決する resolveConstants() メソッドです。実行時に大量の Enum インスタンスを作成する必要がある状況を考えてみましょう。ここで各インスタンスでリフレクションを使用すると、パフォーマンスに深刻な影響を与える可能性があるため、特定の Enum 型の最初のインスタンスでのみクラス メタデータを「遅延読み込み」するのが適切な方法のようです。しかし、これを達成するにはグローバル状態を使用する必要がありますが、これも間違っているようです。これは最良の例ではないかもしれませんが、パフォーマンスの問題に関する私の懸念を示しています。オブジェクトを状態に設定するために、リフレクションとクラス メタデータ イントロスペクションを広範に使用する必要があるユース ケースは他にもあるかもしれません。この種のグローバルな状態を持つことは一般的に受け入れられるのでしょうか、それともそのような目標を達成するための代替手段はありますか? Enum のような単純な値オブジェクトの依存性注入がオーバーヘッドになるような状況で、クラス メタデータを提供する別のクラスを使用するのは好きではありません。

4

0 に答える 0