PHPでJavaのEnumっぽい何かを手っ取り早く

色々出尽くしてそうなねたですが、Enumクラスを定義して、
それを継承してうんにゃらとかはなしな方向で考えます。
JavaのEnumをエミュレートしたいわけでもなく、です。
要件としては、
1.静的にインスタンスにアクセスしたい。
2.そのインスタンスは一意であることを保障したい。
3.タイプヒンティングを使いたい。
とか、そういうことです。

とあるクラスに一意に定義された値で処理を行いたい時にどうしてますか?
オブジェクト定数を使ってもいいのですが、そうするとタイプヒンティングが使えないので、
予定していない値が渡された時の処理を考える必要があります。

 
<?php
/**
 * Type
 */
class Type {
    /**
     * Type A
     * @var integer
     */
    const A = 0;
 
    /**
     * Type B
     * @var integer
     */
    const B = 1;
 
    /**
     * Type C
     * @var integer
     */
    const C = 2;
}
 
/**
 * Chech Type
 * @param integer $type Type to check
 */
function checkType($type) {
    if($type == Type::A) {
        print "Type is Type A\n";
    } else if($type == Type::B) {
        print "Type is Type B\n";
    } else if($type == Type::C) {
        print "Type is Type C\n";
    }
}
 
$a = Type::A;
$b = Type::B;
$c = Type::C;
 
checkType($a);//Type is A
checkType($b);//Type is B
checkType($c);//Type is C
 
checkType(5);//Not expected
checkType(null);//Type is A, but not expected
checkType(new stdClass());//Error, but not expected
 

予定しない値が渡った時の処理を考えるのも、ですが、
せっかくタイプヒンティングがあるので、
タイプヒンティングをうまく使う方法を考えます。

 
<?php
/**
 * Type
 */
class Type {
    /**
     * Instances of this class
     * @var array
     */
    private static $instances = array();
 
    /**
     * Return Type A
     * @return Type
     */
    public static function A() {
        if(!isset(self::$instances[__METHOD__])) {
            self::$instances[__METHOD__] = new Type(0);
        }
        return self::$instances[__METHOD__];
    }
 
    /**
     * Return Type B
     * @return Type
     */
    public static function B() {
        if(!isset(self::$instances[__METHOD__])) {
            self::$instances[__METHOD__] = new Type(1);
        }
        return self::$instances[__METHOD__];
    }
 
    /**
     * Return Type C
     * @return Type
     */
    public static function C() {
        if(!isset(self::$instances[__METHOD__])) {
            self::$instances[__METHOD__] = new Type(2);
        }
        return self::$instances[__METHOD__];
    }
 
    /**
     * Ordinal of this Class
     * @var integer
     */
    private $ordinal;
 
    /**
     * Private constructor
     * @param integer $ordinal Ordinal of this Class
     */
    private function __construct($ordinal) {
        $this->ordianl = $ordinal;
    }
 
    /**
     * Revoke clone
     */
    public function __clone() {
        throw new BadFunctionCallException('Clone is not allowed.');
    }
}
 
/**
 * Chech Type
 * @param Type $type Type to check
 */
function checkType(Type $type) {
    if($type == Type::A()) {
        print "Type is Type A\n";
    } else if($type == Type::B()) {
        print "Type is Type B\n";
    } else if($type == Type::C()) {
        print "Type is Type C\n";
    }
}
 
$a = Type::A();
$b = Type::B();
$c = Type::C();
 
checkType($a);//Type is A
checkType($b);//Type is B
checkType($c);//Type is C
 
checkType(null);//Error
checkType(new stdClass());//Error
checkType(new Type(1));//Error
$a2 = clone $a;//BadFunctionCallException
 

スタティックメソッドでインスタンスにアクセスするようにしました。
privateなconstructorと__cloneメソッドを実装して、
外部からのインスタンス作成と複製を無効にすれば
インスタンスが一意であることが保障されます。
タイプヒンティングを使っているので、
checkType()の内部では$typeはTypeのインスタンスであることが保障されます。

タイプヒンティングは便利なのですが、
足りてないところもあって、やきもきさせてくれます。
例えばスカラー型やオブジェクト型のタイプヒンティングが使えれば、
もっと便利に使えますよね。
PHPにはstdClassというクラスがあるのですが、stdClassはJavaのObject型とは違って、
全てのクラスのスーパークラスではありません。

 
function foo(/** not allowed */integer $i, /** not allowed */object $o) {}
 

Category: PHP | Posted on: 2009/08/20 15:00 | Viewed: 1190

Comments

No comments yet

Add Comment

:

:
:

TrackBacks

このエントリにトラックバックはありません

TrackBack URL

http://www.azul.systems-noel.jp/trackback/item_81.html