開發技術--設計模式

来源:https://www.cnblogs.com/Kate-liu/archive/2019/07/24/11237820.html
-Advertisement-
Play Games

設計模式是對軟體設計中普遍存在(反覆出現)的各種問題,所提出的解決方案。 ...


開發|設計模式

設計模式是對軟體設計中普遍存在(反覆出現)的各種問題,所提出的解決方案。

前言

目前所有的文章思想格式都是:知識+情感。
知識:對於所有的知識點的描述。力求不含任何的自我感情色彩。
情感:用我自己的方式,解讀知識點。力求通俗易懂,完美透析知識。

正文

設計模式有三大類:創建型模式、結構型模式、行為型模式。在進入設計模式之前,需要瞭解面向對象是什麼,介面是什麼,面向對象設計SOLID原則是什麼。
解釋:全文將先從三個問題入手,解決知識儲備問題。進而開始正式設計模式的三大類講解,內容量較大。示例代碼直接可以運行。

知識儲備

1.面向對象是什麼?

基本概念
面向對象的三大特性:封裝、繼承、多態。

封裝:
    【封裝】
     隱藏對象的屬性和實現細節,僅對外提供公共訪問方式。
    【好處】
        a.將變化隔離;       
        b.便於使用;    
        c.提高復用性;   
        d.提高安全性;   
    【封裝原則】
        a.將不需要對外提供的內容都隱藏起來;
        b.把屬性都隱藏,提供公共方法對其訪問。
   

 繼承:
    繼承是一種創建新類的方式,在python中,新建的類可以繼承一個或多個父類,父類又可稱為基類或超類,新建的類稱為派生類或子類。
    
多態:
    多態指的是一類事物有多種形態。python的設計本身就是多態的語言。多態不是語法,是一種設計思想。

註意:三大特性的順序,儘量不要說錯,是有層級關係的。

2.介面是什麼?

基本概念
介面:若幹抽象方法的集合

作用:限制實現介面的類必須按照介面給定的調用方式實現這些方法;對高層模塊隱藏了類的內部實現。

解釋:介面,就是高層,也就是client的調用方式,必須和介面匹配。甚至client不需要關心介面是什麼實現的,只需要看介面就知道怎麼調用。

python實現介面
使用abstractmethod的抽象類方法實現介面。其中pay為介面,所有繼承Payment的類都需要實現該方法,不然那就會報錯,TypeError。
from abc import ABCMeta, abstractmethod

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

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

3.面向對象設計SOLID原則是什麼?

基本概念
S.O.L.I.D設計原則是由Robert C. Martin 在一篇文章中提出的"first five principles" 而來。

Single-responsibility principle :單一職責原則
Open-closed Principle:開閉原則
Liskov substitution principle:里式替換原則
Interface segregation principle:介面隔離原則
Dependency Inversion principle:依賴倒置原則
單一職責原則
英文:
The single responsibility principle revolves around the claim that a certain code module (most often, a class) should only have responsibility over one part of the functionality provided by the software.

中文:
描述:一個類只有一個變化因數,即一個類只有一個職責

解釋:
問題由來:類T負責兩個不同的職責:職責P1,職責P2。當由於職責P1需求發生改變而需要修改 類T時,有可能會導致原本運行正常的職責P2功能發生故障。
解決方案:遵循單一職責原則。分別建立兩個類T1、T2,使T1完成職責P1功能,T2完成職責P2功能。 這樣,當修改類T1時,不會使職責P2發生故障風險;同理,當修改T2時,也不會使職責P1發生故障風險。
開閉原則
英文:
This principle states: “software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification".

中文:
描述:軟體實體(類,模塊,方法等)應該對擴展開放,對修改封閉

解釋:
問題由來:在軟體的生命周期內,因為變化、升級和維護等原因需要對軟體原有代碼進行修改時, 可能會給舊代碼中引入錯誤,也可能會使我們不得不對整個功能進行重構,並且需要原有代碼經過 重新測試。
解決方案:當軟體需要變化時,儘量通過擴展軟體實體的行為來實現變化,而不是通過修改已有的 代碼來實現變化。
里式替換原則
英文:
Liskov Substitution Principle states the following: “in a computer program, if S is a subtype of T, then objects of type T may be replaced with objects of type S (i.e., objects of type S may substitute objects of type T) without altering any of the desirable properties of that program (correctness, task performed, etc.)”.

中文:
描述:如果類型S是類型T的子類型,那麼T的對象可以被S的對象所替換並且不會引發程式行為的變化 ,即1.子類可以完全替代父類。2.子類可以擴展父類的功能,但不能改變父類原有的功能。

解釋:
問題由來:有一功能P1,由類A完成。現需要將功能P1進行擴展,擴展後的功能為P,其中P由原有 功能P1與新功能P2組成。新功能P由類A的子類B來完成,則子類B在完成新功能P2的同時,有可能會 導致原有功能P1發生故障。
解決方案:當使用繼承時,遵循里氏替換原則。類B繼承類A時,除添加新的方法完成新增功能P2外 ,儘量不要重寫父類A的方法,也儘量不要重載父類A的方法。
介面隔離原則
英文:
This principle states that “Clients should not be forced to depend on methods that they do not use”.

中文:
描述:客戶端不應該依賴它不需要的介面,一個類對另一個類的依賴應該建立在最小的介面上。

解釋:
問題由來:類A通過介面I依賴類B,類C通過介面I依賴類D,如果介面I對於類B和類D來說不是最小 介面,則類B和類D必須去實現他們不需要的方法。
解決方案:將臃腫的介面I拆分為獨立的幾個介面,類A和類C分別與他們需要的介面建立依賴關係 。也就是採用介面隔離原則。
依賴倒置原則
英文:
The principle states:“A. High-level modules should not depend on low-level modules. Both shoulddepend on abstractions.B. Abstractions should not depend on details.Details should depend on abstractions”.

中文:
描述:1.高層的模塊不依賴底層的模塊,二者都應該依賴其抽象 2.抽象不依賴細節,細節依賴 抽象

解釋:
問題由來:類A直接依賴類B,假如要將類A改為依賴類C,則必須通過修改類A的代碼來達成。這種 場景下,類A一般是高層模塊,負責複雜的業務邏輯;類B和類C是低層模塊,負責基本的原子操作; 假如修改類A,會給程式帶來不必要的風險。
解決方案:將類A修改為依賴介面I,類B和類C各自實現介面I,類A通過介面I間接與類B或者類 C發生聯繫,則會大大降低修改類A的幾率。

知識正文

基本概念

設計模式分為三大類:
    創建型模式:
        工廠方法模式、抽象工廠模式、創建者模式、原型模式、單例模式

    結構型模式:
        適配器模式、橋模式、組合模式、裝飾模式、外觀模式、享元模式、代理模式

    行為型模式:
        解釋器模式、責任鏈模式、命令模式、迭代器模式、中介者模式、備忘錄模式、觀察者模式、狀態模式、策略模式、訪問者模式、模板方法模式

創建型模式

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

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

優點:
    隱藏了對象創建的實現細節
    客戶端(上層調用者)不需要修改代碼

缺點:
    違反了單一職責原則,將創建邏輯集中到一個工廠類里
    當添加新產品時,需要修改工廠類代碼,違反了開閉原則

解釋:簡單工廠模式,首先實例化工廠,工廠內部對於對象進行分發調用,但是對於其他的調用都集中在一個工廠類中。

代碼實現:
from abc import ABCMeta, abstractmethod

class Payment(metaclass=ABCMeta):
    @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:
    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)
工廠方法模式
內容:定義一個用於創建對象的介面(工廠介面),讓子類決定實例化哪一個產品類。

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

優點:
    每個具體產品都對應一個具體工廠類,不需要修改工廠類代碼
    隱藏了對象創建的實現細節

缺點:
    每增加一個具體產品類,就必須增加一個相應的具體工廠類

解釋:在簡單工廠模式中,進一步將工廠類進行拆分,使用一個類調用一個產品,並且使用介面進行約束。當新增加一個產品類的時候,需要增加一個工廠類與之相對應。

代碼實現:
from abc import ABCMeta, abstractmethod

# 產品類
class Payment(metaclass=ABCMeta):
    @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 BankPay(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)

class BankPayFactory(PaymentFactory):
    def create_payment(self):
        return BankPay()

# client
# 調用者
pf = HuabeiFactory()
p = pf.create_payment()
p.pay(100)  # 花唄支付100元.

pf = BankPayFactory()
p = pf.create_payment()
p.pay(999)  # 銀行卡支付999元.
抽象工廠模式
內容:定義一個工廠類介面,讓工廠子類來創建一系列相關或相互依賴的對象。

例:生產一部手機,需要手機殼、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 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 PhoneFactory(metaclass=ABCMeta):
    @abstractmethod
    def make_shell(self):
        pass
    @abstractmethod
    def make_cpu(self):
        pass
    @abstractmethod
    def make_os(self):
        pass

# ------具體工廠------
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()
# 手機信息:
# 蘋果CPU
# iOS系統
# 蘋果手機殼

p1 = make_phone(HuaweiFactory())
p1.show_info()
# 手機信息:
# 聯發科CPU
# Android系統
# 普通手機小手機殼
建造者模式
內容:將一個複雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。

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

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

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

代碼實現:
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 = SexyGirlBuilder()
director = PlayerDirector()
p = director.build_player(builder)
print(p)  # 漂亮臉蛋, 苗條, 漂亮胳膊, 大長腿
單例模式
內容:保證一個類只有一個實例,並提供一個訪問它的全局訪問點。

角色:
    單例(Singleton)

優點:
    對唯一實例的受控訪問
    單例相當於全局變數,但防止了命名空間被污染

應用場景:日誌類,資料庫的連接

實現方式一:

   直接書寫一個模塊,在裡面書寫一個類,實例化一個對象,在其他的地方調用的時候,調用實例化的對象既可實現單例模式。

實現方式二:

   使用new方法對實例化的對象進行判斷,只要是存在這個實例化對象的時候,就直接將它返回,第一次實例化的時候,直接繼承父類進行創建實例化對象,以後都是直接返回第一次創建的對象。並且,修改值的時候,後面的實例化新類修改值之後,之前的數值也會變化。

代碼實現:
class Singleton:
    """實現單例模式"""
    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, a):
        self.a = a

a = MyClass(10)
b = MyClass(20)

print(a.a)  # 20
print(b.a)  # 20
print(id(a), id(b))  # 1861748392568 1861748392568
總結
抽象工廠模式和建造者模式相比於簡單工廠模式和工廠方法模式而言更靈活也更複雜。

通常情況下、設計以簡單工廠模式或工廠方法模式開始,當你發現設計需要更大的靈活性時,則像更複雜的設計模式演化。

結構型模式

適配器模式
內容:將一個類的介面轉換成客戶希望的另一個介面。適配器模式使得原本由於介面不相容而不能一起工作的那些類可以一起工作。

兩種實現方式:
    類適配器:使用多繼承
    對象適配器:使用組合 

角色:
    目標介面(Target)
    待適配的類(Adaptee)
    適配器(Adapter)

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

代碼實現:
'''類適配器'''
from abc import ABCMeta, abstractmethod


class Payment(metaclass=ABCMeta):
    @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)


# cost支付模式
class Cost(metaclass=ABCMeta):
    @abstractmethod
    def cost(self, money):
        pass


class BankPay(Cost):
    def cost(self, money):
        print("銀聯支付%d元." % money)


class ApplePay(Cost):
    def cost(self, money):
        print("蘋果支付%d元." % money)


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


p = NewBankPay()
p.pay(100)   # 銀聯支付100元.
'''對象適配器'''
from abc import ABCMeta, abstractmethod


class Payment(metaclass=ABCMeta):
    @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)


# cost支付模式
class Cost(metaclass=ABCMeta):
    @abstractmethod
    def cost(self, money):
        pass


class BankPay(Cost):
    def cost(self, money):
        print("銀聯支付%d元." % money)


class ApplePay(Cost):
    def cost(self, money):
        print("蘋果支付%d元." % money)
        
 
# 對象適配器
class PaymentAdapter(Payment):
    def __init__(self, payment):
        self.payment = payment

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


# 頂層調用
# 統一實現pay方法
p = PaymentAdapter(BankPay())
p.pay(100)  # 銀聯支付100元.

p = WechatPay()
p.pay(100)  # 微信支付100元.
橋模式
內容:
    將一個事物的兩個維度分離,使其都可以獨立地變化。

角色:
    抽象(Abstraction)
    細化抽象(RefinedAbstraction)
    實現者(Implementor)
    具體實現者(ConcreteImplementor)

應用場景:
    當事物有兩個維度上的表現,兩個維度都可能擴展時。


優點:
    抽象和實現相分離
    優秀的擴展能力

代碼實現:
    對於形狀與顏色在二維上進行擴展,使用橋模式,相互組合實現。
    形狀屬於抽象
    顏色屬於實現者
from abc import ABCMeta, abstractmethod


# 抽象介面
class Shape(metaclass=ABCMeta):
    def __init__(self, color):
        self.color = color

    @abstractmethod
    def draw(self):
        pass


# 實現者介面
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()
組合模式
內容:將對象組合成樹形結構以表示“部分-整體”的層次結構。組合模式使得用戶對單個對象和組合對象的使用具有一致性。

角色:
    抽象組件(Component)
    葉子組件(Leaf)
    複合組件(Composite)
    客戶端(Client)

適用場景:
    表示對象的“部分-整體”層次結構(特別是結構是遞歸的)
    希望用戶忽略組合對象與單個對象的不同,用戶統一地使用組合結構中的所有對象

優點:
    定義了包含基本對象和組合對象的類層次結構
    簡化客戶端代碼,即客戶端可以一致地使用組合對象和單個對象
    更容易增加新類型的組件

代碼實現:
    將點和線進行組合成為新的圖形。並且點,線,新的圖形具有同樣的方法。在組合的時候,可以進行遞歸使用,並且組合隨機多層組合。
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])  # 組合成為pic1

p2 = Point(4, 4)
l3 = Line(Point(1, 1), Point(0, 0))
pic2 = Picture([p2, l3])  # 組合成為pic2

pic = Picture([pic1, pic2])  # 組合成為pic
pic.draw()
"""
------複合圖形------  # pic
    ------複合圖形------  #pic1
    點(2, 3)
    線段[點(3, 4), 點(6, 7)]
    線段[點(1, 5), 點(2, 8)]
    ------複合圖形------
    
    ------複合圖形------  #pic2
    點(4, 4)
    線段[點(1, 1), 點(0, 0)]
    ------複合圖形------
------複合圖形------
"""
外觀模式
內容:為子系統中的一組介面提供一個一致的界面,外觀模式定義了一個高層介面,這個介面使得這一子系統更加容易使用。

角色:
    外觀(facade)
    子系統類(subsystem classes)

優點:
    減少系統相互依賴
    提高了靈活性
    提高了安全性

代碼實現:
    直接使用一個類進行響應的封裝,直接調用,不用關心底層是怎麼實現的。
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()
代理模式
內容:為其他對象提供一種代理以控制對這個對象的訪問。

應用場景:
    遠程代理:為遠程的對象提供代理(資料庫操作,無圖模式)
    虛代理:根據需要創建很大的對象(手機的無圖模式)
    保護代理:控制對原始對象的訪問,用於對象有不同訪問許可權時(普通用戶與管理員的許可權不一樣,讀寫許可權)

角色:
    抽象實體(Subject)
    實體(RealSubject)
    代理(Proxy)

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

代碼實現:
'''虛代理'''
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 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 self.subj:
            self.subj = RealSubject(self.filename)
        return self.subj.set_content(content)


subj = VirtualProxy("test.txt")  # 此時並沒有讀取文件,還沒有占用記憶體
ret = subj.get_content()  # 此時讀取文件,返回數據
print(ret)
'''保護代理'''
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 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 = ProtectedProxy("test.txt")  # 直接就讀取了文件,占用記憶體
print(subj.get_content())
subj.set_content("abc")  # 在寫入文件的時候,進行判斷許可權,PermissionError: 無寫入許可權

行為型模式

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

角色:
    抽象處理者(Handler)
    具體處理者(ConcreteHandler)
    客戶端(Client)

適用場景:
    有多個對象可以處理一個請求,哪個對象處理由運行時決定
    在不明確接收者的情況下,向多個對象中的一個提交一個請求

優點:
    降低耦合度:一個對象無需知道是其他哪一個對象處理其請求

代碼實現:
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 = 2
h = ProjectDirector()
h.handle_leave(day)  # 項目主管准假2天

day = 12
h = ProjectDirector()
h.handle_leave(day)  # 項目主管職權不足
                     # 部門經理職權不足
                     # 你還是辭職吧
觀察者模式
內容:定義對象間的一種一對多的依賴關係,當一個對象的狀態發生改變時, 所有依賴於它的對象都得到通知並被自動更新。觀察者模式又稱“發佈-訂閱”模式

角色:
    抽象主題(Subject)
    具體主題(ConcreteSubject)——發佈者
    抽象觀察者(Observer)
    具體觀察者(ConcreteObserver)——訂閱者

適用場景:
    當一個抽象模型有兩方面,其中一個方面依賴於另一個方面。將這兩者封裝在獨立對象中以使它們可以各自獨立地改變和復用。
    當對一個對象的改變需要同時改變其它對象,而不知道具體有多少對象有待改變。
    當一個對象必須通知其它對象,而它又不能假定其它對象是誰。換言之,你不希望這些對象是緊密耦合的。

優點:
    目標和觀察者之間的抽象耦合最小
    支持廣播通信 

代碼實現:
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):  # get 數據的時候使用
        return self.__company_info

    @company_info.setter
    def company_info(self, info):  # set 數據的時候使用
        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)
print(s1.company_info)  # None
print(s2.company_info)  # None

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)  # 公司今年業績非常好,給大家發獎金!!!
策略模式
內容:定義一系列的演算法,把它們一個個封裝起來,並且使它們可相互替換。本模式使得演算法可獨立於使用它的客戶而變化。

角色:
    抽象策略(Strategy)
    具體策略(ConcreteStrategy)
    上下文(Context)

優點:
    定義了一系列可重用的演算法和行為
    消除了一些條件語句
    可以提供相同行為的不同實現

缺點:
    客戶必須瞭解不同的策略 

代碼實現:有兩種實現的策略。第一種計算速度快,但是不可靠。另一種計算速度玩,但是可靠。在高峰期與平常時間需要進行響應的切換。
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()  # 用較慢的策略處理[...]
模板方法模式
內容:定義一個操作中的演算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類可以不改變一個演算法的結構即可重定義該演算法的某些特定步驟。

角色:
    抽象類(AbstractClass):定義抽象的原子操作(鉤子操作);實現一個模板方法作為演算法的骨架。
    具體類(ConcreteClass):實現原子操作

適用場景:
    一次性實現一個演算法的不變的部分
    各個子類中的公共行為應該被提取出來並集中到一個公共父類中以避免代碼重覆
    控制子類擴展

代碼實現:
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()

結束語

恭喜各位,看完了程式的設計模式,內容很多,量很足,這些設計模式必須對應相應的場景下才有意義,所以需要自己在以後的項目中靈活使用。
程式的設計模式,確實是打開了我的另一種眼界。目前個人用的多的就是工廠方法模式和單例模式。這也算是自己的第一篇知識總結,力求將我所學的知識,儘量整理成為體系知識。
總結:全文從設計模式開題;引入面向對象知識,引入介面定義;引入SOLID原則。正文主講三大設計模式,將每一種設計模式進行詳細說明講解。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • vue更新換代很快,馬上vue都要出3.0了,這是一個巨大的變革,不過今天講的是vuecli3.0,裡面使用的vue仍然是2的,所有可以放心大膽使用。 Vue CLI 是一個基於 Vue.js 進行快速開發的完整系統,裡面所有具體介紹都可以去官網查看到 "vuecli官網移步" 這裡主要是講一些我實 ...
  • 譯者按: 使用 來捕獲所有的異常 原文: "Async Await Error Handling in JavaScript" 譯者: "Fundebug" 本文采用意譯,版權歸原作者所有 async/await 中的異常處理很讓人混亂。儘管有很多種方式來應對 "async 函數的異常" ,但是連經 ...
  • web前端全套視頻教程,html5,css3,javascript,.AngularJS. 下載地址 ...
  • 1.更換窗體圖標 方法:單擊窗體,更改icon屬性。 2.調整窗體打開時預設位置 方法:單擊窗體,更改StartPotion屬性。 3.修改窗體大小 方法:單擊窗體,更改Size屬性。 4.設置窗體的背景圖片 方法:單擊窗體,更改BackgroundImage屬性。 5.打開新窗體 首先新建一個窗體 ...
  • 1.馮·諾依曼體系 現代電腦的模型都是基於馮·諾依曼體系的,馮·諾依曼體系的基本組成有存儲器,計數器,控制器,輸入和輸出設備,總共有這五大本分組成。下圖是我基於這五大組成部分畫了一個組成圖來表示他們之間的關係,如圖1-1。 圖1-1 基於馮·諾依曼體系,首先控制器從記憶體中取出指令,有控制器來執行, ...
  • 前言 Adapter設計模式,允許客戶端使用介面不相容的類。 昨天收拾一些以前的東西,發現了藏在柜子里的一條線,這條線叫做OTG。這條線的一端是micro usb的輸出口,另一端是usb的輸入口。這條線,就是Adapter。手機如果想要使用U盤,會發現這個U盤的usb輸出口太大了,根本插不進手機的接 ...
  •   資料庫索引對於數據查詢的重要性不可言喻,因此作者在存儲層實現了二級索引,以及利用索引進行掃描的功能。目前僅實現了分區表與非分區表的本地索引(數據與索引共用一個Raft組管理),全局索引及反向索引待以後再實現。 一、存儲結構:   在介紹索引前先瞭解一下數據 ...
  • 背景 在企業發展初期,企業使用的系統很少,通常一個或者兩個,每個系統都有自己的登錄模塊,運營人員每天用自己的賬號登錄,很方便。 但隨著企業的發展,用到的系統隨之增多,運營人員在操作不同的系統時,需要多次登錄,而且每個系統的賬號都不一樣,這對於運營人員 來說,很不方便。於是,就想到是不是可以在一個系統 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...