分享一個編程設(shè)計小技巧(沒有兩三年工作經(jīng)驗估計看不懂)
直奔主題。
假設(shè)有這樣的一個需求,要給Animal類添加一個方法,如果一種動物可以吃掉另一種動物,則返回true,否則返回false。
接口可能會是下面這樣:
struct?Animal?{????virtual?bool?eat(const?Animal&?animal)?const =?0;};
那怎么實現(xiàn)呢?肯定是多態(tài),估計你能想到的第一種方法就是這樣:
基類Animal的每個派生類都實現(xiàn)eat方法,然后根據(jù)參數(shù)運行時類型返回適當?shù)闹怠?/span>
如何獲取參數(shù)類型呢?需要使用typeid。
代碼如下:
bool?Dog::eat(const?Animal&?animal)?const {????if?(typeid(animal)?==?typeid(Cat))?{????????return?true;????}?else?if?(typeid(animal)?==?typeid(Lion))?{????????return?false;????}????return?false;}bool?Lion::eat(const?Animal&?animal)?const {?return?true;?}bool?Cat::eat(const?Animal&?animal)?const {?return?false;?}
這種方法可以滿足需求,但也只適用于類的數(shù)量比較少的情況。如果類的數(shù)量太多,每個重寫的方法實現(xiàn)體里面都會有特別多的if-else。關(guān)于if-else的弊端我之前介紹過,可以看看這里:《少寫點if-else吧,它的效率有多低你知道嗎?》
這里還有些其他的問題:
隨著類數(shù)量的增加,這種代碼會變得很亂,不容易維護。
如果添加了新類型,這種方式不會強制要求派生類考慮新類型,假如新添加了Bear類,如果在某一個派生類里忘記處理Bear類,結(jié)果就可能有誤。
typeid這種東西很雞肋,就不像個純粹的面向?qū)ο蠼Y(jié)構(gòu)。
那是不是可以這樣做?
struct?Animal?{????virtual?bool?eat(const?Dog&)?const =?0;????virtual?bool?eat(const?Cat&)?const =?0;????virtual?bool?eat(const?Lion&)?const =?0;};
然后每個派生類重寫相應(yīng)的方法,這種方法貌似可行,但是只有單方向多態(tài),真正使用起來會有些不方便。而此方法的使用一定要這樣:
animal.eat(cat);animal.eat(dog);animal.eat(lion);
真正的雙方向多態(tài)使用方式應(yīng)該是這樣:
animal1.eat(animal2);上面的方法還是不滿足需求。
可以考慮這種方式:
struct?Dog;struct?Cat;struct?Lion;struct?Animal?{????virtual?bool?eat(const?Animal&)?const?=?0;????virtual?bool?eatenBy(const?Dog&)?const?=?0;????virtual?bool?eatenBy(const?Cat&)?const?=?0;????virtual?bool?eatenBy(const?Lion&)?const?=?0;};struct?Dog?:?public?Animal?{????bool?eat(const?Animal&?animal)?const?override?{?return?animal.eatenBy(*this);?}????bool?eatenBy(const?Dog&)?const?override?{?return?false;?}????bool?eatenBy(const?Cat&)?const?override?{?return?false;?}????bool?eatenBy(const?Lion&)?const?override?{?return?true;?}};
使用方式如下:
animal1.eat(animal2);OK,滿足需求。
這里有幾個關(guān)鍵點:
基類那里需要使用前置聲明,因為基類需要使用派生類的引用。
這里產(chǎn)生了兩次多態(tài),一次是運行時多態(tài),另一次是編譯時多態(tài)。
代碼邏輯清晰,沒有那么多if-else,擴展方便。
這種模式叫雙分派設(shè)計模式,關(guān)鍵在于基于參數(shù)上的方法調(diào)用來確定結(jié)果。
打完收工。
