python--状态模式

例子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

 
 

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343