平たく言えば「継承のガイドライン」です。
前提
基本的にバリエーションを作る場合(シルバー会員クラス、ゴールド会員クラスなど)の継承のガイドラインである点です。
例えば、MicrosoftのテクノロジーのTextBoxコントロールがあって、それを継承したxxxTextBox、yyyTextBoxがあるといった一般的な薄く広く処理を共通化するなどの用途には当てはまらないので注意です。
原則
サブクラスは基底クラスと置換可能でなければならない。
基底クラスとサブクラスを入れ替えてもコンパイルエラーだとか、実行時エラーにはならないよねってガイドライン。そもそも、クライアントのコードは呼び出すクラスをサブクラスを意識しなくても使えないといけないということですね。基底クラス(抽象クラス)だけ意識すればクライアントコードの実装できる状態になっていることが望ましい状態です。
例
クライアントコード(formClient)からは、FormBase、MyForm、NewForどれに置き換えても問題なく(実行時エラー、コンパイルエラーにならない)動作します。
基底クラスを継承する時は基底クラスの替わりにそのサブクラスを使用しても何ら不都合は発生しないようにしなければならない。
なぜかこの原則を守る必要があるのか
今後、例えば親クラスを基底クラスにした新しい子クラスができた場合にクライアントコードはそちらも意識しないといけないことになり変化にさらされてしまうことになるため。
クライアントコードは基底クラス(抽象クラス)だけ意識しておけば、子クラスなどは意識しなくても実装することができる状態であることが望ましいです。
クライアント側が覚える知識量を減らすことが可能です。
原則違反の例
サブクラスにしか存在しないメソッドが登場してくる。
上の例で言えば、addTextBoxのようなメソッドがサブクラスのNewFormにだけ登場してくる場合などです。
問題点
この場合の問題点としては、サブクラスの動きを知っておかないとクライアントコードで使うことができなくなってしまう点です。(クライアント側で知識が必要になってしまう)
なので、クライアント側でこのサブクラスを使う場合はというような条件分岐が出てきかねない作りになってしまい保守性が下がります。
実装テクニック
アクセス権を制限する
外部からアクセスできる権限をそもそも以下の二つだけにする。
- 抽象クラス、インターフェース
- ファクトリクラス(サブクラスを生成する手順を記述するクラス)
サブクラスに関しては外側から見れる必要がないのでprivateとかにしておいてそもそもアクセスできないようにします。
抽象クラスは簡略化し、サブクラスに知識を寄せる
例えば、「〜を変換する」などのサブクラス特有の処理が必要になったら、抽象側に持たせるのではなくサブクラス側の実装に寄せるようにします。
知識はできるだけサブクラス側に隠蔽して抽象はただ、「ファクトリクラスを使って生成して抽象化されたメソッドを呼ぶ」ということだけに意識が持てるような設計にします。
この記事へのコメントはありません。