python 設計模式

python與設計模式

 

聲明:

此文章內容基本摘於 阿里達人 Tuso大神python

感謝大神知識分享,觀摩其文章收益匪淺.web

連接地址:https://yq.aliyun.com/users/s2kca7gsykrca?spm=a2c4e.11153940.blogrightarea71197.1.1a9a6b10UZhTd6算法


1、六大設計原則

  在法理學中,法律規則與法律原則都是法律規範的重要構成。但兩者也會有些不一樣:法律規則是指採起必定的結構形式具體規定人們的法律權利、法律義務以及相應的法律後果的行爲規範,內容比較明確,好比,交通法規中規定,禁止闖紅燈;法律原則是指在必定法律體系中做爲法律規則的指導思想,基本或本原的、綜合的、穩定的原理和準則,內容上只包含「大方針」,而並未有具體規則,好比,若是車上有立刻臨產的孕婦,闖紅燈不會被處罰,這是符合重視生命的原則。設計模式與設計原則,基本符合規則與原則的關係,設計模式是一個個具體問題的解決方案,設計原則則反映了這些設計模式的指導思想;同時,設計原則可衍生出的設計模式也不只限於上述介紹到了23種設計模式,任何一種針對特定業務場景中的解決方法,雖然找不到對應的設計模式與之匹配,但若符合設計原則,也能夠認爲是一種全新的設計模式。從這個意義上來講,設計模式是程序設計方法的形,而設計原則是程序設計方法的神。數據庫

 

一、單一職責原則

  單一職責原則英文原名爲Single Responsibility Principle,簡稱SRP原則。其含義爲:應該有且僅有一個緣由引發類的變動。舉個例子來講明單一職責原則:一個視頻播放系統,一個客戶端類有兩個功能接口,即視頻播放接口和音頻播放接口。雖然這樣的設計很常見,但卻不知足單一職責原則的。緣由是,若是對視頻播放有變動需求或者對音頻播放有修改需求,都會變動視頻客戶端的類結構。符合單一原則的設計是,將視頻播放單元和音頻播放單元各建一個類,播放客戶端繼承兩個類,構成客戶端。
  單一職責原則的最大難點在於職責的劃分,試想,以上劃分是不是符合單一職責了?既是,也不是。試想,若是將視頻傳輸和音頻傳輸的協議信息和數據信息區分開,爲符合這種粒度的單一職責原則就必需要有協議傳輸類和數據傳輸類的劃分。若是接着細分,可能一個簡單的小模塊,都要設計很是多的類。所以,單一職責原則粒度的選擇,應該根據業務流程和人員分工來進行考慮。一些基本的劃分,彷佛已經成了行業規範性的內容,好比,業務邏輯與用戶信息管理的劃分等。編程

 

二、里氏替換原則

  里氏替換原則英文原名爲Liskov Substitution Principle,簡稱LSP原則。它是面向對象設計的最爲基本原則之一。 里氏替換原則的含義爲:任何基類能夠出現的地方,子類必定能夠出現。 LSP是繼承複用的基石,只有當子類能夠替換掉基類,軟件單位的功能不受到影響時,基類才能真正被複用,子類也可以在基類的基礎上增長新的行爲。舉例說明:對於一個鳥類,能夠衍生出麻雀、喜鵲、布穀等子類,這些子類均可繼承鳥類的鳴叫、飛行、吃食等接口。而對於一個雞類,雖然它在生物學上屬於鳥類,但它不會飛,那麼符合LSP設計原則的狀況下,雞就不該該是鳥的一個子類:在鳥類調用飛行接口的地方,雞類並不能出現。若是雞類要使用鳥類的接口,應該使用關聯關係,而不是繼承關係。設計模式

 

三、依賴倒置原則

  依賴倒置原則英文原名爲Dependence Inversion Principle,簡稱DIP原則。它的含義爲:高層模塊不該該依賴於低層模塊,二者都應該依賴其抽象。抽象不該該依賴於細節,細節應該依賴於抽象。咱們將每一個不可細分的邏輯叫做原子邏輯,原子邏輯組裝,造成低層模塊,低層模塊組裝造成高層模塊。依賴倒置原則的含義爲,高層模塊和低層模塊都應該由各自的抽象模塊派生而來,同時接口設計應該依賴於抽象,而非具體模塊。舉個例子:司機與汽車是依賴的關係,司機能夠有實習司機類、老司機類等派生;汽車能夠有轎車、SUV、卡車等派生類。若是司機中設計一個接口drive,汽車是其參數,符合DIP設計原則的參數,應該是在基類司機類中,將基類汽車類做爲參數,而司機的派生類中,drive的參數一樣應該爲基類汽車類,而不該該是汽車類的任一個派生類。若是規定實習司機只能開轎車等業務邏輯,應該在其接口中進行判斷,而不該該將參數替換成子類轎車。安全

 

四、接口隔離原則

  接口隔離原則英文原名爲Interface Segregation Principle,簡稱ISP原則。其含義爲:類間的依賴關係不該該創建一個大的接口,而應該創建其最小的接口,即客戶端不該該依賴那些它不須要的接口。這裏的接口的概念是很是重要的。從邏輯上來說,這裏的接口能夠指一些屬性和方法的集合;從業務上來說,接口就能夠指特定業務下的接口(如函數,URL調用等)。接口應該儘可能小,同時僅留給客戶端必要的接口,棄用沒有必要的接口。舉例說明:若是要根據具體的數據,生成餅圖、直方圖、表格,這個類該如何設計?若是將生成餅圖、直方圖、表格等「接口」(這裏的接口就是「操做」的集合的概念),寫在一個類中,是不符合接口隔離原則的。符合ISP原則的設計應該是設計三個類,每一個類分別實現餅圖、直方圖、表格的繪製。
接口隔離原則和單一職責原則同樣,涉及到粒度的問題,解決粒度大小,一樣依賴於具體的業務場景,須要讀者根據實踐去權衡。服務器

 

五、迪米特法則(最少知識原則)

  迪米特法則(Law of Demeter)也叫最少知識原則,英文Least Knowledge Principle,簡稱LKP原則。其含義爲:一個對象應該對其它對象有最少的瞭解。舉例說明:一個公司有多個部門,每一個部門有多個員工,若是公司CEO要下發通知給每一個員工,是調用接口直接通知全部員工麼?其實否則,CEO只需和它的「朋友」類部門Leader交流就好,部門Leader再下發通知信息便可。而CEO類不須要與員工進行「交流」。
迪米特法則要求對象應該僅對本身的朋友類交流,而不該該對非朋友類交流。那什麼纔是朋友類呢?通常來講,朋友類具備如下特徵:
1)當前對象自己(self);
2)以參量形式傳入到當前對象方法中的對象;
3)當前對象的實例變量直接引用的對象;
4)當前對象的實例變量若是是一個彙集,那麼彙集中的元素也都是朋友;
5)當前對象所建立的對象。網絡

 

六、開閉原則

  開閉原則英文原名爲Open Closed Principle,簡稱OCP原則。其含義爲:一個軟件實體,如類、模塊、函數等,應該對擴展開放,對修改關閉。開閉原則是很是基礎的一個原則,也有人把開閉原則稱爲「原則的原則」。前面講到過,模塊分原子模塊,低層模塊,高層模塊,業務層能夠認爲是最高層次的模塊。對擴展開放,意味着模塊的行爲是能夠擴展的,當高層模塊需求改變時,咱們能夠對低層模塊進行擴展,使其具備知足高層模塊的新功能;對修改關閉,即對低層模塊行爲進行擴展時,沒必要改動模塊的源代碼。最理想的狀況是,業務變更時,僅修改業務代碼,不修改依賴的模塊(類、函數等)代碼,經過擴展依賴的模塊單元來實現業務變化。舉例說明:假設一個原始基類水果類,蘋果類是它的派生類,蘋果中包含水果的各類屬性,如形狀、顏色等;另有兩個類,農民類和花園類,最高層次(業務層次)爲農民在花園種蘋果。若是此時,農民決定不種蘋果了,改種梨,符合OCP原則的設計應該爲基於水果類構建一個新的類,即梨類(對擴展開放),而並不該該去修改蘋果類,使它成爲一個梨類(對修改關閉)。修改應僅在最高層,即業務層中進行。數據結構

 

2、遵循設計原則的好處

  因爲設計原則是設計模式的提煉,於是設計原則的好處與設計模式是一致的,即:代碼易於理解;更適於團體合做;適應需求變化等。

 

3、設計原則與設計模式

  一、建立類設計模式與設計原則

  工廠模式:工廠方法模式是一種解耦結構,工廠類只須要知道抽象產品類,符合最少知識原則(迪米特法則);同時符合依賴倒置原則和里氏替換原則;
  抽象工廠模式:抽象工廠模式具備工廠模式的優勢,但同時,若是產品族要擴展,工廠類也要修改,違反了開閉原則;
  模板模式:優秀的擴展能力符合開閉原則。

 

  二、結構類設計模式與設計原則

  代理模式:代理模式在業務邏輯中將對主體對象的操做進行封裝,合適的應用會符合開閉原則和單一職責原則;事實上,幾乎帶有解耦做用的結構類設計模式都多少符合些開閉原則;
  門面模式:門面模式不符合開閉原則,有時不符合單一職責原則,如若不注意,也會觸碰接口隔離原則;
  組合模式:符合開閉原則,但因爲通常在拼接樹時使用實現類,故不符合依賴倒置原則;
  橋樑模式:橋樑模式堪稱依賴倒置原則的典範,同時也符合開閉原則。

 

  三、行爲類設計模式與設計原則

  策略模式:符合開閉原則,但高層模塊調用時,不符合迪米特法則。行爲類設計模式多少會符合些單一職責原則,典型的如觀察者模式、中介者模式、訪問者模式等;
  責任鏈模式:符合單一職責原則和迪米特法則;
  命令模式:符合開閉原則。

在不一樣的業務邏輯中,不一樣的設計模式也會顯示出不一樣的設計原則特色,從這個意義上來講,設計模式是設計原則的體現,但體現不是固定的,是根據業務而有所不一樣的。

 


 

設計模式的意義:

設計模式基本上本着「高內聚、低耦合」的原則


有哪些設計模式?

1.建立類設計模式
2.結構類設計模式
3.行爲類設計模式


  建立類設計模式:

  單例模式
  工廠模式
  抽象工廠模式
  原型模式
  建造者模式


  結構類設計模式

  代理模式
  裝飾器模式
  適配器模式
  門面模式
  組合模式
  享元模式
  橋樑模式


  行爲類設計模式

  策略模式
  責任鏈模式
  命令模式
  中介者模式
  模板模式
  迭代器模式
  訪問者模式
  觀察者模式
  解釋器模式
  備忘錄模式
  狀態模式

單例模式

  什麼是單例模式?

  單例模式是指:保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。
  具體到此例中,總線對象,就是一個單例,它僅有一個實例,各個線程對總線的訪問只有一個全局訪問點,即唯一的實例。


  單例模式的優勢和應用

    單例模式的優勢:
    一、因爲單例模式要求在全局內只有一個實例,於是能夠節省比較多的內存空間;
    二、全局只有一個接入點,能夠更好地進行數據同步控制,避免多重佔用;
    三、單例可長駐內存,減小系統開銷。


  單例模式的應用舉例:

  一、生成全局唯一的序列號;
  二、訪問全局複用的唯一資源,如磁盤、總線等;
  三、單個對象佔用的資源過多,如數據庫等;
  四、系統全局統一管理,如Windows下的Task Manager;
  五、網站計數器。


  單例模式的缺點

  一、單例模式的擴展是比較困難的;
  二、賦於了單例以太多的職責,某種程度上違反單一職責原則(六大原則後面會講到);
  三、單例模式是併發協做軟件模塊中須要最早完成的,於是其不利於測試;
  四、單例模式在某種狀況下會致使「資源瓶頸」。

 

#encoding=utf8
import threading
import time

#這裏使用方法__new__來實現單例模式
class Singleton(object):#抽象單例
    def __new__(cls, *args, **kw):
        if not hasattr(cls, '_instance'):
            orig = super(Singleton, cls)
            cls._instance = orig.__new__(cls,*args,**kw)
        return cls._instance
#總線
class Bus(Singleton):
    lock = threading.RLock()
    def sendData(self,data):
        self.lock.acquire()
        time.sleep(3)
        print ("Sending Signal Data...",data)
        self.lock.release()
#線程對象,爲更加說明單例的含義,這裏將Bus對象實例化寫在了run裏
class VisitEntity(threading.Thread):
    my_bus=""
    name=""
    def getName(self):
        return self.name
    def setName(self, name):
        self.name=name
    def run(self):
        self.my_bus=Bus()
        self.my_bus.sendData(self.name)

if  __name__=="__main__":
    for i in range(20):
        print("Entity %d begin to run..."%i)
        my_entity=VisitEntity()
        my_entity.setName("Entity_"+str(i))
        my_entity.start()
View Code

 


工廠類相關模式

  工廠模式的定義以下:

  定義一個用於建立對象的接口,讓子類決定實例化哪一個類。
  工廠方法使一個類的實例化延遲到其子類。
  其產品類定義產品的公共屬性和接口,工廠類定義產品實例化的「方式」。


  工廠模式的優勢和應用

    工廠模式、抽象工廠模式的優勢:
    一、工廠模式巨有很是好的封裝性,代碼結構清晰;在抽象工廠模式中,其結構還能夠隨着須要進行更深或者更淺的抽象層級調整,很是靈活;
    二、屏蔽產品類,使產品的被使用業務場景和產品的功能細節能夠分而開發進行,是比較典型的解耦框架。
    工廠模式、抽象工廠模式的使用場景:
    一、當系統實例要求比較靈活和可擴展時,能夠考慮工廠模式或者抽象工廠模式實現。好比,
    在通訊系統中,高層通訊協議會不少樣化,同時,上層協議依賴於下層協議,那麼就能夠對應創建對應層級的抽象工廠,根據不一樣的「產品需求」去生產定製的實例。


  工廠類模式的不足

    一、工廠模式相對於直接生成實例過程要複雜一些,因此,在小項目中,能夠不使用工廠模式;
    二、抽象工廠模式中,產品類的擴展比較麻煩。畢竟,每個工廠對應每一類產品,產品擴展,就意味着相應的抽象工廠也要擴展。

'''
快餐點餐系統
想必你們必定見過相似於麥當勞自助點餐檯一類的點餐系統吧。
在一個大的觸摸顯示屏上,有三類能夠選擇的上餐品:漢堡等主餐、小食、飲料。
當咱們選擇好本身須要的食物,支付完成後,訂單就生成了。
下面,咱們用今天的主角--工廠模式--來生成這些食物的邏輯主體。
'''

# ------------------------商品(漢堡)
class Burger():
    name=""
    price=0.0
    def getPrice(self):
        return self.price
    def setPrice(self,price):
        self.price=price
    def getName(self):
        return self.name

class cheeseBurger(Burger):
    def __init__(self):
        self.name="cheese burger"
        self.price=10.0

class spicyChickenBurger(Burger):
    def __init__(self):
        self.name="spicy chicken burger"
        self.price=15.0

# ------------------------商品(小吃)
class Snack():
    name = ""
    price = 0.0
    type = "SNACK"
    def getPrice(self):
        return self.price
    def setPrice(self, price):
        self.price = price
    def getName(self):
        return self.name


class chips(Snack):
    def __init__(self):
        self.name = "chips"
        self.price = 6.0

class chickenWings(Snack):
    def __init__(self):
        self.name = "chicken wings"
        self.price = 12.0

# ------------------------商品(飲料)
class Beverage():
    name = ""
    price = 0.0
    type = "BEVERAGE"
    def getPrice(self):
        return self.price
    def setPrice(self, price):
        self.price = price
    def getName(self):
        return self.name

class coke(Beverage):
    def __init__(self):
        self.name = "coke"
        self.price = 4.0

class milk(Beverage):
    def __init__(self):
        self.name = "milk"
        self.price = 5.0

# ------------------------食物工廠(定義食品類別以及統一實例化方式)
class foodFactory():
    type = ""
    def createFood(self,foodClass):
        print (self.type," factory produce a instance.")
        foodIns=foodClass()
        return foodIns
class burgerFactory(foodFactory):
    def __init__(self):
        self.type="BURGER"
class snackFactory(foodFactory):
    def __init__(self):
        self.type="SNACK"
class beverageFactory(foodFactory):
    def __init__(self):
        self.type="BEVERAGE"

# 簡單工廠模式(省去實例化工廠類的過程,直接經過簡單工廠實例化商品)
class simpleFoodFactory():
    @classmethod
    def createFood(cls,foodClass):
        print ("Simple factory produce a instance.")
        foodIns = foodClass()
        return foodIns

if __name__ == "__main__":

    # 工廠模式
    # burger_factory=burgerFactory()
    # snack_factorry=snackFactory()
    # beverage_factory=beverageFactory()
    # cheese_burger=burger_factory.createFood(cheeseBurger)
    # print (cheese_burger.getName(),cheese_burger.getPrice())
    # chicken_wings=snack_factorry.createFood(chickenWings)
    # print (chicken_wings.getName(),chicken_wings.getPrice())
    # coke_drink=beverage_factory.createFood(coke)
    # print (coke_drink.getName(),coke_drink.getPrice())

    # 簡單工廠模式(省去了實例化工廠類的過程)
    spicy_chicken_burger = simpleFoodFactory.createFood(spicyChickenBurger)
    print (spicy_chicken_burger.getName(),spicy_chicken_burger.getPrice())

'''
仍是在上述例子中,createFood方法中必須傳入foodClass才能夠指定生成的food實例種類,
若是,將每個細緻的產品都創建對應的工廠(如cheeseBurger創建對應一個cheeseBurgerFactory),
這樣,生成食物時,foodClass也沒必要指定。事實上,此時,burgerFactory就是具體食物工廠的一層抽象。
這種模式,就是抽象工廠模式。
'''
View Code

 

 


建造者模式

  優勢:

  一、封裝性好,用戶能夠不知道對象的內部構造和細節,就能夠直接建造對象;
  二、系統擴展容易;
  三、建造者模式易於使用,很是靈活。在構造性的場景中很容易實現「流水線」;
  四、便於控制細節。
  使用場景:
  一、目標對象由組件構成的場景中,很適合建造者模式。例如,在一款賽車遊戲中,車輛生成時,須要根據級別、環境等,選擇輪胎、懸掛、骨架等部件,構造一輛「賽車」;
  二、在具體的場景中,對象內部接口須要根據不一樣的參數而調用順序有所不一樣時,可使用建造者模式。例如:一個植物養殖器系統,對於某些不一樣的植物,澆水、施加肥料的順序要求可能會不一樣,於是能夠在Director中維護一個相似於隊列的結構,在實例化時做爲參數代入到具體建造者中。


建造者模式的缺點

一、「加工工藝」對用戶不透明。(封裝的兩面性)

# ------------------------商品(漢堡)
class Burger():
    name=""
    price=0.0
    def getPrice(self):
        return self.price
    def setPrice(self,price):
        self.price=price
    def getName(self):
        return self.name

class cheeseBurger(Burger):
    def __init__(self):
        self.name="cheese burger"
        self.price=10.0

class spicyChickenBurger(Burger):
    def __init__(self):
        self.name="spicy chicken burger"
        self.price=15.0

# ------------------------商品(小吃)
class Snack():
    name = ""
    price = 0.0
    type = "SNACK"
    def getPrice(self):
        return self.price
    def setPrice(self, price):
        self.price = price
    def getName(self):
        return self.name


class chips(Snack):
    def __init__(self):
        self.name = "chips"
        self.price = 6.0

class chickenWings(Snack):
    def __init__(self):
        self.name = "chicken wings"
        self.price = 12.0

# ------------------------商品(飲料)
class Beverage():
    name = ""
    price = 0.0
    type = "BEVERAGE"
    def getPrice(self):
        return self.price
    def setPrice(self, price):
        self.price = price
    def getName(self):
        return self.name

class coke(Beverage):
    def __init__(self):
        self.name = "coke"
        self.price = 4.0

class milk(Beverage):
    def __init__(self):
        self.name = "milk"
        self.price = 5.0

class order():
    burger=""
    snack=""
    beverage=""
    def __init__(self,orderBuilder):
        self.burger=orderBuilder.bBurger
        self.snack=orderBuilder.bSnack
        self.beverage=orderBuilder.bBeverage
    def show(self):
        print ("Burger:%s"%self.burger.getName())
        print ("Snack:%s"%self.snack.getName())
        print ("Beverage:%s"%self.beverage.getName())

class orderBuilder():
    bBurger=""
    bSnack=""
    bBeverage=""
    def addBurger(self,xBurger):
        self.bBurger=xBurger
    def addSnack(self,xSnack):
        self.bSnack=xSnack
    def addBeverage(self,xBeverage):
        self.bBeverage=xBeverage
    def build(self):
        return order(self)


class orderDirector():
    order_builder=""
    def __init__(self,order_builder):
        self.order_builder=order_builder
    def createOrder(self,burger,snack,beverage):
        self.order_builder.addBurger(burger)
        self.order_builder.addSnack(snack)
        self.order_builder.addBeverage(beverage)
        return self.order_builder.build()


if  __name__=="__main__":

    # order_builder=orderBuilder()
    # order_builder.addBurger(spicyChickenBurger())
    # order_builder.addSnack(chips())
    # order_builder.addBeverage(milk())
    # order_1=order_builder.build()
    # order_1.show()

    # 統一調度
    orderDirector = orderDirector(orderBuilder())
    display_show = orderDirector.createOrder(spicyChickenBurger(),chips(),milk())
    display_show.show()
View Code

 

原型模式

  原型模式的優勢和使用場景

  優勢:
  一、性能極佳,直接拷貝比在內存裏直接新建實例節省很多的資源;
  二、簡化對象建立,同時避免了構造函數的約束,不受構造函數的限制直接複製對象,是優勢,也有隱患,這一點仍是須要多留意一些。
  使用場景:
  一、對象在修改事後,須要複製多份的場景。如本例和其它一些涉及到複製、粘貼的場景;
  二、須要優化資源的狀況。如,須要在內存中建立很是多的實例,能夠經過原型模式來減小資源消耗。此時,原型模式與工廠模式配合起來,無論在邏輯上仍是結構上,都會達到不錯的效果;
  三、某些重複性的複雜工做不須要屢次進行。如對於一個設備的訪問權限,多個對象不用各申請一遍權限,由一個設備申請後,經過原型模式將權限交給可信賴的對象,既能夠提高效率,又能夠節約資源。


  原型模式的缺點

  一、深拷貝和淺拷貝的使用須要事先考慮周到;
  二、某些編程語言中,拷貝會影響到靜態變量和靜態函數的使用。

'''
你們若是用過相似於Photoshop的平面設計軟件,必定都知道圖層的概念。
圖層概念的提出,使得設計、圖形修改等操做更加便利。
設計師既能夠修改和繪製當前圖像對象,又能夠保留其它圖像對象,邏輯清晰,且能夠及時獲得反饋。
本節內容,將以圖層爲主角,介紹原型模式。
'''

from copy import copy, deepcopy
class simpleLayer:
    background=[0,0,0,0]
    content="blank"
    def getContent(self):
        return self.content
    def getBackgroud(self):
        return self.background
    def paint(self,painting):
        self.content=painting
    def setParent(self,p):
        self.background[3]=p
    def fillBackground(self,back):
        self.background=back
    def clone(self):
        return copy(self)
    def deep_clone(self):
        return deepcopy(self)
if  __name__=="__main__":

    dog_layer=simpleLayer()
    dog_layer.paint("Dog")
    dog_layer.fillBackground([0,0,255,0])
    print ("Original Background:",dog_layer.getBackgroud())
    print ("Original Painting:",dog_layer.getContent())

    # 淺複製 / 深複製
    another_dog_layer=dog_layer.clone()
    # another_dog_layer = dog_layer.deep_clone()

    another_dog_layer.setParent(128)
    another_dog_layer.paint("Puppy")
    print ("Original Background:", dog_layer.getBackgroud())
    print ("Original Painting:", dog_layer.getContent())
    print ("Copy Background:", another_dog_layer.getBackgroud())
    print ("Copy Painting:", another_dog_layer.getContent())
View Code

 

代理模式

  代理模式是一種使用頻率很是高的模式,在多個著名的開源軟件和當前多個著名的互聯網產品後臺程序中都有所應用。
  下面咱們用一個抽象化的簡單例子,來講明代理模式。


  代理模式定義以下:

  爲某對象提供一個代理,以控制對此對象的訪問和控制。代理模式在使用過程當中,應儘可能對抽象主題類進行代理,而儘可能不要對加過修飾和方法的子類代理。
如上例中,若是有一個xServer繼承了Server,並新加了方法xMethod,xServer的代理應以Server爲主題進行設計,而儘可能不要以xServer爲主題,以xServer爲  主題的代理能夠從ServerProxy繼承並添加對應的方法。在JAVA中,講到代理模式,不得不會提到動態代理。動態代理是實現AOP(面向切面編程)的重要實現手段。而在Python中,不多會提到動態代理,而AOP則會以另外一種模式實現:裝飾模式。有關AOP的相關內容,咱們會在裝飾模式這一節中進行說明。

 


  代理模式的優勢和應用場景

  優勢:
  一、職責清晰:很是符合單一職責原則,主題對象實現真實業務邏輯,而非本職責的事務,交由代理完成;
  二、擴展性強:面對主題對象可能會有的改變,代理模式在不改變對外接口的狀況下,能夠實現最大程度的擴展;
  三、保證主題對象的處理邏輯:代理能夠經過檢查參數的方式,保證主題對象的處理邏輯輸入在理想範圍內。
  應用場景:
  一、針對某特定對象進行功能和加強性擴展。如IP防火牆、遠程訪問代理等技術的應用;
  二、對主題對象進行保護。如大流量代理,安全代理等;
  三、減輕主題對象負載。如權限代理等。


  代理模式的缺點

    可能會下降總體業務的處理效率和速度。

#該服務器接受以下格式數據,addr表明地址,content表明接收的信息內容

# 模擬服務器接受/發送/顯示 信息部分
class Server:
    content=""
    def recv(self,info):
        pass
    def send(self,info):
        pass
    def show(self):
        pass
class infoServer(Server):
    def recv(self,info):
        self.content=info
        return "recv OK!"
    def send(self,info):
        pass
    def show(self):
        print ("SHOW:%s"%self.content)

class serverProxy:
    pass
class infoServerProxy(serverProxy):
    server=""
    def __init__(self,server):
        self.server=server
    def recv(self,info):
        return self.server.recv(info)
    def show(self):
        self.server.show()


# 代理模式構建部分
class whiteInfoServerProxy(infoServerProxy):
    white_list=[]
    def recv(self,info):
        try:
            assert type(info)==dict
        except:
            return "info structure is not correct"
        addr=info.get("addr",0)
        if not addr in self.white_list:
            return "Your address is not in the white list."
        else:
            content=info.get("content","")
            return self.server.recv(content)
    def addWhite(self,addr):
        self.white_list.append(addr)
    def rmvWhite(self,addr):
        self.white_list.remove(addr)
    def clearWhite(self):
        self.white_list=[]

if  __name__=="__main__":
    info_struct = dict()
    info_struct["addr"] = 10010
    info_struct["content"] = "Hello World!"

    # 實例化服務器
    info_server = infoServer()
    # 加入代理中
    info_server_proxy = whiteInfoServerProxy(info_server)

    # 當沒有加入白名單,接受數據
    print (info_server_proxy.recv(info_struct))
    info_server_proxy.show()

    print("----------------")

    # 加入白名單,接受數據
    info_server_proxy.addWhite(10010)
    print (info_server_proxy.recv(info_struct))
    info_server_proxy.show()
View Code

 

裝飾器模式

  裝飾器模式的優勢和應用場景

  優勢:
  一、裝飾器模式是繼承方式的一個替代方案,能夠輕量級的擴展被裝飾對象的功能;
  二、Python的裝飾器模式是實現AOP的一種方式,便於相同操做位於不一樣調用位置的統一管理。
  應用場景:
  一、須要擴展、加強或者減弱一個類的功能,如本例。


  裝飾器模式的缺點

  多層裝飾器的調試和維護有比較大的困難。

'''
快餐店賣可樂時,能夠選擇加冰,若是加冰的話,要在原價上加0.3元;
賣牛奶時,能夠選擇加糖,若是加糖的話,要原價上加0.5元。怎麼解決這樣的問題?
'''


class Beverage():
    name = ""
    price = 0.0
    type = "BEVERAGE"
    def getPrice(self):
        return self.price
    def setPrice(self, price):
        self.price = price
    def getName(self):
        return self.name

class coke(Beverage):
    def __init__(self):
        self.name = "coke"
        self.price = 4.0

class milk(Beverage):
    def __init__(self):
        self.name = "milk"
        self.price = 5.0


# 傳統實現方式:
class drinkDecorator():
    def getName(self):
        pass

    def getPrice(self):
        pass

class iceDecorator(drinkDecorator):
    def __init__(self, beverage):
        self.beverage = beverage

    def getName(self):
        return self.beverage.getName() + " +ice"

    def getPrice(self):
        return self.beverage.getPrice() + 0.3

class sugarDecorator(drinkDecorator):
    def __init__(self, beverage):
        self.beverage = beverage

    def getName(self):
        return self.beverage.getName() + " +sugar"

    def getPrice(self):
        return self.beverage.getPrice() + 0.5


if  __name__=="__main__":
    coke_cola=coke()
    print ("Name:%s"%coke_cola.getName())
    print ("Price:%s"%coke_cola.getPrice())
    ice_coke=iceDecorator(coke_cola)
    print ("Name:%s" % ice_coke.getName())
    print ("Price:%s" % ice_coke.getPrice())
View Code

 

適配器模式

  適配器模式定義

  將一個類的接口變換成客戶端期待的另外一種接口,從而使本來因接口不匹配而沒法在一塊兒工做的兩個類可以在一塊兒工做。適配器模式和裝飾模式有必定的類似性,都起包裝的做用,但兩者本質上又是不一樣的,裝飾模式的結果,是給一個對象增長了一些額外的職責,而適配器模式,則是將另外一個對象進行了「假裝」。適配器能夠認爲是對如今業務的補償式應用,因此,儘可能不要在設計階段使用適配器模式,在兩個系統須要兼容時能夠考慮使用適配器模式。


  適配器模式的優勢和使用場景

  優勢:
  一、適配器模式可讓兩個接口不一樣,甚相當系不大的兩個類一塊兒運行;
  二、提升了類的複用度,通過「假裝」的類,能夠充當新的角色;
  三、適配器能夠靈活「拆卸」。
  應用場景:
  一、不修改現有接口,同時也要使該接口適用或兼容新場景業務中,適合使用適配器模式。
  例如,在一個嵌入式系統中,本來要將數據從Flash讀入,如今須要將數據從磁盤讀入,
  這種狀況可使用適配器模式,將從磁盤讀入數據的接口進行「假裝」,以從Flash中讀數據的接口形式,從磁盤讀入數據。

  適配器模式的缺點

  適配器模式與原配接口相比,畢竟增長了一層調用關係,因此,在設計系統時,不要使用適配器模式。

'''
假設某公司A與某公司B須要合做,公司A須要訪問公司B的人員信息,但公司A與公司B協議接口不一樣,該如何處理?
先將公司A和公司B針對各自的人員信息訪問系統封裝了對象接口。
'''

class ACpnStaff:
    name=""
    id=""
    phone=""
    def __init__(self,id):
        self.id=id
    def getName(self):
        print ("A protocol getName method...id:%s"%self.id)
        return self.name
    def setName(self,name):
        print ("A protocol setName method...id:%s"%self.id)
        self.name=name
    def getPhone(self):
        print ("A protocol getPhone method...id:%s"%self.id)
        return self.phone
    def setPhone(self,phone):
        print ("A protocol setPhone method...id:%s"%self.id)
        self.phone=phone

class BCpnStaff:
    name=""
    id=""
    telephone=""
    def __init__(self,id):
        self.id=id
    def get_name(self):
        print ("B protocol get_name method...id:%s"%self.id)
        return self.name
    def set_name(self,name):
        print ("B protocol set_name method...id:%s"%self.id)
        self.name=name
    def get_telephone(self):
        print ("B protocol get_telephone method...id:%s"%self.id)
        return self.telephone
    def set_telephone(self,telephone):
        print ("B protocol get_name method...id:%s"%self.id)
        self.telephone=telephone


class CpnStaffAdapter:
    b_cpn=""
    def __init__(self,id):
        self.b_cpn=BCpnStaff(id)
    def getName(self):
        return self.b_cpn.get_name()
    def getPhone(self):
        return self.b_cpn.get_telephone()
    def setName(self,name):
        self.b_cpn.set_name(name)
    def setPhone(self,phone):
        self.b_cpn.set_telephone(phone)

if __name__=="__main__":
    acpn_staff=ACpnStaff("123")
    acpn_staff.setName("X-A")
    acpn_staff.setPhone("10012345678")
    print ("A Staff Name:%s"%acpn_staff.getName())
    print ("A Staff Phone:%s"%acpn_staff.getPhone())
    bcpn_staff=CpnStaffAdapter("456")
    bcpn_staff.setName("Y-B")
    bcpn_staff.setPhone("99987654321")
    print ("B Staff Name:%s"%bcpn_staff.getName())
    print ("B Staff Phone:%s"%bcpn_staff.getPhone())
View Code

 

門面模式

  門面模式也叫外觀模式定義以下:

    要求一個子系統的外部與其內部的通訊必須經過一個統一的對象進行。門面模式提供一個高層次的接口,使得子系統更易於使用。門面模式注重「統一的對象」,也就是提供一個訪問子系統的接口。門面模式與以前說過的模板模式有相似的地方,都是對一些須要重複方法的封裝。但從本質上來講,是不一樣的。模板模式是對類自己的方法的封裝,其被封裝的方法也能夠單獨使用;而門面模式,是對子系統的封裝,其被封裝的接口理論上是不會被單獨提出來用的。


  門面模式的優勢和使用場景

  優勢:
  一、減小了系統之間的相互依賴,提升了系統的靈活;
  二、提升了總體系統的安全性:封裝起的系統對外的接口才能夠用,隱藏了不少內部接口細節,若方法不容許使用,則在門面中能夠進行靈活控制。
  使用場景:
  一、爲一個複雜的子系統提供一個外界訪問的接口。這類例子是生活仍是蠻常見的,例如電視遙控器的抽象模型,電信運營商的用戶交互設備等;
  二、須要簡化操做界面時。例如常見的扁平化系統操做界面等,在生活中和工業中都很常見。

  門面模式的缺點

  門面模式的缺點在於,不符合開閉原則,一旦系統成形後須要修改,幾乎只能重寫門面代碼,這比繼承或者覆寫等方式,或者其它一些符合開閉原則的模式風險都會大一些。

'''

假設有一組火警報警系統,由三個子元件構成:一個警報器,一個噴水器,一個自動撥打電話的裝置。其抽象以下:
在業務中若是須要將三個部件啓動,例如,若是有一個煙霧傳感器,檢測到了煙霧。在業務環境中須要作以下操做:
'''


class AlarmSensor:
    def run(self):
        print ("Alarm Ring...")
class WaterSprinker:
    def run(self):
        print ("Spray Water...")
class EmergencyDialer:
    def run(self):
        print ("Dial 119...")

# if __name__=="__main__":
#     alarm_sensor=AlarmSensor()
#     water_sprinker=WaterSprinker()
#     emergency_dialer=EmergencyDialer()
#     alarm_sensor.run()
#     water_sprinker.run()
#     emergency_dialer.run()


'''
若是在多個業務場景中須要啓動三個部件,怎麼辦?Ctrl+C加上Ctrl+V麼?
固然能夠這樣,但做爲碼農的基本修養之一,減小重複代碼是應該會被很輕易想到的方法。
這樣,須要將其進行封裝,在設計模式中,被封裝成的新對象,叫作門面。門面構建以下:
'''
class EmergencyFacade:
    def __init__(self):
        self.alarm_sensor=AlarmSensor()
        self.water_sprinker=WaterSprinker()
        self.emergency_dialer=EmergencyDialer()
    def runAll(self):
        self.alarm_sensor.run()
        self.water_sprinker.run()
        self.emergency_dialer.run()

if __name__=="__main__":
    emergency_facade=EmergencyFacade()
    emergency_facade.runAll()
View Code

 

組合模式

  組合模式也叫做部分-總體模式,其定義以下:

    將對象組合成樹形結構以表示「部分」和「總體」的層次結構,使得用戶對單個對象和組合對象的使用具備一致性。


  組合模式的優勢和使用場景

  優勢:
  一、節點增長和減小是很是自由和方便的,這也是樹形結構的一大特色;
  二、全部節點,無論是分支節點仍是葉子結點,無論是調用一個結點,仍是調用一個結點羣,都是很是方便的。
  使用場景:
  一、維護部分與總體的邏輯關係,或者動態調用總體或部分的功能接口,能夠考慮使用組合模式。
  例如,很是多的操做系統(如Linux)都把文件系統設計成樹形結構,
  再好比說分佈式應用中藉助Zookeeper,也能夠組織和調用分佈式集羣中的結點功能。


  組合模式的缺點

  因爲葉子結點和分支結點直接使用了實現類,而不方便使用抽象類,這大大限制了接口的影響範圍;
  若結點接口發生變動,對系統形成的風險會比較大。

'''
每個公司都有本身的組織結構,越是大型的企業,其組織結構就會越複雜。
大多數狀況下,公司喜歡用「樹形」結構來組織複雜的公司人事關係和公司間的結構關係。
通常狀況下,根結點表明公司的最高行政權利單位,分支節點表示一個個部門,而葉子結點則會用來表明每個員工。
每個結點的子樹,表示該結點表明的部門所管理的單位。
假設一個具備HR部門,財務部門和研發部門,同時在全國有分支公司的總公司,其公司結構,
能夠表示成以下邏輯:

'''

class Company:
    name = ''
    def __init__(self, name):
        self.name = name
    def add(self, company):
        pass
    def remove(self, company):
        pass
    def display(self, depth):
        pass
    def listDuty(self):
        pass

class ConcreteCompany(Company):
    childrenCompany = None
    def __init__(self, name):
        Company.__init__(self,name)
        self.childrenCompany = []
    def add(self, company):
        self.childrenCompany.append(company)
    def remove(self, company):
        self.childrenCompany.remove(company)
    def display(self, depth):
        print('-'*depth + self.name)
        for component in self.childrenCompany:
            component.display(depth+1)
    def listDuty(self):
        for component in self.childrenCompany:
            component.listDuty()
class HRDepartment(Company):
    def __init__(self, name):
         Company.__init__(self,name)
    def display(self, depth):
        print ('-'*depth + self.name)
    def listDuty(self): #履行職責
        print ('%s\t Enrolling & Transfering management.' % self.name)

class FinanceDepartment(Company):
    def __init__(self, name):
        Company.__init__(self,name)
    def display(self, depth):
        print ("-" * depth + self.name)
    def listDuty(self): #履行職責
        print ('%s\tFinance Management.'%self.name)

class RdDepartment(Company):
    def __init__(self,name):
        Company.__init__(self,name)
    def display(self, depth):
        print ("-"*depth+self.name)
    def listDuty(self):
        print ("%s\tResearch & Development."% self.name)

if __name__=="__main__":
    root = ConcreteCompany('HeadQuarter')
    root.add(HRDepartment('HQ HR'))
    root.add(FinanceDepartment('HQ Finance'))
    root.add(RdDepartment("HQ R&D"))

    comp = ConcreteCompany('East Branch')
    comp.add(HRDepartment('East.Br HR'))
    comp.add(FinanceDepartment('East.Br Finance'))
    comp.add(RdDepartment("East.Br R&D"))
    root.add(comp)

    comp1 = ConcreteCompany('Northast Branch')
    comp1.add(HRDepartment('Northeast.Br HR'))
    comp1.add(FinanceDepartment('Northeast.Br Finance'))
    comp1.add(RdDepartment("Northeast.Br R&D"))
    comp.add(comp1)

    comp2 = ConcreteCompany('Southeast Branch')
    comp2.add(HRDepartment('Southeast.Br HR'))
    comp2.add(FinanceDepartment('Southeast.Br Finance'))
    comp2.add(RdDepartment("Southeast.Br R&D"))
    comp.add(comp2)

    root.display(1)

    root.listDuty()
View Code

 

 


享元模式(相似於單例模式,限制實例化得個數,共享實例化)

  享元模式定義以下:

  使用共享對象支持大量細粒度對象。大量細粒度的對象的支持共享,可能會涉及這些對象的兩類信息:內部狀態信息和外部狀態信息。內部狀態信息就是可共享出來的信息,它們存儲在享元對象內部,不會隨着特定環境的改變而改變;外部狀態信息就不可共享的信息了。享元模式中只包含內部狀態信息,而不該該包含外部狀態信息。這點在設計業務架構時,應該有所考慮。


  享元模式的優勢和使用場景

  優勢:
  一、減小重複對象,大大節約了系統資源。
  使用場景:
  一、系統中存在大量的類似對象時,能夠選擇享元模式提升資源利用率。咖啡訂購平臺比較小,
  若假設一個電商平臺,每一個買家和賣家創建起買賣關係後,買家對象和賣家對象都是佔用資源的。
  若是一個賣家同時與多個買家創建起買賣關係呢?此時享元模式的優點就體現出來了;
  二、須要緩衝池的場景中,可使用享元模式。如進程池,線程池等技術,就可使用享元模式(事實上,不少的池技術中已經使得了享元模式)。


  享元模式的缺點

  一、享元模式雖然節約了系統資源,但同時也提升了系統的複雜性,尤爲當遇到外部狀態和內部狀態混在一塊兒時,須要先將其進行分離,纔可使用享元模式。不然,會引發邏輯混亂或業務風險;
  二、享元模式中須要額外注意線程安全問題。

class Customer:
    coffee_factory=""
    name=""
    def __init__(self,name,coffee_factory):
        self.name=name
        self.coffee_factory=coffee_factory
    def order(self,coffee_name):
        print ("%s ordered a cup of coffee:%s"%(self.name,coffee_name))
        return self.coffee_factory.getCoffee(coffee_name)

'''
按照通常的處理流程,用戶在網上預訂咖啡,其表明用戶的Customer類中生成一個Coffee類,直到交易流程結束。
整個流程是沒有問題的。若是,隨着網站用戶愈來愈多,單位時間內購買咖啡的用戶也愈來愈多,併發量愈來愈大,
對系統資源的消耗也會愈來愈大,極端狀況下,會形成宕機等嚴重後果。此時,高效利用資源,就顯得很是重要了。
簡單分析下業務流程,高併發下用戶數量增長,而該模型下,每一個用戶點一杯咖啡,就會產生一個咖啡實例,
若是一種咖啡在該時間內被不少用戶點過,那麼就會產生不少一樣咖啡的實例。
避免重複實例的出現,是節約系統資源的一個突破口。
相似於單例模式,咱們這裏在咖啡實例化前,增長一個控制實例化的類:咖啡工廠。
'''

class CoffeeFactory():
    coffee_dict = {}
    def getCoffee(self, name):
        if self.coffee_dict.__contains__(name) == False:
            self.coffee_dict[name] = Coffee(name)
        return self.coffee_dict[name]
    def getCoffeeCount(self):
        return len(self.coffee_dict)

if __name__=="__main__":
    coffee_factory=CoffeeFactory()
    customer_1=Customer("A Client",coffee_factory)
    customer_2=Customer("B Client",coffee_factory)
    customer_3=Customer("C Client",coffee_factory)
    c1_capp=customer_1.order("cappuccino")
    c1_capp.show()
    c2_mocha=customer_2.order("mocha")
    c2_mocha.show()
    c3_capp=customer_3.order("cappuccino")
    c3_capp.show()
    print ("Num of Coffee Instance:%s"%coffee_factory.getCoffeeCount())
View Code

 

橋樑模式

  橋樑模式又叫橋接模式,定義以下:

  將抽象與實現解耦(注意此處的抽象和實現,並不是抽象類和實現類的那種關係,而是一種角色的關係,這裏須要好好區分一下),可使其獨立變化。在形如上例中,Pen只負責畫,但沒有形狀,它終究是不知道要畫什麼的,因此咱們把它叫作抽象化角色;而Shape是具體的形狀,咱們把它叫作實現化角色。抽象化角色和實現化角色是解耦的,這也就意味着,所謂的橋,就是抽象化角色的抽象類和實現化角色的抽象類之間的引用關係。


  橋樑模式的優勢和應用場景

  優勢:
  一、抽象角色與實現角色相分離,兩者能夠獨立設計,不受約束;
  二、擴展性強:抽象角色和實現角色能夠很是靈活地擴展。
  應用場景:
  一、不適用繼承或者原繼承關係中抽象類可能頻繁變更的狀況,能夠將原類進行拆分,拆成實現化角色和抽象化角色。例如本例中,若將形狀、粗細、繪畫樣式等屬於聚集在一個類中,一旦抽象類中有所變更,將形成巨大的風險;
  二、重用性比較大的場景。好比開關控制邏輯的程序,開關就是抽象化角色,開關的形式有不少種,操做的實現化角色也有不少種,採用橋樑模式,(如當前例子)開關便可進行復用,總體會將設計的粒度減少。


  橋樑模式的缺點

  一、增長對系統理解的難度。

'''
在介紹原型模式的一節中,咱們舉了個圖層的例子,這一小節內容,咱們一樣以相似畫圖的例子,說明一種結構類設計模式:橋樑模式。
在一個畫圖程序中,常會見到這樣的狀況:有一些預設的圖形,如矩形、圓形等,還有一個對象-畫筆,調節畫筆的類型(如畫筆仍是畫刷,
仍是毛筆效果等)並設定參數(如顏色、線寬等),選定圖形,就能夠在畫布上畫出想要的圖形了。要實現以上需求,先從最抽象的元素開始設計,
即形狀和畫筆(暫時忽略畫布,同時忽略畫筆參數,只考慮畫筆類型)。
'''

class Shape():
    name=""
    param=""
    def __init__(self,*param):
        pass
    def getName(self):
        return self.name
    def getParam(self):
        return self.name,self.param

class Rectangle(Shape):
    def __init__(self,long,width):
        super().__init__()
        self.name="Rectangle"
        self.param="Long:%s Width:%s"%(long,width)
        print ("Create a rectangle:%s"%self.param)

class Circle(Shape):
    def __init__(self,radius):
        super().__init__()
        self.name="Circle"
        self.param="Radius:%s"%radius
        print ("Create a circle:%s"%self.param)



class Pen():
    shape=""
    type=""
    def __init__(self,shape):
        self.shape=shape
    def draw(self):
        pass

class NormalPen(Pen):
    def __init__(self,shape):
        super().__init__(shape)
        self.type="Normal Line"
    def draw(self):
        print ("DRAWING %s:%s----PARAMS:%s"%(self.type,self.shape.getName(),self.shape.getParam()))
class BrushPen(Pen):
    def __init__(self,shape):
        super().__init__(shape)
        self.type="Brush Line"
    def draw(self):
        print ("DRAWING %s:%s----PARAMS:%s" % (self.type,self.shape.getName(), self.shape.getParam()))

if __name__=="__main__":
    normal_pen=NormalPen(Rectangle("20cm","10cm"))
    brush_pen=BrushPen(Circle("15cm"))
    normal_pen.draw()
    brush_pen.draw()
View Code

 


策略模式

  策略模式定義以下:

  定義一組算法,將每一個算法都封裝起來,並使他們之間可互換。以上述例子爲例,customer類扮演的角色(Context)直接依賴抽象策略的接口,在具體策略實現類中便可定義個性化的策略方式,且能夠方便替換。仔細比較一下橋接模式和策略模式,若是把策略模式的Context設計成抽象類和實現類的方式,那麼策略模式和橋接模式就能夠劃等號了。從類圖看上去,橋接模式比策略模式多了對一種角色(抽象角色)的抽象。兩者結構的高度同構,也只能讓咱們從使用意圖上去區分兩種模式:橋接模式解決抽象角色和實現角色均可以擴展的問題;而策略模式解決算法切換和擴展的問題。


  策略模式的優勢和應用場景

  優勢:
  一、各個策略能夠自由切換:這也是依賴抽象類設計接口的好處之一;
  二、減小代碼冗餘;
  三、擴展性優秀,移植方便,使用靈活。
  應用場景:
  一、算法策略比較常常地須要被替換時,可使用策略模式。如如今超市前臺,會常遇到刷卡、某寶支付、某信支付等方式,就能夠參考策略模式。


  策略模式的缺點

  一、項目比較龐大時,策略可能比較多,不便於維護;
  二、策略的使用方必須知道有哪些策略,才能決定使用哪個策略,這與迪米特法則是相違背的。

'''
假設某司維護着一些客戶資料,須要在該司有新產品上市或者舉行新活動時通知客戶。現通知客戶的方式有兩種:
短信通知、郵件通知。應如何設計該系統的客戶通知部分?
爲解決該問題,咱們先構造客戶類,包括客戶經常使用的聯繫方式和基本信息,同時也包括要發送的內容。

'''

class customer:
    customer_name=""
    snd_way=""
    info=""
    phone=""
    email=""
    def setPhone(self,phone):
        self.phone=phone
    def setEmail(self,mail):
        self.email=mail
    def getPhone(self):
        return self.phone
    def getEmail(self):
        return self.email
    def setInfo(self,info):
        self.info=info
    def setName(self,name):
        self.customer_name=name
    def setBrdWay(self,snd_way):
        self.snd_way=snd_way
    def sndMsg(self):
        self.snd_way.send(self.info)

class msgSender:
    dst_code=""
    def setCode(self,code):
        self.dst_code=code
    def send(self,info):
        pass
class emailSender(msgSender):
    def send(self,info):
        print ("EMAIL_ADDRESS:%s EMAIL:%s"%(self.dst_code,info))
class textSender(msgSender):
    def send(self,info):
        print ("TEXT_CODE:%s EMAIL:%s"%(self.dst_code,info))

if  __name__=="__main__":
    customer_x=customer()
    customer_x.setName("CUSTOMER_X")
    customer_x.setPhone("10023456789")
    customer_x.setEmail("customer_x@xmail.com")
    customer_x.setInfo("Welcome to our new party!")

    text_sender=textSender()
    text_sender.setCode(customer_x.getPhone())
    customer_x.setBrdWay(text_sender)
    customer_x.sndMsg()

    mail_sender=emailSender()
    mail_sender.setCode(customer_x.getEmail())
    customer_x.setBrdWay(mail_sender)
    customer_x.sndMsg()
View Code

 

責任鏈模式

  責任鏈模式的定義以下:

  使多個對象都有機會處理請求,從而避免了請求的發送者和接收者之間的耦合關係。將這些對象連成一條鏈,並沿着這條鏈傳遞該請求,直到有對象處理它爲止。


  責任鏈模式的優勢和應用場景

  優勢:
  一、將請求者與處理者分離,請求者並不知道請求是被哪一個處理者所處理,易於擴展。
  應用場景:
  一、若一個請求可能由一個對請求有鏈式優先級的處理羣所處理時,能夠考慮責任鏈模式。除本例外,銀行的客戶請求處理系統也能夠用責任鏈模式實現(VIP客戶和普通用戶處理方式固然會有不一樣)。

  責任鏈模式的缺點

  一、若是責任鏈比較長,會有比較大的性能問題;
  二、若是責任鏈比較長,若業務出現問題,比較難定位是哪一個處理者的問題。

'''
假設有這麼一個請假系統:
員工若想要請3天之內(包括3天的假),只須要直屬經理批准就能夠了;
若是想請3-7天,不只須要直屬經理批准,部門經理須要最終批准;
若是請假大於7天,不光要前兩個經理批准,也須要總經理最終批准。
相似的系統相信你們都遇到過,那麼該如何實現呢?首先想到的固然是if…else…,
但一旦遇到需求變更,其臃腫的代碼和複雜的耦合缺點都顯現出來。
簡單分析下需求,「假條」在三個經理間是單向傳遞關係,像一條鏈條同樣,
於是,咱們能夠用一條「鏈」把他們進行有序鏈接。

'''

class manager():
    successor = None
    name = ''
    def __init__(self, name):
        self.name = name
    def setSuccessor(self, successor):
        self.successor = successor
    def handleRequest(self, request):
        pass
class lineManager(manager):
    def handleRequest(self, request):
        if request.requestType == 'DaysOff' and request.number <= 3:
            print ('%s:%s Num:%d Accepted OVER' % (self.name, request.requestContent, request.number))
        else:
            print('%s:%s Num:%d Accepted CONTINUE' % (self.name, request.requestContent, request.number))
            if self.successor != None:
                self.successor.handleRequest(request)
class departmentManager(manager):
    def handleRequest(self, request):
        if request.requestType == 'DaysOff' and request.number <= 7:
            print ('%s:%s Num:%d Accepted OVER' % (self.name, request.requestContent, request.number))
        else:
            print ('%s:%s Num:%d Accepted CONTINUE' % (self.name, request.requestContent, request.number))
            if self.successor != None:
                self.successor.handleRequest(request)
class generalManager(manager):
    def handleRequest(self, request):
        if request.requestType == 'DaysOff':
            print ('%s:%s Num:%d Accepted OVER' % (self.name, request.requestContent, request.number))
class request():
    requestType = ''
    requestContent = ''
    number = 0

if  __name__=="__main__":

    # 實例化 給個層級部門
    line_manager = lineManager('LINE MANAGER')
    department_manager = departmentManager('DEPARTMENT MANAGER')
    general_manager = generalManager('GENERAL MANAGER')

    # 將各個層級相互嵌套 1 > 2 > 3
    line_manager.setSuccessor(department_manager)
    department_manager.setSuccessor(general_manager)

    # 模擬請求(1天假)
    req = request()
    req.requestType = 'DaysOff'
    req.requestContent = 'Ask 1 day off'
    req.number = 1
    line_manager.handleRequest(req)
    print("------------------------------------")

    # 模擬請求(5天假)
    req.requestType = 'DaysOff'
    req.requestContent = 'Ask 5 days off'
    req.number = 5
    line_manager.handleRequest(req)
    print("------------------------------------")

    # 模擬請求(10天假)
    req.requestType = 'DaysOff'
    req.requestContent = 'Ask 10 days off'
    req.number = 10
    line_manager.handleRequest(req)
View Code

 


命令模式

  命令模式的定義爲:

  將一個請求封裝成一個對象,從而可使用不一樣的請求將客戶端參數化,對請求排隊或者記錄請求日誌,能夠提供命令的撤銷和恢復功能。命令模式中一般涉及三類對象的抽象:Receiver,Command,Invoker(本例中的waiterSys)只有一個Invoker的命令模式也能夠抽象成一個相似的「星形網絡」,但與以前介紹的中介者模式不一樣,單純的命令模式更像是一個輻射狀的結構,由Invoker直接對Receiver傳遞命令,而通常不反向傳遞,中介者模式「星形網絡」的中心,是個協調者,抽象結節間的信息流所有或者部分是雙向的。另外,命令模式的定義中提到了「撤銷和恢復功能」,也給了各位開發人員一個命令模式使用過程當中的建議:
各個Receiver中能夠設計一個回滾接口,支持命令的「撤銷」。

  命令模式的優勢和應用場景

  優勢:
  一、低耦合:調用者和接收者之間沒有什麼直接關係,兩者經過命令中的execute接口聯繫;
  二、擴展性好:新命令很容易加入,也很容易拼出「組合命令」。
  應用場景:
  一、觸發-反饋機制的系統,均可以使用命令模式思想。如基於管道結構的命令系統(如SHELL),能夠直接套用命令模式;此外,GUI系統中的操做反饋(如點擊、鍵入等),也可使用命令模式思想。


  命令模式的缺點

  一、若是業務場景中命令比較多,那麼對應命令類和命令對象的數量也會增長,這樣系統會膨脹得很大。

'''
飯店的點餐系統有什麼不一樣嘛?
大夥想一想看,在大多數飯店中,當服務員已經接到顧客的點單,錄入到系統中後,根據不一樣的菜品,會有不一樣的後臺反應。
好比,飯店有涼菜間、熱菜間、主食間,那當服務員將菜品錄入到系統中後,涼菜間會打印出顧客所點的涼菜條目,
熱菜間會打印出顧客所點的熱菜條目,主食間會打印出主食條目。那這個系統的後臺模式該如何設計?
固然,直接在場景代碼中加if…else…語句判斷是個方法,可這樣作又一次加劇了系統耦合,
違反了單一職責原則,遇到系統需求變更時,又會輕易違反開閉原則。因此,咱們須要從新組織一下結構。
能夠將該系統設計成前臺服務員系統和後臺系統,後臺系統進一步細分紅主食子系統,涼菜子系統,熱菜子系統。後臺三個子系統設計以下:
'''

class backSys():
    def cook(self,dish):
        pass
class mainFoodSys(backSys):
    def cook(self,dish):
        print ("MAINFOOD:Cook %s"%dish)
class coolDishSys(backSys):
    def cook(self,dish):
        print ("COOLDISH:Cook %s"%dish)
class hotDishSys(backSys):
    def cook(self,dish):
        print ("HOTDISH:Cook %s"%dish)

class waiterSys():
    menu_map=dict()
    commandList=[]
    def setOrder(self,command):
        print ("WAITER:Add dish")
        self.commandList.append(command)

    def cancelOrder(self,command):
        print ("WAITER:Cancel order...")
        self.commandList.remove(command)

    def notify(self):
        print ("WAITER:Nofify...")
        for command in self.commandList:
            command.execute()

class Command():
    receiver = None
    def __init__(self, receiver):
        self.receiver = receiver
    def execute(self):
        pass
class foodCommand(Command):
    dish=""
    def __init__(self,receiver,dish):
        super().__init__(receiver)
        self.receiver=receiver
        self.dish=dish
    def execute(self):
        self.receiver.cook(self.dish)

class mainFoodCommand(foodCommand):
    pass
class coolDishCommand(foodCommand):
    pass
class hotDishCommand(foodCommand):
    pass


class menuAll:
    menu_map=dict()
    def loadMenu(self):#加載菜單,這裏直接寫死
        self.menu_map["hot"] = ["Yu-Shiang Shredded Pork", "Sauteed Tofu, Home Style", "Sauteed Snow Peas"]
        self.menu_map["cool"] = ["Cucumber", "Preserved egg"]
        self.menu_map["main"] = ["Rice", "Pie"]
    def isHot(self,dish):
        if dish in self.menu_map["hot"]:
            return True
        return False
    def isCool(self,dish):
        if dish in self.menu_map["cool"]:
            return True
        return False
    def isMain(self,dish):
        if dish in self.menu_map["main"]:
            return True
        return False

if  __name__=="__main__":
    dish_list=["Yu-Shiang Shredded Pork","Sauteed Tofu, Home Style","Cucumber","Rice"]#顧客點的菜
    waiter_sys=waiterSys()
    main_food_sys=mainFoodSys()
    cool_dish_sys=coolDishSys()
    hot_dish_sys=hotDishSys()
    menu=menuAll()

    # 加載菜單
    menu.loadMenu()

    # 點菜單分揀所點的菜分屬哪一個檔口
    for dish in dish_list:
        if menu.isCool(dish):
            cmd=coolDishCommand(cool_dish_sys,dish)
        elif menu.isHot(dish):
            cmd=hotDishCommand(hot_dish_sys,dish)
        elif menu.isMain(dish):
            cmd=mainFoodCommand(main_food_sys,dish)
        else:
            continue
        waiter_sys.setOrder(cmd)
    waiter_sys.notify()
View Code

 

中介者模式

  中介者模式的定義爲:

  用一箇中介對象封裝一系列的對象交互。中介者使各對象不須要顯式地互相做用,從而使其耦合鬆散,並能夠獨立地改變它們之間的交互。


  中介者模式的優勢和應用場景

  優勢:
  一、減小類與類的依賴,下降了類和類之間的耦合;
  二、容易擴展規模。
  應用場景:
  一、設計類圖時,出現了網狀結構時,能夠考慮將類圖設計成星型結構,這樣就可使用中介者模式了。
  如機場調度系統(多個跑道、飛機、指揮塔之間的調度)、路由系統;
  著名的MVC框架中,其中的C(Controller)就是M(Model)和V(View)的中介者。

 

  中介者模式的缺點

  一、中介者自己的複雜性可能會很大,例如,同事類的方法若是不少的話,本例中的execute邏輯會很複雜。

'''
有一個手機倉儲管理系統,使用者有三方:
銷售、倉庫管理員、採購。需求是:銷售一旦達成訂單,銷售人員會經過系統的銷售子系統部分通知倉儲子系統,
倉儲子系統會將可出倉手機數量減小,同時通知採購管理子系統當前銷售訂單;倉儲子系統的庫存到達閾值如下,
會通知銷售子系統和採購子系統,並督促採購子系統採購;採購完成後,採購人員會把採購信息填入採購子系統,
採購子系統會通知銷售子系統採購完成,並通知倉庫子系統增長庫存。
從需求描述來看,每一個子系統都和其它子系統有所交流,在設計系統時,
若是直接在一個子系統中集成對另兩個子系統的操做,一是耦合太大,二是不易擴展。
爲解決這類問題,咱們須要引入一個新的角色-中介者-來將「網狀結構」精簡爲「星形結構」。
(爲充分說明設計模式,某些系統細節暫時不考慮,例如:倉庫滿了怎麼辦該怎麼設計。相似業務性的內容暫時不考慮)
'''

class colleague():
    mediator = None
    def __init__(self,mediator):
        self.mediator = mediator

class purchaseColleague(colleague):
    def buyStuff(self,num):
        print ("PURCHASE:Bought %s"%num)
        self.mediator.execute("buy",num)
    def getNotice(self,content):
        print ("PURCHASE:Get Notice--%s"%content)

class warehouseColleague(colleague):
    total=0
    threshold=100
    def setThreshold(self,threshold):
        self.threshold=threshold
    def isEnough(self):
        if self.total<self.threshold:
            print ("WAREHOUSE:Warning...Stock is low... ")
            self.mediator.execute("warning",self.total)
            return False
        else:
            return True
    def inc(self,num):
        self.total+=num
        print ("WAREHOUSE:Increase %s"%num)
        self.mediator.execute("increase",num)
        self.isEnough()
    def dec(self,num):
        if num>self.total:
            print ("WAREHOUSE:Error...Stock is not enough")
        else:
            self.total-=num
            print ("WAREHOUSE:Decrease %s"%num)
            self.mediator.execute("decrease",num)
        self.isEnough()

class salesColleague(colleague):
    def sellStuff(self,num):
        print ("SALES:Sell %s"%num)
        self.mediator.execute("sell",num)
    def getNotice(self, content):
        print ("SALES:Get Notice--%s" % content)


class abstractMediator():
    purchase=""
    sales=""
    warehouse=""
    def setPurchase(self,purchase):
        self.purchase=purchase
    def setWarehouse(self,warehouse):
        self.warehouse=warehouse
    def setSales(self,sales):
        self.sales=sales
    def execute(self,content,num):
        pass
class stockMediator(abstractMediator):
    def execute(self,content,num):
        print ("MEDIATOR:Get Info--%s"%content)
        if  content=="buy":
            self.warehouse.inc(num)
            self.sales.getNotice("Bought %s"%num)
        elif content=="increase":
            self.sales.getNotice("Inc %s"%num)
            self.purchase.getNotice("Inc %s"%num)
        elif content=="decrease":
            self.sales.getNotice("Dec %s"%num)
            self.purchase.getNotice("Dec %s"%num)
        elif content=="warning":
            self.sales.getNotice("Stock is low.%s Left."%num)
            self.purchase.getNotice("Stock is low. Please Buy More!!! %s Left"%num)
        elif content=="sell":
            self.warehouse.dec(num)
            self.purchase.getNotice("Sold %s"%num)
        else:
            pass


if  __name__=="__main__":
    mobile_mediator=stockMediator()#先配置

    mobile_purchase=purchaseColleague(mobile_mediator)
    mobile_warehouse=warehouseColleague(mobile_mediator)
    mobile_sales=salesColleague(mobile_mediator)

    mobile_mediator.setPurchase(mobile_purchase)
    mobile_mediator.setWarehouse(mobile_warehouse)
    mobile_mediator.setSales(mobile_sales)

    mobile_warehouse.setThreshold(2000)
    mobile_purchase.buyStuff(3000)
    mobile_sales.sellStuff(120)
View Code

 

模板模式

  模板模式定義以下:

  定義一個操做中的算法的框架,而將一些步驟延遲到子類中,使得子類能夠不改變一個算法的結構便可從新定義該算法的某些特定的步驟。子類實現的具體方法叫做基本方法,實現對基本方法高度的框架方法,叫做模板方法。模板模式的優勢和應用


  優勢:

  一、可變的部分能夠充分擴展,不變的步驟能夠充分封裝;
  二、提取公共代碼,減小冗餘代碼,便於維護;
  三、具體過程能夠定製,整體流程方便掌控。
  使用場景:
    一、某超類的子類中有公有的方法,而且邏輯基本相同,可使用模板模式。必要時可使用鉤子方法約束其行爲。具體如本節例子;
    二、比較複雜的算法,能夠把核心算法提取出來,周邊功能在子類中實現。例如,機器學習中的監督學習算法有不少,如決策樹、KNN、SVM等,但機器學習的流程大體相同,都包含輸入樣本、擬合(fit)、預測等過程,這樣就能夠把這些過程提取出來,構造模板方法,並經過鉤子方法控制流程。

 

  模板模式的缺點

  一、模板模式在抽象類中定義了子類的方法,即子類對父類產生了影響,部分影響了代碼的可讀性。

'''
投資股票是種常見的理財方式,我國股民愈來愈多,實時查詢股票的需求也愈來愈大。今天,咱們經過一個簡單的股票查詢客戶端來認識一種簡單的設計模式:模板模式。
根據股票代碼來查詢股價分爲以下幾個步驟:登陸、設置股票代碼、查詢、展現。構造以下的虛擬股票查詢器:
'''

class StockQueryDevice():
    stock_code="0"
    stock_price=0.0
    def login(self,usr,pwd):
        pass
    def setCode(self,code):
        self.stock_code=code
    def queryPrice(self):
        pass
    def showPrice(self):
        pass
    def operateQuery(self,usr,pwd,code):
        self.login(usr,pwd)
        self.setCode(code)
        self.queryPrice()
        self.showPrice()
        return True

class WebAStockQueryDevice(StockQueryDevice):
    def login(self,usr,pwd):
        if usr=="myStockA" and pwd=="myPwdA":
            print ("Web A:Login OK... user:%s pwd:%s"%(usr,pwd))
            return True
        else:
            print ("Web A:Login ERROR... user:%s pwd:%s"%(usr,pwd))
            return False
    def queryPrice(self):
        print ("Web A Querying...code:%s "%self.stock_code)
        self.stock_price=20.00
    def showPrice(self):
        print ("Web A Stock Price...code:%s price:%s"%(self.stock_code,self.stock_price))
class WebBStockQueryDevice(StockQueryDevice):
    def login(self,usr,pwd):
        if usr=="myStockB" and pwd=="myPwdB":
            print ("Web B:Login OK... user:%s pwd:%s"%(usr,pwd))
            return True
        else:
            print ("Web B:Login ERROR... user:%s pwd:%s"%(usr,pwd))
            return False
    def queryPrice(self):
        print ("Web B Querying...code:%s "%self.stock_code)
        self.stock_price=30.00
    def showPrice(self):
        print ("Web B Stock Price...code:%s price:%s"%(self.stock_code,self.stock_price))

def operateQuery(self,usr,pwd,code):
    if not self.login(usr,pwd):
        return False
    self.setCode(code)
    self.queryPrice()
    self.showPrice()
    return True

if  __name__=="__main__":
    web_a_query_dev=WebAStockQueryDevice()
    web_a_query_dev.operateQuery("myStockA","myPwdA","12345")
View Code

 

迭代器模式

  迭代器模式的定義以下:

  它提供一種方法,訪問一個容器對象中各個元素,而又不須要暴露對象的內部細節。

class MyIter(object):
    def __init__(self, n):
        self.index = 0
        self.n = n
    def __iter__(self):
        # 只能打印一遍平方值。解決辦法是,在__iter__中不返回實例,而再返回一個對象,寫成:
        # return self

        return MyIter(self.n)

    def __next__(self):
        if self.index < self.n:
            value = self.index**2
            self.index += 1
            return value
        else:
            raise StopIteration()

if __name__=="__main__":
    x_square=MyIter(10)
    for x in x_square:
        print (x)
    for x in x_square:
        print (x)

'''
這樣,在每次迭代時均可以將迭代器「初始化」,就能夠屢次迭代了。
另外,在python中,使用生成器能夠很方便的支持迭代器協議。
生成器經過生成器函數產生,生成器函數能夠經過常規的def語句來定義,
可是不用return返回,而是用yield一次返回一個結果,在每一個結果之間掛起和繼續它們的狀態,來自動實現迭代協議。
'''
View Code

 

訪問者模式

  訪問者模式的定義以下:

  封裝一些做用於某種數據結構中的各元素的操做,它能夠在不改變數據結構的前提下定義於做用於這些元素的新操做。提到訪問者模式,就不得不提一下雙分派。分派分爲靜態分派和動態分派。首先解釋下靜態分派,靜態分派即根據請求者的名稱和接收到的參數,決定多態時處理的操做。好比在Java或者C++中,定義名稱相同但參數不一樣的函數時,會根據最終輸入的參數來決定調用哪一個函數。雙分派顧名思義,即最終的操做決定於兩個接收者的類型,在本例中,藥品和工做人員互相調用了對方(藥品的accept和工做人員的visit中,對方都是參數),就是雙分派的一種應用。Python原生是不支持靜態分派的,於是也不直接支持更高層次的分派。訪問者模式實現的分派,是一種動態雙分派。但這並不妨礙Python經過訪問者模式實現一種基於類的「雙分派效果」。Python多分派能夠參考David Mertz 博士的一篇文章:可愛的Python:多分派—用多元法泛化多樣性。

  訪問者模式的優勢和應用場景

  優勢:
  一、將不一樣的職責很是明確地分離開來,符合單一職責原則;
  二、職責的分開也直接致使擴展很是優良,靈活性很是高,加減元素和訪問者都很是容易。
  應用場景:
  一、要遍歷不一樣的對象,根據對象進行不一樣的操做的場景;或者一個對象被多個不一樣對象順次處理的狀況,能夠考慮使用訪問者模式。除本例外,報表生成器也可使用訪問者模式實現,報表的數據源由多個不一樣的對象提供,每一個對象都是Visitor,報表這個Element順次Accept各訪問者完善並生成對象。

  訪問者模式的缺點

  一、訪問者得知了元素細節,與最小隔離原則相悖;
  二、元素變動依舊可能引發Visitor的修改。

'''
假設一個藥房,有一些大夫,一個藥品劃價員和一個藥房管理員,它們經過一個藥房管理系統組織工做流程。
大夫開出藥方後,藥品劃價員肯定藥品是否正常,價格是否正確;經過後藥房管理員進行開藥處理。該系統能夠如何實現?
最簡單的想法,是分別用一個一個if…else…把劃價員處理流程和藥房管理流程實現,這樣作的問題在於,
擴展性不強,並且單一性不強,一旦有新葯的加入或者劃價流程、開藥流程有些變更,會牽扯比較多的改動。
今天介紹一種解決這類問題的模式:訪問者模式。
'''


# 構造藥品類和工做人員類:
class Medicine:
    name=""
    price=0.0
    def __init__(self,name,price):
        self.name=name
        self.price=price
    def getName(self):
        return self.name
    def setName(self,name):
        self.name=name
    def getPrice(self):
        return self.price
    def setPrice(self,price):
        self.price=price
    def accept(self,visitor):
        pass
class Antibiotic(Medicine):
    def accept(self,visitor):
        visitor.visit(self)
class Coldrex(Medicine):
    def accept(self,visitor):
        visitor.visit(self)


# 藥品類中有兩個子類,抗生素和感冒藥;
class Visitor:
    name=""
    def setName(self,name):
        self.name=name
    def visit(self,medicine):
        pass
class Charger(Visitor):
    def visit(self,medicine):
        print("CHARGE: %s lists the Medicine %s. Price:%s " % (self.name,medicine.getName(),medicine.getPrice()))
class Pharmacy(Visitor):
    def visit(self,medicine):
        print("PHARMACY:%s offers the Medicine %s. Price:%s" % (self.name,medicine.getName(),medicine.getPrice()))


'''
做人員分爲劃價員和藥房管理員。
在藥品類中,有一個accept方法,其參數是個visitor;而工做人員就是從Visitor類中繼承而來的,也就是說,
他們就是Visitor,都包含一個visit方法,其參數又恰是medicine。藥品做爲處理元素,
依次容許(Accept)Visitor對其進行操做,這就比如是一條流水線上的一個個工人,對產品進行一次次的加工。
整個業務流程還差一步,即藥方類的構建(流水線大機器)。
'''

class ObjectStructure:
    pass
class Prescription(ObjectStructure):
    medicines=[]
    def addMedicine(self,medicine):
        self.medicines.append(medicine)
    def rmvMedicine(self,medicine):
        self.medicines.append(medicine)
    def visit(self,visitor):
        for medc in self.medicines:
            medc.accept(visitor)

# 藥方類將待處理藥品進行整理,並組織Visitor依次處理。

if __name__=="__main__":
    yinqiao_pill=Coldrex("Yinqiao Pill",2.0)
    penicillin=Antibiotic("Penicillin",3.0)
    doctor_prsrp=Prescription()
    doctor_prsrp.addMedicine(yinqiao_pill)
    doctor_prsrp.addMedicine(penicillin)
    charger=Charger()
    charger.setName("Doctor Strange")
    pharmacy=Pharmacy()
    pharmacy.setName("Doctor Wei")
    doctor_prsrp.visit(charger)
    doctor_prsrp.visit(pharmacy)
View Code

 

觀察者模式

  觀察者模式也叫發佈-訂閱模式,其定義以下:

    定義對象間一種一對多的依賴關係,使得當該對象狀態改變時,全部依賴於它的對象都會獲得通知,並被自動更新。觀察者模式的通知方式能夠經過直接調用等同步方式實現(如函數調用,HTTP接口調用等),也能夠經過消息隊列異步調用(同步調用指被觀察者發佈消息後,必須等全部觀察者響應結束後才能夠進行接下來的操做;異步調用指被觀察者發佈消息後,便可進行接下來的操做。)。事實上,許多開源的消息隊列就直接支持發佈-訂閱模式,如Zero MQ等。

 

  觀察者模式的優勢和應用場景

  優勢:
  一、觀察者與被觀察者之間是抽象耦合的;
  二、能夠將許多符合單一職責原則的模塊進行觸發,也能夠很方便地實現廣播。
  應用場景:
  一、消息交換場景。如上述說到的消息隊列等;
  二、多級觸發場景。好比支持中斷模式的場景中,一箇中斷即會引起一連串反應,就可使用觀察者模式。

 

  觀察者模式的缺點

  一、觀察者模式可能會帶來總體系統效率的浪費;
  二、若是被觀察者之間有依賴關係,其邏輯關係的梳理須要費些心思。

'''
在門面模式中,咱們提到過火警報警器。
在當時,咱們關注的是經過封裝減小代碼重複。而今天,咱們將從業務流程的實現角度,來再次實現該火警報警器。

'''

class AlarmSensor:
    def run(self):
        print ("Alarm Ring...")
class WaterSprinker:
    def run(self):
        print ("Spray Water...")
class EmergencyDialer:
    def run(self):
        print ("Dial 119...")

class Observer:
    def update(self):
        pass

class AlarmSensor(Observer):
    def update(self,action):
        print ("Alarm Got: %s" % action)
        self.runAlarm()
    def runAlarm(self):
        print ("Alarm Ring...")

class WaterSprinker(Observer):
    def update(self,action):
        print ("Sprinker Got: %s" % action)
        self.runSprinker()
    def runSprinker(self):
        print ("Spray Water...")

class EmergencyDialer(Observer):
    def update(self,action):
        print ("Dialer Got: %s"%action)
        self.runDialer()
    def runDialer(self):
        print ("Dial 119...")

class Observed:
    observers=[]
    action=""
    def addObserver(self,observer):
        self.observers.append(observer)
    def notifyAll(self):
        for obs in self.observers:
            obs.update(self.action)
class smokeSensor(Observed):
    def setAction(self,action):
        self.action=action
    def isFire(self):
        return True

if __name__=="__main__":
    alarm=AlarmSensor()
    sprinker=WaterSprinker()
    dialer=EmergencyDialer()

    smoke_sensor=smokeSensor()
    smoke_sensor.addObserver(alarm)
    smoke_sensor.addObserver(sprinker)
    smoke_sensor.addObserver(dialer)

    if smoke_sensor.isFire():
        smoke_sensor.setAction("On Fire!")
        smoke_sensor.notifyAll()
View Code

 

解釋器模式

  解釋器模式定義以下:

    給定一種語言,定義它的文法表示,並定義一個解釋器,該解釋器使用該表示來解釋語言中的句子。典型的解釋器模式中會有終結符和非終結符之說,語法也根據兩種終結符,決定語句最終含義。上例中,非終結符就是空格,終結符就是整個句尾。


  解釋器模式的優勢和應用場景

  優勢:
  一、在語法分析的場景中,具備比較好的擴展性。規則修改和制訂比較靈活。
  應用場景:
  一、若一個問題重複發生,能夠考慮使用解釋器模式。這點在數據處理和日誌處理過程當中使用較多,當數據的需求方須要將數據納爲己用時,必須將數據「翻譯」成本系統的數據規格;一樣的道理,日誌分析平臺也須要根據不一樣的日誌格式翻譯成統一的「語言」。
  二、特定語法解釋器。如各類解釋型語言的解釋器,再好比天然語言中基於語法的文本分析等。

 

  解釋器模式的缺點

  一、解釋規則多樣化會致使解釋器的爆炸;
  二、解釋器目標比較單一,行爲模式比較固定,於是重要的模塊中儘可能不要使用解釋器模式。

'''
要開發一個自動識別譜子的吉他模擬器,達到錄入譜便可按照譜發聲的效果。除了發聲設備外(假設已完成),最重要的就是讀譜和譯譜能力了。
分析其需求,整個過程大體上分能夠分爲兩部分:
根據規則翻譯譜的內容;根據翻譯的內容演奏。咱們用一個解釋器模型來完成這個功能。
'''

class PlayContext():
    play_text = None

class Expression():
    def interpret(self, context):
        if len(context.play_text) == 0:
            return
        else:
            play_segs=context.play_text.split(" ")
            for play_seg in play_segs:
                pos=0
                for ele in play_seg:
                    if ele.isalpha():
                        pos+=1
                        continue
                    break
                play_chord = play_seg[0:pos]
                play_value = play_seg[pos:]
                self.execute(play_chord,play_value)
    def execute(self,play_key,play_value):
        pass

class NormGuitar(Expression):
    def execute(self, key, value):
        print ("Normal Guitar Playing--Chord:%s Play Tune:%s"%(key,value))

if __name__=="__main__":
    context = PlayContext()
    context.play_text = "C53231323 Em43231323 F43231323 G63231323"
    guitar=NormGuitar()
    guitar.interpret(context)
View Code

 

備忘錄模式

  備忘錄模式定義以下:

  在不破壞封裝性的前提下,捕獲一個對象的內部狀態,並在該對象以外保存這個狀態。這樣之後就能夠將該對象恢復到原來保存的狀態。在備忘錄模式中,若是要保存的狀態多,能夠創造一個備忘錄管理者角色來管理備忘錄。


  備忘錄模式應用場景

  一、須要保存和恢復數據的相關狀態場景。如保存遊戲狀態的場景;撤銷場景,如Ctrl-Z操做;事務回滾的應用。通常狀況下事務回滾有兩種方式:
    一是把從恢復點開始的操做都反向執行一遍;
    二是直接恢復到恢復點的各類狀態。
  兩種方式各有優缺點,要結合業務場景,決定使用哪一種模式;

  二、副本監控場景。備忘錄能夠看成一個臨時的副本監控,實現非實時和準實時的監控。

'''
打過遊戲的朋友必定知道,大多數遊戲都有保存進度的功能,
若是一局遊戲下來,忘保存了進度,那麼下次只能從上次進度點開始從新打了。
通常狀況下,保存進度是要存在可持久化存儲器上,本例中先以保存在內存中來模擬實現該場景的情形。
以模擬一個戰鬥角色爲例。首先,建立遊戲角色
'''
import random

class GameCharacter():
    vitality = 0
    attack = 0
    defense = 0
    def displayState(self):
        print ('Current Values:')
        print ('Life:%d' % self.vitality)
        print ('Attack:%d' % self.attack)
        print ('Defence:%d' % self.defense)
    def initState(self,vitality,attack,defense):
        self.vitality = vitality
        self.attack = attack
        self.defense = defense
    def saveState(self):
        return Memento(self.vitality, self.attack, self.defense)
    def recoverState(self, memento):
        self.vitality = memento.vitality
        self.attack = memento.attack
        self.defense = memento.defense

class FightCharactor(GameCharacter):
    def fight(self):
        self.vitality -= random.randint(1,10)

class Memento:
    vitality = 0
    attack = 0
    defense = 0
    def __init__(self, vitality, attack, defense):
        self.vitality = vitality
        self.attack = attack
        self.defense = defense



if __name__=="__main__":
    game_chrctr = FightCharactor()
    game_chrctr.initState(100,79,60)
    game_chrctr.displayState()
    memento = game_chrctr.saveState()
    game_chrctr.fight()
    game_chrctr.displayState()
    game_chrctr.recoverState(memento)
    game_chrctr.displayState()
View Code

 

狀態模式

  狀態模式的定義以下:

  當一個對象內在狀態改變時容許其改變行爲,這個對象看起來像改變了其類。

 

  狀態模式的優勢和應用場景

  優勢:
  一、狀態模式的優勢是結構清晰,相比於if…else…簡約了很多;
  二、封裝性好,外部調用沒必要知道內部實現細節。
  應用場景:
  一、行爲狀態改變的場景。這點在各類控制器中很是常見,同時,邏輯結構爲狀態轉移圖的場景中都很是適用。

 

  狀態模式的缺點

  一、在狀態比較多時,子類也會很是多,不便於管理。

  

'''
電梯在咱們周邊隨處可見,電梯的控制邏輯中心是由電梯控制器實現的。電梯的控制邏輯,即便簡單點設計,
把狀態分紅開門狀態,中止狀態和運行狀態,操做分紅開門、關門、運行、中止,那流程也是很複雜的。
首先,開門狀態不能開門、運行、中止;中止狀態不能關門,中止;運行狀態不能開門、關門、運行。
要用一個一個if…else…實現,首先代碼混亂,不易維護;二是不易擴展。至於各類設計原則什麼的……
那該如何實現?在上邊的邏輯中,每一個操做僅僅是一個操做,狀態切換與操做是分離的,
這也形成後來操做和狀態「相互配合」的「手忙腳亂」。若是把狀態抽象成一個類,
每一個狀態爲一個子類,每一個狀態實現什麼操做,不實現什麼操做,僅僅在這個類中具體實現就能夠了。
下面咱們實現這個邏輯。
先實現抽象的狀態類:
'''

class LiftState:
    def open(self):
        pass
    def close(self):
        pass
    def run(self):
        pass
    def stop(self):
        pass

class OpenState(LiftState):
    def open(self):
        print ("OPEN:The door is opened...")
        return self
    def close(self):
        print ("OPEN:The door start to close...")
        print ("OPEN:The door is closed")
        return StopState()
    def run(self):
        print ("OPEN:Run Forbidden.")
        return self
    def stop(self):
        print ("OPEN:Stop Forbidden.")
        return self
class RunState(LiftState):
    def open(self):
        print ("RUN:Open Forbidden.")
        return self
    def close(self):
        print ("RUN:Close Forbidden.")
        return self
    def run(self):
        print ("RUN:The lift is running...")
        return self
    def stop(self):
        print ("RUN:The lift start to stop...")
        print ("RUN:The lift stopped...")
        return StopState()
class StopState(LiftState):
    def open(self):
        print ("STOP:The door is opening...")
        print ("STOP:The door is opened...")
        return OpenState()
    def close(self):
        print ("STOP:Close Forbidden")
        return self
    def run(self):
        print ("STOP:The lift start to run...")
        return RunState()
    def stop(self):
        print ("STOP:The lift is stopped.")
        return self

class Context:
    lift_state=""
    def getState(self):
        return self.lift_state
    def setState(self,lift_state):
        self.lift_state=lift_state
    def open(self):
        self.setState(self.lift_state.open())
    def close(self):
        self.setState(self.lift_state.close())
    def run(self):
        self.setState(self.lift_state.run())
    def stop(self):
        self.setState(self.lift_state.stop())

if __name__=="__main__":
    ctx = Context()
    ctx.setState(StopState())
    ctx.open()
    ctx.run()
    ctx.close()
    ctx.run()
    ctx.stop()
View Code
相關文章
相關標籤/搜索