摘要:最重要的是,任何設(shè)計模式都是一把雙刃劍。另一個設(shè)計模式是迭代器,在語言中以循環(huán)的形式實現(xiàn)。我們可以換個方法,利用策略模式和適配器模式的變種,能夠完美處理這種情景。何時使用它單例模式也許是最簡單的設(shè)計模式了。
使用python解釋設(shè)計模式
原文地址
有沒有好奇過設(shè)計模式是什么呢?在這篇文章中,我們將了解為什么設(shè)計模式是重要的,同時也會給出一些python的例子,解釋為什么以及在什么時候使用設(shè)計模式。
設(shè)計模式是我們?nèi)粘S龅降木幊虇栴}時的優(yōu)化過的可重用的解決方案。一個設(shè)計模式不是可以直接嵌入到我們系統(tǒng)中的一個庫或者一個類;它的含義要比這廣泛的多。它是一個必須在特地場景實現(xiàn)的模板。同時設(shè)計模式也不是針對特地語言的。一個好的設(shè)計模式應(yīng)該可以在幾乎所有-如果不是全部的話-的語言實現(xiàn),這取決于語言的能力。最重要的是,任何設(shè)計模式都是一把雙刃劍。如果在錯誤的場景使用,它將會給你帶來很多麻煩。然而,在合適的場景使用,它將成為你的救世主。
起初,你可以把設(shè)計模式看做解決特定一類問題的非常深刻巧妙的方式。就像是一群人考慮了一個問題的各方面然后想出一個最通用,靈活的解決方案。這個問題可能是你之前看見過的解決過的,但是你的解決方案可能不具備設(shè)計模式中的完備性。
盡管他們把這叫做“設(shè)計模式”,但這和設(shè)計領(lǐng)域并沒有什么關(guān)系。一種模式和傳統(tǒng)意義上關(guān)于分析,設(shè)計和實現(xiàn)的思想相差甚遠,一種模式更多地是象征程序中體現(xiàn)的一個完整的想法,因此有時候設(shè)計模式會出現(xiàn)在分析階段或者更高層的設(shè)計階段。這是挺有趣的一件事因為一個模式在代碼層面有一個直接的實現(xiàn)所以在底層設(shè)計或?qū)崿F(xiàn)之前你可能看不見它的身影。(事實上你可能在這些階段之前都不會意識到自己需要一個特定的設(shè)計模式)
模式的基本概念也可以被當(dāng)作是程序設(shè)計的基本概念:添加一個抽象層。當(dāng)你在抽象某個事物的時候,你也在將特定的細(xì)節(jié)獨立出來,這當(dāng)中最強烈的動機便是把變化的與不變的分開。換個說法就是一旦你在程序中找到了由于種種原因可能變動的部分,你會想要防止它們在代碼中產(chǎn)生其他的變動。這不僅使得代碼更加容易維護,并且更加易讀。
通常,開發(fā)一個優(yōu)雅而易維護的架構(gòu)最困難的部分在于探究“變化的矢量(vector)”(在這里vector指最大的梯度而不是容器類)。意思就是找到在你的系統(tǒng)中發(fā)生變化的最重要的東西。換句話說找到你最巨大的開銷。一旦你找到了改變的vector,你便有了一個架構(gòu)設(shè)計的重點。
所以設(shè)計模式的目的是隔離你代碼中的改變。如果以這個角度來看的話,你已經(jīng)看過一些設(shè)計模式了。比如說,繼承可以被看做是一種設(shè)計模式(盡管它是被編譯器實現(xiàn)的)它讓你可以表達有同樣接口的對象在行為上的差異。組合也可以被看做是一個設(shè)計模式,因為它讓你靜態(tài)或動態(tài)地改變類的對象以及類工作的方式。
另一個設(shè)計模式是迭代器,在語言中以for 循環(huán)的形式實現(xiàn)。迭代器允許你隱藏容器實現(xiàn)的細(xì)節(jié),就像你在一步一步遍歷元素。于是你可以寫出在一個序列上處理所有元素的通用代碼而不需要考慮序列建立的方式,因此你的通用代碼可以被任何生成出迭代器的對象使用。
有三種基本的設(shè)計模式:
結(jié)構(gòu)化模式通常處理實體之間的關(guān)系,使他們之間的合作溝通更加簡單。
創(chuàng)造性模式提供實例化的機制,使得在特殊場景下創(chuàng)建對象變得更加方便。
行為模式通常被用在實體之間的通信,使得他們之間的通信更加簡單和靈活。
為什么我們需要設(shè)計模式設(shè)計模式在原則上是程序設(shè)計中經(jīng)過深思熟慮的解決方案。許多程序員在之前遇到過類似的問題,然后用這些方案來解決它們。如果你碰上了類似的問題,為什么二次創(chuàng)造一個已經(jīng)有給定答案的方案呢?
例子讓我們想象一下你需要一個方法來合并兩個不同的類。而這兩個類在現(xiàn)有的系統(tǒng)的不同地方中已經(jīng)被使用很多了,這使得改變已有代碼非常困難。如果要合并的話,改變已有代碼也需要測試所有改變過的代碼,由于系統(tǒng)依賴不同的組件,很大可能會形成新的bug。我們可以換個方法,利用策略模式(strategy pattern)和適配器模式(adapter pattern)的變種,能夠完美處理這種情景。
class StrategyAndAdapterExampleClass(): def __init__(self, context, class_one, class_two): self.context = context self.class_one = class_one self.class_two = class_two def operation1(self): if self.context == "Context_For_Class_One": self.class_one.operation1_in_class_one_context() else: self.class_two.operational_in_class_two_context()
非常簡單不是嗎?現(xiàn)在,讓我們仔細(xì)地看看策略模式。
策略模式(Strategy Pattern)策略模式是一種讓你在運行時決定程序具體行為的行為模式。你可以將兩個類中不同的算法封裝起來,然后在運行時(runtime)決定你想采取哪個策略。
在我們上面的例子里,策略的選擇是基于context變量在實例化類的時候被賦予的值。如果context變量被賦值為class_one,它將使用class_one,反之亦然。
在哪可以使用想象一下你正在開發(fā)一個創(chuàng)建或者更新用戶紀(jì)錄的類。它需要相同的輸入(姓名,地址,手機,等)但是取決于給定的情況,它在更新和創(chuàng)建時會調(diào)用不同的函數(shù)。你當(dāng)然可以使用if else語句來實現(xiàn),但這樣的話你就必須寫很多個if else語句。直接說明你的上下文不是方便多了?
class User(): def create_or_update(self, name, address, mobile, userid=None): if userid: # it means the user doesn"t exist yet, create a new record else: # it means the user already exists, just update based on the given userid
通常情況下的策略模式會將你的代碼封裝到另外一個類中,但在該例中,使用另外的類會很浪費。記住你不需要完全按照模板來操作。只要符合原則,能解決問題即可。
適配器模式(Adapter Pattern)適配器模式是一個結(jié)構(gòu)化的設(shè)計模式,它讓你使用一個不同的接口來重用一個類,使它能在一個調(diào)用不同方法的系統(tǒng)中使用。
該模式也允許你更改從client class接受的輸入,使其與被適配對象的函數(shù)兼容。
如何使用?適配器類的另一個名稱叫做封裝類,它讓你將一系列操作封裝在一個類里然后在合適的場合重用這些操作。
比較下面兩種實現(xiàn)。
class User(object): def create_or_update(self): pass class Profile(object): def create_or_update(self): pass user = User() user.create_or_update() profile = Profile() profile.create_or_update()
如果我們需要在不同的地方重新做一次,或者在另一個項目中重用這份代碼,我們需要從頭開始再寫一遍。
更好的選擇account_domain = Account() account_domain.NewAccount()
在此處,我們有一個封裝類,為Account:
class User(object): def create_or_update(self): pass class Profile(object): def create_or_update(self): pass class Account(): def new_account(self): user = User() user.create_or_update() profile = Profile() profile.create_or_update()
按這種方式,在需要的時候可以重用你的Account,當(dāng)然你也可以在你的主類下包裝其他的類。
工廠模式工廠模式是一種創(chuàng)造型設(shè)計模式,正如其名,它像工廠一樣生產(chǎn)出對象實例。
該模式的主要目的是將可能擴展到不同類中的創(chuàng)建過程封裝進單個函數(shù)。通過提供合適的上下文,它會返回合適的對象。
何時使用它?使用工廠模式的最佳時機是你的單個實體有多個變種時。假設(shè)你有一個按鈕類,該類有不同的變化,比如圖像按鈕,輸入按鈕,flash按鈕。你可以會根據(jù)不同場景創(chuàng)建不同的按鈕,此時便可以使用工廠來為你創(chuàng)造按鈕。
首先我們先創(chuàng)造三個類:
class Button(object): html = "" def get_html(self): return self.html class Image(Button): html = "" class Input(Button): html = "" class Flash(Button): html = ""
接下來我們可以創(chuàng)建工廠類:
class ButtonFactory(): def create_button(self, typ): targetclass = typ.capitalize() return globals()[targetclass]()
我們可以這樣使用它:
button_obj = ButtonFactory() button = ["image", "input", "flash"] for b in button: print button_obj.create_button(b).get_html()
輸出即為所有按鈕類型的html。按此法,你可以在不同場景創(chuàng)建不同的按鈕并且可以重用代碼。
裝飾器模式裝飾器模式是一種結(jié)構(gòu)型模式。它能夠讓我們在運行時根據(jù)情景為對象添加額外的行為。
該模式的目的是在特定情景下對實例進行額外的操作同時也可以創(chuàng)建原有的實例。該模式允許對一個實例添加多個裝飾器。該模式可以用來替換子類,創(chuàng)建一個類繼承父類的功能。和在編譯時添加行為的子類不同,裝飾器允許你在運行時添加新的行為。
為了實現(xiàn)裝飾器模式,我們可以按照下面的步驟。
將原來的組件類封裝進裝飾器類作為子類。
在裝飾器類中,添加一個字段作為組件的指針。
傳遞一個組件給裝飾器的構(gòu)造函數(shù)來初始化指針。
在裝飾器類中,將所有的組件方法重定向到指針,并且
在裝飾器類中,覆蓋所有需要修改的 組件方法,
何時使用它?使用裝飾器模式的最好場景是當(dāng)你在特定時期
需要給某個實體特殊的行為時。假設(shè)你有一個HTML鏈接元素,一個登出鏈接,你希望基于當(dāng)前頁面做出輕微的改變。此時我們可以使用裝飾器模式。
首先我們需要建立不同的“裝飾”。如果我們在主頁登陸,該鏈接就需要用h2標(biāo)簽包裹。
如果我們在不同的頁面登錄,該鏈接需要用下劃線標(biāo)簽包裹。
如果我們已登錄,那么該鏈接將被包裹成加強標(biāo)簽。一旦我們定義好了“裝飾”,我們就可以開始編程了。
class HtmlLinks(): def set_html(self, html): self.html = html def get_html(self): return self.html def render(self): print(self.html) class LogoutLink(HtmlLinks): def __init__(self): self.html = " Logout " class LogoutLinkH2Decorator(HtmlLinks): def __init__(self, logout_link): self.logout_link = logout_link self.set_html(" {0} ".format(self.logout_link.get_html())) def call(self, name, args): self.logout_link.name(args[0]) class LogoutLinkUnderlineDecorator(HtmlLinks): def __init__(self, logout_link): self.logout_link = logout_link self.set_html(" {0} ".format(self.logout_link.get_html())) def call(self, name, args): self.logout_link.name(args[0]) class LogoutLinkStrongDecorator(HtmlLinks): def __init__(self, logout_link): self.logout_link = logout_link self.set_html(" {0} ".format(self.logout_link.get_html())) def call(self, name, args): self.logout_link.name(args[0]) logout_link = LogoutLink() is_logged_in = 0 in_home_page = 0 if is_logged_in: logout_link = LogoutLinkStrongDecorator(logout_link) if in_home_page: logout_link = LogoutLinkH2Decorator(logout_link) else: logout_link = LogoutLinkUnderlineDecorator(logout_link) logout_link.render()單例模式
單例模式是一種創(chuàng)造型模式,它保證你在runtime時只有一個特定的實例,提供一個全局的指針。
這為其他人調(diào)用該實例變得更加簡單,由于該變量總會返回同樣的東西。
何時使用它?單例模式也許是最簡單的設(shè)計模式了。它給某個特殊的類型提供了獨一無二的實例。為了達成這點 ,你必須操縱該類型的創(chuàng)建過程。一種簡便的方法是將單例委托給一個內(nèi)部的私有類。
class OnlyOne: class __OnlyOne: def __init__(self, arg): self.val = arg def __str__(self): return repr(self) + self.val instance = None def __init__(self, arg): if not OnlyOne.instance: OnlyOne.instance = OnlyOne.__OnlyOne(arg) else: OnlyOne.instance.val = arg def __getattr__(self, name): return getattr(self.instance, name) x = OnlyOne("sausage") print(x) y = OnlyOne("eggs") print(y) z = OnlyOne("spam") print(z) print(x) print(y) print(`x`) print(`y`) print(`z`) output = """ <__main__.__OnlyOne instance at 0076B7AC>sausage <__main__.__OnlyOne instance at 0076B7AC>eggs <__main__.__OnlyOne instance at 0076B7AC>spam <__main__.__OnlyOne instance at 0076B7AC>spam <__main__.__OnlyOne instance at 0076B7AC>spam <__main__.OnlyOne instance at 0076C54C> <__main__.OnlyOne instance at 0076DAAC> <__main__.OnlyOne instance at 0076AA3C> """
由于內(nèi)部類的名字以雙下劃線開頭,所以用戶是不能直接訪問它的。內(nèi)部的類包含了所有你需要的方法,然后將它封裝進你創(chuàng)建的單例類。你第一次創(chuàng)建OnlyOne的時候,它會對實例初始化,在此之后它會忽略你的創(chuàng)建請求。
訪問實例通過使用__getattr__()的方法來重定向你的訪問請求。你可以從輸出看出即使看上去創(chuàng)建了很多實例,但實際上只創(chuàng)建了一個__OnlyOne 對象。OnlyOne的實例是獨立的,但是他們都指向同一個__OnlyOne 對象。
注意上述方法并不限制你只創(chuàng)建一個實例。這也是一種創(chuàng)建限定數(shù)量的對象池的方法。但在此情景下,你可能會遇到對象池分享的問題。如果該問題出現(xiàn)了,你可以創(chuàng)建相應(yīng)的check in 和 check out 方法。
總結(jié)由于python的動態(tài)語言特性,設(shè)計模式在python中并不是非常重要的概念,但是在python中展示設(shè)計模式非常簡單。記住類和函數(shù)本身是一等公民所以有些設(shè)計模式可能不是非常實用。除此之外也有許多其他的設(shè)計模式。在本文中我只介紹了一些在編程中使用較多的設(shè)計模式。如果你對其他設(shè)計模式感興趣,可以訪問相應(yīng)的wiki頁面。
最后一件事,當(dāng)你使用設(shè)計模式時,確保你在試圖解決正確的問題。我之前也提及過,設(shè)計模式是把雙刃劍:如果用在了錯誤的地方,它將把事情變的更糟。如果用在對的地方,它將是至關(guān)重要的。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/37623.html
摘要:友情鏈接譯技能測試解決方案中的數(shù)據(jù)科學(xué)一譯技能測試解決方案中的數(shù)據(jù)科學(xué)二譯技能測試解決方案中的數(shù)據(jù)科學(xué)三 本文是譯文,可以轉(zhuǎn)載,但需注明出處,點擊這里可以獲取原文,有刪減。本系列博文包含四篇文章:【譯】技能測試解決方案:Python中的數(shù)據(jù)科學(xué)(一)——Q1-Q15【譯】技能測試解決方案:Python中的數(shù)據(jù)科學(xué)(二)——Q16-Q30【譯】技能測試解決方案:Python中的數(shù)據(jù)科學(xué)(...
摘要:已獲原作者授權(quán)原系列地址下面我們將以中最簡單的控件控件開始這個系列的教程在中控件用以顯示文字和圖片通常被用來展示信息而非與用戶交互譯者注也可以綁定點擊等事件只是通常不這么用程序員的教程怎么能少了我們尊重這個傳統(tǒng)但我們不說讓我們來秀出吧下面的 已獲原作者授權(quán). 原系列地址: Python Tkinter Hello Tkinter Label 下面我們將以 Tkinter 中最簡單的控...
摘要:因此,當(dāng)任何由返回的函數(shù)被調(diào)用時,的值將在附近的范圍進行查找。下面是解決這一問題的一些方法。另外一個解決方案就是創(chuàng)造一個閉包,利用默認(rèn)函數(shù)立即綁定。當(dāng)缺失時,執(zhí)行類,字典的實例將自動實例化這個數(shù)列。 1、下面這段代碼的輸出結(jié)果是什么?請解釋。 def extendList(val, list=[]): list.append(val) return list list...
摘要:在關(guān)聯(lián)該數(shù)據(jù)集時,重復(fù)行會帶來一定的困擾,為了避免這個困擾,我們只保留重復(fù)數(shù)據(jù)第一個出現(xiàn)的樣本。 本文是譯文,可以轉(zhuǎn)載,但需注明出處,點擊這里可以獲取原文,有刪減。本系列博文包含四篇文章:【譯】技能測試解決方案:Python中的數(shù)據(jù)科學(xué)(一)——Q1-Q15【譯】技能測試解決方案:Python中的數(shù)據(jù)科學(xué)(二)——Q16-Q30 【譯】技能測試解決方案:Python中的數(shù)據(jù)科學(xué)(三)...
閱讀 1049·2021-11-22 13:53
閱讀 1599·2021-11-17 09:33
閱讀 2401·2021-10-14 09:43
閱讀 2863·2021-09-01 11:41
閱讀 2280·2021-09-01 10:44
閱讀 2921·2021-08-31 09:39
閱讀 1457·2019-08-30 15:44
閱讀 1866·2019-08-30 13:02