2016-03-30 6 views
1

この質問は、thisのフォローアップです。私は、複数のベース由来のペアを含むクラス階層を定義しようとしています。例として、クラスAnimalとクラスFoodがあるとします。 Animalには、食べ物をパラメータとして食べ物の好みを示す純粋な仮想関数があります。リスコフ置換の原理と複数の階層

class Food 
{ 
    public: 
    virtual void printName() { 
    //...... 
    } 
}; 

class Animal 
{ 
    public: 
    Food *_preferredFood; 
    virtual void setFoodPreference(Food *food)=0; 

}; 

これらの基本クラスだけを扱うコードを記述し、純粋仮想関数を呼び出す必要があります。たとえば、私はZooManagerクラスを持っています。これは、各動物の食物の好みを設定します。

class ZooManager 
{ 
    vector<Aninal*> animals; 
    public: 
    void setAllPreferences(vector<Food *> foods) { 
     assert(animals.size() == foods.size()); 
     for(int i =0;i<animals.size();i++) { 
      animals[i]->setFoodPreference(foods[i]); 
     } 
    } 
}; 

これまでのところとても良いです。今問題は、FoodAnimalには多くの異なる派生クラスがあることです。 Foodは、派生クラスFruitおよびMeatを有し、Animalは、派生クラスCarnivoreおよびHerbivoreを有する。 Herbivoreは、食品の好みとしてFruitのみを受け入れることができ、Carnivoreは、Meatを受け入れることができます。

class Fruit : public Food 
{ 
}; 
class Meat : public Food 
{ 
}; 
class Carnivore: public Animal 
{ 
    public: 
    void setFoodPreference(Food *food) { 
    this->_preferredFood = dynamic_cast<Meat *>(food); 
    } 
}; 
class Herbivore: public Animal 
{ 
    public: 
    void setFoodPreference(Food *food) { 
    this->_preferredFood = dynamic_cast<Fruit *>(food); 
    } 
}; 

私はリスコフの置換原則に違反することなく、このためのクラス階層構造を作成できますか?この質問ではC++を使用していますが、Java固有の回答も歓迎します。

答えて

2

まず、setFoodPreferenceには、のオプションが無効になっている必要があります。 setFoodPreferenceFood*になり、食品の好みを設定する、または失敗する事後条件があります。

ダイナミックキャストはLSPにも失敗する可能性がありますが、タイプインバリアントが不明瞭になるように配置すると、技術的には失敗しません。

通常、dynamic_castは、渡された引数の型とそのプロパティが、引数に特定のプロパティがあるかどうかを判断するのに十分でないことを意味します。

原則として、setFoodPreference(Food*)は、設定が成功するために引数で渡されたプロパティの内容で指定する必要があります。 Food*の動的タイプはFood*プロパティではありません。

ので:LSPはFoodの任意のサブクラスは、すべてFood不変条件を遵守しなければならないと述べています。 Animalについても同様です。インバリアントをあいまいにしたり、メソッドの動作を予測できないようにすることで、LSP違反を回避できます。基本的に「不特定の理由で失敗する」と言っています。これはあまり満足できるものではありません。

さて、あなたは戻って一歩を踏み出すとあなたのFood*の動的タイプがFood*のインタフェースの一部であることを決定することができます。インターフェイスが不規則に広がってしまい、LSPを嘲笑してしまいます。

LSPのポイントは、Food*について、そのサブクラスの種類について考える必要がなくなります。彼らはどのようにFoodとして動作しますか?あなたのコードは、サブクラスの型に密接に結びついているので、LSPのポイントをバイパスします。

これを回避する方法があります。Foodには何種類の食べ物があるかを列挙していて、Meatに動的にキャストしていたのではなく、肉であればFoodに尋ねました。今度はFoodのインタフェースの点でsetFoodPreferenceの動作を指定できます。

+0

私は列挙型のソリューションが大好きではありません。主に、OOプログラミングを悪用するために頻繁に使用されるため、基本的に子クラス情報を基本クラスに入れているからです。私は 'Base'が' CHILD1、CHILD2、CHILD3'などの値を持つ 'Type'の列挙型を持つコードを見てきました。コードの束が' Base * 'を受け入れるように促し、基底型をチェックします。このようなコードは、誰もが多形性のすべての利点を得ることができると考えていることを反映しています。 'dynamic_cast'離れてもそれほど良くはありません。 – AndyG

+0

@AndyG私はそれが素晴らしい*解決策ではないと言いました。上で書いたように、各動物種は、それがどのような種類の食物であるかを気にしています。基本的に、Foodクラスの背後にある抽象化は、* Foodsはかなり無駄な抽象であるため、*リークします。あなたが乾草を扱うのと同じ方法で生の肉を処理することは、非常に疑わしいことです。 – Yakk

+0

合意。私はOPが自分たちをコーナーに構えていると思う。私のコメントは、あなたに同意することを意図したものではなく、なぜOP問題に対する「解決策」が望ましくないのかを強調する。 – AndyG

1

階層を設計するあなたのアプローチは間違っています。 OOクラスは密接に結合されたルールのグループを表し、ルールは関数であり、密接に結合されたものは共有データを意味します。オブジェクト指向のクラスは実世界のオブジェクトを表現するものではありません。人はそれを言うと間違っている。

具体的な食品の種類に応じている場合、あなたはリスコフ置換原則に違反しています。期間。

あなたがここにいるようにLSPに違反しないようにクラスを正しく設計するには、Animalクラスが独自のルールを達成するために使用できるルールをFoodクラスに配置する必要があります。あるいは、食べ物がクラスであるべきかどうかを決める。あなたの例が示していることは、基本的には本当に悪い文字列の比較です。

関連する問題