コンストラクタの引数や処理を見るとクラスの設計が良いか悪いかある程度見分けがつきそうだ。
そのクラスがオブジェクトなのか、それとも構造体を拡大解釈しただけの産物なのかははっきりと表れるためだ。
例えば人間を表すPersonクラスがあったとしよう。コンストラクタが以下のように引数を求めている。とはいえPersonという概念がクラス化されていれば充分立派という見方もできるが…
public class Person { public Person(string name, int age, float weight, float height){ } }
検証の前にあらかじめ断っておくと、本クラス定義は特定のアプリケーション機能を特定しないエンティティを想定している。
コンストラクタがこれ一つしか無い場合、ほぼ間違いなくただの構造体だ。もしくは構造体にフィールドが隠蔽されない操作がいくつかついている機能かであろう。
nameは良い。オブジェクトの一意性を確立するために名称は必要だろう。だが、ageはというと、年齢不詳の場合0をわざわざ指定するのだろうか。身長、体重にも同様のことが言える。int, floatという宣言から各値が分からない場合-1やそれ以外の無効な数値を設定するのかもしれない。
また年齢、身長、体重がが設定されないとオブジェクトの一意性が確立出来ないというのも奇妙だ。人間なのだから成長や老化により各パラメータが変化することは十分にあり得る。年齢20歳と21歳でオブジェクトが分かれそれらが別々のインスタンスで表され、尚且つ同一人物であることが追跡できない場合どのように対処したら良いのだろうか。
後半の問いに対する解はアプリケーションごとに異なるだろうが前半の問いは意外に明白だ。nameのみでエンティティの一意性が特定できるかどうかはさておき、c#では以下のように宣言が望ましい。
public class Person { private int age; public int Age { get { return this.age; } set { this.age = value; } } private float weight; public float Weight { get { return this.weight; } set { this.weight= value; } } private float height; public float Height { get { return this.height; } set { this.height= value; } } public Person(string name) { ... } }
呼び出し側は年齢のみ指定する場合以下となる。
public Person Sample() { return new Person("Taro") { Age = 21, } }
人間をPersonとしてクラス化している場合まだいい。右手を上に上げるクラスだとか左足を前に出すクラス等が、Handクラス, Legクラスが存在せず単体で存在する場合さらに悲惨なことになる。
public LiftTheLeftHand { public LiftTheLeftHand(RangeOfMovement shoulder, RangeOfMovement elbow, RangeOfMovement wrist ...) { // この後恐ろしいほど引数を要求する } }
コード例のレベルが低すぎか。
こうなる原因はモノがクラスとして存在せず機能を利用する場面で知識が必要となり慌てて準備を行う必要がある。とはいっても、引数で各種パラメータを要求しているという点では良心的だ。なぜならば、特に引数を必要とせずに、クラス内部で独自にstaticalな領域にアクセスしパラメータを参照している場合があるからだ。そうなると更に悲惨だ。このクラスは依存関係により本当の意味での単体テストが実施できない状態となる。
コンストラクタを設計するときはそのクラスの一意性が成立するようパラメータに注意を払う必要がある。そしてクラス設計に対しても同様の注意を払うべきだ。
オブジェクトデザイン (Object Oriented SELECTION)
- 作者: レベッカ・ワーフスブラック,アラン・マクキーン,株式会社オージス総研藤井拓,辻博靖,井藤晶子,山口雅之,林直樹
- 出版社/メーカー: 翔泳社
- 発売日: 2007/09/13
- メディア: 大型本
- 購入: 3人 クリック: 52回
- この商品を含むブログ (42件) を見る