uehaj's blog

Grな日々 - GroovyとかGrailsとかElmとかRustとかHaskellとかReactとかFregeとかJavaとか -

DCIアーキテクチャについて語ってみるよ

Trygve Reenskaug氏とJames O. Coplien氏らが提唱する「DCIアーキテクチャ」について、id:digitalsoulさんが論文を翻訳してくださり、またその解説とサンプル実装(groovy, scala)を示してくださっており、読んでみたところ、大変興味深いので理解した限りを書いてみます。

おじさん登場

たとえば、あるおじさんがいたとします。

このおじさんは、白いスーツ、グラデーションの入ったサングラスと金ぴかのネックレスをつけて新宿歌舞伎町に出かけ「やくざ」として振るまいます。とおりかかったお兄さんがそのおじさんに出会い、目が合ってしまい、因縁を付けられ、お金を巻き上げられてしまいます。

さて、おじさんは家に帰ります。実は、このおじさんは家では良いお父さんとして振る舞います。赤ちゃんはこのおじさんの目を見て笑いかけます。おじさんは相好を崩し、オーよしよし。

さて、同じおじさんに対して、「目を見る」という動作にたいして、お兄さんと赤ちゃんで全く異なる結果を招いていますが、このような動作を従来のクラスベースのオブジェクト指向プログラミングでは自然に記述できません。

継承?

例えば以下の様に継承したとしても、

お父さんオブジェクトと、やくざオブジェクトは別につくれるでしょうが、あるおじさんが、ある時点ではお父さんで,ある時点ではやくざである、というように変化することができません。*1

インターフェース?

インターフェースを使ってみましょう。

この場合、ojisanクラスはヤクザのメソッドとお父さんのメソッドの両方を実装する必要があります。さらに、「目を合わせる」メソッドがどちらの動作をするかを切り替える事ができません。さらに、おじさんが将来「会社員」などの別の立場になったときおじさんクラスをその都度修正したり、追記していく必要があります。継承しているのでおじさんからやくざ・お父さんへの依存性が生じているのもたいへん嫌です。

実装の多重継承?

インターフェースの場合と同様に、やくざ・お父さんへの依存性があります。さらにもちろん、言語によっては実装の多重継承はサポートしていません。

コンポジション?

コンポジションではどうでしょうか?

そんなには悪くないですね。ただ、

  1. おじさんとやくざ/お父さんは異なるインスタンスとなる。
  2. おじさんのメソッドをやくざ/おとうさんは持っていない。明示的にたどるかおじさんへdelegateしてやる必要がある。
  3. 上にも関連するが「やくざ instanceof おじさん」ではないので扱いにくい。そうしたいならインターフェースをimplementsする必要がある。

という問題があります。まあマイナーな問題ではありますが、クラスと手間が増えます。UMLを書くのは面倒なんで、略。

ミックスイン!

インスタンスへのミックスインもしくはそれに相当する機能を言語が持っていれば、この状況はきれいに解決できます。
「おじさん」(データ)インスタンスに対して「やくざ」(ロール、役割り)をミックスインしてやるのです。
たとえば、Scalaのtraitsとwith、Groovyのインスタンスに対するmixin(.metaClass.mixin)、RubyincludeObject.html#extendなどを用いて実現できます。JavaだとQi4jとかでアノテーションとかを使うことになるようです。C++だとtemplateをつかうらしいです。

コンテキスト

ここまでは、1つのデザインパターン、もしくはとある言語機能の便利な使い方に過ぎないと言えましょう。あるいはすでにロールモデリングでつち使われてきたものみたいですね(ミックスインするかどうかは別として)。

さて、DCIアーキテクチャの最後のピースはコンテキストです。

お兄さんが町を歩いていて、おじさんに出会う、という「コンテキスト」において、おじさんに「やくざロール」がミックスインされるのです。

でも仮に、赤ちゃんが同じ新宿歌舞伎町でおじさんに出会ったら、そこでおじさんにミックスインされるのは、「お父さんロール」でしょう。

データにどんなロールがミックスインされるかは、コンテキストが定めるということです。「誰が(主体)」「何をしようとして(目的)」おじさんに出会うかによって、おじさんにそれぞれ適切なロールが注入され、その瞬間からおじさんはそのように振る舞い始めます。コンテキストはロールをデータに結びつけたうえで、相互作用を記述する場です。

利点

DCIアーキテクチャの利点を以下に示します。

メンタルモデルとの一致

DCIアーキテクチャではこのようにデータ、それに付与しうるロール、そしてその結びつける場の記述としてのコンテキストでシステムの振る舞いを記述します。

このような記述が重要なのは、人間が、世界を認識している、そのメンタルモデルと近いからです。
ありとあらゆる属性を含めて「おじさん」をモデリングなんかできっこないし、さらにそれが機能追加によって変化していくと、手に負えません。あるコンテキストである側面(ロール)に着目して記述するだけでよいのです。それが十分かどうかは、わかりませんが、人間ですら、現実世界において、そうせざるを得ないので、そうしているのです。

ぼくらは、とあるやくざとの示談金の交渉とか、事務所に連れ込まれたりした場合のやり取りを想起したり記述したりする際には、そのおじさんが「良いお父さん」であることに着目したりはしません。なぜならそれは想定/記述を簡潔に保つために、そして、よりよく理解するためには、不要で害があるからです。

主観的プログラミング

これは余談。誤解を恐れずに言うなら,特定の立ち位置(多くの場合ソフトウェアの利用者の視点)からの「主観」として現実を切り取る、という人なら誰もがやっている行為をプログラミングに持ち込むということです。考えてみると、主観無しで、客観的に世界を認識できると思うのは、人間としては傲慢な行為でありました。また、わかりやすさという観点から言うと、全てが客観的に書かれた小説や論文というものが有ったら、そんなものは実にわかりにくいでしょう。DCIアーキテクチャのコンテキストの概念は、プログラミング言語自然言語の記述運用法に近づけるものだ、ともいえるかもしれません。

アジャイル

このようなアーキテクチャが変化に強い事はそうなのかなと思います。データは基本的な属性とメソッドだけをもっていて(たとえば「おじさん」なら「年齢」と「名前」ぐらいを持っておく)。おじさんが銀行窓口に来たら「顧客」や「購入者」のロールが割り当てられるでしょう。

凝集性

伝統的なOOPでは、各オブジェクトにフラットに散らばっているしかなかったメソッドが、DCIアーキテクチャにおいては、データの保持のためなのか(モデルのメソッド)、特定コンテキストにおけるロールなのか(ロールのメソッド)、コンテキストにおける処理なのか(コンテキストのメソッド)、によって分別され、コードの配置が必然性を持って確定します。(多分)

まとめ

短い記事ではとても説明しきれる事ではございませんが、卑近な例を挙げて書いてみました。興味を持たれた方は前述の論文や、それに続く幾つかの記事サンプルコードを読んでみてください。

DCIアーキテクチャは、今後設計方法論やツールやフレームワークのサポートなどを含め、ソフトウェア開発における一角を占めていく考え方になるのではないかと思います。(ひかえめ)

なお、私はDCIアーキテクチャでちゃんとしたコードを書いた事が有るわけではないので、本記事は推測や予想が多分に含まれています。誤解や間違い、ウソなどございましたらご指摘いただけますと幸いです。

*1:図を書くのに使った[http://www.yuml.me/:title=yUMLサービス]のバグか仕様かわかりませんが「おじさん」だと意図する図にならなかったのでojisanと表記しいてます。