亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

寫給Python初學者的設計模式入門

2019-11-14 17:31:10
字體:
來源:轉載
供稿:網友

 有沒有想過設計模式到底是什么?通過本文可以看到設計模式為什么這么重要,通過幾個Python的示例展示為什么需要設計模式,以及如何使用。

 設計模式是什么?

  設計模式是經過總結、優化的,對我們經常會碰到的一些編程問題的可重用解決方案。一個設計模式并不像一個類或一個庫那樣能夠直接作用于我們的代碼。反之,設計模式更為高級,它是一種必須在特定情形下實現的一種方法模板。設計模式不會綁定具體的編程語言。一個好的設計模式應該能夠用大部分編程語言實現(如果做不到全部的話,具體取決于語言特性)。最為重要的是,設計模式也是一把雙刃劍,如果設計模式被用在不恰當的情形下將會造成災難,進而帶來無窮的麻煩。然而如果設計模式在正確的時間被用在正確地地方,它將是你的救星。

  起初,你會認為“模式”就是為了解決一類特定問題而特別想出來的明智之舉。說的沒錯,看起來的確是通過很多人一起工作,從不同的角度看待問題進而形成的一個最通用、最靈活的解決方案。也許這些問題你曾經見過或是曾經解決過,但是你的解決方案很可能沒有模式這么完備。

  雖然被稱為“設計模式”,但是它們同“設計“領域并非緊密聯系。設計模式同傳統意義上的分析、設計與實現不同,事實上設計模式將一個完整的理念根植于程序中,所以它可能出現在分析階段或是更高層的設計階段。很有趣的是因為設計模式的具體體現是程序代碼,因此可能會讓你認為它不會在具體實現階段之前出現(事實上在進入具體實現階段之前你都沒有意識到正在使用具體的設計模式)。

  可以通過程序設計的基本概念來理解模式:增加一個抽象層。抽象一個事物就是隔離任何具體細節,這么做的目的是為了將那些不變的核心部分從其他細節中分離出來。當你發現你程序中的某些部分經常因為某些原因改動,而你不想讓這些改動的部分引發其他部分的改動,這時候你就需要思考那些不會變動的設計方法了。這么做不僅會使代碼可維護性更高,而且會讓代碼更易于理解,從而降低開發成本。

  設計出一個優雅的、易于維護的程序難點在于發現我所說的“變化的向量”(在這里,“向量”指的是最大的梯度變化方向(maximum gradient),而并非指一個容器類)。意思是找出系統中變化的最重要的部分,或者換句話說,發現影響系統最大的花銷在哪里。一旦你發現了變化的向量,你就可以圍繞這個重點設計你的程序。

  所以設計模式的目的就是分離代碼中的可變部分。如果你這么去審視這個問題,你會立刻看到多個設計模式。舉個例子,面向對象的繼承(inheritance)可以看做一種設計模式(雖然是由編譯器實現的)。它允許通過同樣的接口(不變的部分)來表現不同的行為(變化的部分)。組合也可以被認為是一種設計模式,因為它允許通過動態或靜態的方式改變實現類的對象以及他們的行為。

  另一個常見的設計模式例子是迭代器。迭代器自Python出現伊始就已經隨for循環的使用而存在了,并且在Python2.2版本的時候被明確成為其一個特性。一個迭代器隱藏了容器內部的具體實現,提供一個依次訪問容器對象內每個元素的方式。所以,你能夠使用通用的代碼對一個序列的每個元素做對應操作而不用去理會此序列是怎么建立的。所以你的代碼能夠對任何能夠產生迭代器的對象有效。

  這里列舉了三種最基本的設計模式:

  1. 結構化模式,通常用來處理實體之間的關系,使得這些實體能夠更好地協同工作。
  2. 創建模式,提供實例化的方法,為適合的狀況提供相應的對象創建方法。
  3. 行為模式,用于在不同的實體建進行通信,為實體之間的通信提供更容易,更靈活的通信方法。

 我們為什么要使用設計模式?

  從理論上來說,設計模式是對程序問題比較好的解決方案。無數的程序員都曾經遇到過這些問題,并且他們使用這些解決方案去處理這些問題。所以當你遇到同樣的問題,為什么要去想著創建一個解決方案而不是用現成的并且被證明是有效的呢?

  例子

  假定現在有一個任務,需要你找到一個有效的方法合并兩個做不同事情的類,在已有系統中這兩個類在許多不同的地方被大量使用,所以移除這兩個類或是改動已有的代碼都是異常困難的。不僅如此,更改已有的代碼會導致大量的測試工作,因為在這樣一種依賴大量不同組件的系統中,這些修改總是會引入一些新的錯誤。為了避免這些麻煩,你可以實現一個策略模式(Strategy Pattern)和適配器模式(Adapter Pattern)的變體,這兩種模式能夠很好的處理這種問題。

1
2
3
4
5
6
7
8
9
10
11
12
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()

  很簡單是吧?現在讓我們來仔細研究一下策略模式。

 策略模式

  策略模式是一種與行為相關的設計模式,允許你在運行時根據指定的上下文確定程序的動作。你可以在兩個類中封裝不同的算法,并且在程序運行時確定到底執行哪種策略。

  在上面的例子中,策略是根據實例化時context變量的值來決定的。如果給定context變量的值是“class_one”,將會執行class_one,否則就會執行class_two。

  我在那里使用它?

  假定你現在正在寫一個類能夠更新或創建一條新的用戶記錄,接收同樣的輸入參數(諸如姓名、地址、手機號等),但是根據不同的情況會調用對應的更新或是創建方法。當然,你可能會用一個if-else判斷處理這個問題,但是如果你需要在不同的地方使用這個類呢?那么你就得不停地重寫if-else判斷。為什么不簡單地通過指定上下文來解決這個問題。

1
2
3
4
5
6
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

  常規的策略模式涉及到將算法封裝到另一個類中,但如果這樣的話,那個類就太浪費了。切記不要死記模板,把握住核心概念靈活的變通,最重要是解決問題。

 適配器模式

  適配器模式是一個結構性的設計模式,允許通過不同的接口為一個類賦予新的用途,這使得使用不同調用方式的系統都能夠使用這個類。

  也可以讓你改變通過客戶端類接收到的輸入參數以適應被適配者的相關函數。

  怎么使用?

  另一個使用適配器類的地方是包裝器(wrapper),允許你講一個動作包裝成為一個類,然后可以在合適的情形下復用這個類。一個典型的例子是當你為一個table類簇創建一個domain類時,你能夠將所有的對應不同表的相同動作封裝成為一個適配器類,而不是一個接一個的單獨調用這些不同的動作。這不僅使得你能夠重用你想要的所有操作,而且當你在不同的地方使用同樣的動作時不用重寫代碼。

  比較一下兩種實現:

  不用適配器的方案

1
2
3
4
5
6
7
8
9
10
11
12
13
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()

  如果我們需要在不同的地方做同樣的事,或是在不同的項目中重用這段代碼,那么我們需要重新敲一遍。

  使用包裝類的解決方案

  看看我們怎么反其道而行:

1
2
account_domain = Account()
account_domain.NewAccount()

  在這種情況下,我們通過一個包裝類來實現賬戶domain類:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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()

  這樣的話,你就能夠在你需要的時候使用賬戶domain了,你也可以將其他的類包裝到domain類下。

 工廠模式

  工廠模式是一種創建型的設計模式,作用如其名稱:這是一個就像工廠那樣生產對象實例的類。

  這個模式的主要目的是將可能涉及到很多類的對象創建過程封裝到一個單獨的方法中。通過給定的上下文輸出指定的對象實例。

  什么時候使用?

  使用工廠模式的最佳時機就是當你需要使用到單個實體的多個變體時。舉個例子,你有一個按鈕類,這個按鈕類有多種變體,例如圖片按鈕、輸入框按鈕或是Flash按鈕等。那么在不同的場合你會需要創建不同的按鈕,這時候就可以通過一個工廠來創建不同的按鈕。

  讓我們先來創建三個類:

1
2
3
4
5
6
7
8
9
10
11
12
13
class Button(object):
    html = ""
    def get_html(self):
        return self.html
 
class Image(Button):
    html = "<img alt="" />"
 
class Input(Button):
    html = "<input type="text" />"
 
class Flash(Button):
    html = ""

  然后創建我們的工廠類:

1
2
3
4
class ButtonFactory():
    def create_button(self, typ):
        targetclass = typ.capitalize()
        return globals()[targetclass]()

  譯注:globals()將以字典的方式返回所有全局變量,因此targetclass = typ.capitalize()將通過傳入的typ字符串得到類名(Image、Input或Flash),而globals()[targetclass]將通過類名取到類的類(見元類),而globals()[targetclass]()將創建此類的對象。

我們可以這么使用工廠類:

1
2
3
4
button_obj = ButtonFactory()
button = ['image', 'input', 'flash']
for b in button:
    print button_obj.create_button(b).get_html()

  輸出將是所有按鈕類型的HTML屬性。這樣罵你就能夠根據不同的情況指定不同類型的按鈕了,并且很易于重用。

 裝飾器模式

  裝飾器模式是一個結構性模式,允許我們根據情況,在運行時為一個對象添加新的或附加的行為。

  目的是為給一個特定的對象實例應用擴展的函數方法,并且同時也能夠產生沒有新方法的原對象。它允許多裝飾器結合用于一個實例,所以你就不會出現實例同單個裝飾器相捆綁的情況了。這個模式是實現子類繼承外的一個可選方式,子類繼承是指從父類集成相應的功能。與子類繼承必須在編譯時添加相應的行為不同,裝飾器允許你在運行時根據需要添加新的行為。

  可以根據以下步驟實現裝飾器模式:

  1. 以原組件類為基類創建裝飾器類。
  2. 在裝飾器類中添加一個組件類的指針域
  3. 將一個組件傳遞給裝飾器類的構造器以初始化組件類指針
  4. 在裝飾器類中,將所有的組件方法指向組件類指針,并且,
  5. 在裝飾器類中,重寫每個需要修改功能的組件方法。

  相關維基百科(http://en.wikipedia.org/wiki/Decorator_pattern)

  什么時候使用?

  使用裝飾器模式的最佳時機是當你有一個根據情況需要添加新的行為的實體時。假設你有一個HTML鏈接元素,一個登出鏈接,并且你希望根據當前頁面對具體的行為做微小的改動。這種情況下,我們可以使用裝飾器模式。

  首先,建立我們所需要的裝飾模式。

  如果我們在主頁并且已經登錄,那么將登出鏈接用h2標簽標記。

  如果我們在不同的頁面并且已經登錄,那么用下劃線標簽標記鏈接

  如果已登錄,用加粗標記鏈接。

  一旦建立了裝飾模式,我們就可以開工了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
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 = "<a href="logout.html"> Logout </a>"
 
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("<strong> {0} </strong>".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()

 單例模式

  單例模式是一個創建型的設計模式,功能是確保運行時對某個類只存在單個實例對象,并且提供一個全局的訪問點來訪問這個實例對象。

  因為對于調用單例的其他對象而言這個全局唯一的訪問點“協調”了對單例對象的訪問請求,所以這些調用者看到的單例內變量都將是同一份。

  什么時候能夠使用?

  單例模式可能是最簡單的設計模式了,它提供特定類型的唯一對象。為了實現這個目標,你必須控制程序之外的對象生成。一個方便的方法是將一個私有內部類的單個對象作為單例對象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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>
'''

  因為內置類是用雙下劃線開始命名,所以它是私有的,用戶無法直接訪問。內置類包含了所有你希望放在普通類中的方法,并且通過外層包裝類的構造器控制其創建。當第一次你創建OnlyOne時,初始化一個實例對象,后面則會忽略創建新實例的請求。

  通過代理的方式進行訪問,使用__getattr__()方法將所有調用指向單例。你可以從輸出看到雖然看起來好像創建了多個對象(OnlyOne),但 __OnlyOne對象只有一個。雖然OnlyOne實例有多個,但他們都是唯一的 __OnlyOne對象的代理。

  請注意上面的方法并沒有限制你只能創建一個對象,這也是一個創建有限個對象池的技術。然而在那種情況下,你可能會遇到共享池內對象的問題。如果這真是一個問題,那你可以通過為共享對象設計簽入“check-in”和遷出“check-out”機制來解決這個問題。

 總結

  在本文中,我只列舉了幾個我再編程中覺得十分重要的設計模式來講,除此之外還有很多設計模式需要學習。如果你對其他的設計模式感興趣,維基百科的設計模式部分(http://en.wikipedia.org/wiki/Design_pattern_%28computer_science%29)可以提供很多信息。如果還嫌不夠,你可以看看四人幫的《設計模式:可復用面向對象軟件的基礎》(http://www.amazon.com/o/asin/0201633612)一書,此書是關于設計模式的經典之作。

  最后一件事:當使用設計模式時,確保你是用來解決正確地問題。正如我之前提到的,設計模式是把雙刃劍:如果使用不當,它們會造成潛在的問題;如果使用得當,它們則將是不可或缺的。

  原文鏈接: pypix.com   翻譯: 伯樂在線 - 熊崽Kevin


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
精品自拍视频在线观看| 精品成人在线视频| 国产自产女人91一区在线观看| 久久韩剧网电视剧| 亚洲色图在线观看| 国产精品久久久久久久av电影| 国产成人极品视频| 96pao国产成视频永久免费| 91精品在线观| 国外视频精品毛片| 日韩激情第一页| 日韩在线视频线视频免费网站| 色婷婷综合成人av| 欧洲美女免费图片一区| 久久精品视频网站| 精品久久久久久久久久久| 精品一区二区亚洲| zzjj国产精品一区二区| 日韩电影网在线| 精品爽片免费看久久| 国内伊人久久久久久网站视频| 亚洲精选中文字幕| 精品亚洲一区二区三区在线观看| 青青青国产精品一区二区| www.xxxx欧美| 亚洲免费一在线| 欧美视频在线视频| 成人性生交大片免费看视频直播| 亚洲老头同性xxxxx| 欧美成人免费va影院高清| 国产精品久久久久久久久久久新郎| 色婷婷综合久久久久| 欧美肥老太性生活视频| 国产精品国产福利国产秒拍| 2019中文字幕全在线观看| 欧美日韩精品在线| 最近2019年日本中文免费字幕| 91精品国产亚洲| 亚洲欧美综合图区| 久久久国产精品一区| 亚洲人成电影网站色xx| 国产综合久久久久久| 国产精品久久久久久五月尺| 国色天香2019中文字幕在线观看| 久久全国免费视频| 欧美日韩国产色| 久久99精品国产99久久6尤物| 色青青草原桃花久久综合| 精品国产91乱高清在线观看| 国产精品日韩一区| 日韩国产精品一区| 亚洲精品小视频在线观看| 一本色道久久综合狠狠躁篇怎么玩| 久久夜色精品亚洲噜噜国产mv| 精品人伦一区二区三区蜜桃网站| 97视频免费在线观看| 亚洲第一精品夜夜躁人人爽| 欧美精品少妇videofree| 亚洲色无码播放| 少妇av一区二区三区| 亚洲精品国产品国语在线| 午夜精品一区二区三区在线视| 色偷偷88888欧美精品久久久| 亚洲一区二区久久| 欧美精品亚州精品| 欧美国产一区二区三区| 久久久91精品国产一区不卡| 久久精品视频播放| 久久伊人色综合| 美女性感视频久久久| 国产亚洲精品美女久久久久| 久久视频在线视频| 欧美激情精品久久久久久久变态| 欧美日韩国产精品一区二区不卡中文| 丰满岳妇乱一区二区三区| 日韩美女主播视频| 国产一区二区动漫| 国产亚洲欧美日韩美女| 亚洲人午夜精品免费| 亚洲电影免费观看高清完整版在线观看| 亚洲午夜未满十八勿入免费观看全集| 欧美性xxxx极品hd欧美风情| 精品亚洲aⅴ在线观看| 欧美高跟鞋交xxxxhd| 亚洲人成伊人成综合网久久久| 亚洲一区二区三区在线免费观看| 精品日本美女福利在线观看| 久久久久久久影视| 亚洲第一精品福利| 91超碰caoporn97人人| 国产成人欧美在线观看| 亚洲精品aⅴ中文字幕乱码| 九九热精品视频| 精品少妇v888av| 精品国产一区二区三区久久狼黑人| 91免费综合在线| 精品激情国产视频| 一本一道久久a久久精品逆3p| 日韩电影中文字幕在线观看| 久久精品一区中文字幕| 亚洲人成在线一二| 最近2019好看的中文字幕免费| 伊人激情综合网| 欧美日韩国产91| 国产精品狠色婷| 日本最新高清不卡中文字幕| 成人亚洲欧美一区二区三区| 欧美最猛性xxxx| 国产精品va在线播放| 96精品久久久久中文字幕| 亚洲精品有码在线| 亚洲色图综合久久| 日韩电影免费在线观看| 中文字幕日韩av综合精品| 日韩精品一区二区视频| 亚洲第一天堂无码专区| 久久亚洲影音av资源网| 国产热re99久久6国产精品| 欧美日韩免费一区| 日韩免费电影在线观看| 韩国精品美女www爽爽爽视频| 国产精品日日摸夜夜添夜夜av| 亚洲高清免费观看高清完整版| 91久久精品在线| 久久中文字幕在线| 中文字幕成人精品久久不卡| 亚洲午夜精品久久久久久久久久久久| 欧美天天综合色影久久精品| 亚洲午夜精品久久久久久久久久久久| www.日本久久久久com.| 91精品视频大全| 日韩国产欧美精品在线| 激情亚洲一区二区三区四区| 欧美成人中文字幕在线| 日韩av中文字幕在线免费观看| 国产精品r级在线| 性色av一区二区三区红粉影视| 亚洲天天在线日亚洲洲精| 日韩成人激情影院| 久久手机免费视频| 亚洲免费精彩视频| 日本精品久久久| 亚洲视频电影图片偷拍一区| 亚洲男人天堂九九视频| 国产成人综合一区二区三区| 日韩网站在线观看| 亚洲91av视频| 26uuu日韩精品一区二区| 国产在线观看91精品一区| 青青在线视频一区二区三区| 亚洲香蕉成视频在线观看| 国产精品成人观看视频国产奇米| 日韩精品福利在线| 在线观看日韩www视频免费| 亚洲丁香婷深爱综合| 亚洲色图色老头| 欧洲中文字幕国产精品| 久久影院在线观看| 国产精品亚洲网站| 久久伊人精品视频| 国产欧美日韩中文字幕| 久久综合国产精品台湾中文娱乐网| 欧美一级淫片videoshd| 亚洲精品午夜精品|