Design Pattern

設計模式

一、設計模式的定義

對軟件設計中廣泛存在(反覆出現)的各類問題,所提出的解決方案。每個設計模式系統地命名、解釋和評價了面向對象系統中一個重要的和重複出現的設計。
就是爲了解決面向對象系統中重要和重複的設計封裝在一塊兒的一種代碼實現框架,可使得代碼更加易於擴展和調用。 算法

  1. 時間複雜度: 第1種列表法的時間複雜度是O(n2).第2種鏈表法的時間複雜度是O(nm)
  2. 若是n大於m時,鏈表法優於列表法,n小於m時,列表法優於鏈表法

二、六大原則(SOLID原則)

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

三、接口

  1. 定義:一種特殊的類,聲明瞭若干方法,要求繼承該接口的類必須實現這種方法
  2. 做用:限制繼承接口的類的方法的名稱及調用方式,隱藏了類的內部實現

設計模式分類

  1. 建立型模式(5種):工廠方法模式、抽象工廠模式、建立者模式、原型模式、單例模式
  2. 結構型模式(7種):適配器模式、橋模式、組合模式、裝飾模式、外觀模式、享元模式、代理模式
  3. 行爲型模式(11種):解釋器模式、責任鏈模式、命令模式、迭代器模式、中介者模式、備忘錄模式、觀察者模式、狀態模式、策略模式、訪問者模式、模板方法模式

1、建立型模式

一、簡單工廠模式

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

class Payment(metaclass=ABCMeta):
    # abstract class
    # 抽象產品角色
    @abstractmethod
    def pay(self, money):
        pass

class Alipay(Payment):
    # 抽象產品角色
    def __init__(self, use_huabei=False):
        self.use_huabei = use_huabei

    def pay(self, money):
        if self.use_huabei:
            print("花唄支付%d元." % money)
        else:
            print("支付寶餘額支付%d元." % money)


class WechatPay(Payment):
    # 抽象產品角色
    def pay(self, money):
        print("微信支付%d元." % money)


class PaymentFactory:
    #工廠角色
    def create_payment(self, method):
        if method == 'alipay':
            return Alipay()
        elif method == 'wechat':
            return WechatPay()
        elif method == 'huabei':
            return Alipay(use_huabei=True)
        else:
            raise TypeError("No such payment named %s" % method)

# client
pf = PaymentFactory()
p = pf.create_payment('huabei')
p.pay(100)
factory.py

二、工廠方法模式

  1. 內容:定義一個用於建立對象的接口(工廠接口),讓子類決定實例化哪個產品類。
  2. 角色:
    • 抽象工廠角色(Creator)
    • 具體工廠角色(Concrete Creator)
    • 抽象產品角色(Product)
    • 具體產品角色(Concrete Product)
  3. 適用場景:
    • 須要生產多種,大量複雜對象的時候
    • 須要下降代碼耦合度的時候
    • 當系統中的產品類常常須要擴展的時候
  4. 優勢:
    • 每一個具體產品都對應一個具體工廠類,不須要修改工廠類代碼
    • 隱藏了對象建立的實現細節
  5. 缺點:
    • 每增長一個具體產品類,就必須增長一個相應的具體工廠類
from abc import ABCMeta, abstractmethod


class Payment(metaclass=ABCMeta):
    # 抽象產品
    # abstract class
    @abstractmethod
    def pay(self, money):
        pass


class Alipay(Payment):
    # 具體產品
    def __init__(self, use_huabei=False):
        self.use_huaei = use_huabei

    def pay(self, money):
        if self.use_huaei:
            print("花唄支付%d元." % money)
        else:
            print("支付寶餘額支付%d元." % money)


class WechatPay(Payment):
    # 具體產品
    def pay(self, money):
        print("微信支付%d元." % money)



class PaymentFactory(metaclass=ABCMeta):
    # 抽象工廠
    @abstractmethod
    def create_payment(self):
        pass


class AlipayFactory(PaymentFactory):
    # 具體工廠
    def create_payment(self):
        return Alipay()


class WechatPayFactory(PaymentFactory):
    # 具體工廠
    def create_payment(self):
        return WechatPay()


class HuabeiFactory(PaymentFactory):
    # 具體工廠
    def create_payment(self):
        return Alipay(use_huabei=True)

# client

pf = HuabeiFactory()
p = pf.create_payment()
p.pay(100)

#若是要新增支付方式
class BankPay(Payment):
    # 具體產品
    def pay(self, money):
        print("銀行卡支付%d元." % money)

class BankPayFactory(PaymentFactory):
    # 具體工廠
    def create_payment(self):
        return BankPay()

bf = BankPayFactory()
b = bf.create_payment()
b.pay(200)
factory_method.py

三、抽象工廠模式

  1. 內容:定義一個工廠類接口,讓工廠子類來建立一系列相關或相互依賴的對象。
  2. 角色:
    • 抽象工廠角色(Creator)
    • 具體工廠角色(Concrete Creator)
    • 抽象產品角色(Product)
    • 具體產品角色(Concrete Product)
    • 客戶端(Client)
  3. 適用場景:
    • 系統要獨立於產品的建立和組合時
    • 強調一系列相關產品的對象設計以便進行聯合調試時
    • 提供一個產品類庫,想隱藏產品的具體實現時
  4. 優勢:
    • 將客戶端與類的具體實現相分離
    • 每一個工廠建立了一個完整的產品系列,使得易於交換產品系列
    • 有利於產品的一致性(即產品之間的約束關係)
  5. 缺點:
    • 難以支持新種類的(抽象)產品
  6. 舉例:生產一部手機,須要手機殼、CPU、操做系統三類對象進行組裝,其中每類對象都有不一樣的種類。對每一個具體工廠,分別生產一部手機所須要的三個對象。
  7. 對比:相比工廠方法模式,抽象工廠模式中的每一個具體工廠都生產一套產品。
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(IPhoneFactory())
p1.show_info()
abstract_factory.py

四、建造者模式

  1. 內容:將一個複雜對象的構建與它的表示分離,使得一樣的構建過程能夠建立不一樣的表示。
  2. 角色:
    • 抽象建造者(Builder)
    • 具體建造者(Concrete Builder)
    • 指揮者(Director)
    • 產品(Product)
  3. 適用場景:
    • 當建立複雜對象的算法應該獨立於對象的組成部分以及它的裝配方式
    • 當構造過程容許被構造的對象有不一樣的表示
  4. 優勢:
    • 隱藏了一個產品的內部結構和裝配過程
    • 將構造代碼與表示代碼分開
    • 能夠對構造過程進行更精細的控制
  5. 對比:建造者模式與抽象工廠模式類似,也用來建立複雜對象。主要區別是建造者模式着重一步步構造一個複雜對象,而抽象工廠模式着重於多個系列的產品對象。
from abc import ABCMeta, abstractmethod
#------產品------
class Player:
    def __init__(self, face=None, body=None, arm=None, leg=None):
        self.face = face
        self.body = body
        self.arm = arm
        self.leg = leg

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

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

    @abstractmethod
    def build_body(self):
        pass

    @abstractmethod
    def build_arm(self):
        pass

    @abstractmethod
    def build_leg(self):
        pass

#------具體建造者------
class SexyGirlBuilder(PlayerBuilder):
    def __init__(self):
        self.player = Player()

    def build_face(self):
        self.player.face = "漂亮臉蛋"

    def build_body(self):
        self.player.body = "苗條"

    def build_arm(self):
        self.player.arm = "漂亮胳膊"

    def build_leg(self):
        self.player.leg = "大長腿"


class Monster(PlayerBuilder):
    def __init__(self):
        self.player = Player()

    def build_face(self):
        self.player.face = "怪獸臉"

    def build_body(self):
        self.player.body = "怪獸身材"

    def build_arm(self):
        self.player.arm = "長毛的胳膊"

    def build_leg(self):
        self.player.leg = "長毛的腿"

#------指揮者------
class PlayerDirector: # 控制組裝順序
    def build_player(self, builder):
        builder.build_body()
        builder.build_face()
        builder.build_arm()
        builder.build_leg()
        return builder.player


# client

builder = Monster()
director = PlayerDirector()
p = director.build_player(builder)
print(p)
builder.py

五、單例模式

  1. 內容:保證一個類只有一個實例,並提供一個訪問它的全局訪問點。
  2. 角色:
    • 單例(Singleton)
  3. 適用場景:
    • 當一個類只能有一個實例而客戶能夠從一個衆所周知的訪問點訪問它時
  4. 優勢:
    • 對惟一實例的受控訪問
    • 單例至關於全局變量,但防止了命名空間被污染

單例的多種實現方式

# mysingleton.py
class My_Singleton(object):
    def foo(self):
        pass
my_singleton = My_Singleton()

#將上面的代碼保存在文件 mysingleton.py 中,而後這樣使用:
from mysingleton import my_singleton
my_singleton.foo()
1.使用模塊
#1.不能支持多線程的單例模式
class Singleton(object):
    @classmethod
    def instance(cls, *args, **kwargs):
        if not hasattr(Singleton, '_instance'):
            Singleton._instance = Singleton()
        return Singleton._instance

a = Singleton.instance()
b = Singleton.instance()
print(a == b)  # True

#2.加上多線程(過渡版-有問題)
import time
class Singleton(object):
    def __init__(self):
        time.sleep(1)
    @classmethod
    def instance(cls,*args,**kwargs):
        if not  hasattr(Singleton,'_instance'):
            Singleton._instance=Singleton()
        return Singleton._instance

import threading
def task():
    obj = Singleton.instance()
    print(obj)

for i in range(10):
    t=threading.Thread(target=task)
    t.start()
"""
<__main__.Singleton object at 0x0000000001313828>
<__main__.Singleton object at 0x0000000001313748>
<__main__.Singleton object at 0x0000000001313908>
...
"""

#三、解決上面存在的問題,實現支持多線程的單列模式:
import time
import threading
class Singleton(object):
    _instance_lock = threading.Lock()
    def __init__(self):
        time.sleep(1)
    @classmethod
    def instance(cls,*args,**kwargs):
        with cls._instance_lock:
            if not hasattr(Singleton,'_instance'):
                Singleton._instance=Singleton()
                return Singleton._instance
            return Singleton._instance

def task():
    obj = Singleton.instance()
    print(obj)

for i in range(10):
    t=threading.Thread(target=task)
    t.start()

"""
<__main__.Singleton object at 0x00000000010B8278>
<__main__.Singleton object at 0x00000000010B8278>
<__main__.Singleton object at 0x00000000010B8278>
...
"""
####問題:建立實例只能調用Singleton.instance()來調用,不能用Singleton()來實現
2.利用類實現
import threading
class Singleton():
    def __init__(self,name):
        self.name=name

    _instance_lock = threading.Lock()
    def __new__(cls, *args, **kwargs):
        with cls._instance_lock:
            if not hasattr(cls,'_instance'):
                cls._instance = super().__new__(cls)
        return cls._instance

def task(i):
    Singleton(i)
    print(id(Singleton(i)))

if __name__ == '__main__':
    for i in range(10):
        t = threading.Thread(target=task, args=(i,))
        t.start()
3.使用__new__
"""
對象是類建立,建立對象時候類的__init__方法自動執行,對象()執行類的 __call__ 方法
類是type建立,建立類時候type的__init__方法自動執行,類() 執行type的 __call__方法(類的__new__方法,類的__init__方法)

obj = Foo()
第1步: 執行type的 __init__ 方法【類是type的對象】
第2步: 執行type的 __call__ 方法
第3步:調用 Foo類(是type的對象)的 __new__方法,用於建立對象。
第4步:調用 Foo類(是type的對象)的 __init__方法,用於對對象初始化。

obj()  執行 Foo 的__call__方法

class SingletonType(type):
    def __init__(self,*args,**kwargs):
        print(1)
        super(SingletonType,self).__init__(*args,**kwargs)

    def __call__(cls, *args, **kwargs):
        print(2)
        obj = cls.__new__(cls,*args, **kwargs)
        cls.__init__(obj,*args, **kwargs) # Foo.__init__(obj)
        return obj

class Foo(metaclass=SingletonType):
    def __init__(self,name):
        print(4)
        self.name = name
    def __new__(cls, *args, **kwargs):
        print(3)
        return object.__new__(cls)
    def __call__(self, *args, **kwargs):
        print(5)
        print(self.name)
obj1 = Foo('name')  # 1 2 3 4
obj1() # 5 name  執行 Foo 的__call__方法

"""
import threading
class Singleton(type):
    _instance_lock=threading.Lock()
    def __call__(cls, *args, **kwargs):
        with  cls._instance_lock:
            if not hasattr(cls,'_instance'):
                cls._instance=super(Singleton, cls).__call__(*args, **kwargs)
        return cls._instance

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

obj1 = Foo('name')
obj2 = Foo('name')
print(obj1==obj2) #true
4.基於metaclass方式實現

建立型模式小結

  1. 抽象工廠模式和建造者模式相比於簡單工廠模式和工廠方法模式而言更靈活也更復雜。
  2. 一般狀況下、設計以簡單工廠模式或工廠方法模式開始,當你發現設計須要更大的靈活性時,則像更復雜的設計模式演化。

2、結構型模式

一、適配器模式

  1. 內容:將一個類的接口轉換成客戶但願的另外一個接口。適配器模式使得本來因爲接口不兼容而不能一塊兒工做的那些類能夠一塊兒工做。
  2. 角色:
    • 目標接口(Target)
    • 待適配的類(Adaptee)
    • 適配器(Adapter)
  3. 適用場景:
    • 想使用一個已經存在的類,而它的接口不符合你的要求
    • (對象適配器)想使用一些已經存在的子類,但不可能對每個都進行子類化以匹配它們的接口。對象適配器能夠適配它的父類接口。
  4. 兩種實現方式:
    • 類適配器:使用多繼承
    • 對象適配器:使用組合
from abc import ABCMeta, abstractmethod


class Payment(metaclass=ABCMeta):
    # abstract class
    @abstractmethod
    def pay(self, money):
        pass


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


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


#------待適配的類-----
class BankPay:
    def cost(self, money):
        print("銀聯支付%d元." % money)

#------待適配的類-----
class ApplePay:
    def cost(self, money):
        print("蘋果支付%d元." % money)


#------類適配器------
# class NewBankPay(Payment, BankPay):
#     def pay(self, money):
#         self.cost(money)

#------類適配器------
# class NewApplePay(Payment, ApplePay):
#     def pay(self, money):
#         self.cost(money)

#-----對象適配器-----
class PaymentAdapter(Payment):
    def __init__(self, payment):
        self.payment = payment

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

#NewBankPay().pay(100)

p = PaymentAdapter(BankPay())
p.pay(100)


# 組合

# class A:
#     pass
#
# class B:
#     def __init__(self):
#         self.a = A()
adapter.py

二、橋模式

  1. 內容:將一個事物的兩個維度分離,使其均可以獨立地變化。
  2. 角色:
    • 抽象(Abstraction)
    • 細化抽象(RefinedAbstraction)
    • 實現者(Implementor)
    • 具體實現者(ConcreteImplementor)
  3. 適用場景:
    • 當事物有兩個維度上的表現,兩個維度均可能擴展時。
  4. 優勢:
    • 抽象和實現相分離
    • 優秀的擴展能力
from abc import ABCMeta, abstractmethod
#------維度1------
class Shape(metaclass=ABCMeta):
    def __init__(self, color):
        self.color = color

    @abstractmethod
    def draw(self):
        pass

#------維度2------
class Color(metaclass=ABCMeta):
    @abstractmethod
    def paint(self, shape):
        pass


class Rectangle(Shape):
    name = "長方形"
    def draw(self):
        # 長方形邏輯
        self.color.paint(self)


class Circle(Shape):
    name = "圓形"
    def draw(self):
        # 圓形邏輯
        self.color.paint(self)


class Line(Shape):
    name = "直線"
    def draw(self):
        # 直線邏輯
        self.color.paint(self)


class Red(Color):
    def paint(self, shape):
        print("紅色的%s" % shape.name)


class Green(Color):
    def paint(self, shape):
        print("綠色的%s" % shape.name)


class Blue(Color):
    def paint(self, shape):
        print("藍色的%s" % shape.name)



shape = Line(Blue())
shape.draw()

shape2 = Circle(Green())
shape2.draw()
bridge.py

三、組合模式

  1. 內容:將對象組合成樹形結構以表示「部分-總體」的層次結構。組合模式使得用戶對單個對象和組合對象的使用具備一致性。
  2. 角色:
    • 抽象組件(Component)
    • 葉子組件(Leaf)
    • 複合組件(Composite)
    • 客戶端(Client)
  3. 適用場景:
    • 表示對象的「部分-總體」層次結構(特別是結構是遞歸的)
    • 但願用戶忽略組合對象與單個對象的不一樣,用戶統一地使用組合結構中的全部對象
  4. 優勢:
    • 定義了包含基本對象和組合對象的類層次結構
    • 簡化客戶端代碼,即客戶端能夠一致地使用組合對象和單個對象
    • 更容易增長新類型的組件
from abc import ABCMeta, abstractmethod


# 抽象組件
class Graphic(metaclass=ABCMeta):
    @abstractmethod
    def draw(self):
        pass


# 葉子組件
class Point(Graphic):
    def __init__(self, x, y):
        self.x = x
        self.y = y

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

    def draw(self):
        print(str(self))


# 葉子組件
class Line(Graphic):
    def __init__(self, p1, p2):
        self.p1 = p1
        self.p2 = p2

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

    def draw(self):
        print(str(self))


# 複合組件
class Picture(Graphic):
    def __init__(self, iterable):
        self.children = []
        for g in iterable:
            self.add(g)

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


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


p1 = Point(2,3)
l1 = Line(Point(3,4), Point(6,7))
l2 = Line(Point(1,5), Point(2,8))
pic1 = Picture([p1, l1, l2])

p2 = Point(4,4)
l3 = Line(Point(1,1), Point(0,0))
pic2 = Picture([p2, l3])

pic = Picture([pic1, pic2])
pic.draw()
composite.py

四、外觀模式

  1. 內容:爲子系統中的一組接口提供一個一致的界面,外觀模式定義了一個高層接口,這個接口使得這一子系統更加容易使用。
  2. 角色:
    • 外觀(facade)
    • 子系統類(subsystem classes)
  3. 優勢:
    • 減小系統相互依賴
    • 提升了靈活性
    • 提升了安全性
class CPU:
    def run(self):
        print("CPU開始運行")

    def stop(self):
        print("CPU中止運行")


class Disk:
    def run(self):
        print("硬盤開始工做")

    def stop(self):
        print("硬盤中止工做")


class Memory:
    def run(self):
        print("內存通電")

    def stop(self):
        print("內存斷電")


class Computer: # Facade
    def __init__(self):
        self.cpu = CPU()
        self.disk = Disk()
        self.memory = Memory()

    def run(self):
        self.cpu.run()
        self.disk.run()
        self.memory.run()

    def stop(self):
        self.cpu.stop()
        self.disk.stop()
        self.memory.stop()


# Client

computer = Computer()
computer.run()
computer.stop()
facade.py

五、代理模式

  1. 內容:爲其餘對象提供一種代理以控制對這個對象的訪問。
  2. 角色:
    • 抽象實體(Subject)
    • 代理(Proxy)
    • 實體(RealSubject)
  3. 應用場景:
    • 遠程代理:爲遠程的對象提供代理
    • 虛代理:根據須要建立很大的對象
    • 保護代理:控制對原始對象的訪問,用於對象有不一樣訪問權限時
  4. 優勢:
    • 遠程代理:能夠隱藏對象位於遠程地址空間的事實
    • 虛代理:能夠進行優化,例如根據要求建立對象
    • 保護代理:容許在訪問一個對象時有一些附加的內務處理
from abc import ABCMeta, abstractmethod
#抽象實體
class Subject(metaclass=ABCMeta):
    @abstractmethod
    def get_content(self):
        pass

    @abstractmethod
    def set_content(self, content):
        pass

#實體
class RealSubject(Subject):
    def __init__(self, filename):
        self.filename = filename
        f = open(filename, 'r', encoding='utf-8')
        print("讀取文件內容")
        self.content = f.read()
        f.close()

    def get_content(self):
        return self.content

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

#遠程代理
class RemoteProxy(Subject):
    def __init__(self,filename):
        self.subj =RealSubject(filename)
    def get_content(self):
        return self.subj.get_content()


#虛代理
class VirtualProxy(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()


    def set_content(self, content):
        if not subj:
            self.subj = RealSubject(self.filename)
        return self.subj.set_content(content)


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

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

    def set_content(self, content):
        raise PermissionError("無寫入權限")



#subj = RealSubject("test.txt")
#subj.get_content()

subj = ProtectedProxy("test.txt")
print(subj.get_content())
subj.set_content("abc")
proxy.py

3、行爲型模式

一、責任鏈模式

  1. 內容:使多個對象都有機會處理請求,從而避免請求的發送者和接收者之間的耦合關係。將這些對象連成一條鏈,並沿着這條鏈傳遞該請求,直到有一個對象處理它爲止。
  2. 角色:
    • 抽象處理者(Handler)
    • 具體處理者(ConcreteHandler)
    • 客戶端(Client)
  3. 適用場景:
    • 有多個對象能夠處理一個請求,哪一個對象處理由運行時決定
    • 在不明確接收者的狀況下,向多個對象中的一個提交一個請求
  4. 優勢:
    • 下降耦合度:一個對象無需知道是其餘哪個對象處理其請求
  5. 缺點:
    • 請求不保證被接收,鏈的末端沒有處理或鏈配置錯誤
from abc import ABCMeta, abstractmethod

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


class GeneralManager(Handler):
    def handle_leave(self, day):
        if day <= 10:
            print("總經理准假%d天" % day)
        else:
            print("你仍是辭職吧")


class DepartmentManager(Handler):
    def __init__(self):
        self.next = GeneralManager()

    def handle_leave(self, day):
        if day <= 5:
            print("部門經理准假%s天" % day)
        else:
            print("部門經理職權不足")
            self.next.handle_leave(day)


class ProjectDirector(Handler):
    def __init__(self):
        self.next = DepartmentManager()

    def handle_leave(self, day):
        if day <= 3:
            print("項目主管准假%d天" % day)
        else:
            print("項目主管職權不足")
            self.next.handle_leave(day)

# Client


day = 12
h = ProjectDirector()
h.handle_leave(day)
chain_of_responsibility.py
from abc import ABCMeta, abstractmethod
#--模仿js事件處理
class Handler(metaclass=ABCMeta):
    @abstractmethod
    def add_event(self,func):
        pass

    @abstractmethod
    def handler(self):
        pass

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

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

    def handler(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 handler(self):
        if self.func:
            return self.func()
        else:
            return self.successor.handler()


#客戶端
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'] = div
div['children'] = 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'].handler()
模仿js事件處理

二、觀察者模式

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


class Observer(metaclass=ABCMeta): # 抽象訂閱者
    @abstractmethod
    def update(self, notice): # notice 是一個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)

    def notify(self): # 推送
        for obs in self.observers:
            obs.update(self)


class StaffNotice(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 Staff(Observer):  # 具體訂閱者
    def __init__(self):
        self.company_info = None

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


# Client

notice = StaffNotice("初始公司信息")
s1 = Staff()
s2 = Staff()
notice.attach(s1)
notice.attach(s2)
notice.company_info = "公司今年業績很是好,給你們發獎金!!!"
print(s1.company_info)
print(s2.company_info)
notice.detach(s2)
notice.company_info = "公司明天放假!!!"
print(s1.company_info)
print(s2.company_info)
observer.py

三、策略模式

  1. 內容:定義一系列的算法,把它們一個個封裝起來,而且使它們可相互替換。本模式使得算法可獨立於使用它的客戶而變化。
  2. 角色:
    • 抽象策略(Strategy)
    • 具體策略(ConcreteStrategy)
    • 上下文(Context)
  3. 優勢:
    • 定義了一系列可重用的算法和行爲
    • 消除了一些條件語句
    • 能夠提供相同行爲的不一樣實現
  4. 缺點:
    • 客戶必須瞭解不一樣的策略
from abc import ABCMeta,abstractmethod
#抽象策略
class Strategy(metaclass=ABCMeta):
    @abstractmethod
    def execute(self, data):
        pass

#具體策略
class FastStrategy(Strategy):
    def execute(self, data):
        print("用較快的策略處理%s" % data)

#具體策略
class SlowStrategy(Strategy):
    def execute(self, data):
        print("用較慢的策略處理%s" % data)

#上下文
class Context:
    def __init__(self, strategy, data):
        self.data = data
        self.strategy = strategy

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

    def do_strategy(self):
        self.strategy.execute(self.data)


# Client

data = "[...]"
s1 = FastStrategy()
s2 = SlowStrategy()
context = Context(s1, data)
context.do_strategy()
context.set_strategy(s2)
context.do_strategy()
strategy.py

四、模板方法模式

  1. 內容:定義一個操做中的算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類能夠不改變一個算法的結構便可重定義該算法的某些特定步驟。
  2. 角色:
    • 抽象類(AbstractClass):定義抽象的原子操做(鉤子操做);實現一個模板方法做爲算法的骨架。
    • 具體類(ConcreteClass):實現原子操做
  3. 適用場景:
    • 一次性實現一個算法的不變的部分
    • 各個子類中的公共行爲應該被提取出來並集中到一個公共父類中以免代碼重複
    • 控制子類擴展
from abc import ABCMeta, abstractmethod
from time import sleep

#----抽象類-----
class Window(metaclass=ABCMeta):
    @abstractmethod
    def start(self):
        pass

    @abstractmethod
    def repaint(self):
        pass

    @abstractmethod
    def stop(self): # 原子操做/鉤子操做
        pass

    # 在父類中定義了子類的行爲
    def run(self):  # 模板方法
        self.start()
        while True:
            try:
                self.repaint()
                sleep(1)
            except KeyboardInterrupt:
                break
        self.stop()

        
#子類中只須要實現部分算法,而不須要實現全部的邏輯
#-----具體類--------
class MyWindow(Window):
    def __init__(self, msg):
        self.msg = msg

    def start(self):
        print("窗口開始運行")

    def stop(self):
        print("窗口結束運行")

    def repaint(self):
        print(self.msg)


MyWindow("Hello...").run()
template_method.py
相關文章
相關標籤/搜索