1. 程式人生 > >[Python設計模式] 第18章 遊戲角色備份——備忘錄模式

[Python設計模式] 第18章 遊戲角色備份——備忘錄模式

狀態管理 撤銷操作 遊戲 .com cover 暫時 必須 https git

github地址:https://github.com/cheesezh/python_design_patterns

題目

用代碼模擬以下場景,一個遊戲角色有生命力,攻擊力,防禦力等數據,在打Boss前後的數據值會發生變化,如果玩家挑戰Boss失敗,我們允許玩家可以將遊戲數據恢復到與Boss決鬥之前的狀態。

基礎版本

class GameRole():
    
    def __init__(self):
        self.vitality = 0
        self.attack = 0
        self.defense = 0
        
    def state_display(self):
        print("當前角色狀態:")
        print("體力:",self.vitality)
        print("攻擊:",self.attack)
        print("防禦:",self.defense)
        
    def get_init_state(self):
        """
        從服務器或者本地磁盤加載初始狀態
        """
        self.vitality = 100
        self.attack = 100
        self.defense = 100
        
    def fight(self):
        """
        與Boss決鬥,數據損耗為0
        """
        self.vitality = 0
        self.attack = 0
        self.defense = 0
        
        
def main():
    mario = GameRole()  
    mario.get_init_state()  # 大戰Boss前,獲取初始角色狀態
    mario.state_display()
    
    backup = GameRole()  # 保存遊戲進度
    backup.vitality = mario.vitality
    backup.attack = mario.attack
    backup.defense = mario.defense
    
    mario.fight()  # 大戰Boss
    mario.state_display()
    
    mario.vitality = backup.vitality  # 讀取進度,重新來過
    mario.attack = backup.attack
    mario.defense = backup.defense
    
    mario.state_display()
    
main()
當前角色狀態:
體力: 100
攻擊: 100
防禦: 100
當前角色狀態:
體力: 0
攻擊: 0
防禦: 0
當前角色狀態:
體力: 100
攻擊: 100
防禦: 100

點評

主要問題在於客戶端調用:

  1. 遊戲角色細節暴露給了客戶端,客戶端需要知道遊戲角色的生命力,攻擊力,防禦力等細節;
  2. 客戶端還要對遊戲角色進行備份;
  3. 如果以後增加“魔法值”或者修改現有數據,都要更改客戶端;
  4. 恢復角色的過程也存在同樣問題;

備忘錄模式

備忘錄模式,在不破壞封裝性的前提下,捕獲一個對象的內部狀態,並在該對象之外保存這個狀態。這樣以後就可將該對象恢復到原先保存的狀態。主要包括三種類:

  • 發起人(Originator):負責創建一個備忘錄Memento,用以記錄當前時刻它的內部狀態,並可使用備忘錄恢復內部狀態。Originator可以根據需要決定Memento存儲Originator的哪些內部狀態;
  • 備忘錄(Memento):負責存儲Originator對象的內部狀態,並可防止Originator以外的其他對象訪問備忘錄Memento。備忘錄有兩個接口,Caretaker只能看到備忘錄的窄接口,它只能將備忘錄傳遞給其他對象。Originator可以看到一個寬接口,允許它訪問返回到先前狀態的所有數據;
  • 管理者(Caretaker):負責保存好備忘錄Memento,不能對備忘錄的內容進行操作或檢查。

在題目的場景中,遊戲角色類相當於Originator,使用同樣的“遊戲角色”實例“backup”來做備忘錄,這在當需要保存全部信息時,是可以考慮的,而用clone的方式來實現Memento的狀態保存可能是更好的方法,但是這樣就相當於對上層應用開放了Originator的全部接口,這對於保存備份有時候並不合適。

那麽當我們不需要保存全部信息以備使用時,需要怎麽辦?當我們需要保存的並不是全部信息,而只是部分信息,那麽就應該有獨立的備忘錄類Memento,它只擁有需要保存的信息的屬性。

改進版本——備忘錄模式

class GameRole():
    
    def __init__(self):
        self.vitality = 0
        self.attack = 0
        self.defense = 0
        
    def state_display(self):
        print("當前角色狀態:")
        print("體力:",self.vitality)
        print("攻擊:",self.attack)
        print("防禦:",self.defense)
        
    def get_init_state(self):
        """
        從服務器或者本地磁盤加載初始狀態
        """
        self.vitality = 100
        self.attack = 100
        self.defense = 100
        
    def fight(self):
        """
        與Boss決鬥,數據損耗為0
        """
        self.vitality = 0
        self.attack = 0
        self.defense = 0
        
    def save_state(self):
        """
        新增“保存狀態”的方法
        """
        return RoleStateMemento(self.vitality, self.attack, self.defense)
    
    def recovery_state(self, memento):
        """
        新增“恢復狀態”的方法
        """
        self.vitality = memento.vitality
        self.attack = memento.attack
        self.defense = memento.defense
        

class RoleStateMemento():
    """
    遊戲角色狀態存儲箱
    """
    def __init__(self, vitality, attack, defense):
        self.vitality = vitality
        self.attack = attack
        self.defense = defense
        

class RoleStateCaretaker():
    """
    角色狀態管理者類
    """
    def __init__(self):
        self.memento = None
        

def main():
    mario = GameRole()
    mario.get_init_state()
    mario.state_display()
    
    state_admin = RoleStateCaretaker()
    state_admin.memento = mario.save_state()  # 遊戲角色的保存對客戶端是透明的
    
    mario.fight()
    mario.state_display()
    
    mario.recovery_state(state_admin.memento)  # 遊戲角色的恢復對客戶端是透明的
    mario.state_display()
    
main()
當前角色狀態:
體力: 100
攻擊: 100
防禦: 100
當前角色狀態:
體力: 0
攻擊: 0
防禦: 0
當前角色狀態:
體力: 100
攻擊: 100
防禦: 100

點評

將需要保存的細節封裝在Memento類中,那一天要更改保存的細節也不用影響客戶端。

Memento模式比較適用於功能復雜的,但需要維護或記錄屬性歷史的類,或者需要保存的屬性只是眾多屬性中的一小部分時,Originator可以根據保存的Memento信息還原到前一狀態。

命令模式也有類似的撤銷作用,如果在某個系統中使用命令模式時,需要實現命令的撤銷功能,那麽命令模式可以使用備忘錄模式來存儲可撤銷操作的狀態。有時候一些對象的內部信息必須保存在對象以外的地方,但是必須要由對象自己讀取,這時,使用備忘錄可以把復雜的對象內部信息對其他的對象屏蔽起來,從而可以恰當的保持封裝的邊界。

當角色的狀態改變的時候,有可能這個狀態無效,這時候就可以使用暫時存儲起來的備忘錄將狀態復原。

[Python設計模式] 第18章 遊戲角色備份——備忘錄模式