修行の場

知人が言っていました。これは修行だと。

Code Complete ch.6: クラス設計時に意識するべきこと

背景

クラスを設計する上で最も重要なのは

  • クラスがうまく何かを抽象化していること
  • クラスが何かをカプセル化していること

です。6章では、抽象化とカプセル化をうまく実行するためのこまごまとしたテクニックがたくさん紹介されています。 テクニックは本を読んでいただくとして、最も重要なことだけさらっとまとめます。

抽象化

ソフトウェアを開発するときは、 何かを抽象化し、その抽象化したものをクラスとして表現します。 (もちろんプログラミング言語によっては他の手段を使うことになります) クラスとして表現した際に、クラスを見て設計の良さを判断するための方法が書かれています。

  • クラスの中心的な目的はあるか?名前から明らかか?
    • 例えば、部屋掃除ロボットを考えたときに、RoomCleaningRobotなら、掃除コマンドを実行できるクラスであると明らかです。
    • また、目的(責務)も"A robot which clean the room"と明らかです。
  • クラスのメソッドの抽象度が揃っているか?クラスの使い方は明らかか?
    • 例えば、ロボットをアプリから見た粒度で抽象化したいと思います。RoomCleaningRobotクラスのメソッドにorganize_roomメソッドとmove_forwardメソッドが混じっていたら抽象度はそろっていません。
  • クラスの実装を意識せずに使えるか?クラスをブラックボックスとして扱えるか?
    • organize_room, clean_roomなら中身を意識せずにメソッドを呼び出して待てば良いです。
    • ただ、roombaのように挙動が完璧でないものはある程度挙動の傾向を意識する必要があったりしますし、 解く問題が難しくなっていくに従い完全にクラスをブラックボックスとして扱うことはできなくなっているような気がします。
    • ただ、外部的な性能だけ知っていれば良いと言う意味では、ブラックボックスとして扱えるとも言えるでしょう。
  • クラスの内部データを扱わずにすむか?
    • ベクトルクラスや行列クラスは一見内部データにアクセスしているように見えますが、必要最低限の露出ですし、行列やベクトルの概念として、要素にアクセスできるのは自然なので良いはずです。
    • 概念としてその要素にアクセスできるべきかどうかがポイントだと思います。
    • つまり、不用意に内部データを扱わなくて済むこと。だと思います。
    • 内部データ != メンバ変数
  • 関係ない情報がクラスに含まれていないか?
  • クラスを更にパーツクラスにできる限り分割できているか?
    • クラスの肥大化を防ぐのに有効に思える

encupselation

  • メンバに対するアクセスは最小か?メンバの露出は最小限か?
    • 内部データを扱わずに済む。をクラス目線で言い換えただけのもの
    • どちらの視点も持っておきたい
  • クラスは何かを隠しているか?アルゴリズム、インターフェースなど。設計に関する決定
    • 例えば、データを格納するだけのものは何も隠蔽していない。
  • loosely coupledか?他のクラスと独立しているか?
  • 使用者を意識せずに作らているか?直接の利用者のみでなく、継承するクラスも含め

ContainmentとInheritence

  • is-a関係でない場合はcontainmentを使う。つまり、Liskov Substitution Principleを満たしているか?
    • 親クラスの機能を使っているのみなら、徐々にインターフェースと子クラスの機能がずれていくはず。
    • インターフェースを使うことが継承の必須条件
  • ベースクラスのデータメンバはprotectedよりもprivateになっているか?
    • こうしないと、継承したクラスのみから中途半端に扱えるデータが出てくる。
    • 継承するクラスが増えるに従い、protectedメンバに関する変更が困難になる。
  • inheritence treeは十分に浅いか?
    • 深すぎるとleafに近いクラスの機能把握が難しくなる。
    • 深いとinheritence treeの理解が難しくなる。