うまく継承やインターフェースを使いたいのですが、継承やインターフェースをどのように使うべきか、また使うべきか使わないべきなのかの判断に苦しんでいます
例えば以下のような例を考えます
あるサブスクサービスでは、ユーザーが以下のようなプランを結び、契約したプランに応じたサービスを受けられるとします。プランは複数種類契約可能です。
プランA ラーメンプラン
毎週月曜日の昼にラーメンを宅配します
ラーメンはお店の器に入れられて宅配されるので、食べ終わったらその器を返す必要があります
器をとりにきて欲しい場合はオプションで申し込むことができますが、別途お金がかかります
プランB 牛丼プラン
毎週月曜日の昼に牛丼を宅配します
紙の入れ物に入れて宅配されるので器を返す必要はありません
プランC 健康チェックプラン
毎月の第1月曜日に、健康のプロがユーザーと1時間面談する時間を設けてくれます
そこでユーザーは健康に関するいろいろなことを相談できます。
開始時間は設定することができます。
見ての通り、同じプランという括りであってもAとBは器回収の有無という違いがありますし、Cに関しては全くの別物という感じがします。
しかしこれを全部同じようにプランという括りで見たいこともあるはずです。例えば、あるユーザーがどんなプランを契約しているのか、というのを考えるときにUserオブジェクトがPlanオブジェクトの集まりを持っていて、それぞれ同じように参照できると便利そうです。
このアイディアを以下のような感じでコードで書きました
PHP
1abstract class Plan{ 2 public string $planName; 3 4 public function planName(){ 5 return $this->planName; 6 } 7 8 abstract public function totalPrice(); 9} 10 11 12class ramenPlan extends Plan{ 13 public string $planName = 'ラーメンプラン'; 14 public int $number; 15 public bool $collectFlg;//器回収フラグ 16 17 public function __construct($number,$cllectFlg=false){ 18 //プロパティに代入 19 } 20 21 public funtion totalPrice(){ 22 //計算 23 } 24} 25 26class beafBowlPlan extends Plan{ 27 public string $planName = '牛丼プラン'; 28 public int $number; 29 30 public function __construct($number){ 31 //プロパティに代入 32 } 33 34 public funtion totalPrice(){ 35 //計算 36 } 37} 38 39class healthConsultationPlan extends Plan{ 40 public string $planName = '健康チェックプラン'; 41 public string $startTime;// 開始時間 42 43 public function __construct($startTime){ 44 //プロパティに代入 45 } 46 47 public funtion totalPrice(){ 48 //計算 49 } 50 51 public function nextSchedule(){ 52 // 次回は何日の何時から何時までかを返す 53 } 54} 55 56 class user { 57 public string $name; 58 public array $plans; 59 60 public function __construct($name,$plans){ 61 //プロパティに代入 62 } 63 64 public function nextHealthConsultationSchedule(){ 65 // plansの中を調べてhealthConsultationPlanがあればnextScheduleメソッドを使用 66 // なければnullを返す 67 } 68 69 }
自分がこれを使いにくいと思っている点があります。
1つめは、どのPlan子クラスも持っているプロパティが異なり、またそれに伴ってコンストラクタが異なるという点です。
自分がネットで見る例だとほとんどが親クラスと子クラスでは持っているプロパティは同じだし、せいぜいが振る舞いの結果が異なるくらいです。
しかし自分の例では違います。これでは同じようにPlanクラスとして各Planを作成しようと思っていても、結局これらのPlanクラスを使用する側(例えばDatabaseからデータを抽出して各Planを作成するようなクラス)ではramen,beefBowl,healthConsultationの何を作るのかを何らかの方法を用いて判断しないといけません。なぜならどのPlanクラスを作成するかによって、コンストラクタに渡すものが異なるからです。
2つめに、プランCが、他のクラスが持っていないnextScheduleメソッドを持っている問題です。
これはUserのnextHealthConsultationScheduleメソッドで使用されます。ただプランCを契約していないUserにとって、nextHealthConsultationScheduleメソッドは全くいらないメソッドです。プランCを契約していないUserがこのメソッドを使用できるようなクラスの作り方でいいのでしょうか。何だか気持ち悪く感じます。
このような問題を考えるとき、どのようにクラスを設計すれば上記のような問題点を解決した上で、なおかつ冒頭で述べたように同じようにプランという括りで見ることができるでしょうか。
よければご意見お聞かせいただければと思います。
