设计模式分类
- 创建型模式(5种):工厂方法模式,抽象工厂模式,建造者模式,原型模式,单例模式。
- 结构型模式(7种):适配器模式,桥模式,组合模式,装饰模式,外观模式,享元模式,代理模式。
- 行为型模式(11种):解释器模式,责任链模式,命令模式,迭代器模式,中介者模式,备忘录模式,观察者模式,状态模式,策略模式,访问者模式,模板方法模式。
创建型模式
-
工厂方法模式
内容:定义一个用于创建对象的接口,让子类决定实例化哪一个产品类。
from abc import ABCMeta, abstractmethod class Payment(metaclass=ABCMeta): @abstractmethod def pay(self, money): pass class AliPay(Payment): def pay(self, money): print(f"ali pay {money} yuan") class WechatPay(Payment): def pay(self, money): print(f"wechat pay {money} yuan") class PaymentFactory(metaclass=ABCMeta): """定义抽象工厂类""" def create_payment(self): pass class AliPayFactory(PaymentFactory): """具体工厂类""" def create_payment(self): return AliPay() class WechatFactory(PaymentFactory): """具体工厂类""" def create_payment(self): return WechatPay() pf = WechatFactory() p = pf.create_payment() p.pay(100)
关键代码:创建过程在其子类执行。
优点:扩展性高,每个具体产品都对应一个具体工厂类,要增加一个产品,只要扩展一个具体工厂类就可以;隐藏了对象创建的实现细节;
缺点:每增加一个具体产品类,就必须增加一个相应的具体工厂类。
主要解决:主要解决接口选择的问题。
何时使用:我们明确地计划不同条件下创建不同实例时。
使用场景:
- 数据库访问:当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。
- 设计一个连接服务器的框架,需要三个协议,“POP3”、“IMAP”、“HTTP”,可以把这三个作为产品类,共同实现一个接口。
-
抽象工厂模式
内容:定义一个工厂类接口,让工厂子类来创建一系列相互依赖的对象。
相比工厂方法模式,抽象工厂模式中的每个具体工厂都生产一套产品。from abc import ABCMeta, abstractmethod # 抽象产品 class Shell(metaclass=ABCMeta): @abstractmethod def show_shell(self): pass 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_shell(self): pass @abstractmethod def make_os(self): pass @abstractmethod def make_cpu(self): pass # 具体产品 class SmallShell(Shell): def show_shell(self): print(f"shell: small shell") class BigShell(Shell): def show_shell(self): print(f"shell: big shell") class AppleShell(Shell): def show_shell(self): print(f"shell: apple shell") class IOS(OS): def show_os(self): print(f"os: apple os") class Android(OS): def show_os(self): print(f"os: Android os") class HongMeng(OS): def show_os(self): print(f"os: HongMeng os") class GaoTongCPU(CPU): def show_cpu(self): print("cpu: gaotong xiaolong cpu") class QiLinCPU(CPU): def show_cpu(self): print("cpu: huawei qili cpu") class AppleCPU(CPU): def show_cpu(self): print("cpu: apple cpu") # 具体工厂 class HUAWEIFactory(PhoneFactory): def make_cpu(self): return QiLinCPU() def make_os(self): return HongMeng() def make_shell(self): return SmallShell() class APPLEFactory(PhoneFactory): def make_cpu(self): return AppleCPU() def make_os(self): return IOS() def make_shell(self): return BigShell() # 客户端 class Phone: def __init__(self, cpu, os, shell): self.cpu = cpu self.os = os self.shell = shell def show_info(self): print("phone info") 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) ph = make_phone(HUAWEIFactory()) ph.show_info()
关键代码:在一个工厂里聚合多个同类产品。
优点:将客户端与类的具体实现相互分离;每个工厂都创建的有一个完整的产品系列;产品之间的约束关系强。
缺点:新产品拓展非常困难。
使用场景:
- QQ 换皮肤,一整套一起换。
- 生成不同操作系统的程序。
-
建造者模式
内容:建造者模式与抽象工厂模式类似,也用来创建复杂对象。主要区别是建造者模式着重一步步构造一个复杂对象,而抽象工厂模式着重于多个系列的产品对象。
from abc import ABCMeta, abstractmethod class Player: def __init__(self, body=None, face=None, arm=None): self.body = body self.face = face self.arm = arm def __str__(self): return f"{self.body}, {self.face}, {self.arm}" class PlayerBuild(metaclass=ABCMeta): @abstractmethod def build_body(self): pass @abstractmethod def build_face(self): pass @abstractmethod def build_arm(self): pass class Monster(PlayerBuild): def __init__(self): self.player = Player() def build_arm(self): print("running build monster arm") self.player.arm = "monster arm" def build_face(self): print("running build monster face") self.player.face = "monster face" def build_body(self): print("running build monster body") self.player.body = "monster body" # 创建复杂对象 class Beauty(PlayerBuild): def __init__(self): self.player = Player() def build_arm(self): print("running build beauty arm") self.player.arm = "beauty arm" def build_face(self): print("running build beauty face") self.player.face = "beauty face" def build_body(self): print("running build beauty body") self.player.body = "beauty body" # 对创建过程进行顺序控制 class BuildPlayerDirector: def build_player(self, builder): builder.build_body() builder.build_face() builder.build_arm() return builder.player builder = Beauty() director = BuildPlayerDirector() player = director.build_player(builder) print(player)
关键代码:1.创建和提供实例,2.管理建造出来的实例的依赖关系。
优点:隐藏产品的内部结构和装配过程;将构造代码与表示代码分开;可以对构造过程进行更精细的控制。
使用场景: 1.需要生成的对象具有复杂的内部结构,2.需要生成的对象内部属性本身相互依赖。
-
单例模式
保证一个类只有唯一的一个实例,并提供一个访问它的全局访问点。
class Singleton: def __new__(cls, *args, **kwargs): """ 在内存中为对象分配空间, 返回对象的引用 """ if not hasattr(cls, "_instance"): cls._instance = super().__new__(cls) return cls._instance class NewFoo(Singleton): def __init__(self, val): self.val = val a = NewFoo(10) b = NewFoo(20) print(a.val, id(a)) # 20 2530103944592 print(b.val, id(b)) # 20 2530103944592
优点:单例相当于全局变量,防止了命名空间被污染;减小内存和资源的开销。
主要解决:一个全局使用的类频繁地创建与销毁。
使用场景:要求生产唯一序列号;创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。
结构型模式
-
适配器模式
内容: 将一个类的接口转换成客户希望的另一个接口,即为了统一接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
from abc import ABCMeta, abstractmethod class BasePay(metaclass=ABCMeta): @abstractmethod def pay(self, money): pass class AliPay(BasePay): def pay(self, money): print(f"ali pay {money} yuan") class WechatPay(BasePay): def pay(self, money): print(f"wechat pay {money} yuan") class BankPay: def cost(self, money): print(f"bank pay {money} yuan") ali = AliPay() ali.pay(100) bank = BankPay() bank.cost(100) 一个使用的是 pay(), 另一个使用的是cost(),接口不统一,无法整合系统。
两种实现方式:
- 类适配器:要适配的接口较少的情况下使用。
from abc import ABCMeta, abstractmethod class BasePay(metaclass=ABCMeta): @abstractmethod def pay(self, money): pass class AliPay(BasePay): def pay(self, money): print(f"ali pay {money} yuan") class WechatPay(BasePay): def pay(self, money): print(f"wechat pay {money} yuan") class BankPay: def cost(self, money): print(f"bank pay {money} yuan") # 适配不规范接口类 class NewBankPay(BasePay, BankPay): def pay(self, money): self.cost(money) print(f"bank pay {money} yuan") ali = AliPay() ali.pay(100) bank = NewBankPay() bank.pay(100)
- 对象适配器:如果很多个类的接口都不规范
from abc import ABCMeta, abstractmethod class BasePay(metaclass=ABCMeta): @abstractmethod def pay(self, money): pass class AliPay(BasePay): def pay(self, money): print(f"ali pay {money} yuan") class WechatPay(BasePay): def pay(self, money): print(f"wechat pay {money} yuan") class BankPay: def cost(self, money): print(f"bank pay {money} yuan") class ApplePay: def cost(self, money): print(f"apple pay {money} yuan") class PaymentAdapter(BasePay): def __init__(self, paytype): self.paytype = paytype def pay(self, money): self.paytype.cost(money) ali = AliPay() ali.pay(100) bank_adp = PaymentAdapter(BankPay()) bank_adp.pay(100) apple_adp = PaymentAdapter(ApplePay()) apple_adp.pay(200)
优点: 1.可以让任何两个没有关联的类一起运行,2.提高了类的复用, 3.灵活性好。
缺点: 过多地使用适配器,会让系统非常零乱,不易整体进行把握。
-
装饰器模式
装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。
-
代理模式
为其他对象提供一种代理,用来控制对这个对象的访问。
应用场景:远程代理(为远程对象提供代理);虚代理(根据需要创建资源消耗多的对象);保护代理(控制对原始对象的访问,用于对象有不同的访问权限)虚代理:
虚代理:用虚代理减少实例化时资源消耗多的操作。 import json from abc import ABCMeta, abstractmethod class Subject(metaclass=ABCMeta): @abstractmethod def get_context(self): pass @abstractmethod def set_context(self, context): pass class RealSubject(Subject): def __init__(self, file=None): # 在实例化时加载大文件,消耗资源 self.file = file with open(file, "r") as f: data = f.read() print("read big big big file") self.context = list("abcde") def get_context(self): return self.context def set_context(self, context): print("write big big big data to file") # 虚代理:在实例化时先不用加载大文件,等真正get_context用到的时候再实例化真实的对象(真正加载文件) class VirtualProject(Subject): def __init__(self, file=None): self.file = file self.sub = None def get_context(self): if not self.sub: self.sub = RealSubject(self.file) return self.sub.get_context() def set_context(self, context): if not self.sub: self.sub = RealSubject(self.file) return self.sub.set_context(context) virt = VirtualProject() rirt.get_context()
保护代理:
import json from abc import ABCMeta, abstractmethod class Subject(metaclass=ABCMeta): @abstractmethod def get_context(self): pass @abstractmethod def set_context(self, context): pass class RealSubject(Subject): def __init__(self, file=None): self.file = file print("read big big big file") self.context = list("abcde") def get_context(self): return self.context def set_context(self, context): print("write big big big data to file") # 保护代理 class ProtectProject(Subject): def __init__(self, file=None): self.sub = RealSubject(file) def get_context(self): return self.sub.get_context() def set_context(self, context=None): raise PermissionError("no write permission!") prot = ProtectProject() prot.get_context() prot.set_context()
优点:
远程代理:隐藏对象位于远程地址空间的事实。
虚代理:进行优化,根据实际要求创建资源消耗大的对象。
保护代理:允许在访问一些对象时有一些附加的处理。
行为型模式
-
观察者模式
用来定义对象间的一种一对多的依赖关系,当一个对象的状态发生变化时,所有依赖它的对象都得到通知并自动更新。观察者模式又称为 “发布—订阅” 模式。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): return self.__company_info @company_info.setter def company_info(self, 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 = StaffNotice("初始化公司信息") s1 = Staff() s2 = Staff() notice.attach(s1) notice.attach(s2) notice.company_info = "公司发布消息11111" print(s1.company_info) print(s2.company_info)
适用场景:当一个对象的改变需要同时改变其他对象,而不知道具体有多少对象有待改变。
优点:目标和观察者之间的抽象耦合最小;支持广播通信。 -
策略模式
定义一系列的算法,把它们一个个封装起来,并且使他们可以相互替换。这种模式可以使得不同的算法按照业务的变化而变化。from abc import ABCMeta, abstractmethod class Strategy(metaclass=ABCMeta): @abstractmethod def execute(self, data): pass class FastStrategy(Strategy): def execute(self, data): print("fast execute") class SlowStrategy(Strategy): def execute(self, data): print("slow execute") class Context: def __init__(self, data, strategy): self.data = data self.strategy = strategy def set_strategy(self, strategy): # 切换算法或者策略 self.strategy = strategy def do_strategy(self): self.strategy.execute(self.data) data = "..." st1 = FastStrategy() context = Context(data, st1) context.do_strategy() st2 = SlowStrategy() context.set_strategy(st2) context.do_strategy()
优点:定义一些列可重用的算法或行为;提供相同行为的不同实现。
缺点:必须了解业务的不同策略。