軟件工程中,設計模式是指軟件設計問題的推薦方案。設計模式通常是描述如何組織代碼和 使用最佳實踐來解決常見的設計問題。算法
建立型模式,處理對象建立的設計模式.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()
第二種是抽象工廠,網絡
它是一組用於建立一系列相關事物對象的工廠方法。是工廠方法模式的一種泛化,應用須要許多工廠方法,那麼將建立一系列對象的過程合併在一塊兒。架構
示例中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()
建造者設計模式
將一個複雜對象的構造過程與其表現分離,若是咱們知道一個對象必須通過多個步驟來建立,而且要求同一個構造過程能夠產生不一樣的表現,就可使用建造者模式。
# 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()
原型設計模式
無非就是克隆一個對象。原始對象的全部數據都被簡單地複製到克隆對象中,用於建立對象的徹底副本。示例圖書信息版本信息,是深拷貝,旨在建立新對象而不影響原先數據
# 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()
結構型模式,介紹處理一個系統中不一樣實體(類、對象等)之間關係的設計模式,關注的是提供一 種簡單的對象組合方式來創造新功能.
適配器模式(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()
修飾器(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()))
外觀設計模式
有助於隱藏系統的內部複雜性,並經過一個簡化的接口向客戶端暴露必要的部分,本質上,外觀(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()
享元設計模式
經過爲類似對象引入數據共享來最小化內存使用,旨在優化性能和內存使用,一個享元(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()
模型—視圖—控制器(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()
代理設計模式(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()
行爲型模式,介紹處理系統實體之間通訊的設計模式
責任鏈(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()
命令設計模式
幫助咱們將一個操做(撤銷、重作、複製、粘貼等)封裝成一個對象。簡而言之,這意味着建立一個類,包含實現該操做所須要的全部邏輯和方法.
撤銷操做確實是命令模式的殺手級特性。
優點:
咱們並不須要直接執行一個命令。命令能夠按照但願執行。
調用命令的對象與知道如何執行命令的對象解耦。調用者無需知道命令的任何實現細節。
若是有意義,能夠把多個命令組織起來,這樣調用者可以按順序執行它們。例如,在實 現一個多層撤銷命令時,這是頗有用的。
# 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()
解釋器(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()
觀察者模式
咱們但願在一個對象的狀態改變時更新另一組對象,觀察者模式描述單個對象(發佈者,又稱爲主持者或可觀察者)與一個或多個對象(訂閱者, 又稱爲觀察者)之間的發佈—訂閱關係。
觀察者模式背後的思想等同於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()
面向對象編程
面向對象編程着力於在對象交互時改變它們的狀態,狀態機是一個抽象機器,有兩個關鍵部分,狀態和轉換。狀態是指系統的當前(激活) 情況,轉換是指從一個狀態切換 到另外一個狀態,因某個事件或條件的觸發而開始。一般,在一次轉換髮生以前或以後會執行一個 或一組動做.
狀態模式是一個或多個有限狀態機(簡稱狀態機)的實現,用於解決一個特定的軟件工程問題
# 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()
策略模式
策略模式(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()
模板設計模式
模板設計模式旨在消除代碼重複。若是咱們發現結構相近的(多個)算法中有重複代碼,則能夠把算法的不變(通用)部分留在一個模板方法/函數中,把易變(不一樣的部分移到動做/鉤子方法/函數中
在實現結構相近的算法時,可使用模板模式來消除冗餘代碼。具體實現方式是使用動做/鉤子方法/函數來完成代碼重複的消除.
# 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()
參考文獻:
《Python設計模式》