BEAR Blog

Because everything is a resource.

DIP Dependency Inversion Principle

| Comments

Robert C. Martin氏による有名なオブジェクト指向設計(OOD)の原則Principles of OODがあります。そのうちDIP:依存逆転原則はクラス同士の結合度をいかに減らすか、またはどちらに依存すべきかを判定する原則です。

  • 上位モジュールは下位モジュールに依存してはならない。両者は抽象に依存すべきである。
  • 抽象は詳細(抽象の実装クラス)に依存してはならない。詳細は抽象に依存すべきである。

以下はオリジナルの論文の結びの言葉です。

依存性逆転の原則は、オブジェクト指向技術で得られると言われる数多くの利益のうち、もっとも根幹にあたる部分に位置付けられます。この原則を適切に適用することは、再利 用可能なフレームワークを構築する上では不可欠なのです。また、変更に際して回復の早い(弾力的な)コードを書く上では決定的な重要性を持ちます。抽象とこまごまとしたものはそれぞれが分離さるので、コードはとてもメンテナンスしやすいものになります。

BEAR.SundayではこのDIPを最大限に重要と考え、コード全域に渡って適用しています。1

実装ではなく抽象に依存する

解説してる文章で最も短く簡潔なのがStrategic Choiceブログの説明です。

DIPを一言で説明すると「抽象に依存せよ」という経験則。
プログラムは具体的なクラスに依存してはいけない。
プログラム内の関係はすべて、抽象クラスかインターフェースで終結すべきである

BEAR.Sundayのクラスでは、以下のように依存は原則全て注入してもらう事を期待します。

    /**      * @Inject      */     public function setPrinter(OutputDevice $printer)     {         $this->printer = $printer;     }

ここで大事なのはタイプヒンティング(この場合OutputDevice)を抽象、つまりインターフェイスかまたは抽象クラスにして具象クラスをタイプヒントにしないことです。タイプヒンティングをエラーチェックのためのアサーションとだけ捉えず、DIPに従った設計のための実装として考えます。

アンチパターン考察

直接生成する

1
$service = new MyService;

$serviceがMyServiceのインスタンスであると静的束縛されています。

スタティックコール

1
MyService::doSomething();

これも実装に依存しています。

コンテナに依存する

$id = $this->container(‘serviceA’)->getB()->getC()->getId();
  • このコードでは抽象への依存はなくコンテナに入った実装に依存しています(DIP違反)
  • メソッドチェーンで繋がれた依存の依存が持っている知識を前提としていています(デメテルの法則違反
  • getterを使う事でidというオブジェクトの構成要素の暴露がされています。(カプセル化)

実装に依存してオブジェクトを探しまわるようなコードから、インターフェイスを通じて依存を受け取るコードに変更します。

Conclusion

Robert C. Martin氏が「オブジェクト指向技術で得られると言われる数多くの利益のうちもっとも根幹にあたる部分」というこのDIPですが、「抽象のタイプヒントを指定しましょう」というのはただの一例で、「抽象に依存せよ」というの適用範囲の大変広いソフトウエア品質に寄与する大原則だと思います。メソッドのアクセス権 (visibility) と同じく実装での機能というより設計指向の実装として、設計者をよりよい設計にガイドする制約だと考えます。

Object Framework – Ray.Aop

| Comments

Apect Oriented Design

メソッド・インターセプター


例えばテスト用途にどんな引き数が渡されても特定の同じ値を返さなければならないとします。あるいはアジリティを重視した開発で、メソッド内のコードや利用データベースが用意できていない段階でも適当に用意した値を返す必要があるとします。

このような場合、通常はテスティングフレームワークを使いモックオブジェクトを生成して利用します。BEAR.SundayのRay.Diのモジュールでモックオブジェクトを用意して差し替える事もできます。しかしRay.Aopの提供するメソッドインターセプターを使えば更に簡単です。

メソッドインターセプターはメソッドの実行を横取りして(interceptして)代理実行します。モックオブジェクトは対象オブジェクトを入れ替えますが、インターセプターは対象オブジェクトとそれを利用するコンシュマークラスの間に割り込み(インターセプト)します。

まずは基本になるオリジナルのメソッド実行と同じ動作をするインタセプターのコードです。

class GreetingInterceptor implements MethodInterceptor {     /**     * (non-PHPdoc)     * @see Ray\Aop.MethodInterceptor::invoke()     */     public function invoke(MethodInvocation $invocation)     {         $result = $invocation->proceed();         return  $result;     } }

MethodInterceptorインターフェイスのinvoke(実行)というメソッドにはMethodInvocation(メソッド実行)オブジェクト$invocationが渡されます。

メソッド実行オブジェクトはメソッドの実行に必要な全ての知識(対象インスタンス、メソッド名、引き数等)を持っています。オリジナルのメソッドを実行するためには$invocation->proceed();を実行します。

この実行の前後に処理を記述したりすることで元も処理をまたぐ事ができます。引き数を操作したり変更したりすることもできます。1またインターセプターを同じメソッドに複数適用することもできます。

テスト用に常にここのメソッドが”HelloTest”を返す為には以下のように変更します。

1
2
3
4
5
6
7
8
9
10
11
12
class GreetingInterceptor implements MethodInterceptor
{
    /**
    * (non-PHPdoc)
    * @see Ray\Aop.MethodInterceptor::invoke()
    */
    public function invoke(MethodInvocation $invocation)
    {
        $result = $invocation->proceed();
        return  $result;
    }
}

このGreetingリソースに限らず、何のリソースのメソッドが”HelloTest”を返す為すためには以下のように変更します。

1
2
3
4
5
6
7
8
9
10
11
12
class GreetingInterceptor implements MethodInterceptor
{
    /**
    * (non-PHPdoc)
    * @see Ray\Aop.MethodInterceptor::invoke()
    */
    public function invoke(MethodInvocation $invocation)
    {
        // $result = $invocation->proceed();
        return  “HelloTest”;
    }
}

このインターセプターを特定のクラス、特定のメソッドにバインドするのも「モジュール」で行います。このバインドはsandbox\Resource\App\Greetingクラス(およびその継承したクラス)のどのメソッドにもMockInterceptorインターセプターを適用します。

1
2
3
4
5
6
7
8
9
10
11
12
class MockResourceInterceptor
{
    private $mock;
    public function __construct($mock)
    {
        $this->mock = $mock;
    }
    public function invoke(MethodInvocation $invocation)
    {
        $return $this->mock;
    } 
}

バインド対象を指定するためのマッチャーはアノテーションを指定したり、Callableオブジェクトを指定することもできます。

@Cacheアノテーション

BEAR.Sundayはいくつかのインターセプターを用意しています。その内、Cacheインターセプターは特に有用でしょう。このアノテーションはメソッド実行の結果を指定の秒数キャッシュします。

1
2
3
4
5
6
7
  /**
   * @Cache(60)
   */
  public function onGet($lang = ‘en’)
  {
      
  }

このインターセプターのソースを見てましょう。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public function __construct(Cache $cache) {
    $this->cache = $cache;
}

public function invoke(MethodInvocation $invocation) {
    $class = get_class($invocation->getThis());
    $args = $invocation->getArguments();
    $id = $this->getId($class, $args);
    $saved = $this->cache->fetch($id);
    if ($saved) {
        return unserialize($saved);
    }
    $data = $invocation->proceed();
    $annotation = $invocation->getAnnotation();
    $time = $annotation->time;
    $this->cache->save($id, serialize($data), $time);
    return $data;
    }

インターセプターはというMethodInvocationインターフェイスを実装します。2 引き数や対象メソッドをキーにキャッシュにを生成していて@Cacheアノテーションで指定された括弧内の秒数だけキャッシュデータを再利用するようになっています。 @Cacheアノテートされたされたリソースはアノテートがされただけでこれが実際にはどのインターセプターがバインドされるのかはこのクラスからは宣言していません。3 このアノテーションが実際にどのインターセプターが適用されるのか、あるいはそもそもインターセプターが適用されない(開発時など)といった構成知識に関わりがありません。Ray.Diの@Injectアノテーションと同じように構成は利用される側でなく利用する側が持ちます。

ランタイムインジェクター

BEAR.Sundayのオブジェクトの実行は最初の1リクエストでオブジェクトフラフのコンストラクションを完了するコンパイルと、以降のランタイムに分けられます。 コンパイルでリクエストをまたいで再利用可能なオブジェクトをつくるために、リクエスト毎にディペンデンシーを変えてインジェクトをしたりすることはできません。4 例えばDBオブジェクト5 のmaster / slaveをメソッドに応じて自動で選択するために、GET(読み込み)かそれ以外のメソッドでインジェクトを変えるという事はRay.Diのインジェクターではできません。 この場合インターセプターを使ってDBオブジェクトをメソッドにセットしてやることができます。 インターセプターはメソッド実行の情報が渡されるので、実行メソッド名をみてmaster/slaveのDBを選択することができます。master/slaveに限らずユーザーIDに応じたDB選択や、DBに応じた初期化などもインターセプターで記述できます。リソースをリクエストするクライントもそれを受けるappリソースも本来の仕事、つまり本質的関心(core concern)にのみ専念し、DBオブジェクトの準備というリソースをまたいだ横断的関心事(cross cutting concern)から関心を分離することができます。

Postsリソースクラス

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
/**
 * Posts
 *
 * @Db
 */
class Posts extends ResourceObject implements DbSetter
{
    /**
     * Table
     *
     * @var string
     */
    private $table = ‘posts’;
    /**
     * DB
     *
     * @var Doctrine\DBAL\Connection
     */
    private $db;

    /**
     * Set DB
     *
     * @param Connection $db
     *
     * @return void
     */
    public function setDb(Connection $db = null)
    {
        $this->db = $db;
    }

    /**
     * Get
     *
     * @return array
     */
    public function onGet()
    {
        $sql = “SELECT id, title, body, created, modified FROM {$this->table};
        $stmt = $this->db->query($sql);
        $this->body = $stmt->fetchAll(PDO::FETCH_ASSOC);
        return $this;
    }

    /**
     * Post
     *
     * @param string   $title
     * @param string   $body
     * @param DateTime $created
     * @param DateTime $modified
     *
     * @return \sandbox\Resource\App\Posts
     */
    public function onPost($title, $body, $created = null, $modified = null)
    {
        $this->db->insert($this->table, ['title' => $title, 'body' => $body]);
        $this->code = 204;
        return $this;
    }
}

@Dbアノテーションクラス

1
2
3
4
5
6
7
8
9
10
11
12
/**
 * Db
 *
 * @Annotation
 * @Target(“CLASS”)
 *
 * @package    BEAR.Framework
 * @subpackage Annotation
 */
final class Db
{
}

モジュール内でDbインジェクターをバインド

1
2
3
4
5
$this->bindInterceptor(
            $this->matcher->annotatedWith(‘BEAR\Framework\Annotation\Db’), // クラスに@Dbとアノテートされた全てのクラス
            $this->matcher->any(), // 全てのメソッド
            [$dbInjector] // 複数バインドできます
        );

Conclusion

Ray.Diのインジェクションシステムはコンシュマーとディペンデンシーの関係を疎にしアプリケーション構成を柔軟にしますがコンパイルされた関係性は再利用されオブジェクトとオブジェクトの結びつき(オブジェクトグラフ)はリクエストをまたいでも変わりません。つまりRay.Diでは早期束縛の依存ののみを扱います。 対してRay.Aopのメソッドインターセプターはコンシュマーとメソッドを動的に束縛します。横断的関心事メソッドをコールしてもそれが実際にオリジナルなメソッドをコールしたかにコンシュマーは関心を払いません。メソッド内ではDBデータを読み込んでるのに、バインドされたインターセプターはmemcacheからデータを読みその値を返し、オリジナルのメソッドはデータ更新の際の最初の1度しか呼ばれないかもしれません。関係性は外部で構成され、その束縛はリクエストの実行時に決定されます。つまり遅延束縛です。 このようにRay.AopのインセプターはAOPとしてメソッドの振る舞いを返るだけでなく、ランタイムでの依存解決にも使われます。例えばDBオブジェクトは実際のメソッドリクエストがあるまで、master/slave/partioningどのDB接続をするべきかは決定することができません。インターセプターとして束縛されたDBインジェクターが依存を動的に注入します。 現代的なPHPフレームワークの多くは、アプリケーションコントローラーの役割を様々な関心をアスペクトととらえそれぞれの実装がなされています。例えばフィルターチェーンであったり、シグナルスロット、イベンドディスパッチャー、イベントサブスクライバー実装パターンや呼び方が違っても問題をアスペクトとしてそれぞれの解決をしようとしているのは同じように思えます。 Ray.AopでのAOPはコンシュマーにもサービスにもAOPフレームワークの依存がなく利用するためのサービスクラスには、イベント通知などイベントハンドリングのための仕事をする必要はありません。Ray.Diで生成されるアスペクトが織り込まれるサービスクラスは、イベントハンドリングをするサービスに含まれた状態で渡されます。該当メソッドの適用インターセプター知識をそれぞれが保持していて、イベントハンドリングがそれぞれのサービス6内で行われます。7 Ray.Aopを使ったアプリケーションコントローラー8 フレームワークやアプリケーションがコンシュマーとサービスの利用の関係をダイナミックにします。これを完全に外側から構成できる拡張性、関心の分離の促進によるソフトウエア品質の向上には期待をしています。9 v0.1.0alphaリリースを機にBEAR.ResourceRay.Di、Ray.AopとBEAR.Sundayのオブジェクトフレームワークというべきものについて記事を一つ一つかいてきました。Ray.Diはオブジェクトの生成を、Ray.Aopはそのオブジェクトのメソッドの利用にこれまでにない拡張性と機能性を与えます。そうやってできたオブジェクトにRESTという制約を被せ、オブジェクトの関係を(データではなく)DSLによって記述される関係性で結合しようとするのがBEAR.Resourceです。Ray.Diはオブジェクトグラフを、BEAR.Resourceはリソースグラフを構成しようとし、それぞれのリソースクラスは自らを構成しようとします。

はてなブックマーク - Object Framework – Ray.Aop


  1. BEAR.SaturdayではAroundアドバイスとして実装されていものと同様のものです。 []
  2. これはJavaのAOPアライアンスMethodInterceptorを元にしたもので、Google Guice, Spring, SeasorのAOPもこのAOPアラインアンスのインターフェイス群を実装しています。 []
  3. 前バージョンのBEAR.Saturdayでは実クラスを指定していて、アスペクトという関心の分離と適用はできたのですがそれが固定化されていました。 []
  4. またPDOなどのPHP標準組み込みオブジェクトもインジェクトできません。 []
  5. PDOと違って組み込みオブジェクトではないので@Injectでコンパイル時にインジェクトすることは可能です []
  6. 詳しくはサービスを含んだプロキシー []
  7. この仕組みはBEAR.Resourceでリソースそれぞれがレンダラーを持っているのと似ています。サービス(レンダラー、イベントハンドラー)にデータ(テンプレート、イベントシグナル)を渡すのではなく、オブジェクトがサービスを内包しているのです。 []
  8. フォームや認証、セキュリティ、ログ []
  9. 一方このパターンを採用する事で発生するデメリットにも注意深く対処していかなければなりません。 []

Object Framework – Ray.Di

| Comments

Dependency Injection


BEAR.Sundayではオブジェクトが必要とするインスタンスを、自ら取得しないでインジェクターに代入してもらうことを期待します。

コンストラクタやセッターメソッド経由で外部から代入されることをインジェクション(注入)、必要とするものをディペンデンシー(依存)と呼びます。ディペンデンシーを利用するクラスはコンシュマーと呼びます。

特に特定タスクを担当しオブジェクトがツールとして使うオブジェクトをサービスオブジェクトと呼びますが、依存はサービスオブジェクトに限りません。DB接続オブジェクトや、配列やスカラー値などの値も含みます。

前回記事の挨拶リソースに戻りましょう。このリソースはメッセージを返す為に、$messageデータを必要としています。1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Greetings extends AbstractObject
{
    $message = [
        'en' => 'Hello World',
        'ja' => 'Konichiwa Sekai',
        'es' => 'Hola Mundo'
    ];

    public function onGet($lang = ‘en’)
    {
        $greeting = $this->message[$lang];
        return $greeting;
    }
}

$messageデータをクラスに固定で持たないで、外部から代入するように変更してみましょう。
このようなセッターメソッドが必要です。

1
2
3
4
5
6
7
8
/**
 * @Inject
 * @Name(“greeting_message”)
 */
public function setMessage(array $message)
{
    $this->message = $message;
}

固定されていたデータが外部から代入できるようになりました。

ディペンデンシーをセッターメソッド経由でインジェクションしてるので、これをセッターインジェクションと呼びます。 またコンストラクターにインジェクトするのをコンストラクターインジェクションと呼びます。BEAR.SundayはRai.DiというDIフレームワークを使っていますが、サポートするインジェクションはこの2つのみです。2

このインジェクションを行うのがディペンデンシーインジェクターです。インジェクターは決められたルールでオブジェクトのコンストラクションを行います。クラスをインスタンス化しディペンデンシーをインジェクトします。コンストラクション後に行われる初期化メソッドの呼び出しや、オブジェクト破棄の直前に呼ばれるメソッドの呼び出し予約など「オブジェクトライフサイクル」に関する設定も行います。BEAR.Sundayでは原則的に全てのオブジェクトの生成はこのディペンデンシー・インジェクターが行い、オブジェクトの中からディペンデンシーを取得することは推奨されません。

インジェクションポイントと@Inject

ユーザーがセッターメソッドに@Injectと注記(アノテート)することでRay.Diは『ここに依存の注入が必要だ』ということが分かります。この「外部からの代入を期待する部分」をインジェクションポイントと呼びます。

※注)アノテーションはDoctrine.CommonsのAnnotationを使用していて、このアノテーションを使うためのuse文が必要です。

1
2
use Ray\Di\Di\Inject;
use Ray\Di\Di\Named;

インジェクションポイントに実際に何をセットするかはモジュールで設定します。モジュールではインジェクションポイントとインスタンスをバインドします。バインドの方法はいくつかありますがここでは特定の名前をつけてインジェクションポイントを指定する方法を使用していています。

実際のモジュールのコードはこのようになります。

1
2
3
4
5
6
7
8
9
10
11
12
class AppModule extends AbstractModule
{
    protected function configure()
    {
        $message = [
            'en' => 'Hello World',
            'ja' => 'Konichiwa Sekai',
            'es' => 'Hola Mundo'
        ];
        $this->bind()->named(‘greeting_message’)->toInsntance($message);
    }
}

インジェクターの生成とオブジェクトグラフ生成

インジェクターを使ってこのクラスにディペンデンシーをインジェクトしてインスタンスを取得します。

1
2
$injector = Injector::create([new AppModule]);
$injector->getInstance(‘name\space\Greeting’);

モジュールにはインジェクションポイントに対してどのインスタンスを提供するかという、いわばアプリケーションの構成知識が凝縮されています。その構成知識を使ってインジェクターはオブジェクトのコンストラクション(生成、インジェクト、ライフサイクルのセット)を行いインスタンスを返します。

インジェクションの連鎖とオブジェクトグラフ

ディペンデンシーインジェクションは専用のライブラリを使わなくても、手動でも行う事ができます。

たとえばUserクラスはDbクラスのインスタンスが必要でDbクラスはDB接続情報の文字列が必要だとします。これを手動のインジェクトするためには例えばこのようなコードが必要でしょう。

1
2
3
$dsn = $_ENV['master_db'];
$db = new Db($dsn);
$user = new User($db);

一行だとこうです

1
$user = new User(new Db($_ENV['master_db']));

このコードで明らかなのは、依存に依存があればその「依存の依存」から先に用意して順番に次の依存に渡さなければならないことです。Userクラスを生成する前に、DBオブジェクトの生成が完了してる必要があります。DBオブジェクトを生成するにはDB接続情報を取得しておく必要があります。これは通常のプログラムではごく当たり前のことです。

ところがRay.DIはUserクラスを作る前にDbオブジェクトを予め作っておく必要はありません。オブジェクトの構成知識を知っているインジェクターは必要な依存を遡って生成、インジェクトしてオブジェクトグラフをコンストラクションします。

例をあげます。

Userクラスを生成するときにRay.DiはそのクラスをつくるためにはDbオブジェクトが必要だと言う事を検知します。Dbオブジェクトを生成しようとしますが、ところがその生成にはDB接続情報が必要とも検知します。インジェクターがもつ構成知識でDB接続情報は得られます。得られた情報を使ってDBオブジェクトを生成します。そうやって依存の依存を順番に辿り依存性の解決(Dependency Resolution)を行い元のインスタンスを生成します。依存がツリー構造になっているこのオブジェクトをオブジェクトグラフと呼びます。

コンストラクションの再利用

BEAR.Sundayではプログラムの中でいつでもインジェクターを使い必要なインスタンスをオンデマンドで生成できますが、実際にはほとんどその出番はありません。boot時のルートオブジェクトグラフ(ページリソースやリソースクライアント)が生成される時に必要なオブジェクト、またはファクトリーの生成が全て完了するからです。3

前バージョンBEAR.Saturday (2008年)ではアプリケーションスクリプトからnew演算子を取り除くことが一つの目標でしたが、BEAR.Sundayではフレームワークサイドでもnewの利用はほとんどありあません。オブジェクトはコンストラクションされるとAPCのストレージに格納され、リクエストをまたいで再利用されます。

つまり現在のBEAR.Sundayではリクエスト毎に異なった処理をコンストラクタで記述することはできません。この制約はメリットとデメリットがあります。オブジェクトシリアライズ前提のためシリアライズできないクロージャや組み込みオブジェクトをコンストラクション時にプロパティにセットできません。一方、固定化されたオブジェクトグラフとより安定したフロー、強力で容易なキャッシュ機構、アノテーションやDI、AOP等を採用しながらも維持している強力なパフォーマンス等は大きなメリットです。

再利用はオブジェクトグラフの膨大な取得コスト4 を最小限にします。30,000を超えるindexページのファンクションコールは500以下になり、実行速度は数十倍になっています。

オブジェクトのコンストラクタは基本的にサービス開始の最初の1リクエストしか通りません。その特徴をv0.1.0alphaインストールの時に用意されるindexページでみてます。

indexページ画面

indexページリソーススクリプト
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Index extends Page
{
    use ResourceInject;
    public function __construct()
    {
        $this['greeting'] =‘Hello, BEAR.Sunday.;
        $this['version'] = [
            'php'  => phpversion(),
            'BEAR' => Framework::VERSION
        ];
        $this['extentions'] = [
            'apc'  => extension_loaded('apc') ? phpversion('apc') : 'n/a',
            'memcache'  => extension_loaded('memcache') ? phpversion('memcache') : 'n/a',
            'mysqlnd'  => extension_loaded('mysqlnd') ? phpversion('mysqlnd') : 'n/a',
            'pdo_sqlite'  => extension_loaded('pdo_sqlite') ? phpversion('pdo_sqlite') : 'n/a',
            'Xdebug'  => extension_loaded('Xdebug') ? phpversion('Xdebug') : 'n/a',
            'xhprof' => extension_loaded('xhprof') ? phpversion('xhprof') : 'n/a'
        ];
    }
    /**
     * Get
     */
    public function onGet()
    {
        $cache = apc_cache_info(‘user’);
        $this['apc'] = [
           'total' => $cache['num_entries'],
           ‘size’ => $cache['mem_size']
        ];
        // page / sec
        $this['performance'] = $this->resource->get->uri(‘app://self/performance’)->request();
        return $this;
    }
}

トレイトを使ったセッターインジェクション

違うクラスでも求める依存が同じなら、同じセッターが利用できます。セッターメソッドはクラスをまたいで横断的に再利用できます。モジュールに新たな設定はありません。 メソッドの横断的利用、PHP5.4の新機能のtraitにすると便利で表記も簡潔になります。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
use Ray\Di\Di\Inject;
use Ray\Di\Di\Named;
trait MessageInject
{
  private $message;
  /**
   * @Inject
   * @Name(“greeting_message”)
   */
  public function setMessage(array $message)
  {
      $this->message = $message;
  }
}

アノテーションのuse文も入っているので、利用クラスでは簡単にインジェクションが表記できます。まとめるとこうなります。

1
2
3
4
5
6
7
8
9
10
class Greetings extends AbstractObject
{
    use MessageInject;

    public function onGet($lang = ‘en’)
    {
        $greeting = $this->message[$lang];
        return $greeting;
    }
}

ボイラープレートになりがちなセッターインジェクションコードがスッキリ一行になり、依存関係を記すセルフドキュメントのようになってるのではないでしょうか。

プロバイダー6

Day.DiによるDIは次の2つのパートで成り立っています。

  • モジュールでのオブジェクトコンストラクション(Construction
  • コンシュマーでの利用(Execution

コンストラクションはモジュールで完了させ、コンシュマーでは利用だけを行います。 肝心なのはコンストラクションとエクスキューションを完全に分離して混ぜないことです。コンシュマーにディペンシーインジェクターやサービスコンテナを渡したりする事は推奨されません。78 ンシュマー内で新しいインスタンスが都度欲しい時はプロバイダーを使います。プロバイダーは最小のファクトリーで、引き数なしのget()というメソッドだけを持ちます。たとえばテンポラリーファイルのハンドルが都度、複数欲しいなら(Provider)$tmpFilePorviderをインジェクトしてもらって、このように使います。

1
2
$tmpFile1 = $this->tmpFileProvider->get();
$tmpFile2 = $this->tmpFileProvider->get();

プロバイダーはどのようにインスタンスを作るかという知識を全て持っています。引き数は渡す事ができず、最小化されたこのファクトリーに伝える事ができるのは生成のタイミングだけです。 なお、newは絶対に使っていけないというわけではありません。ごく小さなオブジェクトやPHPの組み込みオブジェクトなどは問題ないでしょう。

コンテナ?

Ray.Diでは内部でライフサイクル(シングルトンなど)の管理にオブジェクトコンテナを使いますが、依存性の解決には使いません。ユーザーがコンテナを触る必要も出番もほとんどありません。依存性は原則インターフェイスによって解決されます。SOLIDのD、 依存関係逆転の原則(DIP:Dependency Inversion Principle)です。

bootstrapのみで出現

このディペンデンシーインジェクターは通常、bootstrapスクリプト時にルートとなるアプリケーションオブジェクトを取得するのみです。ランタイムではオブジェクトの生成は原則行いません。providerPHPのcloneで複製をつくります。

Conclusion

この記事では、BEAR.Sundayアプリケーションの役割と働きをみてきました。Ray.DiはGoogle Guiceのクローンです。9 全ての機能が移植されてるわけではありませんが、ここで紹介した機能以外にも沢山の機能があります。Doctrine.Commons.Annotationライブラリを使い、AuraというPHP5.4フレームワークのAura.Diライブラリを拡張して作成しています。またRay.Diインジェクター自身の依存も手動でインジェクトされ拡張可能です。

Ray.Di – Guice style annotation-driven dependency injection framework for PHP

インジェクションポイントとディペンデンシーのバイディングはモジュールで設定し、インジェクターはどのオプジェクとが求められればどのインスタンスを渡すかという知識を持っています。モジュールはクラスをどのようにコンストラクトするかではなく求められた依存に対してどのインスタンスを渡すかという設定が行われています。そのため同じ依存を要求する違うクラスに新たな設定は必要なく、セッターメソッドのtraitを使ってより簡素な記述でディペンデンシーが取得できます。

Ray.Diの特徴はオブジェクトの生成と利用が完全に分離されていること10、モジュールでのDSLによるバインディング、APCを使ったオブジェクトグラフコンストラクションの再利用等です。11

Ray.Diは可変点の明確化と最小化というBEAR.Sundayのアーキテクチャ全体を通しての原則を支持します。

またRay.Diはモジュールで特定メソッドの実行にインターセプターをバインドすることが可能で、アスペクト指向プログラミングが利用できます。BEAR.Sundayではフレームワークやアプリケーションの動作や役割を様々なアスペクトの集合だと考えています。次回の記事ではAOPをサポートすRay.AopのBEAR.Sundayでの役割、アスペクト指向デザイン(AOD)により実装されたアプリケーション機能を紹介します。

はてなブックマーク - Object Framework – Ray.Di


  1. つまりこのリソースは$messageデータに依存しています []
  2. プロパティにインジェクトするプロパティインジェクションはサポートされません。 []
  3. 現在のBEAR.Sundayで登場する独立したオブジェクトはアプリケーションはappリソースを除くと基本的には3つしかありません。アプリケーションとリソースクライアントとページリソースです。その他のオブジェクトをそれらの「ルートオブジェクト」を構成するプロパティでしかない場合がほとんどです。 []
  4. アノテーションが必要とするコメント文のパースだけでなくアノテーションの名前解決のためのPHPスクリプトのパースも行われてます []
  5. 例えばYAMLファイルのCSVファイルのパースなどをコンストラクタで行いコンテンツとしてセットすると再利用されるので個別にキャッシュしたりする必要がありません。 []
  6. このセクションはstackoverflowの記事を元にしています。http://stackoverflow.com/questions/2504798/dependency-injection-in-constructors []
  7. コンシュマーがインジェクターを使ってサービスを取得することは、デメテルの法則(最小知識の法則)に違反します。 []
  8. 例外はそのコンシュマーがファクトリークラスの場合です。BEAR.Sundayで唯一インジェクターを依存として受け取りオブジェクトコンストラクションをしてるのはリソースのnewInstance()メソッドです。 []
  9. Guiceが使われているGoogleの代表的なプロダクトにAdSenseがあります []
  10. 利用クラスでサービスコンテナへの依存がない []
  11. コンストラクションは常にキャッシュされ、再利用されることを考慮したコーディングが必要です。 []

BEAR.Resource

| Comments

BEAR.SundayはDIAOPREST、この3つの技術をコアにしたオブジェクトフレームワークをベースにしています。

このオブジェクトフレームワークがある程度完成したのを機に、今回v0.1.0alphaとして一旦まとめました。1 まだ実用レベルではなくこれからこのオブジェクトフレームワークの上に、APIフレームワーク、webアプリケーションフレームワークと、フレームワークのレイヤーを重ねていく予定です。

この記事では現在のv0.1.0alpha実装からBEAR.Sundayの特徴を紹介します。

リソースオブジェクト

BEAR.Sudayで中心になるのがリソースオブジェクトです。MVCで言うとモデルに近いものですがwebからアクセスされる最初のリソースレイヤーのpageリソースはコントローラーのように振る舞います。

それぞれのリソースはURIで表すことができ、スキーマに応じて処理の仕組みが変わります。最も標準的なものはリソースURIとPHPクラスがマップされるリソースオブジェクトです。HTTPのリクエストメソッドに準じたインターフェイスメソッドを持ち、リクエスト処理を記述します。役割に応じてpageリソースやappリソースとスキーマや呼び方は代わりますが基本的な仕組みは同じです。

ウエブページのようなオブジェクトと思ってみてください。

例えばGETリクエストすればリクエスト用のメソッドがコールされ、結果でもあるリソースオブジェクト自身が帰ってきます。

まずはHello Worldページです。

1
2
3
4
5
6
7
8
class Hello extends Page
{
    public function onGet($name)
    {
        $this->body = ‘Hello  . $name;
        return $this;
    }
}

$nameを受け取り連結して返すだけのページです。bodyプロパティに代入して自身を返してる事に注目してください。

このページクラスは以下のように表記しても同じです。2文字列を返しても、クライアントにはbodyプロパティがセットされたオブジェクトが返ります。

1
2
3
4
5
6
7
class Hello extends Page
{
    public function onGet($name)
    {
        return ‘Hello  . $name;
    }
}

4つの公開されたプロパティ

リソースクラスは以下の4つのpublicプロパティを持ちます。

  • リソースコード
  • リソースメタ情報(ヘッダー)
  • リソースコンテンツ(リソース状態)
  • リソース表現 コードはHTTPステータスコードに準じたリソースの状態がはいっています。リソースの本質的な値はリソースコンテンツに入っていますが、クライアントが利用するリソース表現は別のプロパティが用意されています。

このHelloページリソースではリソーススコンテンツ$bodyプロパティをセットしてクライアントに返しています。

レイヤード・リソース


pageリソースはwebクライアウントから最初にコールされるリソースレイヤーです。多くの場合、(更に関心を細分化した)appリソースをページからリクエストします。コントローラーがモデルをリクエストするようなもので、pageリソースはpageコントローラー、appリソースは内部APIとして振る舞うモデルとして機能します。

最初の例の単純な例のpageリソースはコントローラーとして機能するよりも、リソースコンテンツを変更することで自身を構成しています。自身がレスポンスオブジェクトのようですがこれは不作法なことではありません。リソースは関心に応じてレイヤリングしますが、単純なHelloページではそのページだけで完結する場合もあるでしょう。一方、appリソースでも必要があれば関心にレイヤリングを適用して、適切なネストのレイヤーを持つのが良いでしょう。

最初にwebブラウザのリクエストURIに応じてルートされたpageリソースがリクエストされます。pageリソースはappリソースやその他のリソースをリクエストしますが、他のpageリソースもリクエストすることができます。階層構造MVCのように振る舞います。

アプリケーション(app)リソース

アプリケーションリソースはいわば内部APIです。MVCでの主役がMであるように、BEAR.Sundayでもアプリケーション価値の多くはこのappリソースにあります。3

appリソースの簡単な例をコードで紹介します。$lang(言語)を引数で渡すと挨拶文字列を返す 「挨拶リソース」です。

1
2
3
4
5
6
7
8
9
10
11
12
13
class Greetings extends AbstractObject
{
    private $message = [
        'en' => 'Hello World',
        'ja' => 'Konichiwa Sekai',
        'es' => 'Hola Mundo'
    ];

    public function onGet($lang = ‘en’)
    {
        $greeting = $this->message[$lang];
        return $greeting;
    }

これはpageリソースで説明したように、以下のコードと同じものです。

1
2
3
4
5
public function onGet($lang = ‘en’)
{
    $this->body = $this->message[$lang];
    return $this;
}

挨拶のデータは$messageプロパティに格納されていて、引数$lang(言語)によって指定されたメッセージを返します。このリソースクラスはonGetメソッドがあり、GETリクエストだけを行う事ができます。

リソースクライアントからはのリクエストはこのようにして作る事ができます。withQuery()メソッドで与えてるのは順番による引数ではなくて、変数名を指定した名前付き引数(named parameter)であることに注意してください。引数の順番は関係ありません。

リソースリクエスト

リソースリクエストは以下のように記述します。

1
2
3
4
5
$request = $resource
           ->get
           ->uri(‘app://self/greeting’)
           ->withQuery(['lang'] => ‘ja’)
           ->request();

この文で作られたのはリクエストです。このリクエストを実際に行うにはこのリクエストオブジェクトをクロージャのように扱います。

1
$result = $request();

リクエストは再利用することができます。引数を指定して指定した名前付き引数に追加、上書きすることができます。

1
$result = $request(['lang' => 'en]);

イーガーリクエスト

リクエストの結果をすぐに求めるにはリクエストに eagerをつけます。

1
2
3
4
5
6
$result = $resource
          ->get
          ->uri(‘app://self/greeting’)
          ->withQuery(['lang'] => ‘ja’)
          ->eager
          ->request();

この文ではすぐにリクエストが行われます。

リソースのレンダリング

リクエストにより求められたリソースオブジェクトはプロパティにリソースの状態を持っています。プロパティを直接使う事もできますが、それよりオブジェクトとして利用するのがスマートです。オブジェクトはコンテキストに応じて評価されます。

連想配列として扱うとリソースコンテンツのエレメントが取り出せます。例えばユーザーリソースのID等です。

1
echo $result['id']; // $result->body['id'] と同じ

オブジェクトなのでメソッドを利用することはもちろんできます。

1
echo $result->log();

文字列として扱うと、そのリソースはコンテンツを表現に変えようとします。

BEAR.Sundayではリソースからデータを受け取ってテンプレートエンジンに渡すのではなく、リソースがテンプレートエンジンを保持し(データを外部に公開することなく)自身がレンダリングを行います。

図をご覧下さい。


存在するリソースの1つ1つのオブジェクトがレンダラー(Viewオブジェクト)を保持します。4 

例えばユーザーリソースのコンテンツは以下のようなものだとします。

$user->body = ['name' => 'koriym', 'gender' => 'male']

このJSON表現はこうなります。

{"name":"koriym","gender":"male"}

このレンダリングはjson_encode()関数を使うだけの単純なものでしょう。

また、HTML表現は例えば以下のようなものになります。リソース専用のテンプレートを用意してレンダリングしています。

ユーザー名:koriym 性別:male 

リソースは自身の構成やコンテンツに関して関心を持ちますが、表現には直接関心をもたず、セットされたリソースレンダラーに自身($this)を渡してレンダリングを命じます。

レンダリングはリソースオブジェクトが文字列として評価されたときに行われます。

1
2
3
echo $user;
// または
$userHtml = (string)$user;

レンダラーの保持はオプションです。もしリソースがレンダラーを持たなくても、コンテンツが文字列として評価可能ならコンテンツを表現にします。リソースコンテンツが文字列表現ができないフォーマット(例えばarray型)で、レンダラーも持たないときに文字列として評価されれば文字列”を返します。

リソースリクエストのレンダ リング

リソースリクエストが文字列として扱われたときには、リクエストを行い、その結果をレンダリングした結果の文字列が得られます。

1
2
3
echo $request;
// または
$userHtml = (string)$request;

レイジーリクエストとレイジーレンダリング

リソースクライアントが生成したリクエストオブジェクトは文字列として評価されるときに初めてリクエストを行います。

v0.1.0alphaでインストールされるindexデモ画面。これにapp://self/performanceというフレームワーク実行パフォーマンスを測定するappリソースがありindexページではpageのGETリクエストに応じてセットしています。

1
2
3
4
5
$this['performance'] = $this
                       ->resource
                       ->get
                       ->uri(‘app://self/performance’)
                       ->request();

page / secで表示されている値がそうです。

これはeagerが使われていないので、bodyのperformanceにapp://self/performanceへのGETリクエストがセットされたことになります。このリソースが実際に行われるのはコンパイル済みのテンプレートでこのリソースが出現するタイミングです。

smartyのコンパイル済みテンプレートではこの部分です。

1
2
<p>&copy; 2012 <a href=”https://twitter.com/#!/bearsunday”>@BEARSunday</a> (<?php echo $_smarty_tpl->tpl_vars['performance']->value;?>
</p>

ここで初めてリクエストが行われます。パフォーマンスを測定するという目的からすればよページクラスのメソッドで計測するより、テンプレートで表示される直前に計測される値の方がより正確でしょう。またこのパフォーマンスリソースはテンプレートで出現しなければ計測されることもありません。実際に利用されるかされないかを気にしないでページはテンプレートにリクエストをセットすることができます。

リソースキャッシュ

パフォーマンスappリソースを使ったページリソースをキャッシュしても、パフォーマンスリソースの値は正しく反映されることに注目してみてください。これはページが保持してるのはリソースリクエストの結果ではなく、リソースリクエストの方法だからです。リクエスト方法はキャッシュされ再利用されても、その値は毎回変わります。

リソースまとめ

  • リソースリクエストはリクエストの生成と実行というライフスタイルを持ちます。
  • リソースはURIから生成され、リソースリクエストを実行するアダプターはURIのスキーマで決まります。
  • リソースリクエストはURI文字列で表現されます。
  • リソースリクエストの引き数はネームドパラメーターで渡されます
  • 作成されたリソースリクエストはクロージャのように扱えます
  • リソースはレンダラーを個別に保持し自身をリソース状態からリソース表現にレンダリングすることができます。
  • リソース表現は文字列評価することで得られます。
  • レンダラーの使用はオプションです。なければコンテンツが表現にならないか検討します。
  • レンダラーはユーザーが記述することができます。
  • レンダラーはテンプレートエンジンを持ちます。設計上固定化されたものはありませんが、現在のデモではSmarty3が使われています。

他にもサービスを通じて一度しか行われないオブジェクトコンストラクション、不足している引き数を補充するパラメータープロバイダー、スキーマアダプターを構成するDSL、リソースの関係性を内部で持つリソースリンク、webブラウザでタブを開くようにリンク先を加えていくnewリンク、リソースリンクをクローリングしてリソースのリンク構造を構成するクローラーリンク、リソースリンクを露出することなくアプリケーション状態を変更するHATEOUS(Hypermedia as the Engine of Application State)等様々な機能がありますがまた別の機会に紹介したいと思います。5

次回はRay.Di

次回の記事はDIです。BEAR.SundayはRay.Diというディペンデンシーインジェクターを用いてオブジェクトのコンストラションおよびインジェクションを行います。Ray.DiはBEAR.Sundayのために開発したGoogle GuiceクローンのDIフレームワークです。アノテーションを用いつつサービスコンテナにも依存しないよりクリーンなDIが可能です。

PHPでアノテーション

| Comments

アノテーションとは

プログラミングでは、コード中に登場する要素(クラス、メソッドなど)に対して、それ自体に関する情報(メタデータ)を注記できる仕組みのことをアノテーションという。「このメソッドはテスト用である」「ここでコンパイラは警告を出してはならない」「このメソッドはオーバーライドである」などの情報を付記し、コンパイル時や実行時に参照させることができる。

IT用語辞典

このように説明されるアノテーションですが、その源流を調べてると次の文章に出会いました。

アノテーションとは、JDK 1.5で新たに追加される言語仕様であり、Javaコード上でメタデータ(コードそのものではなくコードに関する付加情報)を記述可能にする。これは、マイクロソフトC#における属性(attribute)に相当するシンタックスで、アノテーションはそれを後追いした仕様といえる。

[2004年の@ITnoの記事][1]です。

つまりJavaのアノテーションはC#のアトリビュートに強く影響を受けたものみたいです。
そのことについてC#の作者、[アンダース・ヘルスバーグ][2]氏のインタビューが@ITの[C#への期待。アンダースからの返答][3]という記事の中で見つかりました。

■Java言語の進化(例:Annotationなど)についてどのように考えているか?

アノテーション(Annotation)に関しては、.NETの属性(Attribute)のJavaバージョンといえると思うが、このように.NETで実装してきていることを、やはりJavaでも行ってきているという印象だ。実際にJavaの最新バージョン5.0に搭載された新機能の中で、(先ほどのアノテーションも含めて).NETに触発されて導入されたと思われるものがいくつもある。

その.Netの[属性ページ][4]ではこのように説明されています。

属性は、プログラムにメタデータを追加します。 メタデータは、プログラム内で定義されている型に関する情報です。 すべての .NET アセンブリに、指定した一連のメタデータが含まれ、そこにはそのアセンブリ内で定義されている型および型のメンバーが記述されています。 カスタム属性を追加すると、必要な任意の追加情報を指定できます。

アセンブリ全体、モジュール全体、クラスやプロパティなどの小さいプログラム要素に、1 つ以上の属性を適用できます。

属性は、メソッドやプロパティの場合と同じ方法で引数を受け取ることができます。

プログラムは、リフレクションを使用することにより、そのプログラム専用のメタデータや他のプログラム内のメタデータを調べることができます。

Javaの[注釈][5]ではこのように説明されています。

注釈はプログラムのセマンティクスに直接影響しませんが、ツールやライブラリがプログラムを扱う方法に影響します。そのため、実行中のプログラムのセマンティクスに影響する場合があります。注釈はソースファイル、クラスファイル、または実行時にリフレクションとして読み取ることができます。

PHPでのアノテーション

PHPはアノテーションはネイティブサポートさていませんが、”Status: Under Discussion”のアノテーション提案があります。

  • [Annotations in DocBlock][6]

※[Class Metadata][7]という前の提案は否決されたようです

アノテーションの定義

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class ReflectionAnnotation
{
    private $value;

    public function __construct(\stdClass $value)
    {
        $this->value = $value;
    }

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

アノテーションの表記

1
2
3
4
5
6
7
8
9
10
11
12
/
 * Foo class.
 
 * @Entity {"repositoryClass": "FooRepository"}
 * @Table  {"name": "foos"}
 
 * @author "Guilherme Blanco"
 */
class Foo
{
  // …
}

アノテーションの利用

1
2
$reflClass = new \ReflectionClass(‘Foo’);
var_dump($reflClass->getAnnotations());

PHPの言語としてのサポートにが無いのにも関わらず、現在多くのライブラリ・フレームワークでアノテーションがサポートされています。なぜアノテーションを必要と考えるのでしょうか?提案者はこのように説明しています。

Why do we need Class Metadata?

Frameworks in general rely on metadata information in order to correctly work. They can use it for many purposes:

    phpUnit Providing meta functionality for test cases, examples: @dataProvider for test data iteration, @expectedException for catching exceptions, etc.
    Doctrine For Object-Relational mapping, examples: @Entity, @OneToOne, @Id, etc.
    Zend Framework Server classes Used to automate mappings for XML-RPC, SOAP, etc.
    FLOW3 for dependency injection and validation
    Symfony2 for routing rules
    Others One clear thing that comes to my mind is Validation, Functional Behavior injection (which could take advantage of Traits), etc. Also, any Framework could take advantage of it somehow.

So, any meta mapping injection could be easily achieved via the implementation of a centralized Annotations support.

The .NET framework uses Data Annotation: http://www.asp.net/mvc/tutorials/validation-with-the-data-annotation-validators-cs

An advantage here is the .net framework will process some annotations and inject behavior into the compiled source code.

It's important to note that annotations exist in java and .net but many strong use cases exist in these languages to provide hints to the compiler (@NotNull).

These types of use cases (hints to the Zend lexer/parser or other PHP implementations) are not presented in this RFC.

他に自分が知ってる範囲では、Java Beanの影響を強く受けた[DIng][8]やRESTful PHP frameworkの[Recess][9] や[Zend Framwork2のDi][10]でもアノテーションが使われています。

ネイティブサポートがないという事はPHPでDocCommentの部分1 をPHPでパースしなくてはならなく、速度的にも不利なところがあるのですが、このようにPHPでもフレームワーク・ライブラリを中心に使われるようになってきているようです。デ・ファクトと言えるようなライブラリがないのか、各ライブラリが独自でパースしてるものが多く、GuiceクローンのアノテーションベースのDIコンテナ [Ray.Di][11] を実装したときも最初はそうしていました。

Doctrine\Commons\Annotations

ORMで有名なDoctrineですが、ORMの他にもプロジェクトがいくつか登録されていてライブラリとしてdoctrine ORM使用しているものを単体使用できるようになっています。2

[Docotrine Commons][12]というライブラリがあります。

Common

The Doctrine Common project is a library that provides extensions to core PHP functionality.

“PHPの機能を拡張するライブラリです”という説明で、Doctrineが使っているAutoloaderやCacheもあるのですが、注目は[Doctrine Annotations][13]3 です。

RFC提案されてるようにアノテーションを扱います。

アノテーションの定義

@Annotation、@Targetは Doctrineが使用するアノテーションのアノテーションです。
このアノテーションはクラスとメソッドにアノテートすることができます。

1
2
3
4
5
6
7
8
9
10
11
12
13
/
 * Inject
 
 * @Annotation
 * @Target("CLASS")
 * @Target("METHOD")
 
 * @package    Ray.Di
 * @subpackage Annotation
 */
final class Inject implements Annotation
{
}

setDbメソッドを@Injectとアノテートしました。

1
2
3
4
5
6
7
8
9
/
 * @Inject
 
 * @param DbInterface $db
 /
public function setDb(DbInterface $db)
{
    $this->db = $db;
}

アノテーションの利用

1
2
$reflMethod = new ReflectionMethod(‘MyCompany\Entity\User’, ‘setDb’);
$methodAnnotations = $reader->getMethodAnnotations($reflMethod);

これで全てのアノテーションが取得できます。
あるいは以下のようにして特定アノテーションの値が取得できます。

1
$methodAnnotation = $reader->getMethodAnnotation($reflMethod, $annotation);

Doctrine\Common\Annotations\AnnotationReader が標準的なアノテーションリーダーです。 (Symfony2では標準でサービスコンテナに登録されてるようです)。 他にはそのAnnotationReaderを利用するCachedReader、use文による名前解決を行わない、より簡素な SimpleAnnotationReaderがあります。

またReaderを使わずパースライブラリとして使いたいなら、doc内のアノテーションを行うDoctrine\Common\Annotations\DocParser や PHPスクリプトを解析してのuse文の名前解決を行う Doctrine\Common\Annotations\PhpParser も有用だと思います。use文の名前解決については Symfony2のブログ [Symfony2: アノテーションが改善されました][14]も参考になると思います。4

※他には[addendum][15] , [php-annotations][16]というライブラリもあります。

Conclusion

PHPではアノテーションのネイティブサポートはなく、以前は使用はあまり一般的ではありませんでした。しかしPHPUnitでもすっかりおなじみのようにメタプログラミングを実現するためのツールとしてPHP界でも認知されつつあります。自作のライブラリやアプリケーションでも Doctrine Annoattion を用いれば利用の敷居は下がります。速度的な懸念も CachedReaderを使用したり、Configrationに用いたりすることで問題にならない場合も多いでしょう。

ではどのように使うのが良いのでしょうか?広く使われてる割にはなかなかベストプラクティス系の記事が見つかりませんが、1つ見つけました。興味ある方は一読すれば参考になると思います。

[Annotations Gotchas and Best Practices
][17]

Ray.DiでもこのDoctrine Annotationを採用しリファクタリングを行いました。

  1. リフレクションで取得できます []
  2. (Zend Frameworkのように)このようにライブラリ・ファーストとして部分をライブラリとして単体使用できるのが、最新フレームワークの特徴だと思います []
  3. このドキュメントは2.1のものです []
  4. Fabienさんの記事で、@masakielasticさん翻訳の記事です []

[1]: http://www.atmarkit.co.jp/fjava/kaisetsu/j2eewatch02/j2eewatch02.html [2]: http://ja.wikipedia.org/wiki/%E3%82%A2%E3%83%B3%E3%83%80%E3%83%BC%E3%82%B9%E3%83%BB%E3%83%98%E3%83%AB%E3%82%B9%E3%83%90%E3%83%BC%E3%82%B0 [3]: http://www.atmarkit.co.jp/fdotnet/insiderseye/20060215cscommunity/cscommunity_01.html [4]: http://msdn.microsoft.com/ja-jp/library/z0w1kczw.aspx [5]: http://java.sun.com/j2se/1.5.0/ja/docs/ja/guide/language/annotations.html [6]: https://wiki.php.net/rfc/annotations-in-docblock [7]: https://wiki.php.net/rfc/annotations [8]: http://marcelog.github.com/Ding/ [9]: http://www.recessframework.org/ [10]: https://github.com/ralphschindler/zf2-di-use-cases/blob/master/09-runtime-setter-injection-with-annotation.php [11]: https://github.com/koriym/Ray.Di [12]: http://www.doctrine-project.org/projects/common [13]: http://docs.doctrine-project.org/projects/doctrine-common/en/latest/reference/annotations.html [14]: http://www.symfony.gr.jp/blog/20110523-symfony2-annotations-gets-better [15]: http://code.google.com/p/addendum/ [16]: http://code.google.com/p/php-annotations/ [17]: http://willcode4beer.com/design.jsp?set=annotations_gotchas_best_practices

PHP: Dis Is It.

| Comments

http://www.slideshare.net/slideshow/embed_code/10628706

昨日2011.12.17にPHP Apocalypse というイベントで”PHP: Dis Is It”と題した発表をしました。PHP DisをそのDisそのものよりDisのありようやDis周辺つまり”Meta Dis”視点で考察を行い、PHPという言語のネイチャーを探ろうとしました。

PHPに未来を感じる方いますか?

「PHPに未来を感じる方いますか?」こういう質問をされた発表者の方がいました。僕は勢いよく…と言わないまでも挙手したのですが、周囲を見渡してみると自分一人のようでした。その方の発表では「恐らくPHPは緩やかな下降線を辿っていくと、現在ターミナル医療のような状態だ」というような趣旨の発言で、TL見る限りはそれに同意する方も少なくないようでした。1 

Inconsistency

inconsistency(一貫性の欠如)という誰もが認めるPHPの欠点をキーワードにその原因を”混血”にあるという仮説を立てます。そしてそのInconsistencyを「PHP Disそのもの」にも適用し、本当に言語として駄目設計なら、そのPHP批判にもConsistencyがあるはずなのでは?2 そこにConsistencyが無ければどういう理由なのだろうと考察します。

X Sucks

ソフトウエアバッシングというのはPHPだけではありません。時代を通じでずっと存在し、あらゆる言語にあり、OSにあり、UNIXにもあります。
UNIXはいわば”Mother of Software” 過去から今日にいたってあらゆるソフトウエアのファンデーションなのではないか?それを「時代遅れの異臭のするOS」という批判、歴史的で最大の、しかもUNIX制作者によるUNIX批判を紹介しました。そしてその批判の挫折、失敗の考察を紹介し、ではPHPはどうなのかと繋げます。

最良の者が生き残るのではない

最も強いものが生き残るのではなく、最も賢いものが生き延びるわけでもない。
唯一、生き残るのは変化できるものだけである。

ダーウィンの名著「種の起源」での一節です。PHPは強いアイデンティティを持ちません。3 過去現在に渡って躊躇なく変化してきました。そしてその変化の度に少なからず批判を受けてきたように思います。4 

しかし、その変化し続けるという姿勢が、その変化そのものより重要なのであり、5 自分はそこに未来を見る。これが僕の「 This Is It」です。このように「Dis Is It」で始まったプレゼンテーションを「This Is It」で結びました。

Outlook is Gold.

伝えたかった事はもう一つあります。

物事には常に多面性があって自分たちが見ているのは、常にコインのどちらか片方でしかない。

「物事を深く広く知れば知るほどこの単純な真実に敬意を示し、反対の立場や考えの異なる人に対する物言いに慎重さや思慮深さが出てくる」...ところが現実はそれとは全く反対の方は少なくありません。

「環境に適用しようと変化し続けるものには未来がある可能性が高い」コンピュータ言語の生存をダーウィニズムになぞらえたこの自分の主張も、その多面性のある対象の一考察でしかありません。

高名な専門家が驚くほど一面的で一方的な持論を展開することがあります。その時でも「これも何かその理由があるんだろう」と常にその背景考察を問いかけ続け、自らに対しても「コインの片側だけで語ってないか?」と疑問を持ち続ける事ができる限り、自分の”自己規律ランプ”はグリーンに点灯できてるんじゃないかと思います。エンジニアとして、問題解決の多面性に対峙する仕事人として、もう少し長く生存できるんじゃないかと考えてます。

「アウトルックにこそ価値がある」…これがもう一つ伝えたかった事です。

67

自分でタイトルを決めてpublicな場で発表するのはこれが初めてでした。聞いて下さった方、主催関係者の方々、みなさんありがとうございました。

  1. つまり自分は少数派だったのですが、同時にだからこそ発表の価値もあるのではと思いました。 []
  2. 名前空間やクロージャなど誰もが指摘してた欠陥が少なくなってきた現在のPHPなら尚更です []
  3. カリスマによる強いグランドデザインがありません []
  4. 「だから、PHPにXXXなんか要らないんだって」XXXに色々な言葉がはいってきたように思えます。 []
  5. マクルーハンの「メディアはメッセージである」とおなじように []
  6. 物事の視点、見解 []
  7. http://lists.sugarlabs.org/archive/iaep/2009-July/006940.html []

Ray.Di on Aura.Di

| Comments

Aura.Di

Ray.DiはAura.Diを使用しています。AuraはPHP5.3用フレームワークで、Paul M.Jones.氏がリードのPHP5.2用フレームワークSolarPHPの現在のメジャーバージョンです。有名なフレームワークでは無いかもしれませんが、ライブラリファースト、コンパクトでクリーンなコード、100%テストカバレッジ等、リファレンスとすべき多くの点があるのではと思います。

Ray.Diは基本的にはアノテーションベースのDIコンテナですが、アノテーションを全く使わないAura.Diの上に構築されています。なのでどちらの方法でも依存性の注入を行う事ができます。前回の記事ではアノテーションを使った方法だけ紹介しましたが、この記事では両方の方法を紹介してそれぞれ比較したいと思います。

まずはそのどちらも使えるインジェクターの生成からです。

インジェクターの生成

Containerクラスのインスタンスと、インターフェイスとクラスを紐付けるモジュールの二つを引き数に取ります。ContainerクラスにはForge、ForgeにはConfig、ConfigにはAnnotationインスタンスが必要です。

1
$di = new Injector(new Container(new Forge(new Config(new Annotation))), new AppModule);

あるいは、

instance.php

1
2
require_once  '/path/to/Ray.Di/src.php';
return new Injector(new Container(new Forge(new Config(new Annotation))), new AppModule);

このようにincludeを使って

1
$di = include '/path/to/scripts/instance.php';

インスタンスをスクリプトから代入します。

クリーンな依存関係

使用される全てのクラスがインターフェイスを持ち、それぞれ必要とされるクラスのコンストラクタで受け取っています。固定化されたクラス関係は存在せずクラスの依存関係はクリーンで、ユーザー作成のコンポーネントとも入れ替え可能です。DIコンテナが扱うクラスだけでなく、DIコンテナそのものも実装(実クラス)ではなく、インターフェイスでつながれています。1

コンストラクタ・インジェクトション

Ray.Diはコンストラクターインジェクションとセッターインジェクション(メソッドを使ったインジェクション)をサポートします。2。3rdパーティのものや既存のライブラリ等、アノテーションが使えない場合の方法と使う方法を別にして紹介します。

ターゲットクラス

ターゲットになるクラスです。ListerクラスのコンストラクタにFindインターフェイスを実装したインスタンス(Finder)を渡す必要があります。

1
2
3
4
5
6
7
8
9
10
namespace MovieApp {
    class Lister {
        public $finder;
        public function __construct(Find $finder){
            $this->finder = $finder;
        }
    }
    class Finder implements Find {}
    interface Find{}
}

アノテーションを使わないコンストラクタ・インジェクション

イーガーセット

1
2
3
4
5
    $di = include __DIR__ . '/scripts/instance.php';
    $di->getContainer()->params['MovieApp\Lister'] = array(
       'finder' => new MovieApp\Finder
    );
    $lister = $di->getInstance('MovieApp\Lister');

params[クラス名]として、ネームドパラメーター3 で引き数を指定します。この準備は通常アプリケーションのブート時等に1度だけ行います。getInstance()時にはコンストラクタ引き数を指定していませんが、”予約”した方法で引き数が渡されインスタンスが生成されます。

レイジーセット

1
2
3
4
5
    $di = include __DIR__ . '/scripts/instance.php';
    $di->getContainer()->params['MovieApp\Lister'] = array(
        'finder' => $di->getContainer()->lazyNew('MovieApp\Finder')
    );
    $lister = $di->getInstance('MovieApp\Lister');

イーガーセットでは準備の段階で引き数に必要なインスタンスを生成しましたが、もしかしたら使わないかも、あるいは準備時にはまだインスタンスが確定できないものはlazyNewというメソッドを使ったレイジーセットが行えます。インスタンスの代わりにインスタンスの生成方法をセットしておいてgetInstance()時に遅延実行されコンストラクタ引き数として渡されます。引き数1つめにクラス名、2つ目に引き数をネームドパラメーターで指定します。

クラス同様、コンストラクタインジェクションの設定も親クラスから小クラスに継承されます。つまり、Finderクラスを継承した子クラスの取得時にも適用されます。またgetInstance()の第二引き数でインスタンス取得時に、設定した引き数を指定したパラメーターだけ上書きすることができます。4

アノテーションを使うコンストラクタ・インジェクション

ターゲットのメソッドに@Injectアノテーションでマークします。Ray.Diにインスタンスを代入しなければならない事が伝わります。

1
2
3
4
5
6
7
8
9
10
11
12
13
namespace MovieApp {
    class Lister {
        public $finder;
        /**
         * @Inject
         */
        public function __construct(Find $finder){
            $this->finder = $finder;
        }
    }
    class Finder implements Find {}
    interface Find{}
}

AbstractModuleを継承したモジュールのconfigureメソッド内でインターフェイスと実クラスを指定します。AbstractModuleにはインターフェイスとクラスを結ぶ様々なメソッドがあり、英語表現のようなDSL5 でインターフェイスとクラスを紐づけます。

1
2
3
4
5
6
7
    class Module extends \Ray\Di\AbstractModule
    {
        public function configure()
        {
            $this->bind('MovieApp\Find)->to('MovieApp\Finder')->in(Scope::SINGLETON);
        }
    }

前回の記事ではインスタンスを直接してしましたが、この例では実クラスを指定してin()でそのクラスはシングルトンスコープで利用されるように指定しています。二回目以降の注入には同じインスタンスが再利用されます。

1
2
    $di->setModule(new Module);
    $lister = $di->getInstance('MovieApp\Lister');

そのモジュールをセットしたインジェクターでインスタンスを取得します。

Conclusion

アノテーションを使用しないでクラス名やメソッド名を指定してそこの何を入れるかを指定する方法と、アノテーションを使ってインジェクトするポイントを指定しインターフェイスとクラスをワイアリングする方法と、依存オブジェクトの2つの指定の方法、Ray.Diはそのどちらも可能という事を見てきました。前者はXMLやYAMLファイルなどのスタティックな設定を持つことが多く、Symfomy2やFlow3、Ding等はこの方式です。何処で注入するかと場所に注目して指定する方法と、何が注入されるかに注目する指定する方法、の2つとも言えないでしょうか。6

  1. InjectorとAnnotation以外は全てAuraのコンポーネントです。Configだけ一部機能追加してますが他のクラスはAura.Diそのままです。 []
  2. 現在プロパティインジェクションは実装されていません []
  3. 引き数を順番ではなく変数名で指定 []
  4. $host, $id, $passを引き数に取るようなコンストラクタでgetInstance($class, array(‘host’ => $host);と指定すると$id, $passはデフォルトの値で$hostだけを指定できます。 []
  5. Guiceでこのように表現されてました []
  6. 個人的には前者はコンテナやコンパイルなど実装の都合から生まれた方法で、後者はインターフェイス指向をより意識した方法ではないかと思うのですがどうでしょうか []

Ray – Annotation Based Dependency Injection System for PHP

| Comments

Ray.Di

Ray.Di は DI (Dependency Injection: 依存性注入) のためのフレームワークです。Google Guiceにインスパイアされ、Aura.Diを利用したPHP用DIコンテナです。メソッドインターセプターによるアスペクト指向プログラミングをサポートします。

この記事は初学者向けのDIやAOPの解説は含みませんが、1サンプルを通じてなるべく分かりやすく全体構成を説明したいと思います。

ターゲットオブジェクト

インジェクト対象となるメソッドに@Injectとマークします。@PostConstuctはインスタンスコンストラクトされ後の初期化メソッドを表します。@Transactional, @Templateはユーザーが定義したアスペクト指向プログラミングのためのアノテーションで、@Aspectと共に用い、そのメソッドがインターセプトされる事を指定します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/**
 * @Aspect
 */
class User
{
    private $db;
    /**
     * @Inject
     * @Named("pdo=pdo_user")
     */
    public function __construct(\PDO $pdo)
    {
        $this->db = $pdo;
    }
    /**
     * @PostConstruct
     */
    public function init()
    {
        // if not exist...
        $this->db->query("CREATE TABLE User (Id INTEGER PRIMARY KEY, Name TEXT, Age INTEGER)");
    }
    /**
     * @Transactional
     */
    public function createUser($name, $age)
    {
        $sth = $this->db->prepare("INSERT INTO User (Name, Age) VALUES (:name, :age)");
        $sth->execute(array(':name' => $name, ':age' => $age));
    }
    /**
     * @Template
     */
    public function readUsers()
    {
        $sth = $this->db->query("SELECT name, age FROM User");
        $result = $sth->fetchAll(\PDO::FETCH_ASSOC);
        return $result;
    }
}

モジュール

モジュールではインターフェイスと実クラスやインスタンス、ファクトリを紐づけるコードを記述します。ユーザー定義のアノテーションはインターセプターと紐づけます。インターセプターはネスト可能でこの例では@TransactionalとマークされたメソッドはTimerとTransactionの機能がネストされて適用されます。

1
2
3
4
5
6
7
8
9
10
class UserModule extends AbstractModule
{
    protected function configure()
    {
        $pdo = new \PDO('sqlite::memory:', null, null);
        $this->bind('PDO')->annotatedWith('pdo_user')->toInstance($pdo);
        $this->registerInterceptAnnotation('Transactional', array(new Timer, new Transaction));
        $this->registerInterceptAnnotation('Template', array(new Template));
    }
}

インターセプター

メソッド実行に割り込み、元メソッドの前後の処理をコーディングします。このタイマーインターセプターでは タイマーのスタートとストップの間に$invocation->proceed();として元のメソッドが実行されています。 array(new Timer, new Transaction)と指定することで、タイマースタート、トランザクションスタート、元メソッド実行、トランザクションコミット、タイマーストップと処理がネストされインターセプターに挟まれたその中心で元メソッドが実行されます。


@IT総合トップ > @IT CORE > Java Solution > Java EE 5マイグレーションプラクティス(1)より


Manjesh’s Blog Aspect Oriented Programming and Unity 2.0 より

タイマーインターセプター

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
 * Timer interceptor
 */
class Timer implements MethodInterceptor
{
    public function invoke(MethodInvocation $invocation)
    {
        echo "Timer start\n";
        $mtime = microtime(true);
        $invocation->proceed();
        $time = microtime(true) - $mtime;
        echo "Timer stop:[" . sprintf('%01.7f', $time) . "] sec\n\n";
    }
}

トランザクションインターセプター

リフレクションを使い元オブジェクトのプライベートプロパティのPDOオブジェクトを操作してトランザクションを実現しています。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
 * Transaction interceptor
 */
class Transaction implements MethodInterceptor
{
    public function invoke(MethodInvocation $invocation)
    {
        $object = $invocation->getThis();
        $ref = new \ReflectionProperty($object, 'db');
        $ref->setAccessible(true);
        $db = $ref->getValue($object);
        $db->beginTransaction();
        try {
            echo "begin Transaction" . json_encode($invocation->getArguments()) . "\n";
            $invocation->proceed();
            $db->commit();
            echo "commit\n";
        } catch (\Exception $e) {
            $db->roleback();
        }
    }
}

テンプレートインターセプター

連想配列をフォーマットされた文字列に変換しています。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* Template interceptor
*/
class Template implements MethodInterceptor
{
    public function invoke(MethodInvocation $invocation)
    {
        $view = '';
        $result = $invocation->proceed();
        foreach ($result as $row) {
            $view .= "Name:{$row['Name']}\tAge:{$row['Age']}\n";
        }
        return $view;
    }
}

インジェクター

インジェクターを生成し、モジュールをセットして対象のインスタンスを取得します。インスタンス取得時にオブジェクトグラフ(必要オブジェクトのリレーション)が解決され依存するオブジェクトが全て生成(またはレイジーロード可能なオブジェクトを取り出す機能のみを持ったオブジェクトプロバイダー)がセットされ対象インスタンスが生成されます。

モジュールは通常のwebアプリケーションならbootstrapで1回だけ作成します。@Aspectとマークされメソッドインターセプトされるオブジェクトは、Weaveオブジェクトというメソッドがインターセプトされ代理実行されるプロキシーオブジェクトに変わります。元のオブジェクトのメソッドを受付け、元のオブジェクトのように振る舞う代理オブジェクトです。

1
2
3
4
5
6
7
8
9
$injector = include 'path/to/scripts/instance.php';
$injector->setModule(new UserModule);
$user = $injector->getInstance('Ray\Di\Sample\User');
/* @var $user \Ray\Di\Sample\User */
$user->createUser('Koriym', rand(18,35));
$user->createUser('Bear', rand(18,35));
$user->createUser('Yoshi', rand(18,35));
$users = $user->readUsers();
var_export($users);

実行結果

Timer start begin Transaction["Koriym",33] commit Timer stop:[0.0001919] sec Timer start begin Transaction["Bear",32] commit Timer stop:[0.0001190] sec Timer start begin Transaction["Yoshi",27] commit Timer stop:[0.0001149] sec Name:Koriym Age:19 Name:Bear Age:28 Name:Yoshi Age:18

オリジナル実行

オリジナルのメソッドをそのまま実行した場合する場合のコードと結果です。ターゲットクラスに依存技術がなく、プレーンな形で実行とテストが可能です。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
$pdo = new \PDO('sqlite::memory:', null, null);
$user = new \Ray\Di\Sample\User($pdo);
$user->init();
$user->createUser('Koriym', rand(18,35));
$user->createUser('Bear', rand(18,35));
$user->createUser('Yoshi', rand(18,35));
$users = $user->readUsers();
var_export($users);
array (
  0 =>
  array (
    'Name' => 'Koriym',
    'Age' => '33',
  ),
  1 =>
  array (
    'Name' => 'Bear',
    'Age' => '20',
  ),
  2 =>
  array (
    'Name' => 'Yoshi',
    'Age' => '27',
  ),
)

Conclusion

依存オブジェクトが注入される元のクラスには特定のベースクラスの継承や、DIコンテナ等特定の技術に対する依存がありません。利用するオブジェクトや値は全て外部から入力されます。インターフェイスやアノテーションを、クラスやインスタンスまたはファクトトリークラスと結びつけたモジュールを用いて、インジェクターが必要とするオブジェクトを注入 1 します。

アノテーションでマークされたメソッドはインターセプトされるメソッドと解釈され、代理オブジェクトによってそのメソッドが代理実行されます。各処理を横断的に共有する関心の実行に役立つと同時に、メソッド内の処理を本質的なものと付帯的なもの、それぞれドメインロジック(ビジネスルール)、アプリケーションロジック(認証やロギング)と分離する事にも役立ちます。オブジェクト指向プログラミングの大原則に関心事の分離があるとするとアスペクト指向プログラミングはそれを補完する横断的関心事の分離に他なりません。

この記事ではRayの基本的な使用法の紹介だけにとどめ、DIやAOPの効用や用語、概念の詳しい解説は行いませんでした。またパフォーマンスやこの技術が向いている問題領域、不向きな領域、アプリケーションでの可能性や、現在ある課題にも触れていません。プレビューリリースとして基本機能を簡単にご紹介しました。

DIに関するより良い議論

先日行われたZendConでZend FrameworkチームのエンジニアのRalph Schlenderさんがzf2のZend Diについてスライドを公開しています。zf2のDIだけでなく、特に前半DIについて語られています。素晴らしい内容で、共感する内容も多いです。紹介します。

Try it

ここで紹介したサンプルアプリはこのテストで簡単に実行することができます。ご協力頂ければ大変ありがたいです。現在は簡単な英文マニュアルがあるだけですがkoriym/Aura.Diで公開しています。

  1. 外部から代入 []

Steve Jobs 1955-2011

| Comments

2011年10月5日 Steve Jobs氏死去。
謹んでご冥福をお祈りします。

Steve Jobs

16の時にApple IIcを使い始めて以来のApple製品ユーザーです。

人々を引きつける彼のプレゼンテーションでも最も特別なのはやはりMacworld 2007 Keynoteでしょう。その冒頭でJobsは「世の中を変えてしまう製品というのがある。一度でも幸運なことだが、Appleは過去に2つやりとげた。1984年のMacintosh, 2001年のiPod …」とそれから初めて発表するiPhoneへの紹介に繋げます。

この2つの製品の登場は今も強い印象を持っています。ただし単なる熱狂ではなくて疑問と期待が入り交じった不思議なものでした。MacintoshはApple IIで成功した要素「カラー、拡張性、TVをモニターにできる、ゲームに向いた機能」の「全て」を持っていませんでした。

iPodが出現したときには128MのメモリのMP3プレーヤーに人気があり、市場に「ハードディスクを首からぶら下げて持ってる曲を全部持ち運びたい」という要求はありませんでした。iPodの日本初お披露目は(自分の記憶によれば)「ラフォーレ原宿」のロビーのところです。ガラスケースに入った発売前のiPodがうやうやしく回転してました。たまに見る人もいましたが、それがコンピューターメーカーの製品、ましてやすぐ数年後に音楽の聞き方を全く変えてしまう革新的な製品であると見てる人は全然いなかったと思います。

MacintoshもiPodも価格が高すぎるとも言われ、どちらも市場に登場した時には少なからず批判もありました。



今は分かります。
真にイノベイティブな製品は同時代の人に登場の時からすぐに熱狂的に支持されるものではないということを。

Keynoteの中でJobsは我々の世界の先駆者としてAllan Kayの言葉を引用します。

ソフトウェアに対して本当に真剣な人は、独自のハードウェアを作るべきだ。

iPhone登場30年前の言葉ですが、今のAppleそのものです。
そのAllan Kayが好んで使った言葉があります。

“Perspective is worth 80 IQ.”

“Knowledge is silver. Outlook is gold. IQ is a lead weight.”

物事を見る視点や見解の大切さを説いた言葉ですが、Steve Jobsは正に世界を人と違った視点で見てた人だと思います。人とは違う考えを持つことを恐れず、世界を変えられるとの信念を持つ大胆さ、そしてそれを実行できる能力を兼ね備えていた、世界はそういう惜しい人を無くしました。自分も今日は大きな喪失感と共に一日を終えました。

最後にJobsが制作に最も拘ったと言われるCM映像1 を紹介してこの記事を終えます。追放されたAppleに復帰したJobsがどん底のAppleの再生を願い制作したそうです。

Steve Jobs本人のナレーションによる「Think Different」です。

クレージーな人達がいる。
反逆者,厄介者と呼ばれる人達。
四角い穴に、丸い杭を打ち込むように
物事をまるで違う目で見る人たち。

彼らは規制を嫌う。彼らは現状を肯定しない。

彼らの言葉に心をうたれる人がいる。
反対する人も、賞賛する人も、けなす人もいる。
しかし、彼らを無視することは、
誰にも出来ない。

なぜなら彼らは物事を変えたからだ。
彼らは人間を前進させた。

彼らはクレージーと言われるが、
私たちは彼らを天才と思う。

自分が世界を変えられること
本気で信じる人達こそが、
本当に世界を変えているのだから。

  1. http://jp.wsj.com/japanrealtime/2011/10/06/%EF%BD%97%EF%BD%93%EF%BD%8A%E6%97%A5%E6%9C%AC%E7%89%88%E7%B7%A8%E9%9B%86%E9%95%B7%E3%81%8C%E8%A6%8B%E3%81%9F%E3%82%B8%E3%83%A7%E3%83%96%E3%82%BA%E6%B0%8F%E3%81%AE%E5%AE%8C%E3%81%BA%E3%81%8D%E4%B8%BB/ []

The New Era of PHP Web Development

| Comments

カンファレンスの基調講演かよというような大げさなタイトルですが、参加したPHP勉強会@関東で空き時間があって、持っていったMacBook Airに入っていた以前つくった資料の断片がありその場で飛び入りを決め発表させてもらいました。

元々は自分が5.3+専用のフレームワークをつくるときに、何故作るのか、今求められるものとは何か、現在のweb開発、PHP、フレームワークの潮流とはどういうものなのか、等と自分のフレームワーク制作用に調べた時につくった資料の断片で公開の予定のないものでした。

自分なりに客観性を持って考察したのですが、やはりこういう観察や考察は多分に主観的なところとかあると思います。これと別スライドのCQRSパターンとMozilla laboのCANVASを使ったAceエディターが前のbespineから大分進化してて充分実用レベルです、デザイナーや開発者どうしでのコラボレーションに充分使えます。GitHubで使われてますが、開発で使うと使い勝手いいですよいう話、以上3つの話をいたしました。