2015-10-19 25 views
4

我在決定在python程序中放置方法時遇到了麻煩,它似乎像我習慣依賴的鴨子打字方法與我的OOP本能不一致。如何使用鴨打字來編寫OOP一致的代碼?

爲了說明,假設我們有三個類:Hero,Sword和Apple。英雄可以裝備一把劍,英雄可以吃一個蘋果。

如果我要按照我的OOP的直覺,我覺得代碼是這樣的:

duckless.py

class Hero: 
    def __init__(self): 
     self.weapon = None 
     self.inTummy = None 

    def equip(self, weapon): 
     weapon.define() 
     print("I shall equip it.") 
     self.weapon = weapon 

    def eat(self, food): 
     food.define() 
     print("I shall consume it.") 
     self.inTummy = food 

class Sword: 
    def define(self): 
     print("'tis a shiny sword") 

class Apple: 
    def define(self): 
     print("'tis a plump apple") 

hero = Hero() 
swd = Sword() 
apl = Apple() 

hero.equip(swd) 
hero.eat(apl) 

這感覺非常的直觀性和可讀性。

如果我是鴨類,不過,我覺得自己的代碼會是這個樣子:

duckfull.py

class Hero: 
    def __init__(self): 
     self.weapon = None 
     self.inTummy = None 

    def onEquip(self): 
     print("I shall equip it.") 

    def onEat(self): 
     print("I shall eat it.") 

class Sword: 
    def define(self): 
     print("'tis a shiny sword") 

    def equip(self, hero): 
     self.define() 
     hero.onEquip() 
     hero.weapon = self 


class Apple: 
    def define(self): 
     print("'tis a plump apple") 

    def eat(self, hero): 
     self.define() 
     hero.onEat() 
     hero.inTummy = self 

hero = Hero() 
swd = Sword() 
apl = Apple() 

swd.equip(hero) 
apl.eat(hero) 

鴨子類型的代碼具有明顯的優勢我可以執行嘗試 - 除了在任何時間,以確定我是否執行「合法」的行動:

try: 
    apl.equip() 
except AttributeError: 
    print("I can't equip that!") 

這感覺非常pythonic,而替代方案會要求我執行可怕的類型檢查

然而,從面向對象的角度來看,感覺怪怪的是一個負責裝備本身,它接收一個英雄作爲參數。裝備行爲看起來像是由英雄執行的動作,因此,我覺得該方法應該屬於英雄類。

def eat(self, hero): 
    self.define() 
    hero.onEat() 
    hero.inTummy = self 

感覺非常陌生。

要麼是更pythonic嗎?更多的OOP是否一致?我應該一起看看不同的解決方案嗎?

在此先感謝。

+1

我認爲這屬於我們的代碼審查頁面,而不是StackOverflow。 – Prune

+0

對不起。我是新來的網站。有沒有辦法在那裏提出這個問題,或者我應該刪除它並在合適的頁面中重新提問? – zayora

+1

你可以定義一個'Item'超類,並讓'Hero'實現一個'activate()'方法,它調用被覆蓋的'Item'的'activate()'函數?這樣,你可以簡單地調用hero.activate(item),並根據你的Item的子類,它會相應地調用'eat()'或'equip()'。 – MeetTitan

回答

4

沒有明確的答案;這取決於你的課程。在你的Hero.equip中檢查isinstance(weapon, Weapon)來檢查物品是否是武器並不是那麼可怕。另外,如果你要的對象都在你的第二個例子包括,您可以將更多的處理成英雄:

class Hero: 
    def __init__(self): 
     self.weapon = None 
     self.inTummy = None 

    def equip(self, weapon): 
     print("I shall equip it.") 
     self.weapon = weapon 

class Sword: 
    def equip(self, hero): 
     hero.equip(self) 

這似乎有點奇怪,但它不一定是壞事在一個類上有一個方法,該方法只是委託給另一個類上的相關方法(例如,在這裏調用sword.equip只需調用hero.equip)。你也可以用相反的方法來做,並且Hero.equip請致電weapon.equip()weapon.ready()或其他任何物品,如果物品不是武器,並且沒有這樣的屬性,將會失敗。

另一件事是你可以在你的第一個例子中仍然有鴨子打字行爲,它只是在你後來嘗試用武器做別的事情時纔會引發錯誤。像這樣:

hero.equip(apple) # no error 
hero.weapon.calculateDamage() # AttributeError: Apple object has no attribute `damage` 

這可能不被認爲是理想的,因爲你不知道你配備了一個無效的武器,直到後來。但是這就是鴨子式的作品:你不知道你是否做錯了什麼,直到你實際上嘗試了一個觸發這種錯誤的行爲。

如果你打算用一個物體去扔它,保齡球和鴨子一樣會起作用。只有當你試圖讓它游泳或飛行,或者你會注意到保齡球不是鴨子。同樣,如果你只需要裝備一把「武器」就可以將它綁在腰帶上或者拿在手中,你可以用蘋果和劍來做到這一點;直到你試圖在戰鬥中真正揮舞蘋果時,你纔會注意到任何不適。

+0

優秀的答案,我分享你的看法。 Upvoted。 – Pynchia

+0

我明白,一旦我嘗試從蘋果中調用武器的方法,我可以讓它返回一個錯誤,但對我來說,蘋果似乎有點不自然。另一方面,您的另一種方法似乎更符合我的目標。在這種情況下,你認爲在哪裏可以將劍物歸屬於英雄的「武器」屬性? Hero.equip()裏面?謝謝您的幫助! – zayora

+0

_如果你打算用一個物體去掉它,那麼保齡球和鴨子一樣都會起作用。在做出這個答案時沒有動物受到傷害? – ninjalj