python設計模式

 

軟件工程中,設計模式是指軟件設計問題的推薦方案。設計模式通常是描述如何組織代碼和 使用最佳實踐來解決常見的設計問題。算法

 

建立型模式,處理對象建立的設計模式.sql

 

工廠模式編程

工廠一般有兩種形式:json

第一種是工廠方法(Factory Method),設計模式

它是一個方法(Python 術語來講,是一個函數),對不一樣的輸入參數返回不一樣的對象;api

示例中函數connection_factory是一個工廠方法,基於輸入文件路徑的擴展名返回一個 JSONConnector或XMLConnector的實例服務器

import xml.etree.ElementTree as etree
import json


class JSONConnector:

    def __init__(self, filepath):
        self.data = dict()
        with open(filepath, mode='r', encoding='utf-8') as f:
            self.data = json.load(f)

    @property
    def parsed_data(self):
        return self.data


class XMLConnector:

    def __init__(self, filepath):
        self.tree = etree.parse(filepath)

    @property
    def parsed_data(self):
        return self.tree


def connection_factory(filepath):
    if filepath.endswith('json'):
        connector = JSONConnector
    elif filepath.endswith('xml'):
        connector = XMLConnector
    else:
        raise ValueError('Cannot connect to {}'.format(filepath))
    return connector(filepath)


def connect_to(filepath):
    factory = None
    try:
        factory = connection_factory(filepath)
    except ValueError as ve:
        print(ve)
    return factory


def main():
    sqlite_factory = connect_to('data/person.sq3')
    print()

    xml_factory = connect_to('data/person.xml')
    xml_data = xml_factory.parsed_data
    liars = xml_data.findall(".//{}[{}='{}']".format('person',
                                                     'lastName', 'Liar'))
    print('found: {} persons'.format(len(liars)))
    for liar in liars:
        print('first name: {}'.format(liar.find('firstName').text))
        print('last name: {}'.format(liar.find('lastName').text))
        [print('phone number ({})'.format(p.attrib['type']),
               p.text) for p in liar.find('phoneNumbers')]

    print()

    json_factory = connect_to('data/donut.json')
    json_data = json_factory.parsed_data
    print('found: {} donuts'.format(len(json_data)))
    for donut in json_data:
        print('name: {}'.format(donut['name']))
        print('price: ${}'.format(donut['ppu']))
        [print('topping: {} {}'.format(t['id'], t['type'])) for t in donut['topping']]

if __name__ == '__main__':
    main()
factory_method.py

第二種是抽象工廠,網絡

它是一組用於建立一系列相關事物對象的工廠方法。是工廠方法模式的一種泛化,應用須要許多工廠方法,那麼將建立一系列對象的過程合併在一塊兒。架構

示例中main()函數,該函數請求用戶的姓名和年齡,並根據用戶的年齡決定該玩哪一個遊戲。mvc

# Author:song
class Frog:

    def __init__(self, name):
        self.name = name

    def __str__(self):
        return self.name

    def interact_with(self, obstacle):
        print('{} the Frog encounters {} and {}!'.format(self,
                                                         obstacle, obstacle.action()))


class Bug:

    def __str__(self):
        return 'a bug'

    def action(self):
        return 'eats it'


class FrogWorld:
        """抽象工廠,其主要職責是建立遊戲的主人公和障礙物"""
    def __init__(self, name):
        print(self)
        self.player_name = name

    def __str__(self):
        return '\n\n\t------ Frog World ———'

    def make_character(self):
        return Frog(self.player_name)

    def make_obstacle(self):
        return Bug()


class Wizard:

    def __init__(self, name):
        self.name = name

    def __str__(self):
        return self.name

    def interact_with(self, obstacle):
        print('{} the Wizard battles against {} and {}!'.format(self, obstacle, obstacle.action()))


class Ork:

    def __str__(self):
        return 'an evil ork'

    def action(self):
        return 'kills it'


class WizardWorld:
    """抽象工廠,其主要職責是建立遊戲的主人公和障礙物"""
    def __init__(self, name):
        print(self)
        self.player_name = name

    def __str__(self):
        return '\n\n\t------ Wizard World ———'

    def make_character(self):
        return Wizard(self.player_name)

    def make_obstacle(self):
        return Ork()


class GameEnvironment:

    def __init__(self, factory):
        self.hero = factory.make_character()
        self.obstacle = factory.make_obstacle()

    def play(self):
        self.hero.interact_with(self.obstacle)


def validate_age(name):
    try:
        age = input('Welcome {}. How old are you? '.format(name))
        age = int(age)
    except ValueError as err:
        print("Age {} is invalid, please try \
        again…".format(age))
        return (False, age)
    return (True, age)


def main():
    name = input("Hello. What's your name? ")
    valid_input = False
    while not valid_input:
        valid_input, age = validate_age(name)
    game = FrogWorld if age < 18 else WizardWorld
    environment = GameEnvironment(game(name))
    environment.play()

if __name__ == '__main__':
    main()
abstract_method.py

 

建造者設計模式

將一個複雜對象的構造過程與其表現分離,若是咱們知道一個對象必須通過多個步驟來建立,而且要求同一個構造過程能夠產生不一樣的表現,就可使用建造者模式。

# Author:song
# coding: utf-8


class Computer:

    def __init__(self, serial_number):
        self.serial = serial_number
        self.memory = None      # 單位爲GB
        self.hdd = None         # 單位爲GB
        self.gpu = None

    def __str__(self):
        info = ('Memory: {}GB'.format(self.memory),
                'Hard Disk: {}GB'.format(self.hdd),
                'Graphics Card: {}'.format(self.gpu))
        return '\n'.join(info)


class ComputerBuilder:

    def __init__(self):
        self.computer = Computer('AG23385193')

    def configure_memory(self, amount):
        self.computer.memory = amount

    def configure_hdd(self, amount):
        self.computer.hdd = amount

    def configure_gpu(self, gpu_model):
        self.computer.gpu = gpu_model


class HardwareEngineer:

    def __init__(self):
        self.builder = None

    def construct_computer(self, memory, hdd, gpu):
        self.builder = ComputerBuilder()
        [step for step in (self.builder.configure_memory(memory),
                           self.builder.configure_hdd(hdd),
                           self.builder.configure_gpu(gpu))]

    @property
    def computer(self):
        return self.builder.computer


def main():
    engineer = HardwareEngineer()
    engineer.construct_computer(hdd=500, memory=8, gpu='GeForce GTX 650 Ti')
    computer = engineer.computer
    print(computer)

if __name__ == '__main__':
    main()
builder.py

 

原型設計模式

無非就是克隆一個對象。原始對象的全部數據都被簡單地複製到克隆對象中,用於建立對象的徹底副本。示例圖書信息版本信息,是深拷貝,旨在建立新對象而不影響原先數據

# Author:song
# coding: utf-8

import copy
from collections import OrderedDict


class Book:

    def __init__(self, name, authors, price, **rest):
        '''rest的例子有:出版商,長度,標籤,出版日期'''
        self.name = name
        self.authors = authors
        self.price = price
        self.__dict__.update(rest)

    def __str__(self):
        mylist = []
        ordered = OrderedDict(sorted(self.__dict__.items()))
        for i in ordered.keys():
            mylist.append('{}: {}'.format(i, ordered[i]))
            if i == 'price':
                mylist.append('$')
            mylist.append('\n')
        return ''.join(mylist)


class Prototype:

    def __init__(self):
        self.objects = dict()

    def register(self, identifier, obj):
        self.objects[identifier] = obj

    def unregister(self, identifier):
        del self.objects[identifier]

    def clone(self, identifier, **attr):
        found = self.objects.get(identifier)
        if not found:
            raise ValueError('Incorrect object identifier: {}'.format(identifier))
        obj = copy.deepcopy(found)
        obj.__dict__.update(attr)
        return obj


def main():
    b1 = Book('The C Programming Language', ('Brian W. Kernighan', 'Dennis M.Ritchie'), price=118, publisher='Prentice Hall',
              length=228, publication_date='1978-02-22', tags=('C', 'programming', 'algorithms', 'data structures'))

    prototype = Prototype()
    cid = 'k&r-first'
    prototype.register(cid, b1)
    b2 = prototype.clone(cid, name='The C Programming Language(ANSI)', price=48.99,
                         length=274, publication_date='1988-04-01', edition=2)

    for i in (b1, b2):
        print(i)
    print('ID b1 : {} != ID b2 : {}'.format(id(b1), id(b2)))

if __name__ == '__main__':
    main()
prototype.py

 

結構型模式,介紹處理一個系統中不一樣實體(類、對象等)之間關係的設計模式,關注的是提供一 種簡單的對象組合方式來創造新功能.

 

適配器模式(Adapter pattern)

是一種結構型設計模式,幫助咱們實現兩個不兼容接口之間的兼容,無需修改不兼容模型的源代碼就能得到接口的 一致性

"""。在Synthesizer類中,主要動做由play()方法執行。在Human類中,主要動做由speak()方法執行,客戶端僅知道如何調用execute()方法,並不知道play()和speak()。在不改變Synthesizer和Human類的前提運行代碼"""


class Synthesizer:

    def __init__(self, name):
        self.name = name

    def __str__(self):
        return 'the {} synthesizer'.format(self.name)

    def play(self):
        return 'is playing an electronic song'


class Human:

    def __init__(self, name):
        self.name = name

    def __str__(self):
        return '{} the human'.format(self.name)

    def speak(self):
        return 'says hello'



class Computer:
    def __init__(self, name):
        self.name = name

    def __str__(self):
        return 'the {} computer'.format(self.name)

    def execute(self):
        return 'executes a program'


class Adapter:

    def __init__(self, obj, adapted_methods):
        self.obj = obj
        self.__dict__.update(adapted_methods)

    def __str__(self):
        return str(self.obj)


def main():
    objects = [Computer('Asus')]
    synth = Synthesizer('moog')
    objects.append(Adapter(synth, dict(execute=synth.play)))
    human = Human('Bob')
    objects.append(Adapter(human, dict(execute=human.speak)))

    for i in objects:
        print('{} {}'.format(str(i), i.execute()))

if __name__ == "__main__":
    main()
Adapter_pattern.py

 

修飾器(Decorator)模式

可以以透明的方式(不會影響其餘對象)動態地將功能添加到一個對象中。

# Author:song
# coding: utf-8

import functools


def memoize(fn):
    known = dict()

    @functools.wraps(fn)
    def memoizer(*args):
        if args not in known:
            known[args] = fn(*args)
        return known[args]

    return memoizer


@memoize
def nsum(n):
    '''返回前n個數字的和'''
    assert(n >= 0), 'n must be >= 0'
    return 0 if n == 0 else n + nsum(n-1)


@memoize
def fibonacci(n):
    '''返回斐波那契數列的第n個數'''
    assert(n >= 0), 'n must be >= 0'
    return n if n in (0, 1) else fibonacci(n-1) + fibonacci(n-2)

if __name__ == '__main__':
    from timeit import Timer
    measure = [{'exec': 'fibonacci(100)', 'import': 'fibonacci',
                'func': fibonacci}, {'exec': 'nsum(200)', 'import': 'nsum',
                                     'func': nsum}]
    for m in measure:
        t = Timer('{}'.format(m['exec']), 'from __main__ import \
            {}'.format(m['import']))
        print('name: {}, doc: {}, executing: {}, time: \
            {}'.format(m['func'].__name__, m['func'].__doc__,
                       m['exec'], t.timeit()))
mymath.py

 

 外觀設計模式

有助於隱藏系統的內部複雜性,並經過一個簡化的接口向客戶端暴露必要的部分,本質上,外觀(Facade)是在已有複雜系統之上實現的一個抽象層。這種模式是爲複雜系統提供一個簡單接口的理想方式

from enum import Enum
from abc import ABCMeta, abstractmethod

State = Enum('State', 'new running sleeping restart zombie')


class User:
    pass


class Process:
    pass


class File:
    pass


class Server(metaclass=ABCMeta):

    @abstractmethod
    def __init__(self):
        pass

    def __str__(self):
        return self.name

    @abstractmethod
    def boot(self):
        pass

    @abstractmethod
    def kill(self, restart=True):
        pass


class FileServer(Server):

    def __init__(self):
        '''初始化文件服務進程要求的操做'''
        self.name = 'FileServer'
        self.state = State.new

    def boot(self):
        print('booting the {}'.format(self))
        '''啓動文件服務進程要求的操做'''
        self.state = State.running

    def kill(self, restart=True):
        print('Killing {}'.format(self))
        '''殺死文件服務進程要求的操做'''
        self.state = State.restart if restart else State.zombie

    def create_file(self, user, name, permissions):
        '''檢查訪問權限的有效性、用戶權限,等等'''

        print("trying to create the file '{}' for user '{}' with permissions {}".format(name, user, permissions))


class ProcessServer(Server):

    def __init__(self):
        '''初始化進程服務進程要求的操做'''
        self.name = 'ProcessServer'
        self.state = State.new

    def boot(self):
        print('booting the {}'.format(self))
        '''啓動進程服務進程要求的操做'''
        self.state = State.running

    def kill(self, restart=True):
        print('Killing {}'.format(self))
        '''殺死進程服務進程要求的操做'''
        self.state = State.restart if restart else State.zombie

    def create_process(self, user, name):
        '''檢查用戶權限、生成PID,等等'''

        print("trying to create the process '{}' for user '{}'".format(name, user))


class WindowServer:
    pass


class NetworkServer:
    pass


class OperatingSystem:

    '''外觀'''

    def __init__(self):
        self.fs = FileServer()
        self.ps = ProcessServer()

    def start(self):
        [i.boot() for i in (self.fs, self.ps)]

    def create_file(self, user, name, permissions):
        return self.fs.create_file(user, name, permissions)

    def create_process(self, user, name):
        return self.ps.create_process(user, name)


def main():
    os = OperatingSystem()
    os.start()
    os.create_file('foo', 'hello', '-rw-r-r')
    os.create_process('bar', 'ls /tmp')

if __name__ == '__main__':
    main()
facade.py

 

享元設計模式

經過爲類似對象引入數據共享來最小化內存使用,旨在優化性能和內存使用,一個享元(Flyweight)就是一個包含狀態獨立的不可變(又稱固有的)數據的 共享對象。依賴狀態的可變(又稱非固有的)數據不該是享元的一部分,由於每一個對象的這種信 息都不一樣,沒法共享。

# coding: utf-8

import random
from enum import Enum

TreeType = Enum('TreeType', 'apple_tree cherry_tree peach_tree')


class Tree:
    pool = dict()

    def __new__(cls, tree_type):
        obj = cls.pool.get(tree_type, None)
        if not obj:
            obj = object.__new__(cls)
            cls.pool[tree_type] = obj
            obj.tree_type = tree_type
        return obj

    def render(self, age, x, y):
        print('render a tree of type {} and age {} at ({}, {})'.format(self.tree_type, age, x, y))


def main():
    rnd = random.Random()
    age_min, age_max = 1, 30    # 單位爲年
    min_point, max_point = 0, 100
    tree_counter = 0

    for _ in range(10):
        t1 = Tree(TreeType.apple_tree)
        t1.render(rnd.randint(age_min, age_max),
                  rnd.randint(min_point, max_point),
                  rnd.randint(min_point, max_point))
        tree_counter += 1

    for _ in range(3):
        t2 = Tree(TreeType.cherry_tree)
        t2.render(rnd.randint(age_min, age_max),
                  rnd.randint(min_point, max_point),
                  rnd.randint(min_point, max_point))
        tree_counter += 1

    for _ in range(5):
        t3 = Tree(TreeType.peach_tree)
        t3.render(rnd.randint(age_min, age_max),
                  rnd.randint(min_point, max_point),
                  rnd.randint(min_point, max_point))
        tree_counter += 1

    print('trees rendered: {}'.format(tree_counter))
    print('trees actually created: {}'.format(len(Tree.pool)))

    t4 = Tree(TreeType.cherry_tree)
    t5 = Tree(TreeType.cherry_tree)
    t6 = Tree(TreeType.apple_tree)
    print('{} == {}? {}'.format(id(t4), id(t5), id(t4) == id(t5)))
    print('{} == {}? {}'.format(id(t5), id(t6), id(t5) == id(t6)))

if __name__ == '__main__':
    main()
flyweight

 

模型—視圖—控制器(Model-View-Controller,MVC)模式

模型—視圖—控制器(Model-View-Controller,MVC)模式是應用到面向對象編程的Soc原則。 模式的名稱來自用來切分軟件應用的三個主要部分,即模型部分、視圖部分和控制器部分。MVC 被認爲是一種架構模式而不是一種設計模式。架構模式與設計模式之間的區別在於前者比後者的範疇更廣

# Author:song
quotes = ('A man is not complete until he is married. Then he is finished.',
          'As I said before, I never repeat myself.',
          'Behind a successful man is an exhausted woman.',
          'Black holes really suck...', 'Facts are stubborn things.')


class QuoteModel:
    """模型極爲簡約,只有一個get_quote()方法,基於索引n從quotes元組中返回對應的名人
名言(字符串)"""

    def get_quote(self, n):
        try:
            value = quotes[n]
        except IndexError as err:
            value = 'Not found!'
        return value


class QuoteTerminalView:
    """視圖有三個方法,分別是show()、error()和select_quote()。show()用於在屏幕上輸
出一句名人名言(或者輸出提示信息Not found!);error()用於在屏幕上輸出一條錯誤消息;
select_quote()用於讀取用戶的選擇"""

    def show(self, quote):
        print('And the quote is: "{}"'.format(quote))

    def error(self, msg):
        print('Error: {}'.format(msg))

    def select_quote(self):
        return input('Which quote number would you like to see?')


class QuoteTerminalController:
    """控制器負責協調。__init__()方法初始化模型和視圖。run()方法校驗用戶提供的名言索
引,而後從模型中獲取名言,並返回給視圖展現"""0
    def __init__(self):
        self.model = QuoteModel()
        self.view = QuoteTerminalView()

    def run(self):
        valid_input = False
        while not valid_input:
            n = self.view.select_quote()
            try:
                n = int(n)
            except ValueError as err:
                self.view.error("Incorrect index '{}'".format(n))
            else:
                valid_input = True
        quote = self.model.get_quote(n)
        self.view.show(quote)


def main():
    controller = QuoteTerminalController()
    while True:
        controller.run()

if __name__ == '__main__':
    main()
mvc.py

 

代理設計模式(Proxy design pattern)

訪問某個對象以前執行一個或多個重要的操做。對象在訪問實際對象以前執行重要操做而得其名。

使用代理模式實現一個實際類的替代品,這 樣能夠在訪問實際類以前(或以後)作一些額外的事情。四個不一樣類型

  遠程代理,表明一個活躍於遠程位置(例如,咱們本身的遠程服務器或雲服務)的對象。

 虛擬代理,將一個對象的初始化延遲到真正須要使用時進行。

 保護/防禦代理,用於對處理敏感信息的對象進行訪問控制。

 當咱們但願經過添加幫助信息(好比,引用計數)來擴展一個對象的行爲時,可使用 智能(引用)代理。

# Author:song
class SensitiveInfo:

    def __init__(self):
        self.users = ['nick', 'tom', 'ben', 'mike']

    def read(self):
        print('There are {} users: {}'.format(len(self.users), ' '.join(self.users)))

    def add(self, user):
        self.users.append(user)
        print('Added user {}'.format(user))


class Info:

    '''SensitiveInfo的保護代理'''

    def __init__(self):
        self.protected = SensitiveInfo()
        self.secret = '0xdeadbeef'

    def read(self):
        self.protected.read()

    def add(self, user):
        sec = input('what is the secret? ')
        self.protected.add(user) if sec == self.secret else print("That's wrong!")


def main():
    info = Info()
    while True:
        print('1. read list |==| 2. add user |==| 3. quit')
        key = input('choose option: ')
        if key == '1':
            info.read()
        elif key == '2':
            name = input('choose username: ')
            info.add(name)
        elif key == '3':
            exit()
        else:
            print('unknown option: {}'.format(key))

if __name__ == '__main__':
    main()
proxy.py

 

行爲型模式,介紹處理系統實體之間通訊的設計模式

 

責任鏈(Chain of Responsibility)模式

用於讓多個對象來處理單個請求 時,或者用於預先不知道應該由哪一個對象(來自某個對象鏈)來處理某個特定請求時。

 可使用廣播計算機網絡的類比來理解責任鏈模式

# Author:song

"""
MainWindow、MsgText和SendDialog是具備不一樣行爲的控件,
MainWindow僅能處理close和default事件,
SendDialog僅能處理paint事件,
MsgText僅能處理down事件
main()函數展現如何建立一些控件和事件,以及控件如何對那些事件做出反應。
全部事件都會被髮送給全部控件
"""
class Event:

    def __init__(self, name):
        self.name = name

    def __str__(self):
        return self.name


class Widget:

    def __init__(self, parent=None):
        self.parent = parent

    def handle(self, event):
        """handle()方法使用動態分發,經過hasattr()和getattr()決定一個特定請求(event)
應該由誰來處理。若是被請求處理事件的控件並不支持該事件,則有兩種回退機制。若是控件有
parent,則執行parent的handle()方法。若是控件沒有parent,但有handle_default()方
法,則執行handle_default()。"""
        handler = 'handle_{}'.format(event)
        if hasattr(self, handler):
            method = getattr(self, handler)
            method(event)
        elif self.parent:
            self.parent.handle(event)
        elif hasattr(self, 'handle_default'):
            self.handle_default(event)


class MainWindow(Widget):

    def handle_close(self, event):
        print('MainWindow: {}'.format(event))

    def handle_default(self, event):
        print('MainWindow Default: {}'.format(event))


class SendDialog(Widget):

    def handle_paint(self, event):
        print('SendDialog: {}'.format(event))


class MsgText(Widget):

    def handle_down(self, event):
        print('MsgText: {}'.format(event))


def main():
    mw = MainWindow()
    sd = SendDialog(mw)
    msg = MsgText(sd)

    for e in ('down', 'paint', 'unhandled', 'close'):
        evt = Event(e)
        print('\nSending event -{}- to MainWindow'.format(evt))
        mw.handle(evt)
        print('Sending event -{}- to SendDialog'.format(evt))
        sd.handle(evt)
        print('Sending event -{}- to MsgText'.format(evt))
        msg.handle(evt)

if __name__ == '__main__':
    main()
chain.py

 

命令設計模式

幫助咱們將一個操做(撤銷、重作、複製、粘貼等)封裝成一個對象。簡而言之,這意味着建立一個類,包含實現該操做所須要的全部邏輯和方法.

撤銷操做確實是命令模式的殺手級特性。

優點:

 咱們並不須要直接執行一個命令。命令能夠按照但願執行。

 調用命令的對象與知道如何執行命令的對象解耦。調用者無需知道命令的任何實現細節。

 若是有意義,能夠把多個命令組織起來,這樣調用者可以按順序執行它們。例如,在實 現一個多層撤銷命令時,這是頗有用的。

# Author:song
import os

verbose = True


class RenameFile:

    def __init__(self, path_src, path_dest):
        self.src, self.dest = path_src, path_dest

    def execute(self):
        if verbose:
            print("[renaming '{}' to '{}']".format(self.src, self.dest))
        os.rename(self.src, self.dest)

    def undo(self):
        """經過undo()方法支持撤銷操做。在這裏,撤銷操做再次使用os.rename()
將文件名恢復爲原始值。"""
        if verbose:
            print("[renaming '{}' back to '{}']".format(self.dest, self.src))
        os.rename(self.dest, self.src)


class CreateFile:
    """。__init__()函數接受熟悉的
path參數和一個txt字符串,默認向文件寫入hello world文本"""
    def __init__(self, path, txt='hello world\n'):
        self.path, self.txt = path, txt

    def execute(self):
        if verbose:
            print("[creating file '{}']".format(self.path))
        with open(self.path, mode='w', encoding='utf-8') as out_file:
            out_file.write(self.txt)

    def undo(self):
        delete_file(self.path)


class ReadFile:

    def __init__(self, path):
        self.path = path

    def execute(self):
        if verbose:
            print("[reading file '{}']".format(self.path))
        with open(self.path, mode='r', encoding='utf-8') as in_file:
            print(in_file.read(), end='')


def delete_file(path):
    """數接受一個字符串類型的文件路
徑,並使用os.remove()來刪除它"""
    if verbose:
        print("deleting file '{}'".format(path))
    os.remove(path)


def main():
    """main()函數使用這些工具類/方法。參數orig_name和new_name是待建立文件的原始名稱
以及重命名後的新名稱。commands列表用於添加(並配置)全部咱們以後想要執行的命令。注
意,命令不會被執行,除非咱們顯式地調用每一個命令的execute()
下一步是詢問用戶是否須要撤銷執行過的命令。用戶選擇撤銷命令或不撤銷。若是選擇撤銷,
則執行commands列表中全部命令的undo()。然而,因爲並非全部命令都支持撤銷,所以在
undo()方法不存在時產生的AttributeError異常要使用異常處理來捕獲
"""
    orig_name, new_name = 'file1', 'file2'

    commands = []
    for cmd in CreateFile(orig_name), ReadFile(orig_name), RenameFile(orig_name, new_name):
        commands.append(cmd)

    [c.execute() for c in commands]

    answer = input('reverse the executed commands? [y/n] ')

    if answer not in 'yY':
        print("the result is {}".format(new_name))
        exit()

    for c in reversed(commands):
        try:
            c.undo()
        except AttributeError as e:
            pass

if __name__ == '__main__':
    main()
comment.py

 

解釋器(Interpreter)模式

解釋器模式僅能引發應用的高級用戶的興趣。這是由於解釋器模式背後的主 要思想是讓非初級用戶和領域專家使用一門簡單的語言來表達想法.

解釋器模式用於爲高級用戶和領域專家提供一個類編程的框架,但沒有暴露出編程語言那樣的複雜性.

對每一個應用來講,至少有如下兩種不一樣的用戶分類。

 基本用戶:這類用戶只但願可以憑直覺使用應用。他們不喜歡花太多時間配置或學習應 用的內部。對他們來講,基本的用法就足夠了。

 高級用戶:這些用戶,實際上一般是少數,不介意花費額外的時間學習如何使用應用的 高級特性。若是知道學會以後能獲得如下好處,他們甚至會去學習一種配置(或腳本) 語言。

# Author:song
# coding: utf-8
"""使用了Pyparsing建立一種
DSL來控制一個智能屋,而且看到使用一個好的解析工具以模式匹配來解釋結果更加簡單"""
from pyparsing import Word, OneOrMore, Optional, Group, Suppress, alphanums


class Gate:

    def __init__(self):
        self.is_open = False

    def __str__(self):
        return 'open' if self.is_open else 'closed'

    def open(self):
        print('opening the gate')
        self.is_open = True

    def close(self):
        print('closing the gate')
        self.is_open = False


class Garage:

    def __init__(self):
        self.is_open = False

    def __str__(self):
        return 'open' if self.is_open else 'closed'

    def open(self):
        print('opening the garage')
        self.is_open = True

    def close(self):
        print('closing the garage')
        self.is_open = False


class Aircondition:

    def __init__(self):
        self.is_on = False

    def __str__(self):
        return 'on' if self.is_on else 'off'

    def turn_on(self):
        print('turning on the aircondition')
        self.is_on = True

    def turn_off(self):
        print('turning off the aircondition')
        self.is_on = False


class Heating:

    def __init__(self):
        self.is_on = False

    def __str__(self):
        return 'on' if self.is_on else 'off'

    def turn_on(self):
        print('turning on the heating')
        self.is_on = True

    def turn_off(self):
        print('turning off the heating')
        self.is_on = False


class Boiler:

    def __init__(self):
        self.temperature = 83  # in celsius

    def __str__(self):
        return 'boiler temperature: {}'.format(self.temperature)

    def increase_temperature(self, amount):
        print("increasing the boiler's temperature by {} degrees".format(amount))
        self.temperature += amount

    def decrease_temperature(self, amount):
        print("decreasing the boiler's temperature by {} degrees".format(amount))
        self.temperature -= amount


class Fridge:

    def __init__(self):
        self.temperature = 2  # 單位爲攝氏度

    def __str__(self):
        return 'fridge temperature: {}'.format(self.temperature)

    def increase_temperature(self, amount):
        print("increasing the fridge's temperature by {} degrees".format(amount))
        self.temperature += amount

    def decrease_temperature(self, amount):
        print("decreasing the fridge's temperature by {} degrees".format(amount))
        self.temperature -= amount


def main():
    word = Word(alphanums)
    command = Group(OneOrMore(word))
    token = Suppress("->")
    device = Group(OneOrMore(word))
    argument = Group(OneOrMore(word))
    event = command + token + device + Optional(token + argument)

    gate = Gate()
    garage = Garage()
    airco = Aircondition()
    heating = Heating()
    boiler = Boiler()
    fridge = Fridge()

    tests = ('open -> gate',
             'close -> garage',
             'turn on -> aircondition',
             'turn off -> heating',
             'increase -> boiler temperature -> 5 degrees',
             'decrease -> fridge temperature -> 2 degrees')
    open_actions = {'gate': gate.open,
                    'garage': garage.open,
                    'aircondition': airco.turn_on,
                    'heating': heating.turn_on,
                    'boiler temperature': boiler.increase_temperature,
                    'fridge temperature': fridge.increase_temperature}
    close_actions = {'gate': gate.close,
                     'garage': garage.close,
                     'aircondition': airco.turn_off,
                     'heating': heating.turn_off,
                     'boiler temperature': boiler.decrease_temperature,
                     'fridge temperature': fridge.decrease_temperature}

    for t in tests:
        if len(event.parseString(t)) == 2:  # 沒有參數
            cmd, dev = event.parseString(t)
            cmd_str, dev_str = ' '.join(cmd), ' '.join(dev)
            if 'open' in cmd_str or 'turn on' in cmd_str:
                open_actions[dev_str]()
            elif 'close' in cmd_str or 'turn off' in cmd_str:
                close_actions[dev_str]()
        elif len(event.parseString(t)) == 3:  # 有參數
            cmd, dev, arg = event.parseString(t)
            cmd_str, dev_str, arg_str = ' '.join(cmd), ' '.join(dev), ' '.join(arg)
            num_arg = 0
            try:
                num_arg = int(arg_str.split()[0])  # 抽取數值部分
            except ValueError as err:
                print("expected number but got: '{}'".format(arg_str[0]))
            if 'increase' in cmd_str and num_arg > 0:
                open_actions[dev_str](num_arg)
            elif 'decrease' in cmd_str and num_arg > 0:
                close_actions[dev_str](num_arg)

if __name__ == '__main__':
    main()
interpreter.py

 

觀察者模式

咱們但願在一個對象的狀態改變時更新另一組對象,觀察者模式描述單個對象(發佈者,又稱爲主持者或可觀察者)與一個或多個對象(訂閱者, 又稱爲觀察者)之間的發佈—訂閱關係。

觀察者模式背後的思想等同於MVC和關注點分離原則背後的思想,即下降發佈者與訂閱者 之間的耦合度,從而易於在運行時添加/刪除訂閱者。此外,發佈者不關心它的訂閱者是誰。它只是將通知發送給全部訂閱者。

若但願在一個對象的狀態變化時可以通知/提醒全部 相關者(一個對象或一組對象),則可使用觀察者模式。觀察者模式的一個重要特性是,在運行時,訂閱者/觀察者的數量以及觀察者是誰可能會變化,也能夠改變

# Author:song
class Publisher:

    def __init__(self):
        self.observers = []

    def add(self, observer):
        if observer not in self.observers:
            self.observers.append(observer)
        else:
            print('Failed to add: {}'.format(observer))

    def remove(self, observer):
        try:
            self.observers.remove(observer)
        except ValueError:
            print('Failed to remove: {}'.format(observer))

    def notify(self):
        [o.notify(self) for o in self.observers]


class DefaultFormatter(Publisher):

    def __init__(self, name):
        Publisher.__init__(self)
        self.name = name
        self._data = 0

    def __str__(self):
        return "{}: '{}' has data = {}".format(type(self).__name__, self.name, self._data)

    @property
    def data(self):
        return self._data

    @data.setter
    def data(self, new_value):
        try:
            self._data = int(new_value)
        except ValueError as e:
            print('Error: {}'.format(e))
        else:
            self.notify()


class HexFormatter:

    def notify(self, publisher):
        print("{}: '{}' has now hex data = {}".format(type(self).__name__,
                                                      publisher.name, hex(publisher.data)))


class BinaryFormatter:

    def notify(self, publisher):
        print("{}: '{}' has now bin data = {}".format(type(self).__name__,
                                                      publisher.name, bin(publisher.data)))


def main():
    df = DefaultFormatter('test1')
    print(df)

    print()
    hf = HexFormatter()
    df.add(hf)
    df.data = 3
    print(df)

    print()
    bf = BinaryFormatter()
    df.add(bf)
    df.data = 21
    print(df)

    print()
    df.remove(hf)
    df.data = 40
    print(df)

    print()
    df.remove(hf)
    df.add(bf)
    df.data = 'hello'
    print(df)

    print()
    df.data = 15.8
    print(df)

if __name__ == '__main__':
    main()
observer.py

 

面向對象編程

面向對象編程着力於在對象交互時改變它們的狀態,狀態機是一個抽象機器,有兩個關鍵部分,狀態和轉換。狀態是指系統的當前(激活) 情況,轉換是指從一個狀態切換 到另外一個狀態,因某個事件或條件的觸發而開始。一般,在一次轉換髮生以前或以後會執行一個 或一組動做.

狀態模式是一個或多個有限狀態機(簡稱狀態機)的實現,用於解決一個特定的軟件工程問題

# Author:song
from state_machine import State, Event, acts_as_state_machine, after, before, InvalidStateTransition


@acts_as_state_machine
class Process:
    created = State(initial=True)
    waiting = State()
    running = State()
    terminated = State()
    blocked = State()
    swapped_out_waiting = State()
    swapped_out_blocked = State()

    wait = Event(from_states=(created, running, blocked,
                              swapped_out_waiting), to_state=waiting)
    run = Event(from_states=waiting, to_state=running)
    terminate = Event(from_states=running, to_state=terminated)
    block = Event(from_states=(running, swapped_out_blocked),
                  to_state=blocked)
    swap_wait = Event(from_states=waiting, to_state=swapped_out_waiting)
    swap_block = Event(from_states=blocked, to_state=swapped_out_blocked)

    def __init__(self, name):
        self.name = name

    @after('wait')
    def wait_info(self):
        print('{} entered waiting mode'.format(self.name))

    @after('run')
    def run_info(self):
        print('{} is running'.format(self.name))

    @before('terminate')
    def terminate_info(self):
        print('{} terminated'.format(self.name))

    @after('block')
    def block_info(self):
        print('{} is blocked'.format(self.name))

    @after('swap_wait')
    def swap_wait_info(self):
        print('{} is swapped out and waiting'.format(self.name))

    @after('swap_block')
    def swap_block_info(self):
        print('{} is swapped out and blocked'.format(self.name))


def transition(process, event, event_name):
    try:
        event()
    except InvalidStateTransition as err:
        print('Error: transition of {} from {} to {} failed'.format(process.name,
                                                                    process.current_state, event_name))


def state_info(process):
    print('state of {}: {}'.format(process.name, process.current_state))


def main():
    RUNNING = 'running'
    WAITING = 'waiting'
    BLOCKED = 'blocked'
    TERMINATED = 'terminated'

    p1, p2 = Process('process1'), Process('process2')
    [state_info(p) for p in (p1, p2)]

    print()
    transition(p1, p1.wait, WAITING)
    transition(p2, p2.terminate, TERMINATED)
    [state_info(p) for p in (p1, p2)]

    print()
    transition(p1, p1.run, RUNNING)
    transition(p2, p2.wait, WAITING)
    [state_info(p) for p in (p1, p2)]

    print()
    transition(p2, p2.run, RUNNING)
    [state_info(p) for p in (p1, p2)]

    print()
    [transition(p, p.block, BLOCKED) for p in (p1, p2)]
    [state_info(p) for p in (p1, p2)]

    print()
    [transition(p, p.terminate, TERMINATED) for p in (p1, p2)]
    [state_info(p) for p in (p1, p2)]

if __name__ == '__main__':
    main()
state.py

 

策略模式
策略模式(Strategy pattern)鼓勵使用多種算法來解決一個問題,其殺手級特性是可以在運 行時透明地切換算法(客戶端代碼對變化無感知)。所以,若是你有兩種算法,而且知道其中一 種對少許輸入效果更好,另外一種對大量輸入效果更好,則可使用策略模式在運行時基於輸入數據決定使用哪一種算法

策略模式的另外一個應用是建立不一樣的樣式表現,爲了實現可移植性(例如,不一樣平臺之間斷行的不一樣)或動態地改變數據的表現.

# Author:song
# coding: utf-8

import time
SLOW = 3  # 單位爲秒
LIMIT = 5   # 字符數
WARNING = 'too bad, you picked the slow algorithm :('


def pairs(seq):
    n = len(seq)
    for i in range(n):
        yield seq[i], seq[(i + 1) % n]


def allUniqueSort(s):
    if len(s) > LIMIT:
        print(WARNING)
        time.sleep(SLOW)
    srtStr = sorted(s)
    for (c1, c2) in pairs(srtStr):
        if c1 == c2:
            return False
    return True


def allUniqueSet(s):
    if len(s) < LIMIT:
        print(WARNING)
        time.sleep(SLOW)
    return True if len(set(s)) == len(s) else False


def allUnique(s, strategy):
    return strategy(s)


def main():
    while True:
        word = None
        while not word:
            word = input('Insert word (type quit to exit)> ')
            if word == 'quit':
                print('bye')
                return

            strategy_picked = None
            strategies = {'1': allUniqueSet, '2': allUniqueSort}
            while strategy_picked not in strategies.keys():
                strategy_picked = input('Choose strategy: [1] Use a set, [2] Sort and pair> ')

                try:
                    strategy = strategies[strategy_picked]
                    print('allUnique({}): {}'.format(word, allUnique(word, strategy)))
                except KeyError as err:
                    print('Incorrect option: {}'.format(strategy_picked))

if __name__ == '__main__':
    main()
strategy.py

 

模板設計模式

模板設計模式旨在消除代碼重複。若是咱們發現結構相近的(多個)算法中有重複代碼,則能夠把算法的不變(通用)部分留在一個模板方法/函數中,把易變(不一樣的部分移到動做/鉤子方法/函數中

在實現結構相近的算法時,可使用模板模式來消除冗餘代碼。具體實現方式是使用動做/鉤子方法/函數來完成代碼重複的消除.

# Author:song
from cowpy import cow

"""實現一個橫幅生成器。想法很簡單,將一段文本發送給一個函數,該函數要
生成一個包含該文本的橫幅。橫幅有多種風格,好比點或虛線圍繞文本。橫幅生成器有一個默認
風格,但應該可以使用咱們本身提供的風格函數generate_banner()是咱們的模板函數。它接受一個輸入參數(msg,但願橫幅包含
的文本)和一個可選參數(style,但願使用的風格)。默認風格是dots_style"""


def dots_style(msg):
    msg = msg.capitalize()
    msg = '.' * 10 + msg + '.' * 10
    return msg


def admire_style(msg):
    msg = msg.upper()
    return '!'.join(msg)


def cow_style(msg):
    msg = cow.milk_random_cow(msg)
    return msg


def generate_banner(msg, style=dots_style):
    print('-- start of banner --')
    print(style(msg))
    print('-- end of banner --\n\n')


def main():
    """main()函數向橫幅發送文本「happy coding」,並使用全部可用風格將橫幅輸出到標準輸出"""
    msg = 'happy coding'
    [generate_banner(msg, style) for style in (dots_style, admire_style, cow_style)]

if __name__ == '__main__':
    main()
template.py

 

參考文獻:

《Python設計模式》

相關文章
相關標籤/搜索