1. 程式人生 > >[Python設計模式] 第16章 上班,幹活,下班,加班——狀態模式

[Python設計模式] 第16章 上班,幹活,下班,加班——狀態模式

ngs 業務需求 當前時間 finished 過大 大量 封裝 hour abs

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

題目

用代碼模擬一天的工作狀態,上午狀態好,中午想睡覺,下午漸恢復,加班苦煎熬。

基礎版本——函數版

hour = 0
work_finished = False

def write_program():
    if hour < 12:
        print("當前時間: {} 點, 上午工作,精神百倍".format(hour))
    elif hour < 13:
        print("當前時間: {} 點, 餓了,午飯,犯困,午休".format(hour))
    elif hour < 17:
        print("當前時間: {} 點, 下午狀態還可以,繼續努力".format(hour))
    elif work_finished == True:
        print("當前時間: {} 點, 收工,下班".format(hour))
    elif hour < 21:
        print("當前時間: {} 點, 加班中,好累".format(hour))
    else:
        print("當前時間: {} 點, 不行了,睡著了".format(hour))
        
hour = 9
write_program()

hour = 10
write_program()

hour = 12
write_program()

hour = 13
write_program()

hour = 14
write_program()

hour = 17
work_finished = True
# work_finished = False
write_program()

hour = 19
write_program()

hour = 22
write_program()
當前時間: 9 點, 上午工作,精神百倍
當前時間: 10 點, 上午工作,精神百倍
當前時間: 12 點, 餓了,午飯,犯困,午休
當前時間: 13 點, 下午狀態還可以,繼續努力
當前時間: 14 點, 下午狀態還可以,繼續努力
當前時間: 17 點, 收工,下班
當前時間: 19 點, 收工,下班
當前時間: 22 點, 收工,下班

改進版本1.0——初步封裝

class Work():
    def __init__(self):
        self.hour = 0
        self.task_finished = False
        
    def write_program(self):
        if self.hour < 12:
            print("當前時間: {} 點, 上午工作,精神百倍".format(self.hour))
        elif self.hour < 13:
            print("當前時間: {} 點, 餓了,午飯,犯困,午休".format(self.hour))
        elif self.hour < 17:
            print("當前時間: {} 點, 下午狀態還可以,繼續努力".format(self.hour))
        elif self.work_finished == True:
            print("當前時間: {} 點, 收工,下班".format(self.hour))
        elif self.hour < 21:
            print("當前時間: {} 點, 加班中,好累".format(self.hour))
        else:
            print("當前時間: {} 點, 不行了,睡著了".format(self.hour))
            
work = Work()
work.hour = 9
work.write_program()

work.hour = 10
work.write_program()

work.hour = 12
work.write_program()

work.hour = 13
work.write_program()

work.hour = 14
work.write_program()

work.hour = 17
work.work_finished = True
# work_finished = False
work.write_program()

work.hour = 19
work.write_program()

work.hour = 22
work.write_program()
當前時間: 9 點, 上午工作,精神百倍
當前時間: 10 點, 上午工作,精神百倍
當前時間: 12 點, 餓了,午飯,犯困,午休
當前時間: 13 點, 下午狀態還可以,繼續努力
當前時間: 14 點, 下午狀態還可以,繼續努力
當前時間: 17 點, 收工,下班
當前時間: 19 點, 收工,下班
當前時間: 22 點, 收工,下班

點評

  • 這個類中的write_program方法過長,而且有很多判斷分支,意味著它的責任過大了。面向對象設計其實就是希望做到代碼的責任分解。所以這個類違背了單一職責原則
  • 此外,write_program方法裏有這麽多判斷,使得任何需求的改動或增加,都需要去更改這個方法。所以這個類也違背了開放-封閉原則

狀態模式

狀態模式,當一個對象的內在狀態改變是允許改變其行為,這個對象看起來像是改變了其類。[DP]

狀態模式主要解決的是當控制一個對象狀態轉換的條件表達式過於復雜時的情況。把狀態的判斷邏輯轉移到表示不同狀態的一系列類當中,可以把復雜的判斷邏輯簡化。

from abc import ABCMeta, abstractmethod


class State():
    __metaclass__ = ABCMeta
    
    @abstractmethod
    def handle(self, context):
        pass
    
    
class StateA(State):
    
    def handle(self, context):
        context.set_state(StateB())
    
    
class StateB(State):
    
    def handle(self, context):
        context.set_state(StateA())
        

class Context():
    
    def __init__(self, state):
        self.state = state
        
    def set_state(self, state):
        self.state = state
        print("當前狀態: {}".format(self.state.__class__))
        
    def request(self):
        self.state.handle(self)  # 精髓
        
        
def main():
    context = Context(StateA())
    context.request()
    context.request()
    context.request()
    context.request()
    
main()  
當前狀態: <class '__main__.StateB'>
當前狀態: <class '__main__.StateA'>
當前狀態: <class '__main__.StateB'>
當前狀態: <class '__main__.StateA'>

狀態模式的好處與用處

狀態模式的好處是將與特定狀態相關的行為局部化,並且將不同狀態的行為分割開來。[DP]就是將特定的狀態相關的行為都放入一個對象中,由於所有與狀態相關的代碼都存在於某個ConcretState中,所以通過定義新的子類可以很容易的增加新的狀態和轉換[DP]。這樣做的目的就是為了消除龐大的條件分支語句,大的分支判斷會使得它們難以修改和擴展。狀態模式通過把各種狀態轉移邏輯分不到State的子類之間,來減少相互之間的依賴。

什麽時候需要考慮使用狀態模式呢?當一個對象的行為取決於它的狀態,並且它必須在運行時刻根據狀態改變它的行為,就可以使用狀態模式。另外,如果業務需求某項業務有多個狀態,通常都是一些枚舉常量,狀態的變化都是依靠大量的分支判斷語句來實現,此時應該考慮將每一種業務狀態定義為一個State子類,這樣這些對象就可以不依賴於其他對象而獨立變化了,如果某天客戶需求改了,增加或減少業務狀態或改變狀態流程,都不是困難了。

改進版本2.0——狀態模式

from abc import ABCMeta, abstractmethod


class State():
    __metaclass__ = ABCMeta
    
    @abstractmethod
    def write_program(self, work):
        pass
    
class ForenoonState(State):
    
    def write_program(self, work):
        if work.hour < 12:
            print("當前時間: {} 點, 上午工作,精神百倍".format(work.hour))
        else:
            work.set_state(NoonState())
            work.write_program()
            
    
class NoonState(State):
    
    def write_program(self, work):
        if work.hour < 13:
            print("當前時間: {} 點, 餓了,午飯,犯困,午休".format(work.hour))
        else:
            work.set_state(AfternoonState())
            work.write_program()
            
            
class AfternoonState(State):
    
    def write_program(self, work):
        if work.hour < 17:
            print("當前時間: {} 點, 下午狀態還可以,繼續努力".format(work.hour))
        else:
            work.set_state(EveningState())
            work.write_program()

            
class EveningState(State):
    
    def write_program(self, work):
        if work.task_finished == True:
            work.set_state(RestState())
            work.write_program()
        elif work.hour < 21:
            print("當前時間: {} 點, 加班中,好累".format(work.hour))
        else:
            work.set_state(SleepingState())
            work.write_program()
            

class SleepingState(State):
    
    def write_program(self, work):
        print("當前時間: {} 點, 不行了,睡著了".format(work.hour))
        
        
class RestState(State):
    
    def write_program(self, work):
        print("當前時間: {} 點, 收工,下班".format(work.hour))
        
        
class Work():
    
    def __init__(self, state):
        self.state = state
        self.hour = 0
        self.task_finished = False
        
    def set_state(self, state):
        self.state = state
        
    def write_program(self):
        self.state.write_program(self)  # 精髓
        
work = Work(ForenoonState())
work.hour = 9
work.write_program()

work.hour = 10
work.write_program()

work.hour = 12
work.write_program()

work.hour = 13
work.write_program()

work.hour = 14
work.write_program()

work.hour = 17
work.work_finished = True
# work_finished = False
work.write_program()

work.hour = 19
work.write_program()

work.hour = 22
work.write_program()
            
當前時間: 9 點, 上午工作,精神百倍
當前時間: 10 點, 上午工作,精神百倍
當前時間: 12 點, 餓了,午飯,犯困,午休
當前時間: 13 點, 下午狀態還可以,繼續努力
當前時間: 14 點, 下午狀態還可以,繼續努力
當前時間: 17 點, 加班中,好累
當前時間: 19 點, 加班中,好累
當前時間: 22 點, 不行了,睡著了

點評

假如老板規定“員工必須在20點之前離開公司”,那麽只需要增加一個“強制下班狀態”,然後改動一下“傍晚工作狀態”就可以了。而這是不影響其他狀態的代碼的。

[Python設計模式] 第16章 上班,幹活,下班,加班——狀態模式