クラス設計メモ

機能をどうクラスに分けるかについて,C++ を触りだした頃からの意識の変遷のメモ.特に何かの根拠があると言う訳ではなく,私が(特に C++ を触りだした初期の頃に)感じていた事です.

「ある機能を実現する際に何個のクラスを出現させても良いか(使用者に記述を強いても良いか)」と言う事をしばしば考えます.HTTP を例にとって考えてみます.

std::string host("www.example.com");
std::string path("/");
int port = 80;

clx::http session(host, port);
session.get(path);
std::cout << session.body() << std::endl;

これは,CLX C++ Libraries - http のサンプルコードなのですが,HTTP でのやり取りと取得したデータのパース(ステータスコード,ヘッダ,ボディに分割する)を全て http と言う一つのクラスで行っています.しかし,いくつかの事を考えると,このクラスは下記のようにパース部分は別クラスにした方が良いように思います(セッションの管理と取得結果の管理を別クラスにする).

clx::http session(host, port);
clx::http_response result = sesson.get(path);
std::cout << result.body() << std::endl;

では,なぜそうしなかったかと言うと,(どれだけ共感されるか分からないですが)「訳の分からないクラスがたくさん出現すると,それだけで(使うのに)ハードルが上がってしまう」と感じていたからです.今はそうでもないのですが,C++ で書き始めた頃はそう言った事を非常に強く感じていたのを覚えています.

初学者がクラス設計する際にやってしまいがちな事として,「何でもできる万能クラス」を作成してしまうと言うものがあります.これは,欲しい機能をどのように複数のクラスに分割して良いかが分からないなどの理由もありますが,もっと単純な事として,大量の初見のクラスをいきなり目の当たりにすると,心理的抵抗感が高くなってしまうと言うものがあるような気がしました(そう言ったものは使いにくいと感じ,自分が作成する際には単一のクラスに全て詰め込んでしまう).

ただ,これに関しては,その後に面白い事に気付いた事があります.その後に Ruby で同じく http 通信のコードを書く機会があったのですが,そのコードは下記のようなものでした.

session = Net::HTTP.new(host, port)
result = session.get(path)
puts(result.body)

これは,C++ の後者のコードとほぼ同じなのですが,C++ のコードに比べて抵抗感はそれほどでもありませんでした.Ruby で書いている時点では HTTP がどういうプロトコルなのかを理解してた為と言う理由もあるとは思うのですが,http_response と言う型を書かせないだけでも使用者側が感じる心理的抵抗感はだいぶ違うなと感じました.

何故,今さらこのような懐古文を書いたかと言うと,C++0x で auto と言う言語機能が加わります.これを用いると,C++ の後者のコードは以下のように記述できるようになります.

clx::http session(host, port);
auto result = sesson.get(path);
std::cout << result.body() << std::endl;

auto と言うキーワードが定着するまでには,(コンパイラの普及,解説ページの普及など)まだしばらく時間がかかると思いますが,クラスの使用者に型を書かせなくても良くなる事で,ある機能を実装する際のクラスへの分割単位がもう少し小さくなってくるかもなぁと感じています.

「クラスインターフェースは完全かつ最小限に」と言う格言があります.「完全」と言うのはなかなか難しく,今でもどこまでやれば完全なのかと言うのは自信が持てないのですが,「最小限にする」と言うのはもう少し浸透していきそうかなと期待しています.