〖Python〗-- 設計模式 《大話設計模式》Python版代碼實現 二十三種設計模式及其python實現

【設計模式】

參考博客    《大話設計模式》Python版代碼實現    二十三種設計模式及其python實現html

1、設計模式介紹

設計模式(Design Patterns)python

  ——可複用面向對象軟件的基礎算法

  設計模式(Design pattern)是一套被反覆使用、多數人知曉的、通過分類編目的、代碼設計經驗的總結。使用設計模式是爲了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性。 毫無疑問,設計模式於己於他人於系統都是多贏的,設計模式使代碼編制真正工程化,設計模式是軟件工程的基石,如同大廈的一塊塊磚石同樣。項目中合理的運用設計模式能夠完美的解決不少問題,每種模式在如今中都有相應的原理來與之對應,每個模式描述了一個在咱們周圍不斷重複發生的問題,以及該問題的核心解決方案,這也是它能被普遍應用的緣由。shell

  Christopher Alexander:「每個模式描述了一個在咱們周圍不斷重複發生的問題,以及該問題的解決方案的核心。這樣你就能一次又一次地使用該方案而沒必要作重複勞動。」每個設計模式系統地命名、解釋和評價了面向對象系統中一個重要的和重複出現的設計。編程

  設計模式四個基本要素:模式名稱、問題、解決方案、效果設計模式

2、設計模式分類

  經典的《設計模式》一書概括出23種設計模式,這23種模式又可歸爲,建立型、結構型和行爲型3大類微信

2.1.建立型模式數據結構

  前面講過,社會化的分工愈來愈細,天然在軟件設計方面也是如此,所以對象的建立和對象的使用分開也就成爲了必然趨勢。由於對象的建立會消耗掉系統的不少資源,因此單獨對對象的建立進行研究,從而可以高效地建立對象就是建立型模式要探討的問題。這裏有6個具體的建立型模式可供研究,它們分別是:架構

簡單工廠模式(Simple Factory);app

工廠方法模式(Factory Method);

抽象工廠模式(Abstract Factory);

建立者模式(Builder);

原型模式(Prototype);

單例模式(Singleton)。

說明:嚴格來講,簡單工廠模式不是GoF總結出來的23種設計模式之一。

依賴於繼承的建立型模式:工廠方法模式
依賴於組合的建立性模式:抽象工廠模式、建立者模式

2.2 結構型模式

  在解決了對象的建立問題以後,對象的組成以及對象之間的依賴關係就成了開發人員關注的焦點,由於如何設計對象的結構、繼承和依賴關係會影響到後續程序的維護性、代碼的健壯性、耦合性等。對象結構的設計很容易體現出設計人員水平的高低,這裏有7個具體的結構型模式可供研究,它們分別是:

適配器模式(Adapter);

橋模式(Bridge);

組合模式(Composite);

裝飾模式(Decorator);

外觀模式(Facade);

享元模式(Flyweight)

代理模式(Proxy);

2.3 行爲型模式

在對象的結構和對象的建立問題都解決了以後,就剩下對象的行爲問題了,若是對象的行爲設計的好,那麼對象的行爲就會更清晰,它們之間的協做效率就會提升,這裏有11個具體的行爲型模式可供研究,它們分別是:

解釋器模式(Interpreter)。

責任鏈模式(Chain of Responsibility);

命令模式(Command);

迭代器模式(Iterator);

中介者模式(Mediator);

備忘錄模式(Memento);

觀察者模式(Observer);

狀態模式(State);

策略模式(Strategy);

訪問者模式(Visitor);

模板方法模式(Template Method);

3. 設計模式的六大原則

一、開閉原則(Open Close Principle)

  開閉原則就是說:一個軟件實體如類、模塊和函數應該對擴展開放,對修改關閉。即軟件實體應儘可能在不修改原有代碼的狀況下進行擴展。在程序須要進行拓展的時候,不能去修改原有的代碼,實現一個熱插拔的效果。因此一句話歸納就是:爲了使程序的擴展性好,易於維護和升級。想要達到這樣的效果,咱們須要使用接口和抽象類,後面的具體設計中咱們會提到這點。

二、里氏替換原則(Liskov Substitution Principle)

  里氏替換原則(Liskov Substitution Principle LSP)面向對象設計的基本原則之一。 里氏代換原則中說,任何基類能夠出現的地方,子類必定能夠出現。及全部引用基類(父類)的地方必須能透明地使用其子類的對象。LSP是繼承複用的基石,只有當衍生類能夠替換掉基類,軟件單位的功能不受到影響時,基類才能真正被複用,而衍生類也可以在基類的基礎上增長新的行爲。里氏代換原則是對「開-閉」原則的補充。實現「開-閉」原則的關鍵步驟就是抽象化。而基類與子類的繼承關係就是抽象化的具體實現,因此里氏代換原則是對實現抽象化的具體步驟的規範。—— From Baidu 百科

三、依賴倒置原則(Dependence Inversion Principle)

  這個是開閉原則的基礎,高層模塊不該該依賴低層模塊,兩者都應該依賴其抽象;抽象不該該依賴細節;細節應該依賴抽象。換言之,要針對接口編程,而不是針對實現編程。具體內容:是對接口編程,依賴於抽象而不依賴於具體。

四、接口隔離原則(Interface Segregation Principle)

  這個原則的意思是:使用多個隔離的接口,比使用單個接口要好。仍是一個下降類之間的耦合度的意思,從這兒咱們看出,其實設計模式就是一個軟件的設計思想,從大型軟件架構出發,爲了升級和維護方便。總結歸納:使用多個專門的接口,而不使用單一的總接口,即客戶端不該該依賴那些它不須要的接口;下降依賴,下降耦合。

五、迪米特法則(最少知道原則)(Demeter Principle)

  爲何叫最少知道原則,就是說:一個實體應當儘可能少的與其餘實體之間發生相互做用,使得系統功能模塊相對獨立。

六、單一職責原則:

  不要存在多於一個致使類變動的緣由。通俗的說,即一個類只負責一項職責。

4、寫在正式介紹設計模式以前

面向對象三大特性:封裝,繼承,多態

封裝:
  一、把數據和函數包裝在類裏
  二、類的邊界限制了一些外界的訪問 ---》 私有屬性

繼承:解決代碼複用 重寫方法叫作:override 重寫,複寫

多態:父類能夠表明子類

抽象類:
  抽象類不能被實例化
  抽象類的抽象方法,必須在子類中實現

接口:
  一種特殊的類,聲明瞭若干方法,要求繼承該接口的類必須實現這些方法。
  做用:限制繼承接口的類的方法的名稱及調用方式;隱藏了類的內部實現。
  接口就是一種抽象的基類(父類),限制繼承它的類必須實現接口中定義的某些方法

from abc import abstractmethod, ABCMeta

#定義抽象類
class Payment(metaclass=ABCMeta):
    @abstractmethod
    def pay(self, money):
        """支付方法,參數money,每一個繼承的類都要實現這個方法"""
        pass

#實現Payment接口
class Alipay(Payment):
    def pay(self, money):
        print("支付寶支付%s元"%money)


class ApplePay(Payment):
    def pay(self, money):
        print("蘋果支付%s元"%money)


class WechatPay(Payment):
    def pay(self, money):
        print("微信支付%s元"%money)


def func(x):
    x.pay(100)
接口應用代碼舉例

5、各類設計模式 

一、簡單工廠模式

建立對象放在了工廠類中,而不是再暴露在外邊,經過工廠類實現全部操做。

from abc import abstractmethod, ABCMeta
#定義支付方法的抽象類
class Payment(metaclass=ABCMeta):
    @abstractmethod
    def pay(self, money):
        pass
#定義不一樣的支付方法
class Alipay(Payment):
    def __init__(self, enable_yuebao=False):
        self.enable_yuebao = enable_yuebao

    def pay(self, money):
        if self.enable_yuebao:
            print("餘額寶支付%s元" % money)
        else:
            print("支付寶支付%s元" % money)


class ApplePay(Payment):
    def pay(self, money):
        print("蘋果支付%s元" % money)

#簡單工廠模式,一個工廠定義一個生產方式。經過傳入的參數判斷,不一樣的支付類型
class PaymentFactory:
    def create_payment(self, method):
        if method == "alipay":
            return Alipay()
        elif method == "applepay":
            return ApplePay()
        elif method == "yuebao":
            return Alipay(enable_yuebao=True)
        else:
            raise NameError(method)

f = PaymentFactory()
p = f.create_payment("alipay")
p.pay(100)
簡單工廠模式

二、工廠方法模式

 

具體工廠依賴具體產品

內容:定義一個用於建立對象的接口(工廠接口),讓子類決定實例化哪個產品類。
角色:
  抽象工廠角色(Creator)
  具體工廠角色(Concrete Creator)
  抽象產品角色(Product)
  具體產品角色(Concrete Product)
模式特色:定義一個用於建立對象的接口,讓子類決定實例化哪個類。這使得一個類的實例化延遲到其子類。
  這個模式和簡單工廠有區別,簡單工廠模式只有一個工廠,工廠方法模式相比簡單工廠模式將每一個具體產品都對應了一個具體工廠。

適用場景:
  須要生產多種、大量複雜對象的時候
  須要下降耦合度的時候
  當系統中的產品種類須要常常擴展的時候
優勢:
  每一個具體產品都對應一個具體工廠類,不須要修改工廠類代碼
  隱藏了對象建立的實現細節
缺點:
  每增長一個具體產品類,就必須增長一個相應的具體工廠類

 

內容:不直接向客戶端暴露對象建立的實現細節,而是經過一個工廠類來負責建立產品類的實例。
角色:
  工廠角色(Creator)
  抽象產品角色(Product)
  具體產品角色(Concrete Product)
優勢:
  隱藏了對象建立的實現細節,工廠根據條件產生不一樣功能的類。
  客戶端不須要修改代碼
缺點:
  違反了單一職責原則,將建立邏輯幾種到一個工廠類裏
  當添加新產品時,須要修改工廠類代碼,違反了開閉原則

from abc import abstractmethod, ABCMeta

#定義支付類型的抽象類
class Payment(metaclass=ABCMeta):
    @abstractmethod
    def pay(self, money):
        pass

#繼承支付抽象類
class Alipay(Payment):
    def pay(self, money):
        print("支付寶支付%s元" % money)


class ApplePay(Payment):
    def pay(self, money):
        print("蘋果支付%s元"%money)

#定義工廠的抽象類,用於支付類的生產
class PaymentFactory(metaclass=ABCMeta):
    @abstractmethod
    def create_payment(self):
        pass

#繼承工廠抽象類,具體工廠負責具體的產品
class AlipayFactory(PaymentFactory):
    def create_payment(self):
        return Alipay()

class ApplePayFactory(PaymentFactory):
    def create_payment(self):
        return ApplePay()

#具體使用,利用具體工廠的對象 實例 具體支付的對象 而後 調用支付方法
# 用戶輸入
# 支付寶,120 
af = AlipayFactory() 
ali = af.create_payment()
ali.pay(120)
工廠方法模式

三、抽象工廠模式

 

意圖:提供一個建立一系列相關或相互依賴對象的接口,而無需指定它們具體的類。

適用性:

  一個系統要獨立於它的產品的建立、組合和表示時。

  一個系統要由多個產品系列中的一個來配置時。

  當你要強調一系列相關的產品對象的設計以便進行聯合使用時。

  當你提供一個產品類庫,而只想顯示它們的接口而不是實現時。

內容:定義一個工廠類接口,讓工廠子類來建立一系列相關或相互依賴的對象。
例:生產一部手機,須要手機殼、CPU、操做系統三類對象進行組裝,其中每類對象都有不一樣的種類。對每一個具體工廠,分別生產一部手機所須要的三個對象。
角色:
  抽象工廠角色(Creator)
  具體工廠角色(Concrete Creator)
  抽象產品角色(Product)
  具體產品角色(Concrete Product)
  客戶端(Client)

相比工廠方法模式,抽象工廠模式中的每一個具體工廠都生產一套產品。

適用場景:
  系統要獨立於產品的建立與組合時
  強調一系列相關的產品對象的設計以便進行聯合使用時
  提供一個產品類庫,想隱藏產品的具體實現時
優勢:
  將客戶端與類的具體實現相分離
  每一個工廠建立了一個完整的產品系列,使得易於交換產品系列
  有利於產品的一致性(即產品之間的約束關係)
缺點:
  難以支持新種類的(抽象)產品

from abc import abstractmethod, ABCMeta

# ------抽象產品------
class PhoneShell(metaclass=ABCMeta):
    @abstractmethod
    def show_shell(self):
        pass

class CPU(metaclass=ABCMeta):
    @abstractmethod
    def show_cpu(self):
        pass

class OS(metaclass=ABCMeta):
    @abstractmethod
    def show_os(self):
        pass


# ------抽象工廠------
class PhoneFactory(metaclass=ABCMeta):
    @abstractmethod
    def make_shell(self):
        pass

    @abstractmethod
    def make_cpu(self):
        pass

    @abstractmethod
    def make_os(self):
        pass


# ------具體產品------
class SmallShell(PhoneShell):
    def show_shell(self):
        print("普通手機小手機殼")

class BigShell(PhoneShell):
    def show_shell(self):
        print("普通手機大手機殼")

class AppleShell(PhoneShell):
    def show_shell(self):
        print("蘋果手機殼")


class SnapDragonCPU(CPU):
    def show_cpu(self):
        print("驍龍CPU")


class MediaTekCPU(CPU):
    def show_cpu(self):
        print("聯發科CPU")


class AppleCPU(CPU):
    def show_cpu(self):
        print("蘋果CPU")


class Android(OS):
    def show_os(self):
        print("Android系統")


class IOS(OS):
    def show_os(self):
        print("iOS系統")


# ------具體工廠------

class MiFactory(PhoneFactory):
    def make_cpu(self):
        return SnapDragonCPU()

    def make_os(self):
        return Android()

    def make_shell(self):
        return BigShell()


class HuaweiFactory(PhoneFactory):
    def make_cpu(self):
        return MediaTekCPU()

    def make_os(self):
        return Android()

    def make_shell(self):
        return SmallShell()


class IPhoneFactory(PhoneFactory):
    def make_cpu(self):
        return AppleCPU()

    def make_os(self):
        return IOS()

    def make_shell(self):
        return AppleShell()


# ------客戶端------
class Phone:
    def __init__(self, cpu, os, shell):
        self.cpu = cpu
        self.os = os
        self.shell = shell

    def show_info(self):
        print("手機信息:")
        self.cpu.show_cpu()
        self.os.show_os()
        self.shell.show_shell()

# ------創造產品------
def make_phone(factory):
    cpu = factory.make_cpu()
    os = factory.make_os()
    shell = factory.make_shell()
    return Phone(cpu, os, shell)

p1 = make_phone(HuaweiFactory())
p1.show_info()
抽象工廠模式

四、建造者模式

 

意圖:將一個複雜對象的構建與它的表示分離,使得一樣的構建過程能夠建立不一樣的表示。

適用場景:
  當建立複雜對象的算法(Director)應該獨立於該對象的組成部分(Builder)以及它們的裝配方式時
  當構造過程容許被構造的對象有不一樣的表示時(不一樣Builder)。
角色:
  抽象建造者(Builder)
  具體建造者(Concrete Builder)
  指揮者(Director)
  產品(Product)

  建造者模式與抽象工廠模式類似,也用來建立複雜對象。主要區別是建造者模式着重一步步構造一個複雜對象,而抽象工廠模式着重於多個系列的產品對象

優勢:
  隱藏了一個產品的內部結構和裝配過程
  將構造代碼與表示代碼分開
  能夠對構造過程進行更精細的控制

from abc import abstractmethod, ABCMeta

#------產品------
class Player:
    def __init__(self, face=None, body=None, arm=None, leg=None):
        self.face = face
        self.arm = arm
        self.leg = leg
        self.body = body

    def __str__(self):
        return "%s, %s, %s, %s" % (self.face, self.arm, self.body, self.leg)


#------建造者------
class PlayerBuilder(metaclass=ABCMeta):
    @abstractmethod
    def build_face(self):
        pass
    @abstractmethod
    def build_arm(self):
        pass
    @abstractmethod
    def build_leg(self):
        pass
    @abstractmethod
    def build_body(self):
        pass
    @abstractmethod
    def get_player(self):
        pass

# 建造生產的類
class BeautifulWomanBuilder(PlayerBuilder):
    def __init__(self):
        self.player = Player()
    def build_face(self):
        self.player.face = "漂亮臉蛋"
    def build_arm(self):
        self.player.arm="細胳膊"
    def build_body(self):
        self.player.body="細腰"
    def build_leg(self):
        self.player.leg="長腿"
    def get_player(self):
        return self.player

# 用於生產 產品的類
class PlayerDirector:
    def build_player(self, builder):
        builder.build_body()
        builder.build_arm()
        builder.build_leg()
        builder.build_face()
        return builder.get_player()

#客戶端
director = PlayerDirector()
builder = BeautifulWomanBuilder()
p = director.build_player(builder)
print(p)
建造者模式

五、單例模式

 

意圖:建立一個類,保證一個類只有一個實例,並提供一個訪問它的全局訪問點。單例至關因而全局變量,內部的方法可能會被更改

角色:
  單例(Singleton)
適用場景
  當類只能有一個實例並且客戶能夠從一個衆所周知的訪問點訪問它時;
  當這個惟一實例應該是經過子類化可擴展的,而且客戶應該無需更改代碼就能使用一個擴展的實例時。
優勢:
  對惟一實例的受控訪問
  單例至關於全局變量,但防止了命名空間被污染
與單例模式功能類似的概念:全局變量、靜態變量(方法)

class Singleton(object):
    def __new__(cls, *args, **kwargs):
        if not hasattr(cls, "_instance"):
            cls._instance = super(Singleton, cls).__new__(cls)
        return cls._instance

class MyClass(Singleton):
    def __init__(self, name):
            self.name = name


a = MyClass("a")

print(a)
print(a.name)

b = MyClass('b')
#
print(b)
print(b.name)
b.name = 'xxx'
#
print(a)
print(a.name)
單例模式
單例模式:

    單例模式  每次只實例化一個對象!造對象的過程是__new__方法,給對象傳數據是__init__的方法,作成一個實例!
    類無論怎樣建立多個實例,對象都會以第一個實例爲準
    
#方式一:文件導入,文件內建立一個對象,經過這個對象導入方法
        
#方式二:經過靜態方法實例化對象

class Foo:
    
    # 定義靜態變量實例
    _instance = None
    
    def __init__(self):
        pass
        
    @staticmethod
    def get_instance(cls):
        if cls._instance:
            return cls._instance
        else:
            obj = cls()
            cls._instance = obj
            return obj

#方式三: __new__ 

class Foo(object):
    _instance = None

    def __init__(self):
        pass

    def __new__(cls, *args, **kwargs):
        if cls._instance:
            return cls._instance
        else:
            obj = object.__new__(cls, *args, **kwargs)
            cls._instance = obj
            return obj

用途:
    - 自定義curb組件時,對於類構建的信息,僅需一個實例
    - 發佈文章,特殊字符的過濾: KindEditor
單例模式深究

六、適配器模式

 

意圖:將一個類的接口轉換成客戶但願的另外一個接口。適配器模式使得本來因爲接口不兼容而不能一塊兒工做的那些類能夠一塊兒工做。
角色:
  目標接口(Target)
  待適配的類(Adaptee)
  適配器(Adapter)
兩種實現方式:
  類適配器:使用多繼承
  對象適配器:使用組合

適用場景:
  你想使用一個已經存在的類,而它的接口不符合你的要求;
  你想建立一個能夠複用的類,該類能夠與其餘不相關的類或不可預見的類(即那些接口可能不必定兼容的類)協同工做。
  (對象適配器)想使用一些已經存在的子類,但不可能對每個都進行子類化以匹配它們的接口。對象適配器能夠適配它的父類接口。

類適配器和對象適配器有不一樣的權衡。
類適配器規則:
  使用一個具體的Adapter類對Adaptee和Target進行匹配。結果是當咱們想要匹配一個類以及全部他的子類時,類Adapter將不能勝任工做。
  使用Adapter能夠重定義Adapter的部分行爲,由於Adapter是Adaptee的一個子類。
  僅僅引入一個對象,並不須要額外的指針以間接獲得adaptee.
對象適配器規則:
  容許一個Adapter與多個Adaptee -- 即Adaptee自己以及它的全部子類(若是有子類的話)一一同時工做。Adapter也能夠一次給全部的Adaptee添加功能。
  使得重定義 Adaptee 的行爲比較困難。這須要生成Adaptee的子類而且使得 Adapter 引用這個子類而不是引用Adaptee自己。

from abc import abstractmethod, ABCMeta


class Payment(metaclass=ABCMeta):
    @abstractmethod
    def pay(self, money):
        raise NotImplementedError


class Alipay(Payment):
    def pay(self, money):
        print("支付寶支付%s元"%money)


class ApplePay(Payment):
    def pay(self, money):
        print("蘋果支付%s元"%money)

#------待適配類------
class WechatPay:
    def huaqian(self, money):
        print("微信支付%s元"%money)

#------類適配器------
class RealWeChatPay(Payment, WechatPay):
    def pay(self, money):
        return self.huaqian(money)


#------對象適配器------
class PayAdapter(Payment):
    def __init__(self, payment):
        self.payment = payment #類的一個對象

    def pay(self, money):
        return self.payment.huaqian(money)


RealWeChatPay().pay(100)
#PayAdapter(WechatPay()).pay(1000)
適配器模式
from abc import abstractmethod, ABCMeta

class Payment(metaclass=ABCMeta):
    @abstractmethod
    def pay(self, money):
        """支付方法,參數money"""
        pass

#實現Payment接口
class Alipay(Payment):
    def pay(self, money):
        print("支付寶支付%s元"%money)


class ApplePay(Payment):
    def pay(self, money):
        print("蘋果支付%s元"%money)

class PaymentX(metaclass=ABCMeta):
    @abstractmethod
    def zhifu(self, money):
        """支付方法,參數money"""
        pass

class WechatPay(PaymentX):
    def zhifu(self, money):
        print("微信支付%s元"%money)

class CreditCardPay(PaymentX):
    def zhifu(self, money):
        print("信用卡支付%s元" % money)

class PaypalPay(PaymentX):
    def zhifu(self, money):
        print("信用卡支付%s元" % money)

#類適配器
class RealWechatPay(Payment, WechatPay):
    def pay(self, money):
        self.zhifu(money)

p = RealWechatPay()
p.pay(100)

#對象適配器
class RealPay(Payment):
    def __init__(self, payment_cls):
        self.__a = payment_cls()
    def pay(self, money):
        self.__a.zhifu(money)

def test_payment(p):
    p.pay(100)

test_payment(RealPay(CreditCardPay))
適配器

七、組合模式

 

意圖:將對象組合成樹形結構以表示「部分-總體」的層次結構。組合模式使得用戶對單個對象和組合對象的使用具備一致性。
角色:
  抽象組件(Component)
  葉子組件(Leaf)
  複合組件(Composite)
  客戶端(Client)

適用場景:
  表示對象的「部分-總體」層次結構(特別是結構是遞歸的)
  但願用戶忽略組合對象與單個對象的不一樣,用戶統一地使用組合結構中的全部對象
優勢:
  定義了包含基本對象和組合對象的類層次結構
  簡化客戶端代碼,即客戶端能夠一致地使用組合對象和單個對象
  更容易增長新類型的組件
缺點:
  很難限制組合中的組件

from abc import abstractmethod, ABCMeta

class Graphic(metaclass=ABCMeta):
    @abstractmethod
    def draw(self):
        pass

    @abstractmethod
    def add(self, graphic):
        pass

    def getchildren(self):
        pass

# 圖元

class Point(Graphic):
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def draw(self):
        print(self)

    def add(self, graphic):
        raise TypeError

    def getchildren(self):
        raise TypeError

    def __str__(self):
        return "點(%s, %s)" % (self.x, self.y)


class Line(Graphic):
    def __init__(self, p1, p2):
        self.p1 = p1
        self.p2 = p2

    def draw(self):
        print(self)

    def add(self, graphic):
        raise TypeError

    def getchildren(self):
        raise TypeError

    def __str__(self):
        return "線段[%s, %s]" % (self.p1, self.p2)


class Picture(Graphic):
    def __init__(self):
        self.children = []

    def add(self, graphic):
        self.children.append(graphic)

    def getchildren(self):
        return self.children

    def draw(self):
        print("------複合圖形------")
        for g in self.children:
            g.draw()
        print("------END------")


pic1 = Picture()
point = Point(2,3)
pic1.add(point)
pic1.add(Line(Point(1,2), Point(4,5)))
pic1.add(Line(Point(0,1), Point(2,1)))

pic2 = Picture()
pic2.add(Point(-2,-1))
pic2.add(Line(Point(0,0), Point(1,1)))

pic = Picture()
pic.add(pic1)
pic.add(pic2)

pic.draw()
pic1.draw()
point.draw()
組合模式

八、代理模式

意圖:爲其餘對象提供一種代理以控制對這個對象的訪問。在須要用比較通用和複雜的對象指針代替簡單的指針的時候,使用Proxy模式。
角色:
  抽象實體(Subject)
  實體(RealSubject)
  代理(Proxy)
適用場景:
  遠程代理:爲遠程的對象提供代理。一個對象在不一樣的地址空間提供局部表明。
  虛代理:根據須要建立很大的對象。
  保護代理:控制對原始對象的訪問,用於對象有不一樣訪問權限時。保護代理用於對象應該有不一樣的訪問權限的時候。例如,在Choices 操做系統[ CIRM93]中KemelProxies爲操做系統對象提供了訪問保護。 
  智能指引(Smart Reference )取代了簡單的指針,它在訪問對象時執行一些附加操做。 它的典型用途包括:對指向實際對象的引用計數,這樣當該對象沒有引用時,能夠自動釋放它。
優勢:
  遠程代理:能夠隱藏對象位於遠程地址空間的事實
  虛代理:能夠進行優化,例如根據要求建立對象
  保護代理:容許在訪問一個對象時有一些附加的內務處理

  當第一次引用一個持久對象時,將它裝入內存。在訪問一個實際對象前,檢查是否已經鎖定了它,以確保其餘對象不能改變它。

from abc import ABCMeta, abstractmethod

class Subject(metaclass=ABCMeta):
    @abstractmethod
    def get_content(self):
        pass
    def set_content(self, content):
        pass

#文件操做,獲取文件內容和設置
class RealSubject(Subject):
    def __init__(self, filename):
        self.filename = filename
        print("讀取%s文件內容"%filename)
        f = open(filename)
        self.content = f.read()
        f.close()

    def get_content(self):
        return self.content

    def set_content(self, content):
        f = open(self.filename, 'w')
        f.write(content)
        self.content = content
        f.close()

class ProxyA(Subject):
    def __init__(self, filename):
        self.subj = RealSubject(filename)

    def get_content(self):
        return self.subj.get_content()


#---虛代理
class ProxyB(Subject):
    def __init__(self, filename):
        self.filename = filename
        self.subj = None

    def get_content(self):
        if not self.subj:
            self.subj = RealSubject(self.filename)
        return self.subj.get_content()

x = ProxyB('abc.txt')
"""abc.txt 內文本 hello world"""
#print(x.get_content())


# --保護代理
class ProxyC(Subject):
    def __init__(self, filename):
        self.subj = RealSubject(filename)

    def get_content(self):
        self.subj.get_content()

    def set_content(self, content):
        raise PermissionError

filename = "abc.txt"
username = input()
if username!="somebody":
    p = ProxyC(filename)
else:
    p = ProxyA(filename)

print(p.get_content())
代理模式

九、責任鏈模式

 

意圖:使多個對象都有機會處理請求,從而避免請求的發送者和接收者之間的耦合關係。將這些對象連成一條鏈,並沿着這條鏈傳遞該請求,直到有一個對象處理它爲止。
角色:
  抽象處理者(Handler)
  具體處理者(ConcreteHandler)
  客戶端(Client)
適用場景:
  有多個對象能夠處理一個請求,哪一個對象處理由運行時決定
  在不明確接收者的狀況下,向多個對象中的一個提交一個請求
  可處理一個請求的對象集合應被動態指定。
優勢:
  下降耦合度:一個對象無需知道是其餘哪個對象處理其請求
缺點:
  請求不保證被接收:鏈的末端沒有處理或鏈配置錯誤
例:
  請假部門批准:leader ---> 部門經理 ---> 總經理
  Javascript事件浮升機制

from abc import ABCMeta, abstractmethod

class Handler(metaclass=ABCMeta):
    @abstractmethod
    def handle_leave(self, day):
        pass

class GeneralManagerHandler(Handler):
    def handle_leave(self, day):
        if day < 10:
            print("總經理批准%d天假"%day)
        else:
            print("呵呵")

class DepartmentManagerHandler(Handler):
    def __init__(self):
        self.successor = GeneralManagerHandler()
    def handle_leave(self, day):
        if day < 7:
            print("部門經理批准%d天假"%day)
        else:
            print("部門經理無權准假")
            self.successor.handle_leave(day)


class ProjectDirectorHandler(Handler):
    def __init__(self):
        self.successor = DepartmentManagerHandler()
    def handle_leave(self, day):
        if day < 3:
            print("項目主管批准%d天假"%day)
        else:
            print("項目主管無權准假")
            self.successor.handle_leave(day)


day = 9
h = ProjectDirectorHandler()
h.handle_leave(day)
責任鏈模式(請假流程)
class Handler(metaclass=ABCMeta):
    @abstractmethod
    def add_event(self, func):
        pass

    @abstractmethod
    def handle(self):
        pass

class BodyHandler(Handler):
    def __init__(self):
        self.func = None

    def add_event(self, func):
        self.func = func

    def handle(self):
        if self.func:
            return self.func()
        else:
            print("已到最後一級,沒法處理")


class ElementHandler(Handler):
    def __init__(self, successor):
        self.func = None
        self.successor = successor

    def add_event(self, func):
        self.func = func

    def handle(self):
        if self.func:
            return self.func()
        else:
            return self.successor.handle()

# 客戶端

# <body><div><a>

body = {'type': 'body', 'name': 'body', 'children': [], 'father': None}

div = {'type': 'div', 'name': 'div', 'children': [], 'father': body}

a = {'type': 'a', 'name': 'a', 'children': [], 'father': div}

body['children'].append(div)
div['children'].append(a)

body['event_handler'] = BodyHandler()
div['event_handler'] = ElementHandler(div['father']['event_handler'])
a['event_handler'] = ElementHandler(a['father']['event_handler'])

def attach_event(element, func):
    element['event_handler'].add_event(func)

def func_div():
    print("這是給div的函數")

def func_a():
    print("這是給a的函數")

def func_body():
    print("這是給body的函數")

#attach_event(div, func_div)
attach_event(a, func_a)
attach_event(body, func_body)

a['event_handler'].handle()
模仿js事件處理

十、迭代器模式

意圖:提供一種方法順序訪問一個聚合對象中的各個元素,而又不須要暴露該對象的內部表示

適用性:
  訪問一個聚合對象的內容而無需暴露它的內部表示。
  支持對聚合對象的多種遍歷。
  爲遍歷不一樣的聚合結構提供一個統一的接口(即, 支持多態迭代)。

實現方法:__iter__、__next__

class Range:
 
    def __init__(self,start,end):
        self.start = start
        self.end = end
 
    def __iter__(self): #將對象轉變成迭代器
        return self
 
    def __next__(self): #自取值
        if self.start == self.end:
            raise StopIteration
 
        n = self.start
        self.start+=1
        return n
 
for i in Range(1,10):
    print(i)
迭代器模式

十一、觀察者模式

 

意圖:定義對象間的一種一對多的依賴關係,當一個對象的狀態發生改變時, 全部依賴於它的對象都獲得通知並被自動更新。觀察者模式又稱「發佈-訂閱」模式
角色:
  抽象主題(Subject)
  具體主題(ConcreteSubject)——發佈者
  抽象觀察者(Observer)
  具體觀察者(ConcreteObserver)——訂閱者
適用場景:
  當一個抽象模型有兩方面,其中一個方面依賴於另外一個方面。將這二者封裝在獨立對象中以使它們能夠各自獨立地改變和複用。
  當對一個對象的改變須要同時改變其它對象,而不知道具體有多少對象有待改變。
  當一個對象必須通知其它對象,而它又不能假定其它對象是誰。換言之,你不但願這些對象是緊密耦合的。
優勢:
  目標和觀察者之間的抽象耦合最小
  支持廣播通訊
缺點:
  多個觀察者之間互不知道對方存在,所以一個觀察者對主題的修改可能形成錯誤的更新。

from abc import ABCMeta, abstractmethod

class Observer(metaclass=ABCMeta):
    @abstractmethod
    def update(self, notice):
        pass

class Notice:
    def __init__(self):
        self.observers = []

    def attach(self, obs):
        self.observers.append(obs)

    def detach(self, obs):
        self.observers.remove(obs)
        obs.company_info=None

    def notify(self):
        for obj in self.observers:
            obj.update(self)


class ManagerNotice(Notice):
    def __init__(self, company_info=None):
        super().__init__()
        self.__company_info = company_info

    @property
    def company_info(self):
        return self.__company_info

    @company_info.setter
    def company_info(self, info):
        self.__company_info = info
        self.notify()


class Manager(Observer):
    def __init__(self):
        self.company_info = None

    def update(self, noti):
        self.company_info = noti.company_info


notice = ManagerNotice()

abes = Manager()
dws = Manager()

# print(alex.company_info)
# print(wusir.company_info)

notice.attach(abes)
notice.attach(dws)
#
notice.company_info="公司運行良好"
#
# print(alex.company_info)
# print(wusir.company_info)

notice.company_info="公司將要上市"

print(abes.company_info)
print(dws.company_info)
#
notice.detach(dws)
# #
notice.company_info="公司要破產了,趕快跑路"
#
print(abes.company_info)
print(dws.company_info)
觀察者模式

十二、策略模式

意圖:定義一系列的算法,把它們一個個封裝起來,而且使它們可相互替換。本模式使得算法可獨立於使用它的客戶而變化。
角色:
  抽象策略(Strategy)
  具體策略(ConcreteStrategy)
  上下文(Context)
適用場景:
  許多相關的類僅僅是行爲有異。「策略」提供了一種用多個行爲中的一個行爲來配置一個類的方法。
  須要使用一個算法的不一樣變體。例如,你可能會定義一些反映不一樣的空間/時間權衡的算法。當這些變體實現爲一個算法的類層次時,可使用策略模式。
  算法使用了客戶端無需知道的數據。可以使用策略模式以免暴露覆雜的、與算法相關的數據結構。
  一個類中的多種行爲以多個條件語句的形式存在,能夠將這些行爲封裝如不一樣的策略類中。

from abc import ABCMeta, abstractmethod
import random

class Sort(metaclass=ABCMeta):
    @abstractmethod
    def sort(self, data):
        pass


class QuickSort(Sort):
    def quick_sort(self, data, left, right):
        if left < right:
            mid = self.partition(data, left, right)
            self.quick_sort(data, left, mid - 1)
            self.quick_sort(data, mid + 1, right)

    def partition(self, data, left, right):
        tmp = data[left]
        while left < right:
            while left < right and data[right] >= tmp:
                right -= 1
            data[left] = data[right]
            while left < right and data[left] <= tmp:
                left += 1
            data[right] = data[left]
        data[left] = tmp
        return left

    def sort(self, data):
        print("快速排序")
        return self.quick_sort(data, 0, len(data) - 1)


class MergeSort(Sort):
    def merge(self, data, low, mid, high):
        i = low
        j = mid + 1
        ltmp = []
        while i <= mid and j <= high:
            if data[i] <= data[j]:
                ltmp.append(data[i])
                i += 1
            else:
                ltmp.append(data[j])
                j += 1

        while i <= mid:
            ltmp.append(data[i])
            i += 1

        while j <= high:
            ltmp.append(data[j])
            j += 1

        data[low:high + 1] = ltmp


    def merge_sort(self, data, low, high):
        if low < high:
            mid = (low + high) // 2
            self.merge_sort(data, low, mid)
            self.merge_sort(data, mid + 1, high)
            self.merge(data, low, mid, high)

    def sort(self, data):
        print("歸併排序")
        return self.merge_sort(data, 0, len(data) - 1)


class Context:
    def __init__(self, data, strategy=None):
        self.data = data
        self.strategy = strategy

    def set_strategy(self, strategy):
        self.strategy = strategy

    def do_strategy(self):
        if self.strategy:
            self.strategy.sort(self.data)
        else:
            raise TypeError


li = list(range(100000))
random.shuffle(li)


context = Context(li, MergeSort())
context.do_strategy()
#print(context.data)

random.shuffle(context.data)

context.set_strategy(QuickSort())
context.do_strategy()
策略模式

1三、模板方法模式

意圖:定義一個操做中的算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類能夠不改變一個算法的結構便可重定義該算法的某些特定步驟。
角色:
  抽象類(AbstractClass):定義抽象的原子操做(鉤子操做);實現一個模板方法做爲算法的骨架。
  具體類(ConcreteClass):實現原子操做
適用場景:
  一次性實現一個算法的不變的部分,並將可變的行爲留給子類來實現。
  各個子類中的公共行爲應該被提取出來並集中到一個公共父類中以免代碼重複。
  控制子類擴展。模板方法只在特定點調用某操做,這樣就只容許在這些點進行擴展。


優勢:
  定義了一系列可重用的算法和行爲
  消除了一些條件語句
  能夠提供相同行爲的不一樣實現
缺點:
  客戶必須瞭解不一樣的策略
  策略與上下文之間的通訊開銷
  增長了對象的數目

from abc import ABCMeta, abstractmethod


class IOHandler(metaclass=ABCMeta):
    @abstractmethod
    def open(self, name):
        pass
    @abstractmethod
    def deal(self, change):
        pass
    @abstractmethod
    def close(self):
        pass
    def process(self, name, change):
        self.open(name)
        self.deal(change)
        self.close()

class FileHandler(IOHandler):
    def open(self, name):
        self.file = open(name,"w")

    def deal(self, change):
        self.file.write(change)

    def close(self):
        self.file.close()


f = FileHandler()
f.process("abc.txt", "Hello World")
模板方法模式
相關文章
相關標籤/搜索