设计模式(python描述)
目录
- 一、创建型设计模式
- 二、结构型设计模式
- 三、行为型设计模式
一、创建型设计模式
1、简单工厂模式
内容:
不直接向高层类暴露对象创建的实现细节,而是通过一个工厂类来负责创建产品类的实例。
角色:
- 工厂角色(Creator)
- 抽象产品角色(Product)
- 具体产品角色(Concrete Product)
python示例:
from abc import ABCMeta, abstractmethod class Payment(metaclass=ABCMeta): """ 抽象产品角色 # 抽象类,必须子类必须实现这个方法。 """ @abstractmethod def pay(self, money): pass class Alipay(Payment): """ 具体产品角色 """ def __init__(self, huabei=False): self.huabei = huabei def pay(self, money): if slelf.huabei: 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(huabei=True) else: rasie TypeError('No such class named %s' % method) pf = PaymentFactory() p = pf.create_payment('huabei') p.pay(100)
优点:
- 隐藏了对象创建的实现细节
- 高层类不需要修改代码
缺点:
- 违反了单一职责原则,将创建逻辑集中到一个工厂类里
- 当添加新产品时,需要修改工厂类代码,违反了开闭原则
例如,如果现在有一个ApplePayment的pay接口,需要支付,那么就必须改动工厂角色的代码,使之能支持新的扩展。
class ApplePay(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 ApplePay(huabei=True) elif method == 'apple': return Alipay() else: rasie TypeError('No such class named %s' % method)
这样,明显违背了开闭原则,以及单一职责原则。对程序扩展是十分不便的。
2、工厂方法模式
内容:
定义一个用于创建对象的接口(工厂接口),让子类决定实例化哪一个产品类。
角色:
- 抽象工厂角色(Creator)
- 具体工厂角色(Concrete Creator)
- 抽象产品角色(Product)
- 具体产品角色(Concrete Product)
python示例:
from abc import ABCMeta, abstractmethod class Payment(metaclass=ABCMeta): """ 抽象产品角色 # 抽象类,必须子类必须实现这个方法。 """ @abstractmethod def pay(self, money): pass class Alipay(Payment): """ 具体产品角色 """ def __init__(self, huabei=False): self.huabei = huabei def pay(self, money): if slelf.huabei: print("花呗支付%d元" % money) else: print("支付宝支付%d元" % money) class WechatPay(Payment): def pay(self, money): print("微信支付%d元" % money) class PaymentFactory(metaclass=ABCMeta): @abstractmethod def create_payment(self, method): pass class AlipayFactory(PaymentFactory): def create_payment(self): return Alipay() class WechatFactory(PaymentFactory): def create_payment(self): return Wechatpay() class HuaBeiFactory(PaymentFactory): def create_payment(self): return Alipay(huabei=True) hf = HuaBeiFactory() p = hf.create_payment() p.pay(100)
工厂方法模式,使原来的工厂角色,细分称为了抽象工厂角色,和具体工厂角色。这样,符合单一职责原则,一个工厂方法只做了一件事。并且也符合开闭原则,如果要扩展新的支付接口,只需要重新写一个具体产品角色,和具体工厂角色。
class ApplePay(Payment): def pay(self, money): print("苹果支付%d元" % money) class AppleFactory(PaymentFactory): def create_payment(self): return ApplePay()
优点:
- 每个具体产品都对应一个具体工厂类,不需要修改工厂类代码
- 隐藏了对象创建的实现细节
缺点:
- 每增加一个具体产品类,就需要增加一个相应的具体工厂类
如果要是接口很多的话,这样就会有很多的类出现在代码中。
3、抽象工厂模式
内容:
定义一个工厂类接口,让工厂子类来创建一系列相关或者相互依赖的对象。
角色:
- 抽象工厂角色
- 具体工厂角色
- 抽象产品角色
- 具体产品角色
- 客户端(高层模块)
python示例:
生产一部手机,需要手机壳、CPU、操作系统三类对象进行组装,其中每类对象都有不同的种类。对每个具体工厂,分别生产一部手机所需要的三个对象。
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): ''' 抽象工厂 ''' @abstratmethod def make_shell(self): pass @abstratmethod def make_cpu(self): pass @abstratmethod def make_os(self): pass # 具体产品角色 class SmallShell(PhoneShell): def show_shell(slef): print("普通手机小手机壳") class BigShell(PhoneShell): def show_shell(slef): print("普通手机大手机壳") class AppleShell(PhoneShell): def show_shell(slef): print("苹果手机壳") class SnapDragonCPU(CPU): def show_cpu(slef): print("骁龙CPU") class MediaTekCPU(CPU): def show_cpu(slef): print("联发科CPU") class AppleCPU(CPU): def show_cpu(slef): print("苹果手机CPU") class Andriod(OS): def show_os(slef): print("Andriod系统") class IOS(OS): def show_os(slef): print("IOS系统") # 具体工厂角色 class MiFactory(PhoneFactory): def make_shell(self): return BigShell() def make_cpu(self): return SnapDragonCPU() def make_os(self): return Android() class HuaweiFactory(PhoneFactory): def make_shell(self): return SmallShell() def make_cpu(self): return MediaTekCPU() def make_os(self): return Android() class IPhone(PhoneFactory): def make_shell(self): return AppleShell() def make_cpu(self): return AppleCPU() def make_os(self): return IOS() # 高层模块/客户端 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(MiFactory()) p1.show_info()
优点:
- 将客户端与类的具体实现相分离
- 每个工厂创建了一个完整的产品系列,使得易于交换产品系列
- 有利于产品的一致性(即产品之间的约束关系)
缺点:
- 难以支持新种类的(抽象)产品
如果我的手机现在不只是由手机壳、CPU、操作系统组成,还有硬盘,那么就必须对抽象工厂和具体工厂类进行修改来支持新的产品。
4、建造者模式
内容:
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
角色:
- 抽象建造者
- 具体建造者
- 指挥者
- 产品
python示例:
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. %sm. %s, %s' % (self.face, self.body, self.arm ,self.leg) # 抽象建造者 class PlayerBuild(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(PlayerBuild): 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(PlayerBuild): 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 PlayerDiector: # 控制组装顺序 def build_play(self, builder): builder.build_body() builder.build_face() builder.build_arm() builder.build_leg() return builder.player # 产品 builder = SexyGirlBuilder() director = PlayerDirector() p = director.build_player(builder) print(p)
建造者模式与抽象工厂模式相似,用是用来创建复杂对象,主要区别是建造者模式着重一步步构造一个复杂对象,而抽象工厂模式着重于多个系列的产品对象。
优点:
- 隐藏了一个产品的内部结构和装配过程
- 将构造代码与表示代码分开
- 可以对构造过程进行更精细的控制
5、单例模式
内容:
保证一个类只有一个实例,并提供一个访问它的全局访问点。
角色:
- 单例
python示例:
class Singleton: def __new__(cls, *args, **kwargs): if not hasattr(cls, '__instance'): cls._instence = super(Singleton, cls).__new__(cls): return cls._instance class MyClass(Singleton): def __init__(self): self.a = a a = MyClass(10) b = MyClass(20)
class SingleTon(type): def __init__(self, classname, fatherclass, dic): super(SingleTon, self).__init__(classname, fatherclass, dic) def __call__(self, *args, **kwargs): if not hasattr(self, 'singleton'): obj = object.__new__(self) self.singleton = self.__init__(obj, *args, **kwargs) return self.singleton class Person(metaclass=SingleTon): def __init__(self): self.name = 'walle' p = Person() p1 = Person() print(id(p)) print(id(p1))
优点:
- 对唯一实例的受控访问
- 单例相当于全局变量,但防止了命名空间被污染。
二、结构型设计模式
1、适配器模式
内容:
将一个类的接口转化成客户希望的另一个接口。适配器模式使得原本由于接口不兼容而不能再一起工作的那些类可以在一起工作。
角色:
- 目标接口
- 带适配的类
- 适配器
实现方式:
- 类适配器(使用多继承)
- 对象适配器(使用组合)
python示例:
还是支付接口,如果银联支付的接口与其他支付的接口不一样。当两个系统合并的时候,就会出现问题。因此,需要一个适配器将接口进行统一。
from abc import ABCMeta, abstractmethod class Payment(metaclass=ABCMeta): """ 抽象产品角色 # 抽象类,必须子类必须实现这个方法。 """ @abstractmethod def pay(self, money): pass class Alipay(Payment): """ 具体产品角色 """ def __init__(self, huabei=False): self.huabei = huabei def pay(self, money): print("支付宝支付%d元" % money) class WechatPay(Payment): def pay(self, money): print("微信支付%d元" % money) class BankPay: def cost(self, money): print("银联支付%d元" % money) class ApplePay: def cost(self, money): print("苹果支付%d元" % money) # 类适配器,通过类的继承来完成代码复用,有局限性。 class NewBankPay(Payment, BankPay): def pay(self, money): self.cost() # 对象适配器,通过组合来完成代码复用,推荐。 class PaymentAdapter(Payment): def __init__(self, payment): self.payment = payment def pay(self, money): self.payment.cost(money) # p = Alipay() p = NewBankPay() p.pay(100)
当然使用组合适配器更加的灵活与反方便。
使用场景:
- 想使用一个已经存在的类,但是它的接口不符合你的要求
- (对象适配器)想使用一些已经存在的子类,但是不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配它的父类接口。
2、桥模式
内容:
将一个事物的两个维度分类,使其都可以独立地变化。
角色:
- 抽象(Abstraction)
- 细化抽象(RefinedAbstraction)
- 实现者
- 具体实现者
python示例:
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 Cirlce(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) shape = Rectangle(Red()) shape.draw() shape2 = Circle(Green()) shape2.draw()
这种利用组合的结构是一种松耦合的方式,如果要扩展颜色,或者形状,只需要加上对应的类就可以了。有就是说在这两个维度上都可以进行任意的扩展。不用添加代码,或者修改代码。
class Line(Shape): name = '直线' def draw(self): self.color.paint(self) class Blue(color): def paint(self, shape): print("蓝色的%s"%shape.name)
应用场景
- 当事物有两个维度上的表现,两个维度都可能扩展的时候。
优点:
- 抽象和实现相分离
- 优秀的扩展能力
3、组合模式
内容:
将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
角色:
- 抽象组件
- 叶子组件
- 复合组件
- 高层模块
python示例
from abc import ABCMeta, abstractmethod # 抽象组件 class Graphic(metaclass=ABCMeta): @abstractmethod def draw(self): pass # 叶子组件 class Point(Graphic): def __init__(self): 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): 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.draw() p2 = Poine(4, 4) l3 = Line(Point(1, 1), Point(1, 0)) pic2 = Picture([p2, l2]) pic = Picture([pic1, pic2]) pic.draw()
使用场景:
- 表示对象的"部分-整体"层次结构(特别是结构是递归的时候)
- 希望用户忽略组合对象与单个对象的不同,用户统一地使用组合结构中的所有对象。
优点:
- 定义了包含基本对象和组合对象的类层次结构
- 简化高层模块代码,即高层模块可以一致地使用组合对象和单个对象
- 更容易增加新类型的组件
4、外观模式
内容:
为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
角色:
- 外观
- 子系统类
python示例
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: 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() computer = Computer() computer.run() computer.stop()
优点:
- 减少了系统的相互依赖
- 提高了灵活性
- 提高了安全性
5、代理模式
内容:
为其他对象提供一种代理以控制对这个对象的访问。
角色:
- 抽象实体
- 实体
- 代理
应用场景:
- 远程代理:为远程的对象提供代理。数据不在本地系统中存在,在远程的服务器中存在,这个时候就需要写一个远程代理的类,这个类向远程对象请求。
- 虚代理:根据需要创建很大的对象。浏览器的无图模式,如果我不需要看这个对象,显示的是一个小方框,只有当我需要的时候,才会根据这个小方框去请求数据。根据需要创建对象,或者访问拿到对象。
- 保护代理:控制对原始对象的访问,用于对象有不同访问权限时。对于一个对象,访问权限是有限制的。例如:普通用户和管理员的权限不一样,普通用户只有读的权限,管理员用户有读写的权限。这样就可以为用户提供一个保护代理,如果你是普通用户只提供读的权限,如果你是管理员用户,提供读写权限。
python示例
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) print("读取数据") self.content = f.read() f.close() def get_content(self): return self.content def set_content(self): f = open(self.filename, 'w') f.write(content) f.close() # 当文件很大,不用虚代理,当创建对象的时候,会占用很大的内存,即使不执行get_content(),内存也会被占用。 class VirtualProxy(Subject): """ 虚代理 """ def __init__(self, filename): self.filename = filename self.suj = None def get_content(self): if not self.subj: self.subj = RealSubject(self.filename) return self.subj.get_content() def set_content(self): if not self.subj: self.subj = RealSubject(self.filename) return self.subj.set_content() subj = RealSubject('test.txt') #创建对象的时候就需要将数据读取到内存。 subj.get_content() subj1 = VirtualProxy('test.txt') #创建对象的时候只需要放一个变量。 subj1.get_content() class ProtectProxy(Subject): def __init__(self, filename): self.subj = RealSubject(filename) def get_content(self): return self.subj.get_content() def set_content(self, content): rasise PermisssionError("无写入权限") subj = ProtectProxy('test.text') subj.set_content('abc')
优点:
- 远程代理:可以隐藏对象位于远程地址空间的事实
- 虚代理:可以进行优化,例如根据要求创建对象
- 保护代理:允许在访问一个对象时有一些附加的内务处理
三、行为型设计模式
1、责任链模式
内容:
使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
角色:
- 抽象处理者
- 具体处理者
- 高层模块
python示例
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 dat < 10: print("总经理准假%d天"%day) else: print("你还是辞职吧") class DepartmentManager(Handler): def __init__(self): self.next = GeneralManager() def handler_leave(self, day): if day <= 5: print("部门经理准假%d天" % day) else: print("部门经理职权不足") self.next.handle_leave(day) class ProjectDirector(Hadnler): def __init__(self): self.next = DepartmentManager() def handler_leave(self, day): if day <= 1: print("项目主管准假%d天" % day) else: print("项目主管职权不足") self.next.handler_leave(day) day = 4 h = ProjectDirctor() h.handle_leave(day)
使用场景:
- 有多个对象可以处理一个请求,哪个对象处理由运行时决定
- 在不明确接收者的情况下,向多个对象中的一个提交一个请求
优点:
- 降低耦合度:一个对象无需知道是其他哪一个对象处理其请求
scrapy的pipline的实现就是用的责任链模式。
2、观察者模式
内容:
定义对象间的一种一对多的依赖关系,当一个对象的状态发生变化时,所有依赖于它的对象都得到通知并被自动更新。观察者模式又称“发布-订阅”模式。
当发布者的状态发生变化的时候,这些订阅者就会自动去更新,而不再是需要判断,手动更新。
发布者跟订阅者之间的关系,应该是一个松耦合的。也就是对于订阅者而言,可以取消订阅。
角色:
- 抽象主题
- 具体主题
- 抽象观察者
- 具体观察者
python示例
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): super().__init__() self.__company_info = comany_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 = '公司今年业绩非常好,给大家发奖金!!' print(s1.company_info) notice.detach(s2) notice.company_info = "公司明天放假!!" print(s1.company_info) print(s2.company_info)
使用场景:
- 当一个抽象模型有两个方面,其中一个方面依赖于另外一个方面。将这两者封装在独立的对象中以使它们可以各自独立地改变和复用。
- 当对一个对象的改变需要同时改变其它对象,而不知道具体有多少对象有待改变。
- 当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之,你不希望这些对象是紧密耦合的。
优点:
- 目标和观察者之间的抽象耦合最小
- 支持广播通信
3、策略模式
内容:
定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。本模式使得算法可独立于使用它的客户而变化。
角色:
- 抽象策略
- 具体策略
- 上下文
Python示例
例如:网约车。当一个人要打车的时候,需要匹配车辆,这个时候算法需要考虑几个方面,距离近(地图网络距离),司机评分要好,价格等。
现在有两个算法,都能匹配到司机。
第一个算法,永远匹配到的是并且是最好的司机,但是,运行慢。
另外一个算法,是近似算法,不能保证是最好。但是运行快。
这两个算法会不断的切换,比如订单的高峰期,可能会用第二个(快但不准),低峰期的时候用算法一(准确并且性价比好)。
from abc import ABCMeta, abcstractmethod class Strategy(metaclass=ABCMeta): @abcstractmethod def execute(self, data): pass class FastStratege(Strategy): def execute(self, data): print("用较快的策略处理%s"%data) class SlowStratege(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) data = "[...]" s1 = FastStrategy() context = Context(s1, data) context.do_strategy() s2 = SlowStrategy() context.set_strategy(s2, data) context.do_strategy()
4、模板方法模式
内容:
定义了一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可冲定义该算法的某些特定步骤。
角色:
- 抽象类:定义抽象的原子操作(钩子操作);实现一个模块方法作为算法的骨架。
- 具体类:实现原子操作。
python示例
from abc import ABCMeta, abcstractmethod import time 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() time.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()
使用场景:
- 一次性实现一个算法的不变部分
- 各个子类中的公共行为应该被提取出来并集中到一个公共父类中以避免代码重复
- 控制子类扩展。
python是动态语言,因此在设计模式中,并不是百分百跟设计模式切合。但是有大半部分还是有很用的,对写出漂亮的代码很有帮助。设计模式,当写一些大的项目,跟底层的开源项目的时候,会很有用,因为是给别人用的代码,因此需要写的严谨一些。