8.19 實現(xiàn)狀態(tài)對象或者狀態(tài)機

2018-02-24 15:26 更新

問題

你想實現(xiàn)一個狀態(tài)機或者是在不同狀態(tài)下執(zhí)行操作的對象,但是又不想在代碼中出現(xiàn)太多的條件判斷語句。

解決方案

在很多程序中,有些對象會根據(jù)狀態(tài)的不同來執(zhí)行不同的操作。比如考慮如下的一個連接對象:

class Connection:
    """普通方案,好多個判斷語句,效率低下~~"""

    def __init__(self):
        self.state = 'CLOSED'

    def read(self):
        if self.state != 'OPEN':
            raise RuntimeError('Not open')
        print('reading')

    def write(self, data):
        if self.state != 'OPEN':
            raise RuntimeError('Not open')
        print('writing')

    def open(self):
        if self.state == 'OPEN':
            raise RuntimeError('Already open')
        self.state = 'OPEN'

    def close(self):
        if self.state == 'CLOSED':
            raise RuntimeError('Already closed')
        self.state = 'CLOSED'

這樣寫有很多缺點,首先是代碼太復(fù)雜了,好多的條件判斷。其次是執(zhí)行效率變低,因為一些常見的操作比如read()、write()每次執(zhí)行前都需要執(zhí)行檢查。

一個更好的辦法是為每個狀態(tài)定義一個對象:

class Connection1:
    """新方案——對每個狀態(tài)定義一個類"""

    def __init__(self):
        self.new_state(ClosedConnectionState)

    def new_state(self, newstate):
        self._state = newstate
        # Delegate to the state class

    def read(self):
        return self._state.read(self)

    def write(self, data):
        return self._state.write(self, data)

    def open(self):
        return self._state.open(self)

    def close(self):
        return self._state.close(self)

# Connection state base class
class ConnectionState:
    @staticmethod
    def read(conn):
        raise NotImplementedError()

    @staticmethod
    def write(conn, data):
        raise NotImplementedError()

    @staticmethod
    def open(conn):
        raise NotImplementedError()

    @staticmethod
    def close(conn):
        raise NotImplementedError()

# Implementation of different states
class ClosedConnectionState(ConnectionState):
    @staticmethod
    def read(conn):
        raise RuntimeError('Not open')

    @staticmethod
    def write(conn, data):
        raise RuntimeError('Not open')

    @staticmethod
    def open(conn):
        conn.new_state(OpenConnectionState)

    @staticmethod
    def close(conn):
        raise RuntimeError('Already closed')

class OpenConnectionState(ConnectionState):
    @staticmethod
    def read(conn):
        print('reading')

    @staticmethod
    def write(conn, data):
        print('writing')

    @staticmethod
    def open(conn):
        raise RuntimeError('Already open')

    @staticmethod
    def close(conn):
        conn.new_state(ClosedConnectionState)

下面是使用演示:

>>> c = Connection()
>>> c._state
<class '__main__.ClosedConnectionState'>
>>> c.read()
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "example.py", line 10, in read
        return self._state.read(self)
    File "example.py", line 43, in read
        raise RuntimeError('Not open')
RuntimeError: Not open
>>> c.open()
>>> c._state
<class '__main__.OpenConnectionState'>
>>> c.read()
reading
>>> c.write('hello')
writing
>>> c.close()
>>> c._state
<class '__main__.ClosedConnectionState'>
>>>

討論

如果代碼中出現(xiàn)太多的條件判斷語句的話,代碼就會變得難以維護(hù)和閱讀。這里的解決方案是將每個狀態(tài)抽取出來定義成一個類。

這里看上去有點奇怪,每個狀態(tài)對象都只有靜態(tài)方法,并沒有存儲任何的實例屬性數(shù)據(jù)。實際上,所有狀態(tài)信息都只存儲在 Connection 實例中。在基類中定義的 NotImplementedError 是為了確保子類實現(xiàn)了相應(yīng)的方法。這里你或許還想使用8.12小節(jié)講解的抽象基類方式。

設(shè)計模式中有一種模式叫狀態(tài)模式,這一小節(jié)算是一個初步入門!

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號