設計模式簡述

設計模式概述

什麼是設計模式

設計模式是針對面向對象的,所謂的設計就是在編寫程序以前對其有一個基礎的架構,如須要建立哪些類,定義那些變量,有哪些方法。每個設計模式系統的命名、解釋和評價了面向對象系統中一個重要和重複出現的設計。設計模式四個基本要素:模式名稱、問題、解決方案、效果。python

一些須要的知識點

面向對象的三大特性:算法

  • 封裝:封裝指的是兩方面 , 一方面把相關的功能概括到一個類中, 另外一方面把數據封裝到對象中。shell

  • 繼承:繼承就是爲了提高代碼的複用性,更加靈活。數據庫

  • 多態:多態指的是一個變量能夠有多種狀態或者多種形態。編程

接口:設計模式

  • 本質是一個抽象類,要求繼承接口的類,必須實現接口內定義的一些方法。微信

  • 抽象類就是不能實例化的類。架構

  • 若是繼承抽象類後同樣沒有實現抽象方法,那麼它也是一個抽象類。app

  • 做用:限制繼承接口的類的方法的名稱及調用方式;隱藏了類的內部實現。框架

 
#在python中沒有嚴謹的限制函數簽名,只限制了函數名。
from abc import abstractmethod, ABCMeta
class Interface(metaclass=ABCMeta):
    @abstractmethod
    def method(self, arg):
        pass

 

設計模式六大原則:

  • 開閉原則:一個軟件實體如類、模塊和函數應該對擴展開放,對修改關閉。即軟件實體應儘可能在不修改原有代碼的狀況下進行擴展。

  • 里氏(Liskov)替換原則:全部引用基類(父類)的地方必須能透明地使用其子類的對象。

    • 可以進行統一的調用,函數簽名必須一致。

  • 依賴倒置原則:高層模塊不該該依賴低層模塊,兩者都應該依賴其抽象;抽象不該該依賴細節;細節應該依賴抽象。換言之,要針對接口編程,而不是針對實現編程。

    • A,B同時調用C模塊,C模塊進行修改,那麼AB模塊可能也須要修改,因此接口就是哪些東西不能改的限制,先有接口後又代碼。

  • 接口隔離原則:使用多個專門的接口,而不使用單一的總接口,即客戶端不該該依賴那些它不須要的接口。

    • 若是不實現不須要的方法,那麼就沒法實例化,可使用多繼承來繼承多個小基類。

  • 迪米特法則:一個軟件實體應當儘量少地與其餘實體發生相互做用。

    • 解耦,讓依賴越少,那麼自由度越高。

  • 單一職責原則:不要存在多於一個致使類變動的緣由。

    • 通俗的說,即一個類只負責一項職責。 

設計模式分類

建立型模式:工廠方法模式,抽象工廠模式,建立者模式,原型模式,單例模式。結構型模式:適配器模式,橋模式,組合模式,裝飾模式,外觀模式,享元模式,代理模式。行爲型模式:解釋器模式,責任鏈模式,命令模式,迭代器模式,中介者模式,備忘錄模式,觀察者模式,狀態模式,策略模式,模板方法模式。

建立型模式

建立型模式的重點在於如何建立一個對象。

簡單工廠模式

簡單工廠模式:

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

  • 角色:工廠角色,抽象產品角色,具體產品角色。

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

  • 缺點:

    • 違反了單一職責原則,將建立邏輯集中到一個工廠類裏。

    • 當添加新產品時,須要修改工廠類代碼,違反了開閉原則

 
from abc import abstractmethod, ABCMeta
​
class Payment(metaclass=ABCMeta):
    @abstractmethod
    def pay(self, money):
        raise NotImplementedError
​
class Alipay(Payment):
    def __init__(self, use_huabei=False):
        self.use_huabei = use_huabei
​
    def pay(self, money):
        if self.use_huabei:
            print("花唄支付%s元" % money)
        else:
            print("支付寶支付%s元" % money)
​
class ApplePay(Payment):
    def __init__(self, s):
        passdef 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 == 'huabei':
            return Alipay(use_huabei=True)
        else:
            raise NameError(method)
​
pf = PaymentFactory()
p = pf.create_payment('huabei')
p.pay(100)

 

工廠方法模式:

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

  • 角色:抽象工廠角色,具體工廠角色,抽象產品角色,具體產品角色。

  • 工廠方法模式擁有繼承抽象工廠類的抽象工廠類。

  • 使用場景:須要生產多種大量的對象的時候;須要下降耦合度的時候;當系統的產品種類須要擴展的時候。

  • 優勢: 每一個具體產品對應一個具體工廠類,隱藏了對象建立的細節。

  • 缺點:每增長一個具體產品類,就要添加一個具體工廠類。

 
from abc import abstractmethod, ABCMeta
​
class Payment(metaclass=ABCMeta):
    @abstractmethod
    def pay(self, money):
        raise NotImplementedError
​
class Alipay(Payment):
    def __init__(self, use_huabei=False):
        self.use_huabei = use_huabei
​
    def pay(self, money):
        if self.use_huabei:
            print("花唄支付%s元" % money)
        else:
            print("支付寶支付%s元" % money)
​
class ApplePay(Payment):
    def pay(self, money):
        print("蘋果支付%s元"%money)
​
class PaymentFactory(metaclass=ABCMeta):
    @abstractmethod
    def create_payment(self):
        passclass AlipayFactory(PaymentFactory):
    def create_payment(self):
        return Alipay(use_huabei=False)
​
class ApplePayFactory(PaymentFactory):
    def create_payment(self):
        return ApplePay()
​
class HuabeiFactory(PaymentFactory):
    def create_payment(self):
        return Alipay(use_huabei=True)

 

抽象工廠模式:

  • 多個對象須要一塊兒使用的時候使用,多個小對象組合成一個大對象。如面向對象中實現代碼複用的組合。

  • 角色:抽象工廠角色,具體工廠角色,抽象產品角色,具體產品角色,客戶端。

  • 抽象工廠模式中每一個具體工廠都生產一套產品。

  • 適用場景:系統要獨立於產品的建立與組合時;強調一系列相關的產品對象的設計以便進行聯合使用時;提供一個產品類庫,想隱藏產品的具體實現時。

  • 優勢:將客戶端與類的具體實現相分離;每一個工廠建立了一個完整的產品系列,使得易於交換產品系列;有利於產品的一致性(即產品之間的約束關係)。

  • 缺點:難以支持新種類的(抽象)產品

 
from abc import abstractmethod, ABCMeta
​
# ------抽象產品------
class PhoneShell(metaclass=ABCMeta):
    @abstractmethod
    def show_shell(self):
        passclass CPU(metaclass=ABCMeta):
    @abstractmethod
    def show_cpu(self):
        passclass 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()

 

建造者模式

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

  • 角色:抽象建造者,具體建造者,指揮者,產品。

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

  • 適用場景:

  • 當建立複雜對象的算法(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):
        passclass 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
​
#無所謂beautfulbuilder編寫的順序
class PlayerDirector:
    def build_player(self, builder):
        builder.build_body()
        builder.build_face()
        builder.build_arm()
        builder.build_leg()
        return builder.get_player()
​
pd = PlayerDirector()
pb = BeautifulWomanBuilder()
p = pd.build_player(pb)
print(p)

單例模式

保證一個類只有一個實例對象,並提供一個訪問它的全局訪問點,日誌對象就是單例,數據庫鏈接對象也是單例的。

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

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

單例模式

保證一個類只有一個實例對象,並提供一個訪問它的全局訪問點,日誌對象就是單例,數據庫鏈接對象也是單例的。適用場景:當類只能有一個實例並且客戶能夠從一個衆所周知的訪問點訪問它時。優勢:對惟一實例的受控訪問;單例至關於全局變量,但防止命名空間污染。

 
from abc import abstractmethod, ABCMeta
​
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=None):
        if name is not None:
            self.name = name
​
a = MyClass("a")
​
print(a)
print(a.name)
​
b = MyClass("b")
​
print(b)
print(b.name)
​
print(a)
print(a.name)

 

結構型模式

適配器模式

當兩個類不能一塊兒使用,那麼增長一些方法使它們可以一塊兒使用,這就叫適配器模式。類適配器處理多個待適配類時,十分麻煩,因此經過組合來寫適配器,也就是對象適配器,會更好。

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

  • 角色:目標接口,待適配的類,適配器。

  • 實現方式:類適配器,對象適配器。

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

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 BankPay:
    def huaqian(self, money):
        print("銀行卡支付%s元" % money)
​
# # 類適配器
# class NewWechatPay(WechatPay, Payment):
#     def pay(self, money):
#         self.huaqian(money)
# 對象適配器
class PaymentAdapter(Payment):
    def __init__(self, p):
        self.payment = p
​
    def pay(self, money):
        self.payment.huaqian(100)
​
p = PaymentAdapter(BankPay())
p.pay(100)

 

代理模式

爲其餘對象提供一種代理以控制對這個對象的訪問。角色:抽象實體,實體,代理。使用場景:

  • 遠程代理:爲遠程的對象提供代理。

  • 虛代理:根據須要建立很大的對象。

  • 保護代理:控制對原始對象的訪問,用於對象有不一樣訪問權限時。優勢:

  • 遠程代理:能夠隱藏對象位於遠程地址空間的事實。

  • 虛代理:能夠進行優化,例如根據要求建立對象。

  • 保護代理:容許在訪問一個對象時有一些附加的內務處理。

 
from abc import ABCMeta, abstractmethod
​
class Subject(metaclass=ABCMeta):
    @abstractmethod
    def get_content(self):
        passclass RealSubject(Subject):
    def __init__(self, filename):
        print("讀取%s文件內容"%filename)
        f = open(filename)
        self.content = f.read()
        f.close()
​
    def get_content(self):
        return self.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()
​
p = ProxyB("abc.txt")

 

組合模式

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

  • 表示對象的「部分-總體」層次結構(特別是結構是遞歸的)

  • 但願用戶忽略組合對象與單個對象的不一樣,用戶統一地使用組合結構中的全部對象

  • 優勢:定義了包含基本對象和組合對象的類層次結構;簡化客戶端代碼,即客戶端能夠一致地使用組合對象和單個對象;更容易增長新類型的組件。
  • 缺點:很難限制組合中的組件。

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()
pic1.add(Point(2,3))
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)

pic1.draw()

 行爲型模式

觀察者模式(發佈-訂閱模式)

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

角色:

  • 抽象主體(發佈者)
  • 具體主體
  • 抽象發佈者
  • 具體發佈者

優勢:目標和觀察者之間的抽象耦合最小。

缺點:多個觀察者之間互不知道對方存在,所以一個觀察者對主題的修改可能形成錯誤的更新。

適用場景:

  • 當一個抽象模型有兩方面,其中一個方面依賴於另外一個方面。將這二者封裝在獨立對象中以使它們能夠各自獨立地改變和複用。
  • 當對一個對象的改變須要同時改變其它對象,而不知道具體有多少對象有待改變。

  • 對象之間不是緊密耦合。
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)

    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

策略模式

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

優勢:定義了一系列可重用的算法和行爲;消除了一些條件語句;提供行爲的不一樣實現。

缺點:客戶必須瞭解所有策略;

適用場景:

  • 相同算法的不一樣變體
  • 封裝一些條件語句
  • 適用一些客戶不須要知道的數據。
class Sort(metaclass=ABCMeta):
    @abstractmethod
    def sort(self, data):
        pass

class Merge_sort(Sort):
    def sort(self, data):
        pass
class Quick_sort(Sort):
    def sort(self, data):
        pass
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

責任鏈模式

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

角色:抽象處理者,具體處理者,客戶端。

使用場景:

  • 有多個對象能夠處理一個請求,哪一個對象處理由運行時決定
  • 在不明確接收者的狀況下,向多個對象中的一個提交一個請求

優勢:下降耦合度,不須要知道是那個對象處理的請求。

缺點:請求不能保證必定被接受。

from abc import ABCMeta, abstractmethod

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


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


class DepartmentManagerHandler(Handler):
    def __init__(self):
        self.successor = GeneralManagerliHandler()
    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天假")
        else:
            print("項目主管無權准假")
            self.successor.handle_leave(day)


day = 10
h = ProjectDirectorHandler()
h.handle_leave(day)

模板方法模式

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

角色:

  • 抽象類,定義抽象的原子操做,實現一個原子方法做爲算法的骨架。
  • 具體類,實現原子操做

適用場景:

  • 一次性實現一個算法中不可變的部分。
  • 把各個子類中的公共行爲提出來,避免重複。
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()
相關文章
相關標籤/搜索