Kokudoriing

技術系与太話ブログ

Command/Query分離原則とreturn this

Command/Query分離原則はバーランド・メイヤー大先生のありがたいお言葉です。
内容はと言うと、

オブジェクトやシステムのインターフェイス&実装を「CommandとQueryに分けろよ」という提案。
Commandは値を持たず、副作用(つうか、主作用だけど)だけを持ちます。
Queryは副作用を持たず値だけを返します。

「メイヤー先生の偉大さとCommand-Query分離 / 檜山正幸のキマイラ飼育記」
http://d.hatena.ne.jp/m-hiyama/20101216/1292469108 より引用

以下はCommand/Query分離原則を順守した簡単な例です。

var stream = new Stream();

stream.write(item); // command

var result = stream.read(); // query

なるほど、CommandとQueryが分離されているので責務の境界が明白です。
書くと言えば書くんです。読むといえば読むんです。非常にわかりやすい。

・・ですが、ついついこういう風に書きたくなってしまいます。

var stream = new Stream();

// writeはvoidでなくthis(この場合はstream)を返す
var result = stream.write(item).read();

CommandとQueryを分離する事の意義は非常によくわかります。
しかし、何故Commandはvoidを返す(つまり値を返さない)のでしょう?

Martin Fowler卿の仰るところによると、CommandとQueryの分離を明示的に示すためです。

この原則は、戻り値の型によって両者を区別するというものである。
たいていの場合、うまく機能するため、これはよいルールである。

「CommandQuerySeparation / Martin Fowler's Bliki in Japanese」
http://capsctrl.que.jp/kdmsnr/wiki/bliki/?CommandQuerySeparation より引用


ただ、両者を区別したいのであれば他に良い方法もあったのではないでしょうか。
例えばJavaにおけるgetterや、C#におけるgetプロパティはQueryそのものです。

であればsetter(Command)がvoidを返さなければならない理由は何でしょうか?

考えられる理由としてはreturn thisをする意味的理由が無いため、です。
それの何が問題になるのか、例えばCommandが値を返すケースを考えます。

ん、上記では既にCommandは値を返さないと説明をしたわけですが。
これはファウラーやメイヤー自身も認めていることですが、Command/Query分離原則にはいくつかの例外があります。

Meyerは「コマンド・問い合わせ」原則を全面的に使用したいと考えているようだが、 そこには例外がある。
スタックのpopが、状態を変更するモディファイアの良い例だ。
Meyerは、「このメソッドはできるだけ避けたほうがよいが、便利なイディオムである」と明確に述べている。
なので、私はできるだけこの原則に従うようにはするが、原則を破ることもいとわない心構えでいる。

「CommandQuerySeparation / Martin Fowler's Bliki in Japanese」
http://capsctrl.que.jp/kdmsnr/wiki/bliki/?CommandQuerySeparation より引用

確かにStackにおけるpopメソッドは副作用(Command)と値の取得(Query)という2つの顔を同時に持ちます。
この時、Stackのpushメソッドの戻り値がvoidであればCommandであることが一目瞭然です。
しかし、pushがthis、すなわちStackを返すのであればどうでしょう?


が、そもそもpush/popなどは名前(もっと言うとシグニチャ)から用意にCommandであるかQueryであるかが把握できる。
というか利用者側にCommandかQueryかを把握できるような名前を付けるべきであって、
戻り値でしか判別出来ないのであれば設計ミスなのでは?と。

そしてCommandがreturn thisしてくれるならば先述したように、どうせ一行後に書くCommandをQueryと同じ行に書けるようになる。
そのベネフィットの大きさはともかくとして、メリットは有るわけだ。

例えばjQueryはCommandであるはずのjQuery.fn.eachはreturn thisしている。
(C# Ix のIEnumerable#ForEach拡張メソッドはvoidを返している)

jQueryやLINQの影響もあってか、かなり前からメソッドチェーン記法が世間的に受け入れられているような気がする。
どうだろう。戻り値でCommandかQueryかを判別する旨味はそこまであるのだろうか。


蛇足.)

言語がこの考えをサポートしてくれればいいのに、と思ったりする。
言語が状態を変更するメソッドを検知したり、 プログラマが目印をつけたりできる、というのはどうだろう。
言語が自動的に検知できないのは、状態を変更しないというルールがシステムのObservableStateにしか適用できないからである。

「CommandQuerySeparation / Martin Fowler's Bliki in Japanese」
http://capsctrl.que.jp/kdmsnr/wiki/bliki/?CommandQuerySeparation より引用

状態を変更、までは行かないまでも、C#のプロパティはCommand/Queryを言語レベルでサポートできているような気もする。
問題は非同期であって、これはどうだろう・・。
C#5.0にはasync/await構文が入ったので非同期を同期っぽく書けるにはかけるが、asyncはプロパティには非対応だし・・。
やるとしたらRxで、とかなんですかね・・?

ただ命名規則でgetter/setterを区別するなら、Queryはgetプレフィックス付けておけば解決する問題なような気もする。
あと、副作用を隔離する事に長けているHaskellが予測可能性高いのもこういった理由なのかなあと。