2015-10-21 32 views
0

我想要拿出一個面向對象的設計,並有難以滿足Liskov替代原則。下面是一個說明性的例子:Liskov替代原則與多繼承heirachies

class Food 
{ 
    public: 
    virtual void printName() { 
    //...... 
    } 
}; 
class Fruit : public Food 
{ 
}; 
class Meat : public Food 
{ 
}; 
class Animal 
{ 
    public: 
    Food *_preferredFood; 
    virtual void setFoodPreference(Food *food)=0; 

}; 
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); 
    } 
}; 

我怎麼能執行以下操作:

  1. 動物的每個子類都應該允許設置食物偏好,而不會破壞LSP
  2. 食物偏愛每個派生類動物是食物的一個子類

前面的例子,如果有人延伸Animal創建MarineMammal,foo d偏好可能是Fish(他們將通過延伸Food創建)。

+0

出於興趣,您正在尋找的實際多態行爲是什麼?即客戶端代碼將針對基類調用什麼確切的方法?我問這是因爲我認爲你冒險解決錯誤的問題。動物可能會乍看起來像繼承問題,但它更可能是現實中的一個特徵問題。 –

+0

你說得對,我不是在尋找多態行爲。我真正想要的是任何擴展「Animal」的人來定義正確的「setFoodPreference」函數。當然,我可以爭辯說我想調用''setFoodPreference''。 – SPMP

回答

1

Carnivore::setFoodPreference只接受MeatHerbivore::setFoodPreference只接受Fruit,那麼他們不符合同一合同。這意味着他們實際上不是相同的方法。當你調用這個方法時,你必須知道你是在與食肉動物還是草食動物打交道,以避免錯誤的類型。當你忘記檢查這個時,你可能會冒險創建一個在運行時出現鑄造錯誤形式的錯誤。

解決方法是分離這兩種方法。

我建議您從公共界面刪除setFoodPreference,而是直接將方法Carnivore::setMeatPreference(Meat *meat)Herbivore::setFruitPreference(Fruit *fruit)添加到子類。這樣,任何設定食物偏好的代碼都必須知道它處理的是什麼樣的動物和食物類型,所以你不能再編寫試圖設置不兼容食物類型的代碼。

在內部,兩種方法都可以從公共基類設置protected Food *_preferredFood。或者更好,請致電protected void setPreferredFood(Food *food)這是一個private Food* _preferredFood的二傳手。該變量絕對不應公開以確保正確封裝。

+1

我認爲我應該保持_preferredFood私人,並setFoodPreference保護虛擬。因爲我需要強制派生類允許設置首選項。 – SPMP

+0

@ user2308211爲什麼「虛擬」?當任何子類僅通過它們自己的方法調用該方法時,可以在其中放置任何特定於該子類的代碼。 – Philipp

+0

我應該說,純粹是虛擬的。確保他們實施這些方法。雖然它不是一個完整的解決方案,但有人可以從Animal中派生出來,並只實現setFoodPreference。 – SPMP