業務では「少し違うけど似たような処理」を実装することが求められるシーンは多いと思います。このような処理を実装する場合は継承を使うのは基本NGになります。以下の記事でも詳しく解説しています。
継承以外にも似たような処理を作る解決策は色々あります。
対策案1:別クラスを作る。
純粋にAとBで別クラスを作る方法です。プログラミングやオブジェクト指向の習いたての頃は「似ている処理は共通化しないといけないのかな」と思いがちになるのですが一概にそういうわけではないのです。
メリット
- 継承関係もない。
- リスコフの置換原則にもそもそも継承がないので違反しない。
- 可読性的にも問題ない。
デメリット
重複コードができてしまう。
ビジネスロジックが重複してしまう場合があります。
対策案2:持つ(集約する)
BがAの機能が欲しい場合に「BがAをクラスのメンバとして持つ」という考え方です。
Bのメソッドの中でAの処理を行うので外部(クライアント側)に知識が漏れません。
実装方法
コンストラクタで別クラスを受けてプライベート変数(_を先頭につけた変数)などに外部クラスを代入します。
デメリット
「一部分の処理だけが欲しい」のか、「全ての処理が欲しい」のかがわからなくなってしまう点です。
集約を使う上でのガイドライン
実際に使っているのは一部だが、「別クラスの概念自体」が欲しい場合は使っても良い
例えば、「ファイルクラス」と「ファイル監視クラス」があった場合に「ファイル監視クラス」ではファイルクラスの一部しかメンバを使わない場合であっても「ファイルという概念自体」を監視しているのでこれはファイルクラス自体をメンバとして持たせてしまっても問題ないケースになります。
逆に、一部のプロパティやメソッドが欲しいだけで概念自体はいらないと言った場合は集約を使うのはNGになります。
対策案3:共通化する(「昔:構造化プログラミング時代」からある考え方)
単に共通する処理をstaticとして外出しする考え方です。
問題点
値とロジックがバラバラになり知識が分散しやすくなる(非カプセル)
クライアント(使い側)により詳細な知識の(引数)が必要になる。クラスの場合だったらnewするときにコンストラクタに引数を渡すというクライアント側への知識は必要ですが、「共通化」ほどは知識が必要にはなってきません。(時と場合によるかもしれませんが 笑)
ガイドライン
ただ、「共通化」が全てにおいて悪になるというわけではないです。
値に対するロジックではなく、独立したロジックになっているか。
単純に「文字列のスペース区切りをカンマ区切りにしたい」などのようなビジネスロジックが関係ないロジックを記述するケースなど。
値とロジックがごっちゃになっているケースなどは避けましょう。
対策案4:最小カプセル化
例えば、DDDの「バリューオブジェクト」のように個々のメンバに対してもクラスを作ってそれを参照する考え方です。
この記事へのコメントはありません。