General-Purpose Modules Are Deeper
- モジュールを汎用にするか用途特化にするか
-
汎用サイドの言い分
-
広範な問題に対応できる
- まだ見ぬ将来の問題にも
- 将来の工数短縮
- ch3. Working Code Isn’t Enough にも沿っているように見える
-
-
用途特化サイドの言い分
- 機能を汎用的に作ったはいいが、一度も使われないかも
-
汎用的すぎると、今現在挙がっている問題にうまく対処できない
- 【補】使い方が面倒だったりする?(認知の負荷)
- 都度リファクタリングして汎用にしていく
- インクリメンタルアプローチに沿っているように見える
Make classes somewhat general-purpose
-
著者の経験上、「やや汎用」がスイートスポット
- 実装には今現在のニーズを反映する
-
インタフェースは汎用にする
- 複数用途に利用できる程度には汎用
- それでいて今現在のニーズにも簡単に利用できる
-
汎用の利点
- 他用途に流用できれば、将来の工数短縮
-
他用途に流用できても、単純になる(意外な利点)
- インタフェースが単純で、実装をよく隠蔽した「深い」モジュールになる
Example: storing text for an editor
- 用途特化モジュールがダメな例
- これは良くない:
class Text...
void backspace(Cursor cursor);
void delete(Cursor cursor);
- UI側で範囲削除が欲しくなったらTextクラスに↓を生やしますか?
class Text...
void deleteSelection(Selection selection)
-
UIに関する知識の漏出
- 「選択」や「バックスペース」がTextに反映されてしまっている
- ので、UIを開発する者はTextクラスも開発しなければならなくなる
- モジュール設計の目的「個々のモジュールを独立して開発できる」に反する
A more general-purpose API
- 汎用インタフェースの例
class Text...
void insert(Position position, String newText);
void delete(Position start, Position end);
- UI側で1文字削除が欲しくなったら:
text.delete(cursor, text.changePosition(cursor, +1));
- UI側でバックスペースが欲しくなったら:
text.delete(text.changePosition(cursor, -1), cursor);
-
用途特化モジュールとの比較
- 呼び出しのコードはいくぶん長くなるが…
-
明瞭である
- UI開発者から見て、どの文字が消えるのか一目瞭然
-
コード量減る
- Textクラスに生えている大量の用途特化メソッドを消すことができる
-
UIの知識が漏出していない
- インタラクティブエディタ以外からも利用できる
Generaity leads to better information hiding
-
知識の隠蔽
- UIについてTextクラスは知る必要がない
-
認知の負荷が低い
-
開発者が知る必要があるのは、少数の単純なメソッドのみ
- 様々な目的に流用できる
-
-
void backspace()
はfalse abstraction- 抽象化とは「重要でない詳細を削ぎ落とす」こと
-
利用側にとって重要な詳細は抽象化・隠蔽してはいけない
- 「どの文字が消えるか」という知識が隠蔽されている
- が、UIは「どの文字が消えるか」知らなければならない
-
「不明瞭」となり、複雑性をもたらす
- 【補】認知の負荷、もしくは「わからないことがわからない」につながる
- ソフトウェア設計上、誰が・何を・いつ知る必要があるかは最重要
Questions to ask yourself
-
行うは難し
- 汎用クラスを実際に作るのは難しい
- 汎用と用途特化との間のよい落とし所を探るために、次のことを自問自答せよ:
What is the simplest interface that will cover all my current needs?
- 機能を保ったままメソッドの数を減らすことができれば、汎用のメソッドを作ったことになる
- メソッドの数を減らすべきなのは、個々のメソッドを単純なままに保てる場合のみ
- 「メソッドを減らすために大量の引数を追加する」というのは、単純化になっていない可能性がある
In how many situations will this method be used?
-
特定の1用途向けのメソッドは危険信号
Text
クラスのbackspace()
メソッドみたいなやつ
- 汎用メソッドにまとめられないか検討する
Is this api easy to use for my current needs?
class Text...
void insertChar(Position position, Char newChar);
void deleteChar(Position position);
- 汎用メソッドの別解
-
「文字列を挿入したい」「範囲削除したい」というニーズには使いづらい
- 利用側にループを強いる
- 効率も悪い
Conclusion
-
汎用インタフェースは用途特化インタフェースより優れた点多数
-
「深いモジュール」になる傾向
- 少数の、深いメソッド
-
クラス間をきれいに分離
- cf. 用途特化は知識の漏出をまねきがち
-
- 「やや汎用」のモジュールを作ることが、システム全体の複雑性を減らす最良の方法