著作一覧 |
アスキーの鈴木さんからアンクル・ボブのクリーンアーキテクチャをもらったので、読んだ。
おもしろかった。内容にもほぼ同意できるし、良いことがたくさん書いてある。
ただ、読みやすくない(正確ではない。350ページの本に対して付録などを除外しても本文34章に分割しているので、読むのはたやすい。ただし、内容が相当前後するし、依存関係が逆転している章もある。全体像を示してから細部へ進むと言えなくもないのだが、全体像を説明するための用語は細部で説明されるため、逆に全体像を理解するのが難しくなっている(とおれは感じた)ところもある。また、おまけが唐突に最後に来るので、なに昔話してるんだ爺さん、みたいな印象も受けることをもって、読みやすくないとここでは表現した)。
本書が最も重視して、そのためにおそらく書籍の作り方にまで影響しているのは、徹底して、実装(詳細)と仕様(方針)を切り離して、後者だけにフォーカスするという方針だと思える。本気で原理原則だけを説明しきろうとしているのだ。
そのために、アーキテクチャといった場合に(少なくともおれにはそう受け取れる)システムが実現すべき機能を現実のソフトェアとするために考慮すべき制約に基づいた抽象から具象へのマッピングという意味でのアーキテクチャを説明したものではない。
そうではなく、複雑なシステム全体をどう構築するかの設計指針としてのアーキテクチャについて説明したもので、徹底的に抽象化されている。したがって、現実の技術として想定されているのは少なくともオブジェクト指向プログラミングが可能なプログラミング言語であることくらいだ(それすら重要ではないかも知れない)。
言いたいことはSOLIDの5文字からなる頭語で示されている。
Sが単一責任の原則(コンウェイ)
Oがオープン・クローズド原則(メイヤー)
Lがリスコフの置換原則(リスコフ)
Iがインターフェイス分離原則
Dが依存関係逆転の法則(DIPと称している)
2004年頃にマーティンファウラーなどとの議論で出てきた原則らしいので、目新しいものではない。その意味では、本書は2017年に上梓されたようだが、21世紀初頭のアイディアを実際のプロジェクトに適用して確信した後に書籍としてまとめたものだ。
あと、ヤコブソンのユースケース、エンティティ―コントロール―バウンダリー、つまりロバストネス分析に強い影響を受けているのが特徴だ。
SOLIDのうち、SOLは言うまでもないことで明々白々だから良いとして、IとDは()内に人名がないようにアンクルボブ(という名前の由来は付録に出てくる)たちの議論から生まれたもののようだ。
Iは分離と訳されているが、使わないモジュールを参照するな(ビルド時に依存するモジュールは、実際に依存するモジュールのみに限定せよ)、ということに尽きる。10章で詳細が書いてあるが、例がばかげているので意味が通りにくい(全体的に、コードを可能な限り使わずに説明しようとしている(このこと自体が、詳細に依存しないアーキテクチャを説明するための方便のようにすら感じる)のだが、逆に例が誰でも理解できることを意識し過ぎたのかどうにも説得力に欠けてしまっている側面があるとおれには思える。ちなみに、Iの例は、あるモジュールXを呼び出すA、B、Cの3つのモジュールがあるときに、モジュールXのうち、Bのみが依存しているメソッドを修正して(AやCの呼出しには仮に一切影響しないとしても)Xが再ビルドされたら、AやCも再ビルドが必要になるから、XとABCの間にインターフェイスをかませるべきというものだ(makeでタイムスタンプを判断してビルドする系の言語の場合は。だから動的言語のほうがこの面では良いと書いてあったりする)。くだらん(単一責任だけで十分っぽい)が、Javaを使ったシステムだと無いわけではないし、再ビルドしたら変更があるはずだということになるので、そのためにはQAプロセスをAやCに対しても行う必要がでてきて……みたいなことを想定しているのかも知れない。
というわけで、Iについては変更の影響範囲を明確化するために利用しているモジュールと利用されているモジュール、利用されているモジュール内での変更可能性に応じたインターフェイスを用意するべき、ということになるのだろう。いずれにしても、実装に利用する言語などに依存した詳細のように思える。
(追記-20180803 いや、重要だった。内容はくだらないという印象は変わらないのだが、次のDを実現するためには、Iがちゃんと設計できていることが大前提になるからだ。その意味でDと密接不可分の関係であり、実装時のビルドの話などとは切り離して、モジュールの依存関係を明示せよ、という原則として考える必要がある)
それに対してDはえらく重要で、本書の中では繰り返し言及される。というよりも、本書は、ほぼすべてがDの説明といっても良い。また、その重要性は理解できる。
最初、この箇所を依存対象のオブジェクトを注入する(ようするにDI)の意味だと受け取ってあとから混乱したが、もちろん本書はそういった詳細についての本ではなく設計の本だから、意味が異なった。
モジュール間の依存性を正しく整理するためには、必然的に依存関係を逆転させる必要があるという意味だ(確かにそう書いてある額面通りだったということだ)。
ここでの依存には、UMLでのuse(普通の矢印。とがったほうが依存している対象のモジュール)とinherited(白抜き△の矢印。△のほうが依存している対象のクラス)の両方の意味があるのがミソとなっている。
つまり画としては当然のことを書いているのだった。
AがBをuseする。CはBを継承する。このとき、矢印はBを中心にしていて、制御の向き(AがCを利用する)に対してCからBについては反転しているということに過ぎない。
ただし、DIPで解決すべき問題が2種類あるにも関わらず、説明が出現箇所によって1種類しか行わないために、おれにはすごくわかりにくかった。
まず1点は、循環参照に関する問題で、オブジェクトは相互作用するので、CからAへの呼出しもあり得るという点だ。したがって、AがCを呼び出すときに、c->func(this)と書くとそれはC側のメソッドシグネチャはfunc(A* aself)となっていることを意味し、つまり循環参照となり破綻するという問題をどう解決するか(ここまで還元してしまうと全体としては正しくないのだが、SOLIDのDの説明としては一番正しいと思う)ということだ。
ところが、11章でDIPについて詳細の説明っぽいものがあるのだが、この例がわかりにくい。依存関係にある下位モジュールは外部のものであるはずなのに、依存している(useする)モジュール側にファクトリやインターフェイスを置いているからだ。もちろん11章に出てくる図はこの図で正しいために、DIPで実際に実現したいことがぼやけているように思える。
つまり、DIPで解決する2点目として、外部モジュールを交換可能とすることが出てくる。このためには、本来直接useすべき(→が外部モジュールへ向く)依存関係を、内部のインターフェイスに向け、内部のインターフェイスというかプロクシを利用することで(プロクシは直接の外部モジュールと内部モジュールの中間に配置する)依存関係としては外部モジュールが内部のインターフェイスに依存しているように扱うようにする。
中間層はオーバーヘッドなので、薄ければ薄いほどよく、ここで外部モジュールといったものがシステムの一部であれば、呼出し元のインターフェイスに依存させる(というのが11章の図)、ということだった(19章では明確になっているが、ここのコード例もひどい。なおRubyのmixin可能なモジュールをプラギンとして扱えば、19章の悪いコード例そのもので正しく設計しているということもあり得る)。
しかし、ここでぐちゃぐちゃ書いていることの重要性についての認識はアンクルボブとおれとで全く疑問の余地もなく同じだとは思う。DIPを設計に組み込んでおくことで、外部コンポーネントのプラグイン化が実現できるからだ。通常、プラグインは、メインとなるモジュールが公開するインターフェイスに合わせて後刺しするモジュールを後から開発する。そうではなく、既存のモジュールをプラギンとして少ない労力で交換可能とすることで、すべての外部にあるモジュール(フレームワークであったりミドルウェアであったり)を交換可能とする。ここでも依存関係が逆転できているのがミソとなる。これによって、すべての外部に存在するインフラストラクチャをプラギン化できるように考慮する=すべてを詳細として設計本体から切断可能とする、という画だ。
この考えが正しいのは、ビジネスは永く、ソフトウェアの流行は短いからだ。そしてゼロから作り直すのは常に危険だということでもある。その観点から、もっとも必要な抽象エンティティというのは、実はドキュメント(自然言語で記述された)ではないかということになって、おそらくそれは正しいのだがさすがにそれを言うとある意味おしまい的なところがあるのか、そうは書いていない。だが、そういうことだ。
以下その他気づいた点のメモ。
P.136 図14-9はあえてそうしているのかも知れないが、StableにI=1の記述が抜けているので、それまで説明したIという変数の意味が不明になると思う。StableのIが1となるため、instableからのI=1の矢印に対してIの値が等しいので違反、と示す必要があるのではなかろうか。
P.187 上でも書いているが、悪いコード例がくそだ。
P.192 エンティティとはビジネスルール(という抽象)で、ユースケースはアプリケーション(という具象)。なんのメモだ(すでに忘れた)? 用語かな。後半以降エンティティという言葉ががんがん出てくるのだが、ECB(エンティティ、コントローラ、バウンダリー)のEなので、本書の他のパート同様抽象のように扱われているのだが、実際にはコンポーネントだったり実装だったりもするので明確ではないが、この箇所でエンティティを独立したコンポーネントまたは抽象としているように読めたということらしい。
P.234 正しい。
P.262 正しいというかおもしろい。
P.270 フレームワーク作者の自尊心よりも、設計を重視しろというのがおもしろい。(フレームワークは詳細なので身も心も依存するのはだめ、ということが主張しまくられていて、おれにはまったく同意なのだが、ここまできつく言わないとわからない連中がいるんだろうなぁ、と21世紀始まってすぐの頃のStruts以外の方法でWebアプリケーション作るのはばかと言わんばかりの論調やそれをまじめな顔でぬかしている人たちのことを思い出したのだった)
P.276 またいい加減な図。ViewModelを忘れたようだ(とメモしているが、ViewModelも詳細なのでどうでも良い話だった)。
P.283 ドメイン(とここでは記述しているが、P.192あたりでのエンティティ群のこと)を別コンポーネントとしてフレームワーク(=インフラストラクチャ)から分離している画が出てきているので、おれの読み方と考え方と合致していることが確認できた。
P.285 第34章はサイモン・ブラウンという人がアンクルボブのクリーンアーキテクチャを元に、自分の世界と擦り合わせて改変することを説明している。これはとても良い本だなというのが、この箇所で完璧に明らかになる。教条主義ほどくだらないものはないからね。
P.290 publicの用語としての使用方法が気に食わない。
本書を読む場合は、最初に付録Aの思い出話を流し読みするのが良いと思う。延々と古臭いことを書いているが、重要なのは、その時代のシステム制約とソフトウェア、そこで発生した開発上の問題と現実(運用上)の問題、あるとすれば解決の発見、ないとすれば次の時代へ持ち越された問題の本質、そういったことから、どういうモチベーションで本書が書かれているかと、ソフトウェア開発はハードウェアとコンピュータサイエンスの発展によって助けられながらもどうも同じことを繰り返しているようだ(当然、問題も持ち越されているようだ)ということを念頭に入れたり、自分のケースに引き寄せたりすることで、本文が読みやすくなると思う。
Clean Architecture 達人に学ぶソフトウェアの構造と設計(Robert C.Martin)
抽象度合いが高いので、そう簡単には古びない良い本だと思う。(古びさせないために、あえて極端に古い例を出しているのか?と疑問に思うところもある(12章)が、温故知新を肝に銘じているのかも知れない)
特に、無敵にプログラミングしている(というかプログラミングできる)20代とかのうちに読むと良いのではないかと思った。が、開発している間は読む価値大いにある。
電子書籍で買うなら達人出版会が良いと思う。PDF(EPUBもある)だし。
ジェズイットを見習え |