[英]avoiding RTTI in OO design
我最近在某個論壇上看到了一個OO設計問題,並開始考慮使用RTTI。 然而,這一定是糟糕的設計,但我無法想到另一種選擇。 這是一個簡單的問題:
使用OO概念為以下場景創建C ++程序 -
我的名叫巴迪的狗住在后院。 晚上,當他看到一只貓或一只來看望的松鼠時,他會吠叫。 如果他看到一只青蛙,並且他餓了,他就會吃掉它。 如果他看到一只青蛙並且他不餓,他就會玩它。 如果他已經吃過兩只青蛙,並且仍然感到飢餓,他會放手。 如果他看到一只土狼,他會求助。 有時他的朋友Spot停了下來,他們互相追逐。 如果他看到任何其他動物,他只是看着它。 我希望你會有一個動物類,一只貓,狗,松鼠,土狼類繼承自動物類。
我開始考慮在dog類中使用一個see()方法,該方法接受一個Animal參數然后檢查對象的實際類型(青蛙,貓等)並采取所需的操作 - 根據實際類型進行游戲,追逐等。 然而,這將需要RTTI,這必須是糟糕的設計。 任何人都可以建議一個更好的設計,這將避免RTTI,並指出我的想法中的錯誤?
根據您想要強調的內容,使用“OO概念”來滿足此問題的方法有很多種。
這是我能提出的最簡單的解決方案:
class Animal {
public:
virtual void seenBy(Buddy&) = 0;
};
class Buddy {
public:
void see(Cat&) { /* ... */ }
void see(Squirrel&) { /* ... */ }
// ...
};
class Cat : public Animal {
public:
virtual seenBy(Buddy& b) { b.see(*this); }
};
class Squirrel : public Animal {
public:
virtual seenBy(Buddy& b) { b.see(*this); }
};
// classes for Frog, Coyote, Spot...
如果你需要多種“感知”動物,那么直接制作一個虛擬包裝器就可以see
(產生一種雙重調度形式):
// On a parent class
virtual void see(Animal&) = 0;
// On Buddy
virtual void see(Animal& a) { a.seenBy(*this); }
以上要求Animal
類對Buddy
類有所了解。 如果你不喜歡你的方法是被動動詞並且想要將Animal
與Buddy
分離,你可以使用訪問者模式:
class Animal {
public:
virtual void visit(Visitor&) = 0;
};
class Cat : public Animal {
public:
virtual void visit(Visitor& v) { v.visit(*this); }
};
class Squirrel : public Animal {
public:
virtual void visit(Visitor& v) { v.visit(*this); }
};
// classes for Frog, Coyote, Spot...
class Visitor {
public:
virtual void visit(Cat&) = 0;
virtual void visit(Squirrel&) = 0;
// ...
};
class BuddyVision : public Visitor {
public:
virtual void visit(Cat&) { /* ... */ }
virtual void visit(Squirrel&) { /* ... */ }
// ...
};
class Buddy {
public:
void see(Animal& a) {
BuddyVision visitor;
a.visit(visitor);
}
};
第二種機制可以用於Buddy看動物以外的目的(可能是看到Buddy的動物)。 然而,它更復雜。
請注意,OO絕對不是解決此問題的唯一方法。 存在對於這個問題可能更實用的其他解決方案,例如存儲導致Buddy吠叫,吃飯,玩耍等的各種動物的屬性。這另外將Buddy
類與Animal
類分離(即使訪問者模式需要Buddy可以感知到的所有內容的詳盡列表。
該設計特別要求識別某些實體,以便對它們執行某些操作。 因為某些操作與某些實體的關系沒有押韻或理由(即:它都是任意的),你所看到的是基於類型的調度或基於屬性的調度。 我會選擇后者。
為每個實體提供一組屬性。 因此,狗會根據這些特性做出反應。 貓和松鼠會有財產,“狗應該咆哮我。” 當狗遇到具有這種屬性的實體時,它將執行適當的操作。
在這種情況下,實體只不過是其屬性的總和以及基於遇到具有各種屬性的其他實體的行為。 該實體也可能有一些與之相關的州。 不會有特定的狗或貓類 。 只有一個具有類似貓的屬性和行為的實體,以及一個具有類似狗的屬性和行為的實體。
提示:使用虛擬函數(在目標動物上)而不是RTTI。
大多數情況下,您可以通過消息傳遞替換RTTI。
有點
Id id = object->send(WHO_ARE_YOU);
switch(id)
{
case ID_FROG: ...; break;
case ID_CAT: ...; break;
}
消息傳遞原則上比RTTI更靈活:
other_object->send(IS_SCARRY_OF, this);
因為它允許設計目前未知的關系。 明天你的狗會看到在其他DLL中定義的racoon然后在Pascal中。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.