开发技术--设计模式

开发|设计模式

设计模式是对软件设计中普遍存在(反复出现)的各种问题,所提出的解决方案。

前言

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

正文

设计模式有三大类:创建型模式、结构型模式、行为型模式。在进入设计模式之前,需要了解面向对象是什么,接口是什么,面向对象设计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原则。正文主讲三大设计模式,将每一种设计模式进行详细说明讲解。

posted @ 2019-07-24 14:32  Kate_liu  阅读(...)  评论(...编辑  收藏