Python的设计模式
设计模式是什么?
设计模式是经过总结、优化的,对我们经常会碰到的一些编程问题的可重用解决方案。一个设计模式并不像一个类或一个库那样能够直接作用于我们的代码。反之,设计模式更为高级,它是一种必须在特定情形下实现的一种方法模板。设计模式不会绑定具体的编程语言。一个好的设计模式应该能够用大部分编程语言实现(如果做不到全部的话,具体取决于语言特性)。最为重要的是,设计模式也是一把双刃剑,如果设计模式被用在不恰当的情形下将会造成灾难,进而带来无穷的麻烦。然而如果设计模式在正确的时间被用在正确地地方,它将是你的救星。
起初,你会认为“模式”就是为了解决一类特定问题而特别想出来的明智之举。说的没错,看起来的确是通过很多人一起工作,从不同的角度看待问题进而形成的一个最通用、最灵活的解决方案。也许这些问题你曾经见过或是曾经解决过,但是你的解决方案很可能没有模式这么完备。
虽然被称为“设计模式”,但是它们同“设计“领域并非紧密联系。设计模式同传统意义上的分析、设计与实现不同,事实上设计模式将一个完整的理念根植于程序中,所以它可能出现在分析阶段或是更高层的设计阶段。很有趣的是因为设计模式的具体体现是程序代码,因此可能会让你认为它不会在具体实现阶段之前出现(事实上在进入具体实现阶段之前你都没有意识到正在使用具体的设计模式)。
可以通过程序设计的基本概念来理解模式:增加一个抽象层。抽象一个事物就是隔离任何具体细节,这么做的目的是为了将那些不变的核心部分从其他细节中分离出来。当你发现你程序中的某些部分经常因为某些原因改动,而你不想让这些改动的部分引发其他部分的改动,这时候你就需要思考那些不会变动的设计方法了。这么做不仅会使代码可维护性更高,而且会让代码更易于理解,从而降低开发成本。
设计模式的定义:为了解决面向对象系统中重要和重复的设计封装在一起的一种代码实现框架,可以使得代码更加易于扩展和调用
四个基本要素:模式名称,问题,解决方案,效果
六大原则:
- 1.开闭原则:一个软件实体,如类,模块和函数应该对扩展开发,对修改关闭.既软件实体应尽量在不修改原有代码的情况下进行扩展.
- 2.里氏替换原则:所有引用父类的方法必须能透明的使用其子类的对象
- 3.依赖倒置原则:高层模块不应该依赖底层模块,二者都应该依赖其抽象,抽象不应该依赖于细节,细节应该依赖抽象,换而言之,要针对接口编程而不是针对实现编程
- 4.接口隔离原则:使用多个专门的接口,而不是使用单一的总接口,即客户端不应该依赖那些并不需要的接口
- 5.迪米特法则:一个软件实体应该尽可能的少与其他实体相互作用
- 6.单一直责原则:不要存在多个导致类变更的原因.即一个类只负责一项职责
设计模式的分类
- 创建型模式(5种):工厂方法模式、抽象工厂模式、创建者模式、原型模式、单例模式
- 结构型模式(7种):适配器模式、桥模式、组合模式、装饰模式、外观模式、享元模式、代理模式
- 行为型模式(11种):解释器模式、责任链模式、命令模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式、策略模式、访问者模式、模板方法模式
主流的设计模式
接口
定义:一种特殊的类,声明了若干方法,要求继承该接口的类必须实现这种方法
作用:限制继承接口的类的方法的名称及调用方式,隐藏了类的内部实现
# 接口:若干抽象方法的集合 # 作用:限制实现接口的类必须按照接口给定的调用方式实现这些方法;对高层模块隐藏了类的内部实现 from abc import ABCMeta, abstractmethod # 接口 abstract class class PayMent(metaclass=ABCMeta): @abstractmethod # 定义抽象方法的关键字 def pay(self, money): pass # def pay(self, money): # raise NotImplementedError('must implement pay methods') class AliPay(PayMent): # 子类继承接口,必须实现接口中定义的抽象方法,否则不能实例化对象 def pay(self, money): print(f'支付宝支付{money}元') class WechatPay(PayMent): def pay(self, money): print(f'微信支付{money}元') class ApplePay(PayMent): def pay(self, money): print(f'苹果支付{money}元') def finish_pay(p, money): p.pay(money) p1 = AliPay() p2 = WechatPay() p3 = ApplePay() finish_pay(p1, 100) finish_pay(p2, 200) finish_pay(p3, 300) """ 里式替换原则: 所有引用父类的地方必须能透明地使用其子类的对象 """ class User: def show_name(self): print('我是用户A') return '普通用户' class VipUser(User): def show_name(self): print('我是VIP用户') return 'VIP用户' def show_user(user): # 父类和子类的show_name参数和返回值类型必须相同 res = user.show_name() print(res) u = User() show_user(u) """ 接口隔离原则:使用多个专门的接口,而不是用单一的总接口,即客户端不应该依赖那些他不需要的接口 """ class LandAnimal(metaclass=ABCMeta): @abstractmethod def walk(self): pass class WaterAnimal(metaclass=ABCMeta): @abstractmethod def swim(self): pass class SkyAnimal(metaclass=ABCMeta): @abstractmethod def fly(self): pass class Tiger(LandAnimal): def walk(self): print("老虎走路") class Frog(LandAnimal, WaterAnimal): pass
一、简单工厂模式
定义: 不直接向客户暴露对象创建的实现细节,而是通过一个工厂类来负责创建产品类的实例
角色:
-
- 工厂角色
- 抽象产品角色
- 具体产品角色
优点: 隐藏了对象创建代码的细节,客户端不需要修改代码
缺点: 违反了单一职责原则,将创建逻辑集中到一个工厂里面,当要添加新产品时,违背了开闭原则
# -*- coding: utf-8 -*- """ @Datetime: 2019/4/17 @Author: Zhang Yafei """ from abc import ABCMeta, abstractmethod class Payment(metaclass=ABCMeta): """ 抽象产品角色 """ @abstractmethod def pay(self, money): pass class AiliPay(Payment): """ 具体产品角色 """ def __init__(self, enable_yuebao=False): self.enable_yuebao = enable_yuebao def pay(self, money): if self.enable_yuebao: print('使用余额宝支付%s元' % money) else: print('使用支付宝支付%s元' % money) class ApplePay(Payment): """ 具体产品角色 """ def pay(self, money): print('使用苹果支付支付%s元' % money) class PaymentFactory(object): """ 工厂角色 """ @staticmethod def create_payment(method): if method == 'alipay': return AiliPay() elif method == 'yuebao': return AiliPay(True) elif method == 'applepay': return ApplePay() else: return NameError p = PaymentFactory() f = p.create_payment('yuebao') f.pay(100)
二、工厂方法模式
定义: 定义一个创建对象的接口(工厂接口),让子类决定实例化哪个接口
角色:
-
- 抽象工厂角色,
- 具体工厂角色,
- 抽象产品角色,
- 具体产品角色
适用场景: 需要生产多种,大量复杂对象的时候,需要降低代码耦合度的时候,当系统中的产品类经常需要扩展的时候
优点: 每个具体的产品都对应一个具体工厂,不需要修改工厂类的代码,工厂类可以不知道它所创建的具体的类,隐藏了对象创建的实现细节
缺点: 每增加一个具体的产品类,就必须增加一个相应的工厂类
# -*- coding: utf-8 -*- """ @Datetime: 2019/4/17 @Author: Zhang Yafei """ from abc import ABCMeta, abstractmethod class Payment(metaclass=ABCMeta): """ 抽象产品角色 """ @abstractmethod def pay(self, money): pass class AiliPay(Payment): """ 具体产品角色 """ def __init__(self, enable_yuebao=False): self.enable_yuebao = enable_yuebao def pay(self, money): if self.enable_yuebao: print('使用余额宝支付%s元' % money) else: print('使用支付宝支付%s元' % money) class ApplePay(Payment): """ 具体产品角色 """ def pay(self, money): print('使用苹果支付支付%s元' % money) class PaymentFactory(object): """ 工厂角色 """ @staticmethod def create_payment(method): if method == 'alipay': return AiliPay() elif method == 'yuebao': return AiliPay(True) elif method == 'applepay': return ApplePay() else: return NameError p = PaymentFactory() f = p.create_payment('yuebao') f.pay(100) # 如果要新增支付方式 class WechatPay(Payment): def pay(self, money): print('使用微信支付%s元' % money) class WechatPayFactory(PaymentFactory): def create_payment(self): return WechatPay() w = WechatPayFactory() wc = w.create_payment() wc.pay(200)
三、抽象工厂模式
定义: 定义一个工厂类接口,让工厂子类来创建一系列相关或相互依赖的对象
角色:
-
- 抽象工厂角色
- 具体工厂角色
- 抽象产品角色
- 具体产品角色
- 客户端
适用场景:系统要独立于产品的创建和组合时,强调一系列相关产品的对象设计以便进行联合调试时,提供一个产品类库,想隐藏产品的具体实现时
优点:将客户端与类的具体实现相分离,每个工厂创建了一个完整的产品系列,易于交换产品.有利于产品的一致性
缺点:难以支持新种类的产品
# -*- coding: utf-8 -*- """ @Datetime: 2019/4/17 @Author: Zhang Yafei """ 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 Andriod(OS): def show_os(self): print('安卓系统') class IOS(OS): def show_os(self): print('iOS系统') # ------具体工厂------ class MiFactory(PhoneFactory): def make_shell(self): return BigShell() def make_os(self): return Andriod() def make_cpu(self): return SnapDragonCPU() class HuaweiFactory(PhoneFactory): def make_shell(self): return SmallShell() def make_os(self): return Andriod() def make_cpu(self): return MediaTekCPU() class AppleFactory(PhoneFactory): def make_shell(self): return AppleShell() def make_os(self): return IOS() def make_cpu(self): return AppleCPU() # ------客户端------ class Phone: def __init__(self, shell, os, cpu): self.shell = shell self.os = os self.cpu = cpu def show_info(self): print('手机信息') self.cpu.show_cpu() self.shell.show_shell() self.os.show_os() def make_phone(factory): cpu = factory.make_cpu() os = factory.make_os() shell = factory.make_shell() return Phone(shell, os, cpu) p1 = make_phone(AppleFactory()) p1.show_info()
四、建造者模式
定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
角色
-
- 抽象建造者
- 具体建造者
- 指挥者,产品
适用场景:当创建复杂对象的算法应该独立于对象的组成部分以及它的装配方式,当构造过程允许被构造的对象有不同的表示
优点: 隐藏了一个产品的内部结构和装配过程,将构造代码与表示代码分开,可以对构造过程进行更精确的控制
# -*- coding: utf-8 -*- """ @Datetime: 2019/4/17 @Author: Zhang Yafei """ from abc import abstractmethod, ABCMeta # ------产品------ 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 @abstractmethod def get_player(self): pass # ------具体建造者------ class BeautifulWoman(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 = '大长腿' def get_player(self): return self.player # ------指挥者------ class PlayerDirecter(object): @staticmethod def build_player(builder): builder.build_face() builder.build_body() builder.build_arm() builder.build_leg() return builder.get_player() director = PlayerDirecter() builder = BeautifulWoman() p = director.build_player(builder) print(p)
五、单例模式
定义:保证一个类只有一个实例,并提供一个访问它的全局访问点
适用场景:当一个类只能有一个实例而客户可以从一个众所周知的访问点访问它时
优点:对唯一实例的受控访问,相当于全局变量,但是又可以防止此变量被篡改
# -*- coding: utf-8 -*- """ DateTime : 2020/11/30 21:58 Author : ZhangYafei Description: 单例模式 """ """ 单例模式1: 单文件实例化导入 """ # my_singleton.py # # class Singleton(object): # pass # # singleton = Singleton() # # from mysingleton import singleton """ 方式二:使用装饰器,实例的地址和初始化均唯一 """ # import threading # import time # # # def Singleton(cls): # _instance = {} # threading_lock = threading.Lock() # # def _singleton(*args, **kargs): # if cls not in _instance: # with threading_lock: # if cls not in _instance: # _instance[cls] = cls(*args, **kargs) # return _instance[cls] # # return _singleton # # # @Singleton # class A(object): # def __init__(self, x=0): # time.sleep(1) # self.x = x # # # def task(arg): # obj = A(arg) # print(obj, obj.x) # # for i in range(10): # t = threading.Thread(target=task,args=[i,]) # t.start() """ 方式三:使用类: 实例地址和初始化均唯一 """ # import threading # 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(*args, **kwargs) # return Singleton._instance # # # def task(arg): # obj = Singleton.instance() # print(obj) # # # for i in range(10): # t = threading.Thread(target=task,args=[i,]) # t.start() """ 方法三:使用类 多线程情况下不是单例解决方法:加锁""" # import time # import threading # # # class Singleton(object): # _instance_lock = threading.Lock() # # def __init__(self, val): # self.val = val # time.sleep(1) # # @classmethod # def instance(cls, *args, **kwargs): # if not hasattr(Singleton, "_instance"): # with Singleton._instance_lock: # if not hasattr(Singleton, "_instance"): # Singleton._instance = Singleton(*args, **kwargs) # return Singleton._instance # # # def task(arg): # obj = Singleton.instance(arg) # print(obj, obj.val) # # for i in range(10): # t = threading.Thread(target=task,args=[i,]) # t.start() """ 方法四:基于__new__方法 实例地址唯一 可以多次初始化""" """ 4.1 单例模式: __new__ 继承 """ # import threading # import time # # # class SingleTon: # def __new__(cls, *args, **kwargs): # if not hasattr(cls, '_instance'): # # cls._instance = super(SingleTon, cls).__new__(cls) # cls._instance = object.__new__(cls) # return cls._instance # # class MyClass(SingleTon): # def __init__(self, val): # self.val = val # # # """ 4.2 单例模式: 基于自身类的__new__ """ # # # class MyClass2: # __instance = None # # def __init__(self, val): # self.val = val # # def __new__(cls, *args, **kwargs): # if not cls.__instance: # # cls.__instance = super(MyClass2, cls).__new__(cls) # cls.__instance = object.__new__(cls) # return cls.__instance # # # def task1(arg): # obj = MyClass(arg) # print(obj, obj.val) # # # def task2(arg): # obj = MyClass2(arg) # print(obj, obj.val) # # # for i in range(10): # t = threading.Thread(target=task1, args=(i, )) # t.start() # # time.sleep(1) # # for i in range(10): # t2 = threading.Thread(target=task2, args=(i, )) # t2.start() """ 方法五:元类实现单例模式,实例地址和初始化均唯一 """ import threading import time class SingletonType(type): _instance_lock = threading.Lock() def __call__(cls, *args, **kwargs): if not hasattr(cls, "_instance"): with SingletonType._instance_lock: if not hasattr(cls, "_instance"): cls._instance = super(SingletonType, cls).__call__(*args, **kwargs) return cls._instance class Foo(metaclass=SingletonType): def __init__(self,name): time.sleep(1) self.name = name def task(arg): obj1 = Foo(arg) print(obj1, obj2.name) for i in range(10): t = threading.Thread(target=task, args=(i, )) t.start()
六、适配器模式
定义:将一个接口转换为客户希望的另一个接口,该模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作
角色:
-
- 目标接口
- 待适配的类
- 适配器
适用场景:想使一个已经存在的类,但其接口不符合你的要求.想对一些已经存在的子类.不可能每一个都是用子类来进行适配,对象适配器可以适配其父类接口
# -*- coding: utf-8 -*- """ @Datetime: 2019/4/17 @Author: Zhang Yafei """ 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 fuqian(self, money): print('微信支付%s元' % money) # ------类适配器------ class RealWeChatPay(Payment, WeChatPay): def pay(self, money): return self.fuqian(money) # -----对象适配器----- class PayAdapter(Payment): def __init__(self, payment): self.payment = payment def pay(self, money): return self.payment.fuqian(money) # RealWeChatPay().pay(100) p = PayAdapter(WeChatPay()) p.pay(200)
七、桥模式
内容:将一个事物的两个维度分离,使其都可以独立地变化。
角色:
-
- 抽象(Abstraction)
- 细化抽象(RefinedAbstraction)
- 实现者(Implementor)
- 具体实现者(ConcreteImplementor)
应用场景:
-
- 当事物有两个维度上的表现,两个维度都可能扩展时。
优点:
-
- 抽象和实现相分离
- 优秀的扩展能力
# -*- coding: utf-8 -*- """ DateTime : 2020/12/01 12:32 Author : ZhangYafei Description: 桥模式 内容:将一个事物的两个维度分离,使其都可以独立地变化。 角色: 抽象(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()
八、组合模式
定义:将对象组合成树形结构以表示'部分-整体'的层次结构.组合模式使得用户对单个对象和组合对象的使用具有一致性
角色:
-
- 抽象组件
- 叶子组件
- 复合组件
- 客户端
适用场景:表示对象的'部分-整体'层次结构,希望用户忽略组合对象与单个对象的不同,用户统一使用组合结构中的所有对象
优点:定义了包含基本对象和组合对象的类层次结构,简化客户端代码,即客户端可以一致的使用组合对象和单个对象,更容易新增新类型的组件
缺点:很难限制组合中的组件
# -*- coding: utf-8 -*- """ @Datetime: 2019/4/17 @Author: Zhang Yafei """ from abc import abstractmethod, ABCMeta # -------抽象组件-------- class Graph(metaclass=ABCMeta): @abstractmethod def draw(self): pass @abstractmethod def add(self, graph): pass def get_children(self): pass # ---------叶子组件-------- class Point(Graph): def __init__(self, x, y): self.x = x self.y = y def draw(self): print(self) def add(self, graph): raise TypeError def get_children(self): raise TypeError def __str__(self): return '点(%s,%s)' % (self.x, self.y) class Line(Graph): def __init__(self, p1, p2): self.p1 = p1 self.p2 = p2 def draw(self): print(self) def add(self, graph): raise TypeError def get_children(self): raise TypeError def __str__(self): return '线段(%s,%s)' % (self.p1, self.p2) # --------复合组件--------- class Picture(Graph): def __init__(self): self.children = [] def add(self, graph): self.children.append(graph) def get_children(self): return self.children def draw(self): print('-----复合图形-----') for g in self.children: g.draw() print('结束') # ---------客户端--------- pic1 = Picture() point = Point(2, 3) pic1.add(point) 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) pic.draw()
九、外观模式
内容:为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
角色:
-
- 外观(facade)
- 子系统类(subsystem classes)
优点:
-
- 减少系统相互依赖
- 提高了灵活性
- 提高了安全性
# -*- coding: utf-8 -*- """ DateTime : 2020/12/01 12:45 Author : ZhangYafei Description: 外观模式 内容:为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。 角色: 外观(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()
十、代理模式
定义:为其他对象提供一种代理以控制对特定对象的访问
角色:
-
- 抽象实体
- 实体
- 代理
适用场景:远程代理(为远程的对象提供代理),虚代理(根据需要创建很大的对象,即懒加载),保护代理(控制对原始对象的访问,用于具有不同访问权限的对象)
优点:远程代理(可以隐藏对象位于远程地址空间的事实),虚代理(可对大对象的加载进行优化),保护代理(允许在访问一个对象时有一些附加的处理逻辑,例如权限控制)
# -*- coding: utf-8 -*- """ DateTime : 2020/12/01 12:47 Author : ZhangYafei Description: 代理模式 内容:为其他对象提供一种代理以控制对这个对象的访问。 应用场景: 远程代理:为远程的对象提供代理 虚代理:根据需要创建很大的对象 保护代理:控制对原始对象的访问,用于对象有不同访问权限时 角色: 抽象实体(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 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")
十一、观察者模式
定义:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都会得到通知并被自动更新.观察者模式又称为'发布订阅'模式
角色:
-
- 抽象主题
- 具体主题(发布者)
- 抽象观察者
- 具体观察者(订阅者)
适用场景:
-
- 当一个抽象模型有两个方面,其中一个方面依赖于另一个方面.将两者封装在独立的对象中以使它们各自独立的改变和复用
- 当一个对象的改变需要同时改变其他对象,而且不知道具体有多少对象以待改变
- 当一个对象必须通知其他对象,而又不知道其他对象是谁,即这些对象之间是解耦的
优点:目标和观察者之间的耦合最小,支持广播通信
缺点:多个观察者之间互不知道对方的存在,因此一个观察者对主题的修改可能造成错误的更新
# -*- coding: utf-8 -*- """ @Datetime: 2019/4/17 @Author: Zhang Yafei """ from abc import ABCMeta, abstractmethod # 抽象主题 class Oberserver(metaclass=ABCMeta): @abstractmethod def update(self): 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(Oberserver): def __init__(self): self.company_info = None def update(self, noti): self.company_info = noti.company_info # 消息订阅-发送 notice = ManagerNotice() alex = Manager() tony = Manager() notice.attach(alex) notice.attach(tony) notice.company_info = "公司运行良好" print(alex.company_info) print(tony.company_info) notice.company_info = "公司将要上市" print(alex.company_info) print(tony.company_info) notice.detach(tony) notice.company_info = "公司要破产了,赶快跑路" print(alex.company_info) print(tony.company_info)
十二、策略模式
定义:定义一系列的算法把它们一个个封装起来,并且使它们可相互替换.该模式使得算法可独立于使用它的客户而变化
角色:
-
- 抽象策略
- 具体策略
- 上下文
适用场景:许多相关的类仅仅是行为有异,需使用一个算法的不同变体,算法使用了客户端无需知道的数据,一个类中的多个行为以多个条件语句存在可以将其封装在不同的策略类中
优点:定义了一系列可重用的算法和行为,消除了一些条件语句,可提供相同行为的不同实现
缺点:客户必须了解不同的策略,策略与上下文之间的通信开销,增加了对象的数目
# -*- coding: utf-8 -*- """ @Datetime: 2019/4/17 @Author: Zhang Yafei """ import random from abc import ABCMeta, abstractmethod # 抽象策略 class Sort(metaclass=ABCMeta): @abstractmethod def sort(self, data): pass # 具体策略 class QuickSort(Sort): def quick_sort(self, data, left, right): if left < right: mid = self.partation(data, left, right) self.quick_sort(data, left, mid - 1) self.quick_sort(data, mid + 1, right) def partation(self, data, left, right): tmp = data[left] while left < right: while left < right and data[right] >= tmp: right -= 1 data[left] = data[right] while left < right and data[left] <= tmp: left += 1 data[right] = data[left] data[left] = tmp return left def sort(self, data): print("快速排序") return self.quick_sort(data, 0, len(data) - 1) class MergeSort(Sort): def merge(self, data, low, mid, high): i = low j = mid + 1 ltmp = [] while i <= mid and j <= high: if data[i] <= data[j]: ltmp.append(data[i]) i += 1 else: ltmp.append(data[j]) j += 1 while i <= mid: ltmp.append(data[i]) i += 1 while j <= high: ltmp.append(data[j]) j += 1 data[low:high + 1] = ltmp def merge_sort(self, data, low, high): if low < high: mid = (low + high) // 2 self.merge_sort(data, low, mid) self.merge_sort(data, mid + 1, high) self.merge(data, low, mid, high) def sort(self, data): print("归并排序") return self.merge_sort(data, 0, len(data) - 1) # 上下文 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 li = list(range(100000)) random.shuffle(li) context = Context(li, MergeSort()) context.do_strategy() random.shuffle(context.data) context.set_strategy(QuickSort()) context.do_strategy()
# -*- coding: utf-8 -*- """ DateTime : 2020/12/01 13:03 Author : ZhangYafei Description: 策略模式 内容:定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。 角色: 抽象策略(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()
十三、责任链模式
定义:使多个对象有机会处理请求,从而避免请求的发布者和接收者之间的耦合关系,将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象能处理它为止
角色:
-
- 抽象处理者
- 具体处理者
- 客户端
适用场景:有多个对象可以处理一个请求,哪个对象处理由运行时决定
优点:降低耦合度,一个对象无需知道是其他哪一个对象处理其请求
缺点:请求不保证被接收,链的末端没有处理或链配置错误
# -*- coding: utf-8 -*- """ @Datetime: 2019/4/17 @Author: Zhang Yafei """ 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()
# -*- coding: utf-8 -*- """ DateTime : 2020/12/01 12:55 Author : ZhangYafei Description: 责任链模式 内容:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。 角色: 抽象处理者(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 = 12 h = ProjectDirector() h.handle_leave(day)
十四、迭代器模式
定义:提供一种方法可顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部指示
适用场景:实现方法__iter__,__next__
# -*- coding: utf-8 -*- """ @Datetime: 2019/4/17 @Author: Zhang Yafei """ class LinkedList: class Node: def __init__(self, item=None): self.item = item self.next = None class LinkedListIterator: def __init__(self, node): self.node = node # 实现next方法,返回下一个元素 def __next__(self): if self.node: cur_node = self.node self.node = cur_node.next return cur_node.item def __iter__(self): return self def __init__(self, iterable=None): self.head = LinkedList.Node(0) self.tail = self.head self.extend(iterable) # 链表尾部追加元素 def append(self, obj): s = LinkedList.Node(obj) self.tail.next = s self.tail = s # 链表自动增加长度 def extend(self, iterable): for obj in iterable: self.append(obj) self.head.item += len(iterable) def __iter__(self): return self.LinkedListIterator(self.head.next) def __len__(self): return self.head.item def __str__(self): return '<<' + ', '.join(map(str, self)) + '>>' li = [i for i in range(100)] lk = LinkedList(li) print(lk)
补充:
-
可以使用
collection.abs
里面的Iterator
和Iterable
配合isinstance
函数来判断一个对象是否是可迭代的,是否是迭代器对象 -
iter
实际是映射到了__iter__
函数 -
只要实现了
__iter__
的对象就是可迭代对象(Iterable
),正常情况下,应该返回一个实现了__next__
的对象(虽然这个要求不强制),如果自己实现了__next__
,当然也可以返回自己 -
同时实现了
__iter__
和__next__
的是迭代器(Iterator
),当然也是一个可迭代对象了,其中__next__
应该在迭代完成后,抛出一个StopIteration
异常 -
for
语句会自动处理这个StopIteration
异常以便结束for
循环
十五、模板方法模式
定义:定义一个操作中算法的骨架,将一些步骤延迟到子类中,模板方法使得子类可以不改变一个算法的结构即可重定义该算法某些特定的步骤
角色:
-
- 抽象类(定义抽象的原子操作,实现一个模板方法作为算法的骨架)
- 具体类(实现原子操作)
适用场景:一次性实现一个算法不变的部分,各个子类的公共行为,应该被提取出来集中到公共的父类中以避免代码重复,控制子类扩展
# -*- coding: utf-8 -*- """ @Datetime: 2019/4/17 @Author: Zhang Yafei """ 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() # 子类中只需要实现部分算法,而不需要实现所有的逻辑 # -----具体类-------- class FileHandler(IOHandler): def open(self, name): self.file = open(name, 'w') def deal(self, change): self.file.write(change) def close(self): self.file.close() f = FileHandler() f.process('abc.txt', 'hello')
# -*- coding: utf-8 -*- """ DateTime : 2020/12/01 13:06 Author : ZhangYafei Description: 内容:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。 角色: 抽象类(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()