python--状态模式

2018-09-07  本文已影响23人  极光火狐狸

例子1

源码: state3.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
状态模式

换一个博客发布审批流程.

创建文章->请求审阅->审阅->审批成功->发布.
创建文章->请求审阅->审阅->审批失败->编辑文章->请求审阅->审阅->审批成功->发布.

"""

from __future__ import print_function


class State(object):

    def __init__(self):
        self.approve_times = 0
        self.review_times = 0


class Article(State):

    def __init__(self, blog):
        super(Article, self).__init__()
        self.name = "起草状态"
        self.title = None
        self.content = None
        self.blog = blog

    def add_content(self, title, content):
        self.title = title
        self.content = content

    def edit_content(self, title, content):
        self.title = title
        self.content = content

    def request_review(self):
        self.blog.state = self.blog.pending
        return self.blog.state


class Pending(State):

    def __init__(self, blog):
        super(Pending, self).__init__()
        self.name = "待审核状态"
        self.content = None
        self.blog = blog
        self.target = None

    def do_review(self):
        self.review_times += 1

    def approve(self):
        self.approve_times += 1

    def publish(self):
        if self.approve_times:
            return Result.ok
        else:
            self.blog.state = self.blog.article
            return Result.err

    def reject(self):
        self.blog.state = self.blog.article
        return Result.err


class Result(object):

    ok = "审核成功"
    err = "审核失败"


class Blog(object):

    def __init__(self):
        self.article = Article(self)
        self.pending = Pending(self)
        self.state = self.article

    def create_article(self, title, content):
        self.article.add_content(title, content)
        return self.state


if __name__ == '__main__':
    # 测试: 通过流程.
    blog = Blog()
    article = blog.create_article("first blog", "hello world!")                # 创建文章
    pending = article.request_review()                                         # 请求审阅
    pending.do_review()                                                        # 审阅
    pending.approve()                                                          # 审核通过
    result = pending.publish()                                                 # 发布文章
    assert result == Result.ok

    # 测试 拒绝流程
    blog = Blog()
    article = blog.create_article("second blog", "hello world!")
    pending = article.request_review()
    pending.do_review()
    result = pending.reject()
    if result != Result.err:
        raise RuntimeError(Result.err)
    article.edit_content("second blog", "hello world again!")
    pending = article.request_review()
    pending.do_review()
    pending.approve()
    result = pending.publish()
    assert result == Result.ok

    # 测试: 没同意就直接发布, 返回错误Result.err枚举
    blog = Blog()
    article = blog.create_article("second blog", "hello world!")
    pending = article.request_review()
    pending.do_review()
    result = pending.publish()
    assert result == Result.err

 
 

例子2

源码: state2.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
状态模式

状态模式允许我们在程序运行时改变对象的表现.
状态模式隶属于行为模式: 根据不同的行为去改变状态, 由不同的状态操控不同的表现.

下面的这个例子是 模拟 AM/FM 收音机 提供了两个按钮: 频道切换, 电台切换.

频道: AM 和 FM 着两种.
电台: 103.9, 101.7 很多种.

我们人为的去触发不同的按钮, 改变收音机内部状态, 而收音机通过不同的状态去操控
不同的表现.

Implementation of the state pattern

http://ginstrom.com/scribbles/2007/10/08/design-patterns-python-style/

*TL;DR80
Implements state as a derived class of the state pattern interface.
Implements state transitions by invoking methods from the pattern's superclass.
"""

from __future__ import print_function


class State(object):

    """
    State 对象提供了基本抽象, 所谓的基本抽象就是,
    它定义了参数radio必须提供, 因此所有State的子类
    对外也是要求radio参数必须提供.

    State 指定了初始化应该要有的对象属性值, 但是这些
    属性值是无效的属性值, 应再子类中各自分别提供.

    State 对象还定义了scan默认实现方法, 所有State的
    子类对象默认都会拥有这个方法.

    scan 方法所提供的能力是, 在相同对象内提供不
    同电台的切换能力, 并打印出电台切换后的信息.

    对象指的是下面的: AmState 和 FmState 对象.
    """

    def __init__(self, radio):
        self.pos = 0                            # 记录当前所在电台位置
        self.name = None
        self.stations = []                      # 所有电台列表
        self.radio = radio

    def scan(self):
        self.pos += 1
        if self.pos == len(self.stations):      # 如果电台已经是最后一个, 那么就回到第0个.
            self.pos = 0
        print(u"Scanning... Station is %s %s" %
              (self.stations[self.pos], self.name))

    def toggle_amfm(self):
        prepare = self.radio.toggle[self.radio.state]
        print(u"Switching to {}".format(prepare.name))
        self.radio.state = prepare


class AmState(State):

    """
    AmState 对象继承了 State 对象, 因此它初始化参数radio
    必须与State对象保持一致, 并且使用super().__init__()语法
    将参数向上传递, 然后在定义其他对象属性.

    self.stations: 电台
    self.pos: 当前电台位置
    self.name: AM 频道

    toggle_amfm 方法 将 频道切换到 FM 频道.
    """

    def __init__(self, radio):
        super(AmState, self).__init__(radio)
        self.stations = ["1250", "1380", "1510"]
        self.pos = 0
        self.name = "AM"


class FmState(State):
    """
    FmState 对象继承了 State 对象, 因此它初始化参数radio
    必须与State对象保持一致, 并且使用super().__init__()语法
    将参数向上传递, 然后在定义其他对象属性.

    self.stations: 电台
    self.pos: 当前电台位置
    self.name: FM 频道

    toggle_amfm 方法 将 频道切换到 AM 频道.
    """

    def __init__(self, radio):
        super(FmState, self).__init__(radio)
        self.stations = ["81.3", "89.1", "103.9"]
        self.pos = 0
        self.name = "FM"


class Radio(object):

    # """A radio.     It has a scan button, and an AM/FM toggle switch."""
    """
    Radio 收音机对象针对两个按钮进行的不同实现.

    self.amstate: AM频道对象
    self.fmstate: FM频道对象
    self.state: 当前是哪个频道
    这三个对象属性, 的代码组织在设计模式里面被称为组合, 将不同的对象组合到一起.

    toggle_amfm 方法负责运行当前频道的 toggle_amfm 的方法(完成频道切换行为).

    scan 方法负责运行当前频道的 scan 方法(完成电台切换行为).
    """

    def __init__(self):
        """We have an AM state and an FM state"""
        self.amstate = AmState(self)
        self.fmstate = FmState(self)
        self.state = self.amstate           # 核心在这里
        self.toggle = {
            self.amstate: self.fmstate,
            self.fmstate: self.amstate
        }

    def toggle_amfm(self):
        self.state.toggle_amfm()            # 核心在这里

    def scan(self):
        self.state.scan()                   # 核心在这里


# Test our radio out
if __name__ == '__main__':
    radio = Radio()

    actions = [radio.scan] * 2 + [radio.toggle_amfm] + [radio.scan] * 2
    actions *= 2
    # 这里比较难理解, 可以换一种方式来看:
    # actions = []
    # actions.append(radio.scan)
    # actions.append(radio.scan)
    # actions.append(radio.toggle_amfm)
    # actions.append(radio.scan)
    # actions.append(radio.scan)
    # actions.append(radio.scan)
    # actions.append(radio.scan)
    # actions.append(radio.toggle_amfm)
    # actions.append(radio.scan)
    # actions.append(radio.scan)

    for action in actions:
        action()                            # 这里开始模拟触发按钮行为.

### OUTPUT ###
# Scanning... Station is 1380 AM
# Scanning... Station is 1510 AM
# Switching to FM
# Scanning... Station is 89.1 FM
# Scanning... Station is 103.9 FM
# Scanning... Station is 81.3 FM
# Scanning... Station is 89.1 FM
# Switching to AM
# Scanning... Station is 1250 AM
# Scanning... Station is 1380 AM

 
 

上一篇 下一篇

猜你喜欢

热点阅读