日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

《精通python设计模式》读书笔记之——结构型设计模式

發布時間:2023/12/20 python 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 《精通python设计模式》读书笔记之——结构型设计模式 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

結構型設計模式:

結構型設計模式處理一個系統中不同實體(比如,類和對象)之間的關系,關注的是提供一種簡單的對象組合方式來創造新功能。可用于實現不兼容軟件之間的接口兼容。

①.適配器模式

簡介:
適配器模式(Adapter pattern)是一種結構型設計模式,幫助我們實現兩個不兼容接口之間的兼容。
.
解釋一下不兼容接口的真正含義。如果我們希望把一個老組件用于一個新系統中,或者把一個新組件用于一個老系統中,不對代碼進行任何修改兩者就能夠通信的情況很少見。但 又并非總是能修改代碼,或因為我們無法訪問這些代碼(例如,組件以外部庫的方式提供),或因為修改代碼本身就不切實際。在這些情況下,我們可以編寫一個額外的代碼層,該代碼層包含讓兩個接口之間能夠通信需要進行的所有修改。這個代碼層就叫適配器。
.
電子商務系統是這方面眾所周知的例子。假設我們使用的一個電子商務系統中包含一個 calculate_total(order)函數。這個函數計算一個訂單的總金額,但貨幣單位為丹麥克朗(DanishKroner,DKK)。顧客讓我們支持更多的流行貨幣,比如美元(United States Dollar,USD)和歐元(Euro,EUR),這是很合理的要求。如果我們擁有系統的源代碼,那么可以擴展系統,方法是添加一些新函數,將金額從DKK轉換成USD,或者從DKK轉換成EUR。但是如果應用僅以外部庫的方式提供,我們無法訪問其源代碼,那又該怎么辦呢?在這種情況下,我們仍然可以使用這個外部庫(例如,調用它的方法),但無法修改和擴展它。解決方案是編寫一個包裝器(又名適配器)將數據從給定的DKK格式轉換成期望的USD或EUR格式。

現實生活的例子:
我們所有人每天都在使用適配器模式,只不過是硬件上的,而不是軟件上的。例如:USB適配器、電源插頭適配器

軟件的例子:
Grok是一個Python框架,運行在Zope3之上,專注于敏捷開發。Grok框架使用適配器,讓已有對象無需變更就能符合指定API的標準
Python第三方包Traits也使用了適配器模式,將沒有實現某個指定接口(或一組接口)的對象 轉換成實現了接口的對象。

應用案例:
在某個產品制造出來之后,需要應對新的需求之時,如果希望其仍然有效,則可以使用適配器模式。通常兩個不兼容接口中的一個是他方的或者是老舊的。 如果一個接口是他方的,就意味著我們無法訪問其源代碼。如果是老舊的,那么對其重構通常是不切實際的。更進一步,我們可以說修改一個老舊組件的實現以滿足我們的需求,不僅是不切實際的,而且也違反了開放封閉原則。
.
開放封閉原則(open/close principle)是面向對象設計的基本原則之一(SOLID中的O),聲明一個軟件實體應該對擴展是開 放的,對修改則是封閉的。本質上這意味著我們應該無需修改一個軟件實體的源代碼就能擴展其行為。適配器模式遵從開放/封閉原則。
.
因此,在某個產品制造出來之后,需要應對新的需求之時,如果希望其仍然有效,使用適配器是一種更好的方式,原因如下所示:
1. 不要求訪問他方接口的源代碼
2. 不違反開放/封閉原則

代碼實現:

class Synthesizer:"""在Synthesizer類 中,主要動作由play()方法執行。"""def __init__(self, name):self.name = namedef __str__(self):return 'the {} synthesizer'.format(self.name)def play(self):return 'is playing an electronic song'class Human:"""在Human類中,主要動作由speak()方法執行"""def __init__(self, name):self.name = namedef __str__(self):return '{} the human'.format(self.name)def speak(self):return 'says hello'class Computer:"""用來顯示一臺計算機的基本信息"""def __init__(self, name):self.name = namedef __str__(self):return 'the {} computer'.format(self.name)def execute(self):"""execute方法是計算機可以執行的主要動作。這一方法由客戶端代碼調用。:return: str"""return 'executes a program'class Adapter:"""客戶端僅知道如何調用execute()方法,并不知道play()和speak()。在不改變Synthesizer和Human類的前提下,我們該如何做才能讓代碼有效?適配器是救星!我們創建一個通用的Adapter類,將一些帶不同接口的對象適配到一個統一接口中。__init__()方法的obj參數是我們想要適配的對象,adapted_methods是一個字典,鍵值對中的鍵是客戶端要調用的方法,值是應該被調用的方法。"""def __init__(self, obj, execute=None, **kwargs):self.obj = objself.execute = executeself.__dict__.update(kwargs)def __str__(self):return str(self.obj)def main():objects = [Computer('Asus')]synth = Synthesizer('moog')objects.append(Adapter(synth, execute=synth.play, **synth.__dict__))human = Human('Bob')objects.append(Adapter(human, execute=human.speak, **human.__dict__))for i in objects:print('{} {}'.format(str(i), i.execute()))for i in objects:print(i.name)if __name__ == "__main__":main()

適配器模式總結
我們使用適配器模式讓兩個(或多個)不兼容接口兼容。
.
適配器讓一件產品在制造出來之后需要應對新需求之時還能工作。Python框架Grok和第三方包Traits各自都使用了適配器模式來獲得API一致性和接口兼容性。開放/封閉原則與這些方面密切相關。
.
適配器模式的使用,無需修改不兼容模型的源代碼就能獲得接口的一致性。這是通過讓一個通用的適配器類完成相關工作而實現的。雖然在Python中我們可以沿襲傳統方式使用子類(繼承)來實現適配器模式,但這種技術是一種很棒的替代方案。

②.修飾器模式:

簡介:
無論何時我們想對一個對象添加額外的功能,都有下面這些不同的可選方法。
1. 如果合理,可以直接將功能添加到對象所屬的類(例如,添加一個新的方法)
2. 使用組合
3. 使用繼承
.
與繼承相比,通常應該優先選擇組合,因為繼承使得代碼更難復用,繼承關系是靜態的,并且應用于整個類以及這個類的所有實例
.
修飾器模式:
設計模式為我們提供第四種可選方法,以支持動態地(運行時)擴展一個對象的功能,這種方法就是修飾器。修飾器(Decorator)模式能夠以透明的方式(不會影響其他對象)動態地將功能添加到一個對象中。
.
在許多編程語言中,使用子類化(繼承)來實現修飾器模式。在Python中,我們可以(并且應該)使用內置的修飾器特性。一個Python修飾器就是對Python語法的一個特定改變,用于擴展一個類、方法或函數的行為,而無需使用繼承。從實現的角度來說,Python修飾器是一個可調用對象(函數、方法、類),接受一個函數對象fin作為輸入,并返回另一個函數對象fout。這意味著可以將任何具有這些屬性的可調用對象當作一個修飾器。

現實生活的例子:
修飾器模式通常用于擴展一個對象的功能。這類擴展的實際例子有,給槍加一個消音器、使用不同的照相機鏡頭(在可拆卸鏡頭的照相機上)等。

軟件的例子:
Django框架大量地使用修飾器,其中一個例子是視圖修飾器。Django的視圖(View)修飾器可用于以下幾種用途:
1. 限制某些HTTP請求對視圖的訪問
2. 控制特定視圖上的緩存行為
3. 按單個視圖控制壓縮
4. 基于特定HTTP請求頭控制緩存
.
Grok框架也使用修飾器來實現不同的目標,比如下面幾種情況:
1. 將一個函數注冊為事件訂閱者
2. 以特定權限保護一個方法
3. 實現適配器模式

應用案例:
當用于實現橫切關注點(cross-cutting concerns)時,修飾器模式會大顯神威。以下是橫切關注點的一些例子。:
1. 數據校驗
2. 事務處理(這里的事務類似于數據庫事務,意味著要么所有步驟都成功完成,要么事務失敗)
3. 緩存
4. 日志
5. 監控
6. 調試
7. 業務規則
8. 壓縮
9. 加密
.
橫切關注點的概念:
一般來說,應用中有些部件是通用的,可應用于其他部件,這樣的部件被看作橫切關注點。
.
使用修飾器模式的另一個常見例子是圖形用戶界面工具集。在一個GUI工具集中,我們希望能夠將一些特性,比如邊框、陰影、顏色以及滾屏,添加到單個組件/部件。

代碼實現:
Python修飾器通用并且非常強大。你可以在Python官網python.org的修飾器代碼庫頁面中找到許多修飾器的使用樣例。我們將學習如何實現一個memoization修飾器。所有遞歸函數都能因memoization而提速,那么來試試常用的斐波那契數列例子。使用遞歸算法實現斐波那契數列,直接了當,但性能問題較大,即使對于很小的數值也是如此。

Low版:def fibonacci(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 Timert = Timer("fibonacci(8)", 'from __main__ import fibonacci')print(t.timeit()) 普通版:known = {0: 0, 1: 1}def fibonacci(n):assert (n >= 0), 'n must be >= 0'if n in known:return known[n]res = fibonacci(n - 1) + fibonacci(n - 2)known[n] = resreturn resif __name__ == '__main__':from timeit import Timert = Timer('fibonacci(8)', 'from __main__ import fibonacci')print(t.timeit())print(fibonacci(8)) 修飾器版import functoolsdef 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 Timermeasure = [{'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()))

修飾器模式總結:
我們使用修飾器模式來擴展一個對象的行為,無需使用繼承,非常方便。Python進一步擴展了修飾器的概念,允許我們無需使用繼承或組合就能擴展任意可調用對象(函數、方法或類)的行為。我們可以使用Python內置的修飾器特性。
.
修飾器模式是實現橫切關注點的絕佳方案,因為橫切關注點通用但不太適合使用面向對象編程范式來實現。修飾器可以幫助我們保持函數簡潔,同時不犧牲性能。

③.外觀模式

簡介:
外觀設計隱藏了系統的內部復雜性,并通過一個簡化的接口向客戶端暴露必要的部分。本質上,外觀(Facade)是在已有復雜系統之上實現的一個抽象層。

現實生活的例子:
企業的客服部門,汽車或摩托車的啟動鑰匙、計算機、電視等通過一個簡單按鈕就能激活的復雜電子設備

軟件的例子:
django-oscar-datacash模塊是Django的一個第三方組件,用于集成DataCash支付網關。該組件有一個Gateway類,提供對多種DataCash API的細粒度訪問。在那之上,它也包含一個Facade類,提供粗粒度API(提供給那些不需要處理細節的人),并針對審計目的提供保存事務的能力。
.
Caliendo是一個用于模擬Python API的的接口,它包含一個facade模塊。該模塊使用外觀模式來完成許多不同但有用的事情(比如緩存方法),并基于傳給頂層Facade方法的輸入對象決定返回什么方法。

應用案例:
使用外觀模式的最常見理由是為一個復雜系統提供單個簡單的入口點。引入外觀之后,客戶端代碼通過簡單地調用一個方法/函數就能使用一個系統。同時,內部系統并不會丟失任何功能, 外觀只是封裝了內部系統。
.
不把系統的內部功能暴露給客戶端代碼有一個額外的好處:我們可以改變系統內部,但客戶端代碼不用關心這個改變,也不會受這個改變的影響。客戶端代碼不需要進行任何改變
.
如果你的系統包含多層,外觀模式也能派上用場。你可以為每一層引入一個外觀入口點,并讓所有層級通過它們的外觀相互通信。這提高了層級之間的松耦合性,盡可能保持層級獨立

代碼實現:

from enum import Enum from abc import ABCMeta, abstractmethodState = Enum('State', 'new running sleeping restart zombie')class Server(metaclass=ABCMeta):@abstractmethoddef __init__(self):passdef __str__(self):return self.name@abstractmethoddef boot(self):pass@abstractmethoddef kill(self, restart=True):passclass FileServer(Server):"""服務進程FileServer除了Server接口要求實現的方法之外,還有一個create_file()方法用于創建文件。"""def __init__(self):'''初始化文件服務進程要求的操作'''self.name = 'FileServer'self.state = State.newdef boot(self):print('booting the {}'.format(self))'''啟動文件服務進程要求的操作'''self.state = State.runningdef kill(self, restart=True):print('Killing {}'.format(self))'''終止文件服務進程要求的操作'''self.state = State.restart if restart else State.zombiedef create_file(self, user, name, permissions):'''檢查訪問權限的有效性、用戶權限等'''print("trying to create the file '{}' for user '{}' with permissions {}".format(name, user, permissions))class ProcessServer(Server):"""服務進程ProcessServer除了Server接口要求實現的方法之外,還有一個create_process()方法用于創建進程。"""def __init__(self):'''初始化進程服務進程要求的操作'''self.name = 'ProcessServer'self.state = State.newdef boot(self):print('booting the {}'.format(self))'''啟動進程服務進程要求的操作'''self.state = State.runningdef kill(self, restart=True):print('Killing {}'.format(self))'''終止進程服務進程要求的操作'''self.state = State.restart if restart else State.zombiedef create_process(self, user, name):'''檢查用戶權限和生成PID等'''print("trying to create the process '{}' for user '{}'".format(name, user))class OperatingSystem:"""OperatingSystem類是一個外觀。__init__()中創建所有需要的服務進程實例。start()方法是系統的入口點,供客戶端代碼使用。如果需要,可以添加更多的包裝方法作為服務的訪問點,比如包裝方法create_file()和create_process()。從客戶端的角度來看,所有服務都是由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()

外觀模式總結:
在客戶端代碼想要使用一個復雜系統但又不關心系統復雜性之時,這種模式是為復雜系統提供一個簡單接口的理想方式。一臺計算機是一個外觀, 因為當我們使用它時需要做的事情僅是按一個按鈕來啟動它;其余的所有硬件復雜性都用戶無感知地交由BIOS、引導加載程序以及其他系統軟件來處理。現實生活中外觀的例子更多,比如,我們所致電的銀行或公司客服部門,還有啟動機動車所使用的鑰匙。
.
我們討論了兩個使用外觀的Django第三方組件:django-oscar-datacash和Caliendo。前者使用外觀模式來提供一個簡單的DataCash API以及保存事務的能力,后者為多種目的使用了外觀,比如,緩存、基于輸入對象的類型決定應該返回什么。

④.享元模式:

簡介:
享元設計模式通過為相似對象引入數據共享來小化內存使用,提升性能。一個享元(Flyweight)就是一個包含狀態獨立的不可變(又稱固有的)數據的共享對象。

現實生活的例子:
享元模式是一個用于優化的設計模式。因此,要找一個合適的現實生活的例子不太容易。我們可以把享元看作現實生活中的緩存區。例如,許多書店都有專用的書架來擺放新和流行的出版物。這就是一個緩存區,你可以先在這些專用書架上看看有沒有正在找的書籍,如果沒找到, 則可以讓圖書管理員來幫你。

軟件的例子:
Exaile音樂播放器使用享元來復用通過相同URL識別的對象(在這里是指音樂歌曲)。創建一個與已有對象的URL相同的新對象是沒有意義的,所以復用相同 的對象來節約資源。Peppy是一個用Python語言實現的類XEmacs編輯器,它使用享元模式存儲major mode狀態欄的狀態。這是因為除非用戶修改,否則所有狀態欄共享相同的屬性。

應用案例:
享元旨在優化性能和內存使用。所有嵌入式系統(手機、平板電腦、游戲終端和微控制器等)和性能關鍵的應用(游戲、3D圖形處理和實時系統等)都能從其獲益。若想要享元模式有效,需要滿足GoF的《設計模式》一書羅列的以下幾個條件:
1. 應用需要使用大量的對象。
2. 對象太多,存儲/渲染它們的代價太大。一旦移除對象中的可變狀態(因為在需要之時,應該由客戶端代碼顯式地傳遞給享元),多組不同的對象可被相對更少的共享對象所替代。
3. 對象ID對于應用不重要。對象共享會造成ID比較的失敗,所以不能依賴對象ID(那些在客戶端代碼看來不同的對象,終具有相同的ID)

代碼實現:

import random from enum import Enum# 個Enum類型變量描述三種不同種類的水果樹 Treetype = Enum('TreeType', 'apple_tree cherry_tree peach_tree')class Tree:"""pool變量是一個對象池(換句話說,是我們的緩存)。注意:pool是一個類屬性(類的所有實例共享的一個變量。使用特殊方法__new__(這個方法在__init__之 前被調用),我們把Tree類變換成一個元類,元類支持自引用。這意味著cls引用的是Tree類。當客戶端要創建Tree的一個實例時,會以tree_type參數傳遞樹的種 類。樹的種類用于檢查是否創建過相同種類的樹。如果是,則返回之前創建的對象;否則,將這個新的樹種添加到池中,并返回相應的新對象"""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] = objobj.tree_type = tree_typereturn objdef render(self, age, x, y):"""方法render()用于在屏幕上渲染一棵樹。"""print('render a tree of type {} and age {} at ({}, {})'.format(self.tree_type, age, x, y))def main():rnd = random.Random()# 一棵樹的年齡是1到30年之間的一個隨機值。age_min, age_max = 1, 30 # 單位為年# 坐標使用1到100之間的隨機值min_point, max_point = 0, 100tree_counter = 0for _ 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 += 1for _ 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 += 1for _ 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 += 1print('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()

享元模式的小結:
在我們想要優化內存使用提高應用性能之時,可以使用享元。在所有內存受限(想一想嵌入式系統)或關注性能的系統(比如圖形軟件和電子游戲)中,這一點相當重要。基于GTK+的Exaile音樂播放器使用享元來避免對象復制,Peppy文本編輯器則使用享元來共享狀態欄的屬性。
.
一般來說,在應用需要創建大量的計算代價大但共享許多屬性的對象時,可以使用享元。重點在于將不可變(可共享)的屬性與可變的屬性區分開。我們實現了一個樹渲染器,支持三種不同的樹家族。通過顯式地向render方法提供可變的年齡和x,y屬性,我們成功地僅創建了3個不同的對象,而不是18個。

⑤.模型—視圖—控制器模式:

簡介:
關注點分離(Separation of Concerns,SoC)原則是軟件工程相關的設計原則之一。SoC原則背后的思想是將一個應用切分成不同的部分,每個部分解決一個單獨的關注點。。分層設計中的層次(數據訪問層、業務邏輯層和表示層等)即是關注點的例子。使用SoC原則能簡化軟件應用的開發和維護。
.
模型—視圖—控制器(Model-View-Controller,MVC)模式是應用到面向對象編程的Soc原則。
.
MVC 被認為是一種架構模式而不是一種設計模式。架構模式與設計模式之間的區別在于前者比后者的 范疇更廣。
1. 模型是核心的部分,代表著應用的信息本源,包含和管理(業務)邏輯、數據、狀態以及應用的規則。
2. 視圖是模型的可視化表現。視圖的例子有,計算機圖形用戶界面、計算機終端的文本輸出、智能手機的應用圖形界面、PDF文檔、餅 圖和柱狀圖等。視圖只是展示數據,并不處理數據。
3. 控制器是模型與視圖之間的鏈接/粘附。模型與視圖之間的所有通信都通過控制器進行
.
MVC的優勢:
無需修改模型就能使用多個視圖的能力(甚至可以根據需要同時使用多個視圖)。為了實現模型與其表現之間的解耦,每個視圖通常都需要屬于它的控制器。如果模型直接與特定視圖通信,我們將無法對同一個模型使用多個視圖(或者至少無法以簡潔模塊化的方式實現)。

現實生活的例子:
你造一棟新房子,通常會請不同的專業人員來完成安裝管道和電路 、粉刷房子等。。。

軟件的例子:
Web框架web2py是一個支持MVC模式的輕量級Python框架
Django也是一個MVC框架,但是它使用了不同的命名約定。在此約定下,控制器被稱為視圖,視圖被稱為模板。Django使用名稱模型—模板—視圖(Model-Template-View,MTV)來替代MVC。

應用案例:
MVC是一個非常通用且大有用處的設計模式。這一模式提供了以下這些好處:
1. 視圖與模型的分離允許美工一心搞UI部分,程序員一心搞開發,不會相互干擾。
2. 由于視圖與模型之間的松耦合,每個部分可以單獨修改或者擴展,不會相互影響。例如,添加一個新視圖的成本很小,只要 為其實現一個控制器就可以了。
3. 因為職責明晰,維護每個部分也更簡單。

代碼實現:

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 valueclass QuoteTerminalView:"""視圖有三個方法,分別是show()、error()和select_quote()。"""def show(self, quote):"""show()用于在屏幕上輸 出一句名人名言(或者輸出提示信息Not found!);"""print('And the quote is: "{}"'.format(quote))def error(self, msg):"""error()用于在屏幕上輸出一條錯誤消息;"""print('Error: {}'.format(msg))def select_quote(self):"""select_quote()用于讀取用戶的選擇"""return input('Which quote number would you like to see? ')class QuoteTerminalController:"""控制器負責協調"""def __init__(self):"""__init__()方法初始化模型和視圖"""self.model = QuoteModel()self.view = QuoteTerminalView()def run(self):"""run()方法校驗用戶提供的名言索 引,然后從模型中獲取名言,并返回給視圖展示"""valid_input = Falsewhile not valid_input:try:n = self.view.select_quote()n = int(n)vaild_input = Trueexcept ValueError as err:self.view.error("Incorrect index '{}'".format(n))quote = self.model.get_quote(n)self.view.show(quote)def main():controller = QuoteTerminalController()while True:controller.run()if __name__ == '__main__':main()

MVC模式總結:
在從頭開始實現MVC時,請確保創建的模型很智能,控制器很瘦,視圖很傻瓜。
.
- 可以將具有以下功能的模型視為智能模型。
1. 包含所有的校驗/業務規則/邏輯
2. 處理應用的狀態
3. 訪問應用數據(數據庫、云或其他)
4. 不依賴UI
.
- 可以將符合以下條件的控制器視為瘦控制器。
1. 在用戶與視圖交互時,更新模型
2. 在模型改變時,更新視圖
3. 如果需要,在數據傳遞給模型/視圖之前進行處理
4. 不展示數據
5. 不直接訪問應用數據
6. 不包含校驗/業務規則/邏輯
.
- 可以將符合以下條件的視圖視為傻瓜視圖。
1. 展示數據
2. 允許用戶與其交互
3. 僅做小的數據處理,通常由一種模板語言提供處理能力(例如,使用簡單的變量和循環控制)
4. 不存儲任何數據
5. 不直接訪問應用數據
6. 不包含校驗/業務規則/邏輯
.
MVC是一個非常重要的設計模式,用于將應用組織成三個 部分:模型、視圖和控制器。
.
每個部分都有明確的職責。模型負責訪問數據,管理應用的狀態。視圖是模型的外在表現。視圖并非必須是圖形化的;文本輸出也是一種好視圖。控制器是模型與視圖之間的連接。MVC的恰當使用能確保終產出的應用易于維護、易于擴展。
.
Python框架web2py使用MVC作為核心架構理念。即使是簡單的web2py例子也使用了MVC來實現模塊化和可維護性。Django也是一個MVC框架,但它使用的名稱是MTV。
.
使用MVC時,請確保創建智能的模型(核心功能)、瘦控制器(實現視圖與模型之間通信的能力)以及傻瓜式的視圖(外在表現,小化邏輯處理)。

⑥.代理模式

簡介:
在某些應用中,我們想要在訪問某個對象之前執行一個或多個重要的操作,例如,訪問敏感信息——在允許用戶訪問敏感信息之前,我們希望確保用戶具備足夠的權限。操作系統中也存在類似的情況,用戶必須具有管理員權限才能在系統中安裝新程序,這類操作通常使用代理設計模式(Proxy design pattern)來實現。
.
以下是四種不同的知名代理類型:
1. 遠程代理:實際存在于不同地址空間(例如,某個網絡服務器)的對象在本地的代理者。
2. 虛擬代理:用于懶初始化,將一個大計算量對象的創建延遲到真正需要的時候進行。
3. 保護/防護代理:控制對敏感對象的訪問。
4. 智能(引用)代理:在對象被訪問時執行額外的動作。此類代理的例子包括引用計數和線程安全檢查。

現實生活的例子:
芯片(又名芯片密碼)卡是現實生活中使用防護代理的一個好例子。借記或信用卡包含一個芯片,ATM機或讀卡器需要先讀取芯片;在芯片通過驗證后, 需要一個密碼(PIN)才能完成交易。這意味著只有在物理地提供芯片卡并且知道密碼時才能進行交易。
.
使用銀行支票替代現金進行購買和交易是遠程代理的一個例子。支票準許了對一個銀行賬戶 的訪問。

軟件的例子:
Python的weakref模塊包含一個proxy()方法,該方法接受一個輸入對象并將一個智能代理返回給該對象。弱引用是為對象添加引用計數支持的一種推薦方式。
.
ZeroMQ是一組專注于分布式計算的自由開源軟件項目。ZeroMQ的Python實現有一個代理模塊,實現了一個遠程代理。該模塊允許Tornado的處理程序在不同的遠程進程中運行。

代碼實現:

保護代理演示class SensitiveInfo:"""SensitiveInfo類包含我們希望保護的信息。users變量是已有用戶的列表。read()方法 輸出用戶列表。add()方法將一個新用戶添加到列表中。"""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:"""Info類是SensitiveInfo的一個保護代理。secret變量值是客戶端代碼在添加新用戶時被要求告知/提供的密碼。注意,這只是一個例子。現實中,永遠不要執行以下操作:? 在源代碼中存儲密碼? 以明文形式存儲密碼? 使用一種弱(例如,MD5)或自定義加密形式read()方法是SensetiveInfo.read()的一個包裝。add()方法確保僅當客戶端代碼知道 密碼時才能添加新用戶"""def __init__(self):self.protected = SensitiveInfo()self.secret = '111111'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():"""main()函數展示了客戶端代碼可以如何使用代理模式。客戶端代碼創建一個Info類的實例,并使用菜單讓用戶選擇來讀取列表、添加新用戶或退出應用。"""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() 虛擬代理演示:class LazyProperty:def __init__(self, method):self.method = methodself.method_name = method.__name__print('function overriden: {}'.format(self.method))print("function's name: {}".format(self.method_name))def __get__(self, obj, cls):if not obj:return Nonevalue = self.method(obj)print('value {}'.format(value))setattr(obj, self.method_name, value)return valueclass Test:def __init__(self):self.x = 'foo'self.y = 'bar'self._resource = None@LazyPropertydef resource(self):print('initializing self._resource which is: {}'.format(self._resource))self._resource = tuple(range(5)) # 假設這一行的計算成本比較大return self._resourcedef main():t = Test()print(t.x)print(t.y)"""做更多的事情……"""print(t.resource)print(t.resource)print(t.resource)if __name__ == '__main__':main()

應用案例:
因為存在至少四種常見的代理類型,所以代理設計模式有很多應用案例,如下所示:
.
1. 在使用私有網絡或云搭建一個分布式系統時。在分布式系統中,一些對象存在于本地內存中,一些對象存在于遠程計算機的內存 中。如果我們不想本地代碼關心兩者之間的區別,那么可以創建一個遠程代理來隱藏或封裝,使得應用的分布式性質透明化。
2. 因過早創建計算成本較高的對象導致應用遭受性能問題之時。使用虛擬代理引入懶初始化,僅在真正需要對象之時才創建,能夠 明顯提高性能。
3. 用于檢查一個用戶是否有足夠權限來訪問某個信息片段。如果應用要處理敏感信息(例如,醫療數據),我們會希望確保用戶在 被準許之后才能訪問/修改數據。一個保護/防護 代理可以處理所有安全相關的行為。
4. 應用(或庫、工具集、框架等)使用多線程,而我們希望把線程安全的重任從客戶端代碼轉移到應用。這種情況下,可以創建一個 智能代理,對客戶端隱藏線程安全的復雜性。
5. 對象關系映射(Object-Relational Mapping,ORM)API也是一個如何使用遠程代理的例子。包括Django在內的許多流行Web 框架使用一個ORM來提供類OOP的關系型數據庫訪問。ORM是關系型數據庫的代理,數據庫可以部署在任意地方,本地或遠程服務 器都可以。

代理模式小結:
我們使用代理模式實現一個實際類的替代品,這樣可以在訪問實際類之前(或之后)做一些額外的事情。
.
芯片卡和銀行支票是人們每天都在使用的兩個不同代理的例子。芯片卡是一個防護代理,而銀行支票是一個遠程代理。另外,一些流行軟件中也使用代理。Python有一個weakref.proxy()方法,使得創建一個智能代理非常簡單。ZeroMQ的Python實現則使用了遠程代理。
.
我們討論了幾個代理模式的應用案例,包括性能、安全及向用戶提供簡單的API。在第二個代碼示例中,我們實現一個保護代理來處理用戶信息。這個例子可以以多種方式進行改進,特別是關于其安全缺陷和用戶列表實際上未持久化(永久存儲)的問題。

總結

以上是生活随笔為你收集整理的《精通python设计模式》读书笔记之——结构型设计模式的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。