質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

インターフェース

インターフェイスという用語はハードウェア・ソフトウェアの両方に使うことができます。 一般的に、インターフェイスは内部処理の詳細を見せないように設定されます。オブジェクト指向プログラミングにおいて、インターフェイスはabstractクラスとして定義されます。

オブジェクト

オブジェクト指向において、データとメソッドの集合をオブジェクト(Object)と呼びます。

オブジェクト指向

オブジェクト指向プログラミング(Object-oriented programming;OOP)は「オブジェクト」を使用するプログラミングの概念です。オブジェクト指向プログラムは、カプセル化(情報隠蔽)とポリモーフィズム(多態性)で構成されています。

継承

継承(インヘリタンス)はオブジェクト指向プログラミングに存在するシステムです。継承はオブジェクトが各自定義する必要をなくし、継承元のオブジェクトで定義されている内容を引き継ぎます。

Q&A

1回答

587閲覧

クラスの設計における継承やインターフェースの使い方(例あり)

nuu

総合スコア0

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

インターフェース

インターフェイスという用語はハードウェア・ソフトウェアの両方に使うことができます。 一般的に、インターフェイスは内部処理の詳細を見せないように設定されます。オブジェクト指向プログラミングにおいて、インターフェイスはabstractクラスとして定義されます。

オブジェクト

オブジェクト指向において、データとメソッドの集合をオブジェクト(Object)と呼びます。

オブジェクト指向

オブジェクト指向プログラミング(Object-oriented programming;OOP)は「オブジェクト」を使用するプログラミングの概念です。オブジェクト指向プログラムは、カプセル化(情報隠蔽)とポリモーフィズム(多態性)で構成されています。

継承

継承(インヘリタンス)はオブジェクト指向プログラミングに存在するシステムです。継承はオブジェクトが各自定義する必要をなくし、継承元のオブジェクトで定義されている内容を引き継ぎます。

0グッド

0クリップ

投稿2022/01/27 03:30

編集2022/01/27 04:48

うまく継承やインターフェースを使いたいのですが、継承やインターフェースをどのように使うべきか、また使うべきか使わないべきなのかの判断に苦しんでいます

例えば以下のような例を考えます
あるサブスクサービスでは、ユーザーが以下のようなプランを結び、契約したプランに応じたサービスを受けられるとします。プランは複数種類契約可能です。

プラン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がこのメソッドを使用できるようなクラスの作り方でいいのでしょうか。何だか気持ち悪く感じます。

このような問題を考えるとき、どのようにクラスを設計すれば上記のような問題点を解決した上で、なおかつ冒頭で述べたように同じようにプランという括りで見ることができるでしょうか。
よければご意見お聞かせいただければと思います。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

m.ts10806

2022/01/27 03:39

言語によって方言や癖、仕様のちがいがあるのでPHPを例題にするのでしたら質問タグにも「PHP」があったほうが良いと思います。
Zuishin

2022/01/27 03:39

プラン一つ一つにクラスを作るべきではなく、委譲を使うべきケースだと思います。
nuu

2022/01/27 05:33 編集

m.ts10806様 ご指摘ありがとうございます!PHPタグを追加しました。 Zuishin様 回答ありがとうございます!例えばどのような形で作るとよいでしょうか...
Zuishin

2022/01/27 04:54

器を返す、商品を宅配する、面談すると言った各サービス・条件の個々をクラスにし、プランは名称とそれらの条件の集合を保持し、金額を算定するクラスにします。
nuu

2022/01/27 04:59

Zuishin様 ありがとうございます! >>器を返す、商品を宅配する、面談すると言った各サービス・条件の個々をクラスにし これらをクラスにすることは思いつきませんでした、考えてみようと思います!
guest

回答1

0

オブジェクト指向設計を学ぶといいですよ。SOLID原則, クリーンアーキテクチャ, デザインパターン など。
あと、ドメイン駆動設計 (DDD: Domain-Driven Design) も有効かと思います。

1つめは、どのPlan子クラスも持っているプロパティが異なり、またそれに伴ってコンストラクタが異なるという点です。
これでは同じようにPlanクラスとして各Planを作成しようと思っていても、結局これらのPlanクラスを使用する側(例えばDatabaseからデータを抽出して各Planを作成するようなクラス)ではramen,beefBowl,healthConsultationの何を作るのかを何らかの方法を用いて判断しないといけません。なぜならどのPlanクラスを作成するかによって、コンストラクタに渡すものが異なるからです。

Factoryパターンの出番ですね。

2つめに、プランCが、他のクラスが持っていないnextScheduleメソッドを持っている問題です。
これはUserのnextHealthConsultationScheduleメソッドで使用されます。ただプランCを契約していないUserにとって、nextHealthConsultationScheduleメソッドは全くいらないメソッドです。プランCを契約していないUserがこのメソッドを使用できるようなクラスの作り方でいいのでしょうか。何だか気持ち悪く感じます。

持っているか持っていないかに左右されないメソッドを作るといいです。
どのプランにもオプションがあり、オプション処理の一環として内部からnextScheduleを呼び出せばいいと思います。

投稿2022/01/27 05:51

編集2022/01/27 06:25
shiracamus

総合スコア5406

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

nuu

2022/01/27 06:28

回答ありがとうございます! ファクトリーパターンは考えていないのですが、こんな感じでしょうか? といってもかなり汚いし使いにくそうなので、多分全然できてないでしょうが、ここおかしいとかあればご指摘いただければ... <?php interface Plan { public function planName(); public function totalPrice(); public function option(); } class ramenPlan implements Plan { public string $planName = 'ラーメンプラン'; public int $number; public bool $collectFlg; //器回収フラグ public function __construct($number, $cllectFlg = false) { //プロパティに代入 } public function planName(){ return $this->planName; } public function totalPrice(){ //計算 } public function option(){ return ['器回収'=>$this->collectFlg]; } } class beafBowlPlan implements Plan { public string $planName = '牛丼プラン'; public int $number; public function __construct($number) { //プロパティに代入 } public function planName(){ return $this->planName; } public function totalPrice(){ //計算 } public function option(){ return []; } } class healthConsultationPlan implements Plan { public string $planName = '健康チェックプラン'; public string $startTime; // 開始時間 public function __construct($startTime) { //プロパティに代入 } public function planName(){ return $this->planName; } public function totalPrice(){ //計算 } public function option(){ return ['次の時間'=>$this->nextSchedule()]; } public function nextSchedule() { // 次回は何日の何時から何時までかを返す } } また、ファクトリーパターンを使う際はファクトリーパターン用のクラスなりメソッドなりに どのプランか、ラーメンの個数、器回収フラグ、牛丼の個数、健康チェック日時 などを全部渡して、ファクトリーパターンを行うクラス内で判断してクラスを作成するのでしょうか? ファクトリーパターンも結局作成対象のコンストラクタが同じ例しか見たことがなく...
shiracamus

2022/01/27 08:29 編集

メニュー選択画面でメニューを選び、どのオプションを選ぶか決まっていくのではありませんか? Optionクラスを作って、料金や選択情報を持たせるのがいいかと思います。オプション情報の変更はOptionインスタンスのコピーや入れ替えで対応することもできます。 メニューの種類や使い方はドメイン側が知っていて、生成方法はメニュー側が知っているので、生成と利用を分けて考えられるかと思います。
nuu

2022/01/27 08:27

ありがとうございます! 勉強させていただきます!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問