淺談設計模式及python實現

設計模式及Python實現

 

設計模式是什麼?

Christopher Alexander:「每個模式描述了一個在咱們周圍不斷重複發生的問題,以及該問題的解決方案的核心。這樣你就能一次又一次地使用該方案而沒必要作重複勞動。」html

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

起初,你會認爲「模式」就是爲了解決一類特定問題而特別想出來的明智之舉。說的沒錯,看起來的確是經過不少人一塊兒工做,從不一樣的角度看待問題進而造成的一個最通用、最靈活的解決方案。也許這些問題你曾經見過或是曾經解決過,可是你的解決方案極可能沒有模式這麼完備。python

雖然被稱爲「設計模式」,可是它們同「設計「領域並不是緊密聯繫。設計模式同傳統意義上的分析、設計與實現不一樣,事實上設計模式將一個完整的理念根植於程序中,因此它可能出如今分析階段或是更高層的設計階段。頗有趣的是由於設計模式的具體體現是程序代碼,所以可能會讓你認爲它不會在具體實現階段以前出現(事實上在進入具體實現階段以前你都沒有意識到正在使用具體的設計模式)。算法

能夠經過程序設計的基本概念來理解模式:增長一個抽象層。抽象一個事物就是隔離任何具體細節,這麼作的目的是爲了將那些不變的核心部分從其餘細節中分離出來。當你發現你程序中的某些部分常常由於某些緣由改動,而你不想讓這些改動的部分引起其餘部分的改動,這時候你就須要思考那些不會變更的設計方法了。這麼作不只會使代碼可維護性更高,並且會讓代碼更易於理解,從而下降開發成本。shell

三種最基本的設計模式:編程

  1. 建立模式,提供實例化的方法,爲適合的情況提供相應的對象建立方法。
  2. 結構化模式,一般用來處理實體之間的關係,使得這些實體可以更好地協同工做。
  3. 行爲模式,用於在不一樣的實體建進行通訊,爲實體之間的通訊提供更容易,更靈活的通訊方法。

設計模式六大原則

  • 開閉原則:一個軟件實體如類、模塊和函數應該對擴展開放,對修改關閉。即軟件實體應儘可能在不修改原有代碼的狀況下進行擴展。
  • 里氏(Liskov)替換原則:全部引用基類(父類)的地方必須能透明地使用其子類的對象。
  • 依賴倒置原則:高層模塊不該該依賴低層模塊,兩者都應該依賴其抽象;抽象不該該依賴細節;細節應該依賴抽象。換言之,要針對接口編程,而不是針對實現編程。
  • 接口隔離原則:使用多個專門的接口,而不使用單一的總接口,即客戶端不該該依賴那些它不須要的接口。
  • 迪米特法則:一個軟件實體應當儘量少地與其餘實體發生相互做用。
  • 單一職責原則:不要存在多於一個致使類變動的緣由。通俗的說,即一個類只負責一項職責。 

接口

接口:一種特殊的類,聲明瞭若干方法,要求繼承該接口的類必須實現這些方法。
做用:限制繼承接口的類的方法的名稱及調用方式;隱藏了類的內部實現。設計模式

接口就是一種抽象的基類(父類),限制繼承它的類必須實現接口中定義的某些方法。微信

Python中使用ABCMeta、abstractmethod的抽象類、抽象方法來實現接口的功能。接口類定義方法,不具體實現,限制子類必須有該方法。在接口子類中實現具體的功能。app

# 經過抽象類和抽象方法,作抽象用
from abc import ABCMeta
from abc import abstractmethod  # 導入抽象方法

class Father(metaclass=ABCMeta):  # 建立抽象類
    
    @abstractmethod
    def f1(self):
        pass

    @abstractmethod
    def f2(self):
        pass

class F1(Father):
    def f1(self):
        pass

    def f2(self):
        pass

    def f3(self):
        pass

obj = F1()
class Interface:
    def method(self, arg):
        raise NotImplementedError
報錯定義接口

建立型模式

1. 簡單工廠模式

內容:不直接向客戶端暴露對象建立的實現細節,而是經過一個工廠類來負責建立產品類的實例。dom

角色

  • 工廠角色(Creator)
  • 抽象產品角色(Product)
  • 具體產品角色(Concrete Product)

優勢

  • 隱藏了對象建立的實現細節
  • 客戶端不須要修改代碼

缺點

  • 違反了單一職責原則,將建立邏輯幾種到一個工廠類裏
  • 當添加新產品時,須要修改工廠類代碼,違反了開閉原則
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 == 'yuebao':
            return Alipay(enable_yuebao=True)
        elif method == "applepay":
            return ApplePay()
        else:
            raise NameError(method)


f = PaymentFactory()
p = f.create_payment("yuebao")
p.pay(100)
PaymentFactory簡單工廠

2. 工廠方法模式(Factory Method)

內容:定義一個用於建立對象的接口(工廠接口),讓子類決定實例化哪個產品類。

角色

  • 抽象工廠角色(Creator)
  • 具體工廠角色(Concrete 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()


af = AlipayFactory()
ali = af.create_payment()
ali.pay(120)
工廠方法

3. 抽象工廠方法(Abstract Factory

內容:定義一個工廠類接口,讓工廠子類來建立一系列相關或相互依賴的對象。
例:生產一部手機,須要手機殼、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()
抽象工廠

4.  建造者模式(Builder

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

  • 抽象建造者(Builder)
  • 具體建造者(Concrete Builder)
  • 指揮者(Director)
  • 產品(Product)

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

適用場景

  • 當建立複雜對象的算法(Director)應該獨立於該對象的組成部分以及它們的裝配方式(Builder)時
  • 當構造過程容許被構造的對象有不一樣的表示時(不一樣Builder)。

優勢

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

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)
建造者模式

5. 單例模式(Singleton) 

內容:保證一個類只有一個實例,並提供一個訪問它的全局訪問點。
角色

  • 單例(Singleton)

適用場景

  • 當類只能有一個實例並且客戶能夠從一個衆所周知的訪問點訪問它時

優勢

  • 對惟一實例的受控訪問
  • 單例至關於全局變量,但防止了命名空間被污染。

與單例模式功能類似的概念:全局變量、靜態變量(方法)

實現

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


class MyClass(Singleton):
    a = 1

    def __init__(self, name):
        self.name = name


one = MyClass('egon')
two = MyClass('alex')

print(id(one))
print(id(two))
print(one == two)
print(one is two)
1. 使用__new__方法
def singleton(cls, *args, **kw):
    instances = {}

    def get_instance():
        if cls not in instances:
            instances[cls] = cls(*args, **kw)
        return instances[cls]

    return get_instance


@singleton
class MyClass2:
    a = 1


one = MyClass2()
two = MyClass2()

print(id(one))  # 31495472
print(id(two))  # 31495472
print(one == two)
print(one is two)
2. 裝飾器方法
# Python的模塊是自然的單例模式。
# module_name.py
class MySingleton(object):
    def foo(self):
        print('danli')

my_singleton = MySingleton()

# to use
from .module_name import my_singleton
my_singleton.foo()
print(id(my_singleton))

from .module_name import my_singleton
my_singleton.foo()
print(id(my_singleton))
3. import方法
class Singleton(type):
    _instances = {}

    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
        return cls._instances[cls]


# Python2
# class MyClass:
#     __metaclass__ = Singleton


# Python3
class MyClass(metaclass=Singleton):
    pass


one = MyClass()
two = MyClass()

print(id(one))
print(id(two))
print(one == two)
print(one is two)
4. 使用metaclass

6. 原型模式(Prototype

內容:用原型實例指定建立對象的種類,而且經過拷貝這些原型建立新的對象。

使用場景

  • 經過動態裝載;
  • 爲了不建立一個與產品類層次平行的工廠類層次時;
  • 當一個類的實例只能有幾個不一樣狀態組合中的一種時。創建相應數目的原型並克隆它們可能比每次用合適的狀態手工實例化該類更方便一些。

import copy


class Prototype:
    def __init__(self):
        self._objects = {}

    def register_object(self, name, obj):
        """Register an object"""
        self._objects[name] = obj

    def unregister_object(self, name):
        """Unregister an object"""
        del self._objects[name]

    def clone(self, name, **attr):
        """Clone a registered object and update inner attributes dictionary"""
        obj = copy.deepcopy(self._objects.get(name))
        obj.__dict__.update(attr)
        return obj


def main():
    class A:
        def __str__(self):
            return "I am A"

    a = A()
    prototype = Prototype()
    prototype.register_object('a', a)
    b = prototype.clone('a', a=1, b=2, c=3)

    print(a)
    print(b.a, b.b, b.c)


if __name__ == '__main__':
    main()
原型模式

建立型模式總結

使用抽象工廠(Abstract Factory)、原型(Prototype)或者建造者(Builder)的設計甚至比工廠方法(Factory Method)的那些設計更靈活,但它們也更加複雜。一般,設計以使用工廠方法(Factory Method)開始。而且當設計者發現須要更大的靈活性時,設計便會想其餘建立模式煙花。當你在設計標準之間權衡的時候,瞭解多個模式能夠給你提供給更多的選擇餘地。

依賴於繼承的建立型模式:工廠方法模式

依賴於組合的建立型模式:抽象工廠模式、建立者模式

結構性模式

1. 適配器模式(Adapter Class/Object

內容:將一個類的接口轉換成客戶但願的另外一個接口。適配器模式使得本來因爲接口不兼容而不能一塊兒工做的那些類能夠一塊兒工做。
角色

  • 目標接口(Target)
  • 待適配的類(Adaptee)
  • 適配器(Adapter)

兩種實現方式

  • 類適配器:使用多繼承
  • 對象適配器:使用組合

適用場景

  • 想使用一個已經存在的類,而它的接口不符合你的要求
  • (對象適配器)想使用一些已經存在的子類,但不可能對每個都進行子類化以匹配它們的接口。對象適配器能夠適配它的父類接口。

類適配器

  • 用一個具體的Adapter類對Adaptee和Target進行匹配。結果是當咱們想要匹配一個類以及全部他的子類時,類Adaptee將不能勝任工做。
  • 使得Adapter能夠重定義Adaptee的部分行爲,由於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 cost(self, money):
        print("微信支付%s元" % money)


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


# 對象適配器

class RealWechatPay2(Payment):
    def __init__(self):
        self.payment = WechatPay()

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


p = RealWechatPay2()
p.pay(111)
適配器

2. 組合模式(Composite)

內容:將對象組合成樹形結構以表示「部分-總體」的層次結構。組合模式使得用戶對單個對象和組合對象的使用具備一致性。
角色

  • 抽象組件(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()
組合

3. 代理模式 (Proxy)

內容:爲其餘對象提供一種代理以控制對這個對象的訪問。
角色

  • 抽象實體(Subject)
  • 實體(RealSubject)
  • 代理(Proxy)

適用場景

  • 遠程代理:爲遠程的對象提供代理
  • 虛代理:根據須要建立很大的對象
  • 保護代理:控制對原始對象的訪問,用於對象有不一樣訪問權限時

優勢

  • 遠程代理:能夠隱藏對象位於遠程地址空間的事實
  • 虛代理:能夠進行優化,例如根據要求建立對象
  • 保護代理:容許在訪問一個對象時有一些附加的內務處理

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()

    def set_content(self, content):
        return self.subj.set_content(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')
# 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!="alex":
#     p = ProxyC(filename)
# else:
#     p = ProxyA(filename)
#
# print(p.get_content())
代理模式

行爲模式

1. 責任鏈模式(Chain of Responsibility

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

  • 抽象處理者(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)
            return True
        else:
            print("呵呵")
            return False


class DepartmentManagerHandler(Handler):
    def __init__(self):
        self.successor = GeneralManagerHandler()

    def handle_leave(self, day):
        if day < 7:
            print("部門經理批准%d天假" % day)
            return True
        else:
            print("部門經理無權准假")
            return 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)
            return True
        else:
            print("項目主管無權准假")
            return self.successor.handle_leave(day)


day = 11
h = ProjectDirectorHandler()
print(h.handle_leave(day))
請假流程
# --高級例子--模仿js事件處理
from abc import ABCMeta, abstractmethod

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)

# print(body)


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)


# test

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


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


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


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

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

2. 迭代器模式(Iterator

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

適用場景:

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

實現方法:__iter__、__next__

class LinkList:
    """鏈表 頭結點保存鏈表的長度"""

    class Node:
        def __init__(self, item=None):
            self.item = item
            self.next = None

    class LinkListIterator:
        def __init__(self, node):
            self.node = node

        def __next__(self):
            if self.node:
                cur_node = self.node
                self.node = cur_node.next
                return cur_node.item
            else:
                raise StopIteration

        def __iter__(self):
            return self

    def __init__(self, iterable=None):
        self.head = LinkList.Node(0)
        self.tail = self.head
        self.extend(iterable)

    def append(self, obj):
        s = LinkList.Node(obj)
        self.tail.next = s
        self.tail = s
        self.head.item += 1

    def extend(self, iterable):
        for obj in iterable:
            self.append(obj)

    def __iter__(self):
        return self.LinkListIterator(self.head.next)

    def __len__(self):
        return self.head.item

    def __str__(self):
        return "<<" + ", ".join(map(str, self)) + ">>"


li = [i for i in range(100)]
lk = LinkList(li)
print(lk)
鏈表

3. 觀察者模式(Observer

內容:定義對象間的一種一對多的依賴關係,當一個對象的狀態發生改變時, 全部依賴於它的對象都獲得通知並被自動更新。觀察者模式又稱「發佈-訂閱」模式
角色

  • 抽象主題(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

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

    @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()

alex = Manager()
wusir = Manager()

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

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

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

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

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

notice.detach(wusir)

notice.company_info = "公司要破產了,趕快跑路"

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

notice.company_info = "公司已經破產了"

print(alex.company_info)
print(wusir.company_info)
發佈者——訂閱者

4. 策略模式(Strategy

內容:定義一系列的算法,把它們一個個封裝起來,而且使它們可相互替換。本模式使得算法可獨立於使用它的客戶而變化。
角色

  • 抽象策略(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()
策略模式

5. 模板方法(Template Method

內容:定義一個操做中的算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類能夠不改變一個算法的結構便可重定義該算法的某些特定步驟。
角色

  • 抽象類(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")
模板方法

參考文獻:

《二十三種設計模式及其python實現》——李瓊羽

相關文章
相關標籤/搜索