1

親クラスProductと 2 つの子クラスがあります:ToothbrushChainsaw. 以下に示すように設定されています。

親クラスは次のとおりです。

class Product {
    protected $productid;
    protected $type;

    public function __construct( $productid ) {
        $this->productid = $productid;
        // Performs a lookup in the database and then populates the $type property
    }
}

..そしてここに子供たちがいます:

class Toothbrush extends Product {
    public function getPrice() {
        return 5; // returning an integer for simplicity; there's a calculation going on here
    }
}

class Chainsaw extends Product {
    public function getPrice() {
        return 1000; // in USD
    }
}

のリストを繰り返し処理し、アイテムが であるかes$productidであるかに関係なく、アイテムの対応する価格を取得したいと考えています。chainsawtoothbrush

問題(それか?)

今、親クラスは機能を実装するために子クラスに依存すべきではないということを何度も聞いてきました(はい、この質問を他の多くの質問と一緒に読んでいます)。

これが、現在使用しているソリューション (以下) が最適ではないと考えるようになった理由です。

class Product {
...
    public function getPrice() {
        switch($this->type) {
            case 'toothbrush':
                $theproduct=new Toothbrush($this->productid);
                return $theproduct->getPrice();
                break;
            case 'chainsaw':
                $theproduct=new Chainsaw($this->productid);
                return $theproduct->getPrice();
                break;
            }
        }
    }

ここで何かが手抜きされていることは明らかです (30 種類の製品を入手したらどうなるかを考えると身震いします)。抽象化、インターフェイス、および継承について読んだことがありますが、このシナリオでどれが機能するかわかりません。

ありがとうございました!

編集

たくさんの答えを見ていますが、まだそれを釘付けにしたものはありません. 要点は次のとおりです 。productid しかない場合、子メソッドを呼び出すにはどうすればよいですか? (上記のシナリオでは、Productクラスはコンストラクターでデータベースから型を取得し、$typeそれに応じてプロパティを設定します。

4

3 に答える 3

2

親クラスが機能を実装するために子クラスに依存するべきではないということを何度も聞いています。

その通り。そして、親で抽象メソッドとして定義され、子でオーバーライドされている限り、switchへの呼び出しはそうではありませんが、あなたはそのような望ましくない依存関係になります。getPriceその場合、親クラスは具体的な子クラスを知る必要はなく、それらのメソッドを呼び出すことができます。それが奇妙に聞こえる場合は、ポリモーフィズムについて読んでください。OOPを理解するには、この概念を理解することが重要です。

しかし、あなたの問題はさらに深くなります。

主なポイントは次のとおりです。productidしかない場合、子メソッドを呼び出すにはどうすればよいですか?(上記のシナリオでは、Productクラスはコンストラクターのデータベースから型を取得し、それに応じて$typeプロパティにデータを入力します。

明らかに、チェーンソーや歯ブラシのインスタンスを作成することはありません。new Product「今、あなたはチェーンソーです」と言って製品を作成することはできません。オブジェクトの実際のタイプは不変です。あなたは、その価格にアクセスするためだけにチェーンソーであるはずの製品の中に新しいチェーンソーを作成することでそれを回避しようとしました。これはひどく間違っており、あなたはすでにそれを認識していると思います。

これが、コメントでファクトリパターンが提案された理由です。ファクトリは、オブジェクトをインスタンス化し、パラメータに基づいて使用するサブタイプを決定するクラスです。また、このようなswitchステートメントの有効な場所でもあります。

例:

class ProductFactory
{
    public function makeProduct($id)
    {
        $record = perform_your_database_lookup_here();

        switch ($record['type']) {
            case 'toothbrush':
                return new Toothbrush($id, $record);
            case 'chainsaw':
                return new Chainsaw($id, $record);
        }
    }
}

$factory = new ProductFactory();
$product = $factory->makeProduct(123);
echo $product->getPrice();

簡単にするために、データベースルックアップをファクトリに配置しました。より良い解決策は、両方のクラスから完全に分離することです。たとえばProductTableGateway、productsテーブルに関連するすべてのデータベースクエリを担当するクラスなどです。その場合、工場は結果のみを受け取ります。

ちなみに、最終的にはこれらのサブクラスを削除することもお勧めします。本格的なオンラインショップでは、製品タイプごとにクラスがハードコーディングされていません。代わりに、さまざまな属性セットが動的に作成され、さまざまな価格計算が他のクラスに委任されます。しかし、これは高度なトピックであり、今では行き過ぎです。

于 2013-03-19T09:42:30.173 に答える
0

親が子供に依存するべきではないことは正しいので、親でも定義します。

class Product {
    protected $productid;

    public function getPrice() {
        return NULL; // or 0 or whatever default you want
    }
}

class Toothbrush extends Product {
    public function getPrice() {
        return 5; // in USD
    }
}

class Chainsaw extends Product {
    public function getPrice() {
        return 1000; // in USD
    }
}

class Fake extends Product {
}

$f = new Fake();
var_dump($f->getPrice());

これで、子が getPrice() メソッドを定義するかどうかに関係なく、コードは常に機能します。

しかし、おそらく次のようなものを使用する方が良いでしょう

class Product {
    protected $productid;
    protected $price;
    protected $type;

    public function __construct($id, $price, $type) {
      $this->productid = $id;
      $this->price = $price;
      $this->type = $type;
    }

    public function getPrice() {
        return $this->price;
    }
}

$tooth = new Product(1, 5, 'Toothbrush');
$chain = new Product(2, 1000, 'Chainsaw');
于 2013-03-19T08:37:28.053 に答える
0

http://www.php.net/manual/en/language.oop5.abstract.phpを読むことができます。また、子の価格を宣言する代わりに、親で (それを取得するメソッドと共に) 実行し、必要に応じてそのメソッドを子でオーバーライドすることもできます。

于 2013-03-19T08:37:52.807 に答える