2013-05-10 80 views
1

過去幾周我一直在儘可能多地學習OOP,並且我學到了很多東西,但我不確定這個,我的類層次結構應該是什麼樣子?玩家是否應該繼承或擁有一個關卡?

試想兩個班,這裏有一個Level -class:

class Level(object): 
    def __init__(self, level, exp): 
     self.level = level 
     self.exp = exp 

    @property 
    def required_exp(self, level=None): 
     return 80 + 40 * (level or self.level) 

    def add_exp(self, exp): 
     self.exp += exp 
     while self.exp >= self.required_exp: 
      self.exp -= self.required_exp 
      self.level += 1 

顯然它也有一些其他的方法也一樣,但是這是基本的想法。

現在第二類叫做Player。它有一些屬性,如:namehealthlevelexperience pointsdamage

我所學到至今(有人這麼說這)就是問自己一個問題,當我不知道是否使用繼承(is-a-關係)或屬性(has-a-關係)。 問題是:球員是一個級別,還是球員有一個級別?

那麼......既不是。 球員肯定不是一個水平,我敢肯定。 而玩家有它的等級,但等級不等於Level類。 取而代之的是玩家有等級和經驗點,這兩個屬性都是Level類的屬性。您還可以將經驗值添加到播放器,因此從Level繼承它是有意義的。

作爲旁註,namings對我來說也沒有任何意義。這是有道理的有這樣的事情:Level.increase(self)增加一級。 但是,對於玩家來說,在大多數(全部)RPG遊戲中使用Player.level_up(self)會更有意義。 通話Player.increase(self)沒有任何意義,您不能將Player增加1,而是將其級別提高一個。 話又說回來,呼籲Level.level_up(self),並不意味着多大意義,要麼...

所以,我認爲我應該用Level,如果它是一個接口,從它繼承Player(和其他子類),但我米不確定,所以我決定問你,因爲我在這裏學習反正。 我該怎麼辦?使用LevelPlayer最合適的方式是什麼?

+0

聲音像Player-Level在這種情況下是一種經典的「有-α」關係。你說,「玩家有它的水平,但水平不等於水平類。」這到底意味着什麼? – Santa 2013-05-10 20:28:40

+0

@Santa'Level'級別已經有一個叫'level'的屬性。這個'level'屬性是'Player'也應該有的,它只是一個整數,可以告訴我們玩家的水平。 '玩家'也有經驗積分(exp),這也是'Level'的一個屬性。這就是爲什麼從Level繼承Player的意義,否則我們會調用'Player.level.level'來獲得玩家級別的實際數值。 – 2013-05-10 20:32:43

+0

確實,player.level.level有點尷尬。人們會認爲這個級別應該是一個整數,而不是一個完整的級別。這個「level」代碼寫在哪裏,你可以改變它嗎? – aestrivex 2013-05-10 20:41:57

回答

4

我相信我是告訴你問自己這個問題的人:「A級是B級,還是A級是B級?」,所以我覺得我必須回答這個問題。

我並不是100%確定你的關卡的實際效果,但我認爲它與口袋妖怪,魔獸世界,英雄聯盟和其他數千款遊戲中使用的類似。 如果是這樣,你的Level-class已經出錯了,它不應該是這樣的。

你肯定有一件事是肯定的; 「玩家當然不是一個水平」 正如其他人已經提到,你應該只與has-a關係。 球員確實有水平,我們都知道。 但是,您告訴我球員沒有Level類型的屬性,而是一個整數level-用於保存您的Level-類的數值。

這應該已經敲響鐘聲一些,但這裏有一個技巧:這是相同的作爲具有Name -class具有string型屬性,叫做name裏面。

所以答案是: Name級應該已經是一個字符串本身,它應該從string繼承而不是有string屬性裏面,以保持其價值。 同樣適用於您的Level,它已經是一個整數本身,因此從int繼承它,然後添加exp及其方法。

現在您可以使用player.level返回玩家等級的整數值。 爲了得到exp,你「必須」使用player.level.exp。 我引用了「必須」,因爲即使聽起來很奇怪,exp也是關卡的一個屬性。 它基本上與一個數字的小數點一樣,是有道理的?

此外,調用類似player.level.increase()現在更有意義。 雖然它已經是一個整數值,爲什麼不做player.level += 1? 如果你需要一些高級的東西,你可以重寫Level.__add__(self, value)方法。 我想不出一個理由,爲什麼你不需要這個呢? (現在不是你已經制作了required_exp屬性)

+0

再次感謝您的回答,我只是堅持了is-a,並且有一個關係既不合適,也沒有意識到問題出在我的課堂上:)我仍然不太喜歡'exp'不是玩家的屬性,但它適用於任何我認爲它作爲十進制數字,也感謝。我使用'player.level.increase()'來增加玩家的技能點數(每個級別都會得到一個),但是我只是意識到我應該用它來提升事件'level_up',然後訂閱玩家的'add_skill_point'到它... :) – 2013-05-10 21:10:59

+1

@SkamahOne很高興能幫助任何真正願意學習的人:)善於思考這個事件,儘管我會爲他們命名。正如你在你的問題中提到的那樣,'Level.level_up'事件不會比'Level.level_up'方法更有意義。寧可稱之爲'Level.increase',因爲事件正在發生,Level會增加。現在這是可選的,但不是訂閱'add_skill_point',而是創建一個新方法'Player.on_level_up()'並將其訂閱到'Level.increase'。但這取決於你的事件系統和你個人的喜好。 – 2013-05-10 21:18:25

+0

我會這樣做,謝謝:) – 2013-05-10 21:19:30

3

玩家有一個級別。正如你所說,「球員當然不是一個水平。」當你使用繼承時,它意味着玩家是關卡的子類型(這當然根本沒有意義)。

這是一個明確的關係。通過player1.levelup()player1.addxp()等方法與級別進行交互,這些方法引用了level屬性。

+0

當問及關於面向對象的風格時,在關注現有資源如何配合在一起之前,請想一想在抽象中什麼設計對您的應用程序有意義。首先,看清楚該玩家應該有一個關卡,然後弄清楚如何恰當地裝配這些棋子。就像蟒蛇的禪宗一樣; 「應該有一個 - 而且最好只有一個 - 明顯的做法。」 – aestrivex 2013-05-10 20:39:34

+0

即使我沒有接受你,我完全給你一個好的答案! :) – 2013-05-10 21:12:15

0

玩家肯定不是一個級別,但他有一個級別。我沒有看到你在使用上面在玩家類中定義的關卡類的問題,因爲它跟蹤了關卡中關於玩家的所有重要信息。

2

如果你已經確定Player不是Level,那麼繼承不是正確的機制。繼承對像「一輛車是一輛車,一輛摩托車是一輛車」這樣的關係起作用,所以在這種情況下,汽車和摩托車都是車輛的子類。

但是,這種關係不適合於真實的composition,因爲Level不應該被表示爲具有其自身功能的另一個類。確實,玩家的等級爲,但正如您所指出的那樣,player.level_up()player.add_experience()比使用level對象調用這些方法更爲明確。這是因爲與玩家等級的互動通過其方法是有意義的,而不是player.level.level。那麼你應該考慮將你的等級類的功能,以玩家:

Class diagram

0

相反跟蹤這兩個水平和相對於該級別的附加經驗的,你可以簡單地保持軌道總經驗

從總的經驗,你可以得到的水平,並達到一個新的水平所需的經驗量,否則你可能會感興趣的其他任何這樣的數量。

例如,

class Player(object): 
    def __init__(self, exp=0): 
     self.exp = exp 
    @property 
    def level(self): 
     if self.exp<80: 
      return 0 
     return (self.exp-80)//40 + 1 
    @property 
    def required_exp(self): 
     return 80 + 40*self.level 
    def add_exp(self, exp): 
     self.exp += exp 


ringo = Player(exp=80) 
print(ringo.level) 
# 1 

print(ringo.required_exp) 
# 120 
相關問題