設計の原則:モジュール化

ソフトウェア設計には、パターン理解が大切。
でも、その前に、設計の基本原則(設計の基本テクニック)を、押さえておきたい。

いろいろな設計パターンに共通する、その根っこにある設計原則を会得すれば、パターンの理解や実践が、もっと楽に楽しくなる。

設計原則の会得は、ソフトウェア設計の免許皆伝、達人への道であり、ここぞという時の、銀の弾丸になる。

モジュール化

たぶん、これが、いちばん基本の設計原則。

デザインパターンも、アナリシスパターンも、アーキテクチャパターンも、モジュール化原則の適用例になっていると思う。

モジュール化の原則違反の典型例は、一枚岩(モノシリック)で巨大なソフトウェア。
別名、密結合、スパゲティコード。

こういうソフトウェアは、理解しがたく、変更が恐ろしく難しい。
バグがあちこちに隠れていて、副作用が怖く、とてもじゃないが、コードを変更する勇気(度胸?)はない。
「一枚岩」は、頑強なイメージだけど、ソフトウェアとしては、けして良い特性ではない。

ソフトウェア設計でもっとも基本的で、大切な原則は、全体を分割して「モジュール化」すること。
システム全体は、いろいろなモジュールを組み合わせて実現する。
単純に分割するだけでなく、「組み合わせやすい」設計が、「モジュール化」原則のツボになる。

「モジュール化」の概念は、1970年前後に、構造化プログラミングとか、議論されていたころに、ほぼ確立された設計原則。

開発言語、OS、設計・開発手法が、どんなに変遷しても、この原則は変わりようがない。
クラウド化が進もうが、スマートフォンが普及しようが、「モジュール化」は、ソフトウェアの基本設計原則。

というか、ソフトウェアに限らず、建物、船や自動車、電子機器など、モノづくりすべてに共通する設計の基本原則が「モジュール化」。

ソフトウェアもある程度の規模になれば、当然、モジュール分割して、それを組み合わせて全体システムを実現する。

ハードだと、モジュールは、目に見えて、手で触って、確認できる実体。
ハードの設計技術者は、成長の過程で、ある意味、自然に良い設計、扱いやすいもモジュール化を体得していく。

ソフトウェアだと、こういう体感ができない、あるいは理解に個人差が大きい。
だから、モジュール化の会得は難しいかもしれないけど、やはり設計の原則の第一は、「モジュール化」。

モジュール化の価値

機能的にはまったく同じ、ユーザインタフェースも同じで、きちんと動作しているソフトウェアが二つある。

A:モノシリックなソフトウェアシステム(別名:密結合スパゲッティコード)
B:うまくモジュール化されたソフトウェア (別名:高凝集部品の疎結合)

現在、ちゃんと動いている点だけで評価すれば、両方とも、価値は同じ。

でも、変更や拡張の必要がでてきた時に、AとBの価値は、まったく別になる。
Aのソフトウェアは、変更ができないのだから、「変更・拡張」の点で、価値が激減する。

エンタープライズアプリケーションでは「変更容易性」は、ソフトウエア価値の重要ポイント。

ビジネスは生き物だし、時間とともに、ビジネス環境も変化していく。
ビジネスの道具である、エンタープライズアプリケーションは、それに合わせて、容易に変更・発展できることに価値がある。

同じ開発コストをかけたソフトウェアシステムなら、もちろん、モジュール化され、「変更容易性」「発展性」に優れたソフトウェアの方が価値が高いにきまっている。

現在の SI ビジネス(受託開発)の現状は、この「変更容易性」「発展性」が価値として取引されることはないとは思う。
「発展性」なんて、評価できないし、値付けも難しい。

変更が、どだい無理なソフトウェアを押しつけられた保守担当者が、呪いの言葉を吐きながら、格闘している姿で、ソフトウェアの価値の低さが明らかになる。
でも、開発費用は払ってしまっているのだから、今更、その費用の回収は無理。

ビジネス的な動機(対価の裏付け)がなければ、良いモジュール化の設計原則を実践したり、そのためのスキルアップを目指す動機も希薄になるのも当然かもしれない。

でも、ソフトウェア設計でよい仕事をする人は、別に、金銭動機でモジュール化原則を実践しているわけじゃないように思う。

◎設計原則に忠実であったほうが、自分や仲間の仕事が楽になることを経験上知っている。
◎良い設計をした方が、楽しい。仕事にやる気がでる。

両方とも、無理やりそうしているわけではなく、仕事の基本スタイルが設計原則に忠実になっている。

設計原則に忠実で、良い仕事をする技術者をもっと評価できる制度や風土があれば、ソフトウエア技術者の育成には有効だとは思う。

大きな SI 会社や、企業の情報システム部門のしかるべき立場の人には、考えてほしいところではある。

モジュール化はしたけれど

ある程度の規模のソフトウェアになれば、当然、なんらかのモジュール化は行う。
プログラムファイルは一つではないだろうし、パッケージ構成とか、レイヤ化とかは(たぶん)常識。

でも、モジュール化しているはずなのに、変更は難しいし、発展性ゼロというシステムが多いのが現状かもしれない。

別名:泥団子システム

なんか、モジュールっぽいもの(泥団子)をいくつか作って、それを集めて全体を組み立てていく。
泥団子なんで、組み立てていくときに、都合が悪い場所があれば、ちぎったり、くっつけたりが簡単。
力づくで押し付ければ、ぐちゃっとなって、何とか、そこに収まってくれる。

自分たちがすべてのソースコードを変更可能な場合、泥団子方式が、普通の作り方になる。
( Hey ! それが、ソフトウェア開発のいいところだろ?)

泥団子になっちゃうのは、モジュール化や、それに関連する設計原則に違反しまっくているから。
簡単にちぎったり、くっつけたり、力づくで押し込むことができるのは、けして、良いことではないのです。

カプセル化

モジュール化に近い設計原則にカプセル化がある。

要素を集めて、がっちりとした容器に収納すること。

タイムカプセルをイメージしてもらうのが良い。
いろいろな情報をあつめて、堅牢な容器に納める感じ。

●メソッドは、ステートメントの格納容器。
●フィールドとメソッドを集めて、クラスに格納。
●クラスを集めて、パッケージ化。
●パッケージを集めて配置単位( jar )
●配置単位を集めてアプリケーション単位( war )
...

という感じで、ソフトウェアは、カプセル化の原則を実践するための仕組みがたくさん用意されている。

問題は、この仕組みを使っても、良いカプセル化は保障されないこと。

カプセルは、がっちりした容器で、収納したら、カプセルの中身には、外からおいそれと手を出せないのがカプセル化原則の意味。

ところが、メソッド、クラス、パッケージなんて、しょっちゅう変更の対象になる。
変更しないまでも、容器を開いて、中身をいろいろ調べるのが、当たり前。

設計の初期、試行錯誤のフェーズであればともかく、コードをごりごり書くようになり、モジュール間の結合が必要なったフェーズで、カプセルの中身を見たり、いじったりするのが当たり前だと、あっという間に、泥団子システムになってしまう。
たしかに、設計を改善するより、力づくで、ちぎったり、くっつけたり、ぎゅっと押し込んで形を整えたほうが手っ取り早い。

絶対変更するな、容器は開くな、とはいわないけれど、開発が進むにつれて、

●容器の中身を空けない(調べない)
●収容した内容を、おいそれとは、あっちこっちに移動しない

というのがカプセル化の原則ということは意識すべき。

がっちりした容器に格納し、安定するのが、理想形。そこになんとか、近づけようというのが、「カプセル化」原則の意図。

「変更禁止」というアンチパターン

カプセル化原則の誤った適用例が、「コードの変更禁止」。
あるいは、「一度書いたら、もう編集しない」、改善放棄アンチパターン。

いちど、モジュール分割し、クラスやメソッドを書いて、動作確認できたら、原則変更禁止、というスタイル。

これを実践すれば、表面的には、「カプセル化」を実現できる。
中身を開いちゃいけない、いじっちゃいけないのがカプセル化だから。

でも、これ、本末転倒。

カプセル化は、コード変更禁止令で実現するものではなく、その必要性をなくす設計をすべき、という原則。

コードの中身をみたり、クラス内やパッケージ内への追加や変更が頻繁に起きるのは、そこに、設計改善の余地が大きいということ。

クラスやパッケージの役割を単純に、明確にした設計をすれば、そのクラスやパッケージは、安定し、自然に、「カプセル化」された状態に進化していく。

「カプセル化された状態」に近づけようね、というのが「カプセル化」原則を意図なんだ。

もちろん、良い「モジュール」は、かならず、カプセル化されている。

ろくでもない設計を、変更禁止でカプセル化らしく見せようとすると、変更禁止を回避しつつ、変更を実現するためのコードが、不適切な場所に植え付けられ、じわじわと増殖していく。
そうなったら、もう、そのソフトウェアは、変更容易性も発展性も急激に失っていく。
価値の劣化。

情報隠蔽

「カプセル化」原則ととても近い原則がこれ。

「中身を見せない/見ない」というのが、この原則。

これは、関連個所のソースコードを確認することが日常茶飯事の技術者にとって、もっとも会得しにくい設計原則かもしれない。
オープンソースの良いところは、ソースコードを、自分で確認できることにあるのも事実。

ソースが読めない/公開されていない部品は使いにくい、使いたくないという技術者も多いと思う。

でも、中身を安易に見に行くのは、「情報隠蔽」の設計原則違反、ということは意識すべき。

よくできた部品(モジュール)であれば、中身を見る必要はないはず。

中身を見る必要がないように、パッケージ名、クラス名、メソッド名をうまく設計しようね、というのが「情報隠蔽」という設計原則。

モジュールの中身を見ることは勉強になるけど、開発しているときに、使うクラスの中身をしょっちゅう覗きにいくようであれば、「情報隠蔽」がうまくいっていない。
つまり、設計に改善の余地が大きいということ。

公開メソッドの多いクラスとか、public なクラスが多いパッケージは、「情報隠蔽」がうまくいっていない。

クラスの中身やパッケージの中身が透けて見えてくるくらい、公開情報( public 宣言 ) が多い設計は、密結合なコードが増殖する。

ちょっとした変更があちこちに波及するようになる。
それが怖くて、既存のメソッドとか変更できない。

結果として、似たような、ちょっとだけ名前と振舞が違うメソッドをちょっと書いてお茶を濁す。
そのメソッドが、呼び出す側に追加されるのか、呼び出される側に追加されるのか。

どちらにしても、扱いにくい、変更容易性・発展性に欠ける、ソフトウェアの道をまっしぐら。

あと、フィールドをそのまま set/get するメソッドを公開したら、それは、「情報隠蔽」原則に違反している。
それは、フィールドそのものを、public にしているのと同じ。
つまり、悪しき、グローバル変数を作っているということ。
setter/getter に変形しただけで、実体は、グローバル変数。

set/get でフィールドを生で扱うだけのクラスは、役立たずというか、存在意義が疑わしい。

クラスは、本来は、フィールドを完全に隠蔽して、何かフィールドに対して、加工したり、判断した結果を返すメソッドを持つべき。

フィールドの setter は、不変原則(immutable ) 違反でもある。
コンストラクタで、インスタンス生成時に、すべてのフィールドをセットするの原則。

フレームワークとかの都合で、デフォルトのコンストラクタや、フィールドを生で操作する setter/getter が必要になるかもしれない。
でも、それは、本来のクラス設計の原則からは外れている。
設計の腕を磨くには、フィールドが外から見えてしまう設計を、邪悪と感じることがまず第一歩。

HashMap とか、TreeMap とか、内部のデータ構造を公開しているコレクションクラスなんて、設計としては最低だということ。

モジュール化、カプセル化、情報隠蔽

たぶん、どの原則に忠実であっても、同じところにたどり着くはず。
(正解は、ひとつではないかもしれないけど)

それぞれの設計原則は、強調するポイントや、アンチパターンの現れ方が変わるかもしれないけど、「良い設計」としてイメージしている「名前の無い質」は、同じ。

私が「モジュール化」を、もっとも基本的な設計原則と考えるのは、「部品」を組み立てて「全体」を作る、というイメージがもっとも明確だから。

データ、操作、そしてオブジェクト指向

モジュール化は、オブジェクト指向的には、関連する「データ」と「操作」をひとつにまとめて(「カプセル化」して)、かつ、中身を隠蔽することを意味する。
正確には、これは、オブジェクト指向というより、「抽象データ型」の考え方。

「抽象データ型」は、コレクションフレームワークが良い例ですね。

List , Set, Map という情報の表現と操作を、クラスやパッケージにカプセル化している。
もちろん、内部の処理は、可能な限り隠蔽されている。

※ Java では、これは嘘です。
クラス名、コンストラクタ、メソッドに、内部のデータ構造やアルゴリズムのヒントがいろいろ漏れ出している。
性能に関する意思決定をプログラマにまかせているので、そうしているのだろうけど、「抽象データ型」としては、質が低いし、「情報隠蔽」の原則からはずれている。

「抽象」というのは、もともとは、「バイト」や「アドレス」というメモリ構造という物理実体と、if 文と goto 文連発のアルゴリズムの塊を、カプセル化したうえで、可能な限り、情報隠蔽し、「情報の集合」を抽象的に、つまり単純に扱えるようにしたもの。

メモリ上には、データと命令がある。(データと命令しかない)

良く使うデータの塊のパターン、命令の塊のパターンをカプセル化して、情報隠蔽を繰り返してきたのが、言語(とライブラリ、フレームワーク)の発展の歴史ともいえる。

データはデータでモジュール化し、操作は操作でモジュール化する、というアプローチもある。

抽象データ型とか、オブジェクト指向は、そうではなくて、「データと操作」を一つのカプセルに入れる、という発想。

単純なデータを扱うなら、操作だけをモジュール化したほうが再利用はやりやすい。
データの構造に重要な意味がある場合には、データと(その構造に強く関係した)操作を一体にする発想の方が、モジュールとして、利用価値が高くなる。

モジュール化という設計原則を実践するには、

◎強く関連するデータを、クラスやパッケージに集める。
◎そのデータに固有の操作を、そのクラスやパッケージに集めてくる。
◎クラスやパッケージは、可能な限り、公開インタフェースを少なくする。

●パッケージ内の public クラスは一つであるべき。
●その公開クラスの public メソッドも、できるだけ少なくする。

そうやって作ったパッケージが、わかりやすく、使いやすくなっていれば、うまく「モジュール化」できたということ。
そのパッケージがわかりにく、使いにくい、公開クラスやメソッドを増やしたい、というときは、設計改善の余地が大きい。

そのパッケージに集めようとしたデータやメソッドがおそらく、不適切な集め方をしているということ。

パターンとモジュール化

最初に書いたように、どのパターンも「モジュール化」の設計原則の忠実な実践結果。
モジュール化原則違反のパターンは、たぶん、存在しない。
(何をモジュール単位にするか、という考え方の違いはあるだろうけど)

アーキテクチャスタイル、分析パターン、設計パターン、実装パターンを問わず、どれも設計原則の適用結果。
パターンを理解するためには、まず、「モジュール化」という設計原則をおさらいしてみるのが早道。
あるいは、パターンとその適用に格闘しながら、「モジュール化」という設計原則を意識してみる、というのも良いかもしれない。

いずれにしても、単純な設計原則、特に「モジュール化」を理解し、習得し、設計の基本スタイルにすることで、設計の腕は各段に向上するはず。

calendar
      1
2345678
9101112131415
16171819202122
23242526272829
3031     
<< October 2011 >>
システム設計日記を検索
プロフィール
リンク
システム開発日記(実装編)
有限会社 システム設計
twitter @masuda220
selected entries
recent comment
recent trackback
categories
archives
others
mobile
qrcode
powered
無料ブログ作成サービス JUGEM