2012年5月16日水曜日

DIを擬人化して理解してみる

Dependency Injection(DI)とか依存性の注入などと言われてもピンとこない。
仕方がないので、実験的に作っているAmidaMVCというPHPフレームワークでDIコンテナを自作してみました。

まぁDIとはこういうものかな?
というのを擬人化して説明を試みた結果です。

◆ DI以前のオブジェクトとは

あるオブジェクトが動作するため、別のオブジェクトに依存している状況を「依存性がある」と言います。依存性があること自体は問題はありませんが、
依存性が固定されている
のが問題と考えられます。

これを擬人化すると
「俺は、こいつと永遠に一緒だぜ」
となるのではないでしょうか?

コードで書くと

class foo {
  protected $love = NULL;
  function __construct() {
    $this->love = new \My\Love();
  }
}

というところでしょう。

一途というのはいいものだけれど、融通がきかない。
これを何とかするのがDIです。

◆ DI(依存性を注入する)

ようは
「誰とでもOK、注入してして」。
という軽さを売りにするのがDI。

コードにすると、

class foo {
  protected $love = NULL;
  function injectLove( $love ) {
    $this->love = $love;
  }
}

こういうのを汎用性が高いと言うのでしょう。
テストしやすい、というのがDIを正当化するのによく言われます。

ただ実際には使いづらい面があります。

◆ IoC(Inversion of Control)

何しろオブジェクトを作るには何かを注入してやらなければいけない。
独り立ちではなく、逆に誰かに依存してしまいます。

これを擬人化すると、
「一人だと何も出来ないんです。誰か注入してください」
と言う「ひ弱な僕」というのがピッタリな表現でしょう。

このように、使う方から使われる側へとオブジェクトの実装を変更することを
制御の反転(Inversion of Control)
と呼びます(多分)。

◆ Container(コンテナ)

こんなひ弱なオブジェクトたちにはママが必要です。
「あ~、坊やにはこれを注入してあげるからね」
これをコンテナと呼びます。
いろんな作り方があるけど、例えばこんな感じ?

class DiMom {
  function loveFoo() {
    $foo = new foo();
    $foo->injectLove( new \I\Love\You() );
    return $foo;
  }
}
オブジェクトの生成を一手に担い、適切なオブジェクトを注入して依存性を解決してあげる。そんなBig Motherのようなクラスをコンテナと呼ぶのでしょう。

実際には、コードでゴリゴリ書くのではなく設定できるようにすると思いますが。


◆ Service Locator(SL)

以上、色々書きましたが、未だにSLとコンテナの違いがよく分かってません。似ているようで違う、というか、多分自分の書いたコンテナはSLの事な気がしています。いや、何が違うのだろう?

思うに、オブジェクトがコンテナに依存している状態をSLと呼ぶのではないでしょうか?

自分が開発しているフレームワークはChain of Responsibilityを使っていて、必要に応じてモジュール(オブジェクトのことです)を呼び出します。
正確に言うと、ある処理を行うのに必要なモジュールを判断するモジュールがあって、そのモジュールがチェーンに必要なモジュールを追加します。
フレームワークは順番にオブジェクトを生成・実行するだけですが、生成部分にコンテナを使いますが、これだと多分SLという区分になる気がします。

また理解できたと思ったら書いてみます。


◆ 参考文献

色々読んだけど、今ブックマークしてあるもののみリスト。


2012年5月7日月曜日

Dependency Injection とリソース指向について

GW最後の夜、ストレッチをしながらDependency Injection(DI)について考えてみた。DIについて調べ始めてまだ3ヶ月の新人なので、未だによく分からないことだらけだが。

DIってリソース指向と似ているのでは?

と思った気がした。
生成するオブジェクトをリソースとして捉えると、
  1. リソース(オブジェクト)の生成
  2. リソースの操作(依存性の注入)
という手順が成立する。

リソースの操作っていうのは、要するに設定の代入と依存性の注入になる。

そこで、さらに手順を細分化してみると

  1. 生成するオブジェクトを特定する
    new、Singleton、staticなど生成方法を含めて一意に特定する
  2. 設定の注入
    オブジェクトにconfigを代入する
  3. 依存性の注入
    任意のオブジェクトを注入可能とする

という手順になるのかな。

◆ オブジェクトを特定するDIN表記

まずはオブジェクトを特定する記述について考えてみる。
こんな感じかな。
['DIN' => [ $className, $type, $idName] ]
ここで
$classNameは生成するクラス名。
$typeはnew、get、static、のどれか。意味は明白だよね。
$idNameは前に生成したオブジェクトを指定するID名。省略可。英数字ならOKとか。

ちなみにDINはDependency Injection Notationの略(笑
で、DIN表記と呼んでみよう。

◆ 設定の注入

さっき作ったオブジェクトに対して、設定を注入する。

['DIN' => [ $className, $type, $idName],
'config' => [ 'A' => 'a', 'B' => 'b', ] ]

と表現できるでしょう。

◆ 依存性の注入

他のオブジェクトをDIN使って表しておいて、依存性を注入。


['DIN' => [ $className, $type, $idName],
'config' => [ 'A' => 'a', 'B' => 'b', ]
'inject' => [
    'name1' => [ 'class1', 'get', 'test' ],
    'name2' => [ 'class2', 'new' ],
  ]
]
これで、DIN表記を導入したメリットが見えるのでは。

必ずしもnewしたオブジェクトを代入する必要はなくて、get(singleton)で作ったオブジェクトを使うことができる。あるいは、複数のオブジェクトで共通したオブジェクトに依存する、という場合もあろう。


◆ PHPでの実装を想像してみる

DIN表記は、こうだよね。
$di->din( 'className', 'new' );
$di->din( 'className', 'get' );
あるいは
$di->din( [ 'className', 'get', 'test' ] );
$di->din( [ 'className', 'static' ] );
設定を注入してみよう。
( Di::start() )->din( 'className', 'new' )->config( $config );
とか格好良くない?

で、依存性を注入。
( Di::start() )->din( 'className', 'get', 'test' )
->config( $config )
->inject( 'loader', [ 'myLoader', 'new', '1' ] )
->inject( 'loader2', [ 'yourLoader', 'get' ] );
さらに
( Di::continue() )->din( 'className', 'get', 'main' )
->config( $config )
->inject( 'loader', [ 'myLoader', 'new', '1' ] )
->inject( 'loader2', [ 'myLoader', 'new', '1' ] );
と別のオブジェクトも続けて生成できたら、便利かなぁ?

あ、まだオブジェクトを入手してない。
( Di::continue() )->forge()
->obtain( $obj1, [ 'className', 'get', 'test' ] )
->obtain( $obj2, [ 'className', 'get', 'main'' ] )
->saveAs( 'myTest' );
設定を保存できたりして。
で、いつでも呼び出せる。
( Di::start() )->forge( 'myTest' )
->...


う~ん。
全部を実装すると大変そうだ。

特に最後の設定の保存あたり。多分このAPIでは使えない気がする。
たとえば、注入されるオブジェクトを保存した設定で生成したい、という場合に対応できない。また生成されるオブジェクトの性質が一定になってしまう。常にnewとか。ちょっと使い勝手が悪そうだ。

それとデフォルトの依存性を誰が管理するか、決めないと。
コンテナ側でデフォルトは持たない、設定の保存などはコンテナの責任、として
どういうAPIで実装すると分かりやすいのか?


まぁGWの夜の夢ということですが、
実際に実装しないとDIとは何かすら分からない状態なので、
もう少し調べてみてから実装してみよう。

2012年4月24日火曜日

Kinesis KB700PB-us エルゴノミクスキーボード

腱鞘炎になってからキーボードはエルゴノミクスを使っている。
これで治るわけではないが、手が楽になるので気に入っている。


最近はKinesisのKB700PB-usという、二つに分かれるキーボードを購入した。(右はアフィリエイトリンク)。

日本では手に入りにくいエルゴノミクスな英語キーボード。

ちゃんとしたフルキーボードで、
ファンクションキーもあるし、
レイアウトも比較的普通で使いやすい。

キーの感触も悪くない。
値段を考えるとPFUのハッピーハッキングぐらいは期待したけど、まぁ十分かな。

そしてテンキーがない。
これも大事だと思う。



エルゴノミクスというと、こんなすごいキーボードがある。

自分では使ったことはないけど、昔の同僚が腱鞘炎になったときに、これを使っていた。

これが「使いにくい」らしい。

なんでも「わざと」使いにくくなっているらしい。ゆっくりとしかタイプできないので、腱鞘炎がよくなるとか。

そこまで腱鞘炎がひどいわけではないので、今ので十分と思う。




この前までは、マイクロソフトのカーブしたキーボードを使っていた(右上)。

これでも十分にエルゴノミクスだったと思う。ひどい腱鞘炎にはならなかったし、タイプもしやすい。

ただ日本語配列だったのとテンキーが邪魔に思っていて、新しいキーボードが欲しくなった。

下のは英語キーボードと勘違いして買ってしまったBluetoothキーボード。

これもカーブしていているが、最初はキーの配置が使いづらくてとまどった。慣れの問題と思うが、なれる前にKinesisのキーボードを買ってしまったので分からない。

ほんのちょっと、斜めに打てるだけで、手が楽になるので
エルゴノミクスキーボードを気に入っているのだけど、
あまり選べる種類がないのが痛い。

2012年4月20日金曜日

もっと早くUbuntu+PHPStormの開発環境にすればよかった

ちょっと感動したので、書いてみます。

Ubuntuにしてから1ヶ月ほど。
あまりPHPStormを使う機会がなかったのですが、先ほどデバッグ環境を設定しました。
あまりに簡単で書くことにしました。

なお、ローカルでApache2を動かしているという前提です。

PHPStorm

PHPの開発環境として、PHPStormは最強だと思います。
何が最強か上手に指摘できないのですが、意外と軽いわりに機能が豊富。かゆいところに手が届く、といった感じです。そしてデバッガーがちゃんと動きます。今までEclipse+PDTやNetBeans使ったことはありますが、設定に偉い苦労して、やっと動いたと思ったら次の日になると動かないとか、そんなことが多かった気がします。

一度、PHPStormを使ったら、ヘビーな開発は
PHPStormを使う以外は考えられません。

もっともお仕事の作業ではDreamWeaverも捨てがたく。
仕事だとライブラリやらフレームワークをいじくることはほとんどなくて、逆にHTMLやらフロントをいじくることが多いのでHTML←→ソースビューを行き来できるDreamWeaverは便利だと思います。

xdebug

そもそも過去デバッグが動かなかった原因は、xdebugかもしれません。
Windowsのバージョンにバグが多いのか、だいたい正しいdllを選ばないと動きません。
が、Ubuntuでxdebugインストールするのはとても簡単。

Ubuntuソフトウェアセンターでxdebugを検索、
インストールボタンを押したらインストール完了。
アパッチをリスタートして、xdebugが動いてました。

コマンドで言えば(多分)
sudo apt-get install xdebug
sudo service apache2 restart
で終了。

php.iniの修正

さすがに、これだけでデバッガーは動きませんでした。
ここのPHPStorm用xdebugの設定を見ながらphp.iniを修正。

ターミナルから
sudo vi /etc/php5/apache2/conf.d/xdebug.ini

を開いて(一行しか入ってないや)、
そして以下のように修正。


[XDebug]
  zend_extension=/usr/lib/php5/20090626/xdebug.so
  xdebug.remote_enable=1
  xdebug.remote_port=9000
  xdebug.profiler_enable=1
  xdebug.profiler_output_dir=/tmp/

アパッチをリスタートして、phpinfoでxdebugが動いているのを確認。

PHPStormの設定

最後はここの「何の設定なしでxdebugとPHPStorm2でデバッグ」という少々誇張されたタイトルに書いてあるとおりに設定します。

xdebug用のブックマークを作ること。

個人的には「Start debugger」と「Stop debugger」だけブックマークを作って、デバッグを始めるときにブックマークを押します。

そしてPHPStormでは「赤い電話機アイコン」を押して緑色に変わればOK。
ブラウザーをリフレッシュすると、PHPStormでデバッグが開始されます。

path mapping

以上で、動くと思うのですが・・・
たまに「path mapping」を設定してください、といわれます。

どうも、PHPStormがデバッグするには、xdebugからデバッグ中のファイル名を受け取っているようなのですが、ときどきファイルがどれか混乱するようです。

同じファイル名がある場合など、どのファイルをデバッグするか聞いてくることがありますので、適宜選択してあげるとデバッグが始まります。

So, Happy debugging with PHPStorm!

高解像度の27インチモニターが安くなってきている

アマゾンをウィンドウショッピングしていたら、
高解像度の27インチモニターが安くなっていた。

普通の27インチモニターは、これ以上安くならないだろうと思えるぐらいに安い。
大体2~3万円で27インチの大画面が手に入る。

ただし、解像度は1920×1080という「俗に言うフルHD」
作業するには、もう少し解像度が欲しい。

ひとつ上の解像度で27インチを買おうと思うと、
ちょっと前まで、10万円以上していた。

それが6万円にまで下がっていた。
ほ、欲しい。
でも、去年~今年と散財したからなぁ。

心を落ち着かせる為にDPIを計算してみた。

Monitor        横    縦  インチ  DPI
-----------------------------------
NANAO 21     1600  1200  21    95.2
FullHD 27    1920  1080  27    81.6 
MoreHD 27    2560  1440  27   108.8 

Nanaoというのが今使っているディスプレー。
もう6~7年ぐらい使っている。

ちなみに右がちょっと前の作業環境。モニター3台あるけど、使っているのは左の2台だけ。

この右にある古い19インチを27インチモニターにしたいのだけど、フルHDだと解像度が落ちるので、気が乗らない。
でも、一つ上の解像度ならいい感じだ。

もっとも、アップルのiPadに使われるレティーナディスプレーが300dpiぐらい、それより大きなMacだと200dpiを目指しているというから、まだ半分ぐらいの解像度しかないのか・・・

この春のアップルの発表待ちだな。

下はアフィリエイトリンク。
今のところ、この2機種だけのようです。


2012年4月16日月曜日

PostgreSQLのinitdbと違うロケールでDB作成

Ubuntu上にPostgreSQLをインストールして、
phpPgAdminでログインして、EUC-JPでDBを作成しようとしたら、
ERROR: encoding EUC_JP does not match locale ja_JP.UTF-8

と言われた。
SQL文はこんな感じ。
CREATE DATABASE "example" WITH TEMPLATE="template1" ENCODING='EUC_JP'
古いDBをテストするために、EUC-JPでDBを作成する必要がある。
どうやら、initdbの文字コードと違う文字コードでDB作成できないらしい。検索してみると、結構な書き込みを見つけたので、かなりFAQな問題らしい。

ただし、initdbからやり直すという面倒な方法しか見つからない。

色々探して、initdbの文字コードと違う文字コードでDB作成を見つけて
簡単な解決をやっと発見。

要するに、
CREATE DATABASE "example" WITH
TEMPLATE="template0" ENCODING='EUC_JP'
LC_COLLATE='C' LC_CTYPE='C';
とSQLを発行することで解決した。

ポイントは
1)template0を使うこと。
2)LC_CTYPEをCで指定すること。
の2つだけ。
これでUTF-8でinitdbしていてもEUC-JPでDBが作成できる。

◆ただし、もう一つ、この作業の前に行ったことがある。

先に、こんなページを見つけていた。
PostgreSQL 9.1 installation and database encoding

つまり、OSでサポートされるロケールでなければいけないらしい。

Ubuntuでja_JP.EUC-JPを使用するを参考に調べたところ、
今のUbuntuにはEUC-JPが入っていなかった。

そこで、ja_JP.EUC-JPというロケールをOSにインストールしてから
上記のCREATE DATABASEコマンドを発行している。

どんな順序で動くかはテストしてないので、メモということで。



2012年4月2日月曜日

Chrome: Your Profile Could Not Be Opened Correctly [solved]

Took me more than a week to figure this out...

Problem: 

When using Chrome's sync function using Google's account, Chrome does not shutdown gracefuly, leaving zombie process in Ubuntu OS.

The zombie process, somehow, prevents Chrome to load account data, and reports,
Your profile could not be opened correctly...
When this happens, Chrome fails to load any of the password, cookies, from the past history, even if it is on the local data. This is very annoying.

Find out the cause:

After Googled around, found out how to kill the zombie process:
pkill -9 chrome

Then, found Chrome's history data is causing this, so removing it should help:
rm -rf .config/google-chrome/Default/History*
These worked fine, but when Chrome synchronize the account, the History data came back, and the problem occurs again.

To summarize the situation,
some bad data in History is the cause of this problem,
and the bad data is in the Google Sync (up somewhere),
so Chrome sync always downloads the bad data. 

OK, the solution is
to erase the bad data from Google account.

I tried to erase the sync'ed data from Chrome's Preference menu. But nothing  worked. So, I dived into the Google's maze of account setting pages...

This worked for me:

0) disconnect from Google Account.

Not sure if this is necessary, but I was disconnected when I did the following.

1) login to Google Account Setting. 

I used this url to get into my account setting.
https://www.google.com/settings/

no link, I found so far. 

2) turn off the Google Sync

From the account setting page, click on "Profile and privacy". 
then, scroll down to "Sign in to Dashboard" and click.
find "Chrome Sync", and click on "Stop sync and delete data from Google" or something 
(well I'm a Japanese, and I changed the language to English to write this entry. But this page was still in Japanese. so I cannot be sure how the link is written in English.)
wait for a few minutes according to the message. 

3) delete web history

From the account setting page, click on "Go to web history".
Click on "Remove all Web History", and clicked just yes to whatever the question was. 

4) start Chrome and synchronization

and it worked for me!


The Google account pages were just like a maze. 
So, many of the above may change at anytime. But I hope that this list gives some clue about this problem. 

[additional note: 2012/04/20]

The same problem happened again, about two weeks after I wrote this blog entry. So, I stopped syncing 'address bar history', and the problem went away (for now).