2017年05月31日

設計における汎用性とは

  更新日:2017年6月1日(木)
初めての方はこちらを「はじめに

間が空きましたが今回は設計における汎用性とはどういう事なのかを述べてみたいと思います。

構造と汎用性


設計における汎用性というのはどういう事なのかを考える為に具体的な例で考えてみたいと思います。
仮にファイルの一覧を名称順で出力するプログラムPを作るとします。
この場合、このプログラムで主に行わなければならないのはファイルの一覧を取得する事とそれを名称順に並べ替える事の二つになります。並べ替えるというのは普通はソートの機能がライブラリー等で提供されていますが、ここでは自前で作る事とします。

この様な場合に設計としては、例えば以下の様ないくつかの種類が考えられます。
A.
53N024_001.jpg
まず最初に、特に機能分化はさせずにそのままファイルの一覧を取得しながら並べ替えも行ってしまうというパターン。
関数として考えるならば一つの関数で処理してしまうという事になります。

B.
53N024_002.jpg
次に、並べ替えに関しては独立した処理として別のブロックにして、メインの処理はファイルの一覧を取得して、並べ替えの処理は部品を呼び出す形にするパターン。
おそらく一番一般的な形じゃないでしょうか。

C.
53N024_003.jpg
最後に、並べ替えについてはBと同様で更にファイルの一覧を取得する部分も独立した処理として別のブロックにして、全体の動作は制御部をまた独立して設けるという形のパターン。

さて、このA、B、Cの三つのパターンを比べて考えてみたいと思います。
53N024_004.jpg

まずAですが、もしこの様に作ったとするとこのコードは今回のこのPというプログラムとしてしか使えません。つまり他のプログラムQでこのプログラムの並べ替えの処理を使いたいと思った時にはソースをコピーして適切に修正するという、いわゆる「流用」する以外には手がありません。従って汎用性はほぼゼロと言えます。

次にBですが、この場合は並べ替えのブロックであるSは別のプログラムから呼び出せる可能性があります。従ってAよりは汎用性が高い可能性があります。

最後にCですが、この場合はBの場合の考察と同様にファイルの一覧を取得するブロックであるFも別のプログラムから呼び出せる可能性があります。従ってBよりもさらに汎用性が高い可能性があります。

この様にA→B→Cと行くにしたがってより汎用性が高くなる可能性があります。

汎用性とは階層


上の例で分かる様に結局汎用性というのは、
どれだけ他のプログラムから呼び出せるか
という事になります。

上の例ではA→B→Cと進むにつれて呼び出せる部分(量)が増えている事になります。また他のプログラムから呼び出せると言う場合には量も重要ですが、より重要な視点があります。それはどの階層かという事であり適用範囲の広さとも言えます。
この視点で差があるAとBを例にとって説明したいと思います。
53N024_005.jpg

Aについては先程説明した様にこのプログラムPでしか使えません。それがBの構造にした場合にはSはPでもQでも使えるのでPやQの固有の処理よりは一段抽象度の高い下の階層に位置する事になります。
つまりAPIの様な位置づけになるという事になります。これが階層的な視点です。
ちなみにCに関してはBと量の違いだけであって階層は同じです。

階層が量よりも重要で適用範囲を表しているというのは以下の様な事です。
53N024_006.jpg

ここでSYというのは何かしらのシステムを表しています。
SY1のシステムの中でプログラムP1とP2は先のPと同様で固有の処理であって汎用性はありません。しかしこのシステムの中でも一段下の階層のL2は先程のSと同様にP1やP2から呼び出せる共通的なプログラムになります。
仮に別のシステムSY2を考えた時にSY1とSY2のどちらからも呼び出して利用できる処理があるとすれば、それは更に一段下の第3階層になってL3の様な位置づけになります。
更に下の第4階層となるともはや他社のブログラムからも呼び出せるものになってライブラリーとして商品になるのかもしれません。
この様に階層が下がるという事はとりもなおさず適用範囲が広がる事であって利用価値がより高くなるという事になります。従ってこれから開発するソフトウェアのどれだけの量をどれだけ下の階層に持って行く事が出来るかが設計における汎用性を高めるという事になります。
ある面ではほぼこれによって設計の質が決まると言っても過言ではありません。

階層は呼び出し関係?


ところでここまで、階層というのは呼び出し関係だけで説明して来ましたが、実際はそれだけで決まるものではありません。
例として最初の例に戻ってBのパターンで考えてみたいと思います。
53N024_002.jpg

このパターンでは並べ替えの処理を独立したブロックSとした事で別のプログムQからも呼び出せる様にしたものでした。ところでこのSは並べ替えの処理を受け持ちさえすればQからも呼び出せるのでしょうか。
答えは「NO」です。
例えばSのI/Fとして並べ替えの対象として「ファイルの一覧」を受け取るものだったらどうでしょう。
53N024_007.jpg

確かにそのI/FでもPにとっては並べ替えの処理を分離した形にはなってSを下に配置した関係にはなります。しかしQにとっては同様にファイルの一覧を並べ替えるなどという偶然が重ならない限りは利用できない事になります。
つまり単純な呼び出し関係ではなくSの仕様こそがどの階層に位置するかという設計的要素を決定する事になります。

勿論この例の様に「ファイル一覧」をI/Fにする様な事は実際は無いと思います。
なぜならソートは知られている機能で皆仕様を知っているからです。
しかし本当に自前で作る場合にはこういった事はいくらでも起きるのです。そして見かけだけ汎用的な設計にしたようなふりをして実際はまるで汎用的ではないという事がいくらでも発生するのです。
たとえばこの例でもファイル一覧は渡さないにしてもI/Fの形式つまりクラスや構造体がPの固有のものだったらならばやはりQからは呼び出せないのです。

実際の取り組み


以上、設計における汎用性とはどのようなものかを説明してきましたがこれを踏まえて実際に取り組む際の考え方をいくつか示したいと思います。

まずあるプロジェクトでソフトウェアを開発しようとする場合には、そもそも全体像として階層を意識して構造を設計して、それぞれの階層のプログラムはライブラリーとして物理的に独立した形にするべきだという事です。つまり作ってる途中で考えてなるべく汎用的にしようとするのではなく、最初からちゃんと考えるべきだという事です。
しかしながらそうは言ってもライブラリーではない個別のプログラム部分を作ってる最中にも「ここはやっぱり共通的な処理にした方がいいな」と思う様な事もあると思います。本来その場合はライブラリー化するべきですが、もはやパワーが捻出できないような場合は設計はちゃんと分離できる形に考えて後でライブラリー化する様な事でも良いと思います。
極端に言えば「極力下の階層に持って行こう」「どうしたらより下の階層に持って行けるか」といった事を意識しながら作るというだけでもいいのです。そういった事を常に意識していればそれだけでも全然違います。

階層の説明をした時に自社内で使うだけのものならば第4階層は目指す必要が無いと思った人がいるかもしれません。「自分の会社ではライブリーの商品化などは無い」と。
しかし第4階層の様なレベルのものならば自社でこれから将来作るソフトウェアが利用できる可能性があるのです。将来どんなソフトウェアを作る事になるかは分からないので、それは他社がどんなソフトウェアで利用するか分からないのとほぼ同義なのです。
なので常になるべく下の階層を目指すべきです。

ただし、より下の階層を目指すという事はより抽象的になるという事です。
以前にも述べた事がありますが、残念ながら抽象的な思考というのは誰でもが簡単に出来る訳ではありません。その為検討する人間にどの程度の能力があるかによってかかる時間は大きく変わってしまいます。
従って設計にかかるであろうパワーと汎用的になり得られるであろう主に将来的なメリットをシビアに天稟にかける必要があります。間違っても無条件に全てを最大限に汎用的なものにすると決めるべきではありません。
この辺の判断は難しい事なので、まずは全員が各々の能力に従って出来る限り下の階層を目指すという意識を持てるようにする事が最初に取り組むべき事かもしれません。

一覧


メニュー

関連


なぜ汎用的にする必要があるのか
なぜログやエラー処理の組み込みは苦労するのか
事例:ブログ投稿システム-出力部構造


記事を広める



posted by 善 at 21:00 | Comment(0) | TrackBack(0) | 設計 | このブログの読者になる | 更新情報をチェックする
スポンサーリンク
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

※ブログオーナーが承認したコメントのみ表示されます。

この記事へのトラックバック