设计模式 1 (Python版)

设计模式

解释:

概念

《设计模式:可复用面向对象软件的基础》

  • 面向对象的特性:封装,继承,多态

接口:若干方法的集合

限制实现接口的类必须按照接口给定的调用方式实现这些方法

对高层模块隐藏了类的内部实现

#接口实现的两种方法:

#1.写一个父类,其他类必须继承这个方法,若子类不实现这个方法,则抛出异常
#缺点:如果不调用pay,则不会报错
class Payment:
    def pay(self, money):
        raise NotImplementedError

#2.写一个抽象类,让其他类实现这个接口
from abc import ABCMeta, abstractmethod
class Pament(metaclass = ABCMeta):#指定为抽象类
    @abstractmethod#指定为抽象方法
    def pay(self,money):
        pass

class Alipay(Payment):
    def pay(self, money):#1.缺点:如果这里不实现这个方法,并且没有调用p.pay(money),则不会报错
        pass            #2.若不实现此方法,报错:can't instantiate 抽象类的抽象方法
class Wechatpay(Payment):
    def pay(self, money):
        pass
p = Alipay()
def finish_pay(p, money):
    p.pay(money)

设计原则solid

开放封闭原则 Open closed principle

类、模块、函数应该对扩展开放,对修改关闭。(在不修改源代码的时候进行扩展)

里氏替换原则 Liskov substitution principle

所有引用父类的地方都必须透明的使用子类对象

Eg:传入user 可以使用,传入VIP(user) 必须也可以使用

依赖倒置原则 Dependence inversion principle

高层不应该依赖底层,二者都应该依赖抽象;抽象不应该依赖细节,细节应该依赖抽象。

高层逻辑不随底层修改,都依赖接口;先定义接口,按照接口格式写细节。

接口隔离原则 Interface segregation principle

使用多个专用接口,而不是使用单一的总接口,高层代码不应该依赖那些它不需要的接口。

#错误的方式:
class animal:实现walk,fly,swim方法
class Tiger(animal):只实现walk,则报错,必须实现所有的抽象方法
#但Tiger不应该实现fly,swim,所以修改设计模式

#修改后的设计:
class LandAnimal(metaclass = ABCMeta)#实现walk
class WaterAnimal(metaclass = ABCMeta)#实现swim
class SkyAnimal(metaclass = ABCMeta)#实现fly
#采用多继承的方法
class Tiger(LandAnimal):
    def walk:
        pass
class Frog(LandAnimal, WaterAnimal):
    def walk:
        pass
    def swim:
        pass

单一职责原则 Single responsibility principle

不要存在多于一个导致类变更的原因。

一个类只负责一项职责

迪米特法则 Law of Demeter

talk only to your immediate friends and not to strangers 两个模块无需直接通信,则不应该发生直接的相互调用,通过第三方转发调用(降低类之间的耦合,提高模块的独立性)

设计模式分类

创建型模式(5):聚焦于创建对象

工厂方法模式, 抽象工厂模式,创建者模式, 原型模式, 单例模式

结构型模式(7):聚焦于类之间

适配器模式, 桥模式,组合模式,装饰模式,外观模式、享元模式, 代理模式

行为型模式(11):关注类的具体行为

解释器模式, 责任链模式,命令模式迭代器模式中介者模式备忘录模式,观察者模式,状态模式,策略模式,访问者模式,模板方法模式


模式详解

1. 前言:简单工厂模式

解释:不直接向client暴露对象创建的实现细节,而是通过一个工厂类来负责创建产品类的实例。

工厂类:creator, 抽象产品:接口, 具体产品: 继承接口的类

优点:隐藏了对象创建的实现细节,客户端不需要修改代码

缺点:违反单一职责原则,将创建逻辑集中到一个工厂类

​ 添加新产品时,需要修改工厂类代码,违反了开闭原则

from abc import ABCMeta, abstractmethod
#抽象产品
class Pament(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)
# 上面为抽象产品和具体产品
# 下面为生产对象:
#具体工厂
class PaymentFactory:
    def create_payment(self, method):#good part 可以把时间戳、证书之类的隐藏在工厂类里面
        if method == 'alipay':        #而client不需要知道这些
            return Alipay()
        elif method == 'wechat':
            return Wechatpay()
        else:
            raise TypeError("no such pament named%s" % method)
#client调用
pf = PamentFactory()
p = pf.create_payment('alipay')
p.pay(100)

2. 工厂方法模式

增加了抽象工厂


内容:定义一个创建工厂的接口,让子类决定实例化哪一种产品类


抽象工厂类:creator, 具体工厂类: concrete creator, 抽象产品类: product, 具体产品类: concrete product

添加一个工厂接口来约束工厂方法

即创建一个抽象工厂,多个不同的工厂类

优点:每个具体产品由对应的具体工厂类产生,不需要修改工厂类代码;隐藏了对象创建的实现细节

缺点:每添加一个具体产品类,就必须添加一个相应的具体工厂类

#抽象工厂
class PaymentFactory(metaclass = ABCMeta):
    @abstractmethod
    def create_payment(self):
        pass
#具体工厂
class AlipayFactory(PaymentFactory):
    def create_payment(self):
        return Alipay()
class WechatFactory(PaymentFactory):
    def create_payment(self):
        return Wechatpay()
#client调用
pf = AlipayFactory()
p = pf.create_payment()
p.pay(100)

3. 抽象工厂模式(与前面完全不同应用场景)

内容:定义一个工厂类接口,让工厂子类创建一系列相关或依赖的对象

如,产生一个手机,需要手机壳,cpu,os三部分,每种类对象都有不同的种类(对os有安卓和ios)。对每个具体工厂,分别生产一部手机需要三个对象。

相比工厂方法模式,抽象工厂模式种的每个具体工厂都生产一套产品(一系列)。

优点:将客户端与类实现相分离;每个工厂创建了一个完整的产品系列,易于交换产品系列;有利于产品的一致性(产品间的约束关系)

缺点:难以支持新种类的产品,需要添加产品,改变抽象类工厂,改变具体工厂

from abc import ABCMeta, abstractmethod
#抽象产品
class OS(metaclass = ABCMeta):
    @abstractmethod
    def show_OS(self):
        pass
class CPU(metaclass = ABCMeta):
    @abstractmethod
    def show_CPU(self):
        pass
#抽象工厂
class PhoneFactory(metaclass = ABCMeta):
    @abstractmethod
    def make_OS(self):
        pass
    @abstractmethod
    def make_CPU(self):
        pass
#具体产品
class androidOS(OS):
    def show_OS(self):
        print('安卓')
class IOS(OS):
    def show_OS(self):
        print('apple')

class gaotongCPU(CPU):
    def show_CPU(self):
        print('高通')
class lianfakeCPU(CPU):
    def show_CPU(self):
        print('联发科')

#具体工厂(同时限制组装,避免android安装到苹果上)
class MiFactory(PhoneFactory):
    def make_OS(self):
        return androidOS()
    def make_CPU(self):
        return gaotongCPU()

#client
#创建手机样式
class Phone:
    def __init__(self, os, cpu):
        self.os = os
        self.cpu = cpu
    def show_info(self):
        self.os.show_OS()
        self.cpu.show_CPU()
#传入 手机工厂
def make_phone(factory):
    cpu = factory.make_CPU()
    os = factory.make_OS()
    return Phone(os, cpu)
phone1 = make_phone(MiFactory())
phone1.show_info()

4.建造者模式

内容:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

角色:抽象建造,具体建造;指挥者,产品

产品 = 指挥者.方法(建造者)

建造者模式与抽象工厂模式类似,也用来创建复杂对象。

区别:建造者模式着重一步步构造一个复杂对象,抽象工厂模式着重于多个系列的产品对象

优点:隐藏产品内部结构和装配,构造代码表示代码分开,可以对构造过程进行更精细的控制

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
    def build_body(self):
        pass
    def build_arm(self):
        pass
    def build_leg(self):
        pass
 #具体建造者   
class SexyGirl(PlayerBuilder):
    def __init__(self):
        self.player = Player()

    def build_face(self):
        self.player.face = 'lian'
    def build_body(self):
        self.player.body = 'shengti'
    def build_arm(self):
        self.player.arm = 'gebo'
    def build_leg(self):
        self.player.leg = 'tui'
class Monster(PlayerBuilder):
    def __init__(self):
        self.player = Player()
    def build_face(self):
        self.player.face = 'lian2'
    def build_body(self):
        self.player.body = 'shengti2'
    def build_arm(self):
        self.player.arm = 'gebo2'
    def build_leg(self):
        self.player.leg = 'tui2'
#控制组装顺序,如必须现有body,后有face
class PlayerDirector:
    def build_player(self, builder):
        builder.build_body()
        builder.build_face()
        builder.build_arm()
        builder.build_leg()
        return builder.player

#client
builder1 = Monster()
director1 = PlayerDirector()
monster1 = director1.build_player(builder1)

5. 单例模式

内容:保证一个类只有一个实例,并提供一个访问它的全局访问点

角色:单例

优点:对唯一实例的受控访问;单例相当于全局变量,但防止了命名空间被污染

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)
print(b.a)  
#打印都是20
#因为
print(id(a),id(b))#这俩相同

抽象工厂模式和建造者模式相比与简单工厂模式和工厂方法模式更灵活,更复杂

通常,以简单工厂模式或工厂方法模式开始


6. 适配器模式

内容:将一个类的接口转换为client希望的另一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

实现方法有两种:类适配器(多继承实现)

​ 对象适配器(使用组合实现)


角色:目标接口,待适配的类(新写的类),适配器


使用场景:

类适配器:想使用一个已经存在的类,而接口不符合要求。

对象适配器:想使用一些已经存在的子类,但不可能对每个子类都去匹配,对象适配器可以批量适配

#组合:在一个类复用其他类的方法
#class B想使用A的方法,则把A()放入另一个类种
class A:
    pass
class B:
    def __init__(self):
        self.a = A()
from abc import ABCMeta, abstractmethod
class Payment(metaclass = ABCMeta):
    @abstractmethod
    def pay(self, money):
        pass
class Alipay(Payment):
    def pay(self, money):
        print('Alipay')
class Wechatpay(Payment):
    def pay(self, money):
        print('Wechat')
#another coder:
class Bankpay:
    def cost(self, money):
        print('Bankpay')
#client
p = Alipay()
p.pay(100)#正常使用
p = Bankpay()
p.pay(100)#无法使用
#1.复用写好的代码2.bankpay的代码可能在别的地方已经调用了
#********************   第一种适配器:套壳,多继承
class Newbankpay(Payment, Bankpay):#继承payment实现接口统一,继承bankpay实现代码复用
    def pay(self, money):
        self.cost(money)
#********************   第二种适配器:组合,解决多个都含有cost方法的类的问题
class PaymentAdapter(newpayment):#新写的类作为形参传入
    def __init__(self, payment):
        self.payment = payment
    def pay(self, money):
        self.payment.cost(money)
#此时client调用要写
p = PaymentAdapter(Bankpay())
p.pay(100)

7. 桥模式

将一个事物的两个维度进行分离,使其都可以独立的变化。(方便扩展)

内容:抽象,细化抽象,实现,具体实现

eg: 画图有形状和颜色两个维度:形状.draw()作为抽象(调用实现代码),颜色.paint()作为实现(真正实现了代码),必须有了颜色之后才可以实现

优点 :抽象 实现的分离; 优秀的扩展能力

class Shape:
    pass
class Line(Shape):
    pass
class Rectangle(Shape):
    pass
#写红色,蓝色,绿色的:
class RedLine(Line):
    pass
class BlueLine(Line):
    pass
class RedRectangle(Rectangle):#写的类过于多,不容易扩展
    pass                    #耦合方式过于紧密

桥模式:组合

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 = 'rectangle'
    def draw(self):
        self.color.paint(self)
#颜色维度:
class Red(Color):
    def paint(self.shape):
        prnt(shape.name)

#client调用
rec = Rectangle(Red())
rec.draw()

8. 组合模式

内容:将对象组合成树形结构以表示‘部分-整体’的层次结构。组合模式使得用户对单个对象组合对象的使用具有一致性

角色: 抽象组件,叶子节点,复合组件,client

适用场景:表示对象的‘部分-整体’层次结构(递归了)

希望用户忽略组合对象与单个对象的不同,统一的使用组合结构中的所有对象。(如ppt中的图形一样可以组合可以单独使用)

优点: 定义了基本对象和组合对象的类层次结构;简化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 i in iterable:
            self.add(i)
    def add(self, graphic):
        self.children.append(graphic)
    def draw(self):
        print('复合图形')
        for i in self.children:
            i.draw()
        print('复合图形')

p1 = Point(1,3)
p2 = Point(3,4)
l1 = Line(p1, p2)
l2 = Line(Point(2,3),Point(5,6))
pic1 = Picture([p1,p2,l1,l2])
pic1.draw()
#print
复合图形
点(1, 3)
点(3, 4)
线 [点(1, 3)---点(3, 4)]
线 [点(2, 3)---点(5, 6)]
复合图形

9. 外观模式(不用看)

内容:为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

角色:外观,子系统类

优点 : 减少系统相互依赖;提高灵活性;提高安全性

#子系统类
class Cpu:
    def run(self):
        print('cpu run')
    def stop(self):
        print('cpu stop')
class Memory:
    def run(self):
        print('memory run')
    def stop(self):
        print('memory run')
#外观
class Computer:
    def __init__(self):
        self.cpu = Cpu()
        self.memory = Memory()
    def run(self):
        self.cpu.run()
        self.memory.run()
    def stop(self):
        self.cpu.stop()
        self.memory.stop()
#client
#先调用外观接口:
computer1 = Computer()
computer1.run()
computer1.stop()

10. 代理模式

内容:为其他对象提供一种代理以控制对这个对象的访问

角色:subject抽象实体(subject接口,目的:使实体和代理对外具有一致的方法), realsubject实体,proxy代理

应用场景:

远程代理:为远程的对象提供代理(隐藏对象位于远程地址空间的事实)

虚代理: 根据需要创建很大的对象(一个读文件和写文件的类,创建时没必要直接把文件读进来占内存,只在读文件时读进来),可以进行优化,如根据要求来创建对象

保护代理:控制对原始对象的访问,用于对象有不同访问权限时。(允许在访问一个对象时有一些附加的内务处理)

from abc import ABCMeta, abstractmethod
class Subject(metaclass = ABCMeta):
    @abstractmethod
    def get_content(self):#获取一个很大的文件
        pass
    @abstractmethod
    def set_content(self):#写入文件
        pass
#真实类
class RealSubject(Subject):
    def __init__(self, filename):
        self.filename = filename
        f = open(filename, 'r')
        print('读取文件内容')
        self.content = f.read()#在只调用set_content函数时也会占用内存
        f.close()
    def get_content(self):
        return self.content
    def set_content(self, content):
        f = open(self.filename, 'w')
        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)
        self.subj.set_content(content)
#client
#1.
#subj = RealSubject('test.txt')
#此时直接会打印‘读取文件内容’

subj = VirtualProxy('test.txt')
#此时不会读入文件占用内存,调用.get_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 = ProtectedProxy('test.txt')
subj.get_content()#正常使用
subj.set_content()#抛出异常

行为型模式

11. 责任链模式

内容:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,知道有一个对象处理为止。

角色: 抽象处理者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('go fuck off')
class DepartmentManager(Handler):
    def __init__(self):
        self.next = GeneralManager()
    def handle_leave(self, day):
        if day <= 5:
            print('部门经理准假%d 天'% day)
        else:
            print('无法审批,up')
            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('无法审批,up')
            self.next.handle_leave(day)
#client
athing = ProjectDirector()
athing.handle_leave(5)

12. 观察者模式(发布-订阅模式)

内容:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时, 所有依赖于它的对象都得到通知并被自动更新。

角色:抽象主题subject, 具体主题Concrete Subject(发布者), 抽象观察者(Observer),具体观察者(Concrete Observer)订阅者

适用场景:

  1. 当一个抽象模型有两个方面(excel中的表格和图(柱状,扇形等)的关系),其中一个方面依赖于另一个方面。将这两者封装在独立对象中以使它们可以各自独立的改变和复用
  2. 当对一个对象的改变需要同时改变其他的对象,而不知道具体有多少个对象待改变时。
  3. 当一个对象必须通知其他对象,而又不能假定其他对象是谁。(单项紧耦合)

优点: 目标和观察者之间的耦合最小; 支持广播通信

from abc improt 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 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):#只读属性,防止company_info被篡改, 并用类.属性 的方式调用,不需要加括号
        return self.__company_info#读私有成员
    @company_info.setter
    def company_info(self, info):#写私有成员, 如何去修改私有成员company_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#无法运行,因为notice的是私有的
#client
notice = StaffNotice('初始公司')
s1 = Staff()
s2 = Staff()
notice.attach(s1)
notice.attach(s2)
notice.company_info = 'over'
print(s1.company_info)

13. 策略模式

内容:定义一系列算法,把他们一个个封装起来,并且可以在使用的时候相互替换。可以使算法独立于适用的客户而变化。

角色: 抽象策略 strategy, 具体策略 Concrete Strategy, 上下文 context

优点 : 定义了一系列可重用的算法和行为;消除了条件语句;可以提供相同行为的不同实现

缺点: 客户必须了解不同的策略

from abc import ABCMeta, abstractmethod
class Strategy(metaclass = ABCMeta):
    @abstractmethod
    def execute(self, data):
        pass
class FastStrategy(Strategy):
    def execute(self, data):
        print('fast method %s' % data)
class SlowStrategy(Strategy):
    def execute(self, data):
        print('slow method %s' % data)
class Context:#可以对client隐藏默写实现,如随机数的产生,拿到今天的日期,时间戳之类的
    def __init__(self, strategy, data):
        self.strategy = strategy
        self.data = data
    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()   

14. 模板方法模式

内容:定义一个操作中的算法的骨架, 而将一些步骤延迟到子类中。 模板方法使得子类可以不改变一个算法的结构时重定义该算法的某些特定步骤。

角色: 抽象类 abstract class: 定义抽象的原子操作(钩子操作),实现一个模板方法作为算法的骨架。

具体类concrete class: 实现原子操作

适用场景:一次性实现一个算法不变的部分;各个子类中的公共部分行为应该被提取出来并集中到一个公共父类中以避免代码重复; 控制子类扩展(只有在定义的模板中的才可以扩展)

from time import sleep
from abc import ABCMeta, abstractmethod
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('start')
    def stop(self):
        print('stop')
    def repaint(self):
        print(self.msg)
MyWindow('it\'s working').run()
posted @ 2024-05-26 13:21  Roy2048  阅读(2)  评论(0编辑  收藏  举报