设计模式 Python 实现
认识
设计模式
定义
设计模式是对软件设计中普遍存在或反复出向的各种问题所提出的解决方案。每一个设计模式系统地被命名、解释和评价了面向对象系统中一个重要和重复出现的设计
分类
创建型模式
-
隐藏底层模块的逻辑,关注怎么创建对象
-
工厂方法模式、抽象工厂模式、 创建者模式、原型模式、单例模式
结构型模式
-
类之间如何协同工作,应该组成什么结构
-
适配器模式、桥模式、组合模式、装饰模式、外观模式、享元模式、代理模式。
行为型模式
-
关注行为,也就是方法,应该怎样某些行为
-
解释器模式、责任链模式、命令模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式、策略模式、访问者模式、模板方法模式。
接口
定义:若干抽象方法的集合
作用:限制实现接口的类必须按照接口给定的调用方式实现这些方法,对高层模块隐藏了类的内部实现
from abc import ABCMeta, abstractmethod
# 具有抽象方法 的类就是接口类,
class Payment(metaclass=ABCMeta):
@abstractmethod
def pay(self, money):
"""
抽象方法,在实现的类中必须实现的方法。限制实现接口的类必须按照接口给定的调用方式实现这些方法
"""
pass
# 不能说是继承接口类,应该说是实现接口
class Alipay(Payment):
def pay(self, money):
"""
实现接口类中的必须实现的方法
:param money:
:return:
"""
print("支付宝支付了{0}元!".format(money))
class WechatPay(Payment):
def pay(self, money):
"""
实现接口类中的必须实现的方法
:param money:
:return:
"""
print("微信支付了%d元!" % (money))
# 下面是高层代码,在调用的时候是看不到底层类的内部实现
a = Alipay()
w = WechatPay()
a.pay(100)
w.pay(100)
面向对象设计原则
开放封闭原则
一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。
即软件实体应该在不修改原代码的情况下进行修改。
里氏替换原则
里氏替换原则通俗来讲就是:子类可以扩展父类的功能,但不能改变父类原有的功能。也就是说:子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法。
根据上述理解,对里氏替换原则的定义可以总结如下:
- 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法
- 子类中可以增加自己特有的方法
- 当子类的方法重载父类的方法时,方法的前置条件(即方法的输入参数)要比父类的方法更宽松
- 当子类的方法实现父类的方法时(重写/重载或实现抽象方法),方法的后置条件(即方法的的输出/返回值)要比父类的方法更严格或相等
所有引用父类的地方必须能透明地使用其子类的对象
class User(object):
def print_name(self):
pass
class VipUser(User):
def print_name(self):
"""
保证参数和返回值类型需要和父类一样
:return:
"""
pass
def print_name(u):
"""
不论使用User还是继承User的VipUser,调用的方式是一样的。这就要求User和VipUser的方法参数和返回值类型是一样的
:param u:
:return:
"""
u.print_name()
依赖倒置原则
-
高层模块不应该依赖底层模块,二者都应该依赖抽象。
-
抽象不应该依赖细节,细节应该应该依赖抽象。
要针对接口编程,而不是针对实现编程
from abc import ABCMeta, abstractmethod
# 接口,抽象不应该依赖细节
class Payment(metaclass=ABCMeta):
@abstractmethod
def pay(self, money):
pass
# 底层代码和高层代码都是细节,细节应该依赖抽象
# 底层代码
class Alipay(Payment):
def pay(self, money):
print("支付宝支付了{0}元!".format(money))
class WechatPay(Payment):
def pay(self, money):
print("微信支付了%d元!" % (money))
# 高层代码,高层模块不应该依赖底层模块,二者都应该依赖抽象
a = Alipay()
w = WechatPay()
a.pay(100)
w.pay(100)
接口隔离原则
-
使用多个专门的接口,而不使用单一的总接口
-
高层的代码不应该依赖那些它不需要的接口
from abc import ABCMeta, abstractmethod
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):
pass
# 高层的代码不应该依赖那些它不需要的接口
class Frog(LandAnimal, WaterAnimal):
def walk(self):
pass
单一职责原则
不要存在多于一个导致类变更的原因,一个类只负责一项职责,一个类只做一件事。
把面向过程的代码放到类中,虽然用到了类 ,但不是面向对象。
创建型模式
简单工厂模式
简单工厂模式不在 23 种设计模式之中,但很重要。
简单工厂模式不直接向客户端暴露对象创建的细节,而是通过一个工厂类来负责创建产品类的实例。
简单工程模式的角色有:工厂角色、抽象产品角色、具体产品角色
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_huabei = use_huabei
def pay(self, money):
if self.use_huabei == True:
print("花呗支付了{0}元!".format(money))
else:
print("支付宝余额支付了{0}元!".format(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 == 'WechatPay':
return WechatPay()
elif method == 'HuabeiPay':
return Alipay(use_huabei=True)
else:
raise TypeError('No such payment named %s' % method)
# 客户端调用。不直接向客户端暴露对象创建的实现细节,而是通过一个工厂类来负责创建产品类的实例
pf = PaymentFactory()
p = pf.create_payment('HuabeiPay')
p.pay(100)
工厂方法模式
简单工厂模式只创建一个工厂类,当有新的产品时,需要修改工厂类代码。
而工厂方法模式的每个具体产品对应一个具体的工厂类,不需要修改工厂类代码,并且同时也能满足隐藏对象创建的细节。
但是工厂方法模式也是有缺点的:每增加一个具体产品类,就必须增加一个相应的具体方法。
工厂模式方法模式的概念是定义了一个用于 创建对象的接口(工厂接口),让子类决定实例化哪一个产品类。
角色有抽象工厂角色、具体工厂角色、抽象产品角色和具体产品角色。
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_huabei = use_huabei
def pay(self, money):
if self.use_huabei == True:
print("花呗支付了{0}元!".format(money))
else:
print("支付宝余额支付了{0}元!".format(money))
class WechatPay(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 Alipay()
class HuabeiFactory(PaymentFactory):
def create_payment(self):
return Alipay(use_huabei=True)
hfp = HuabeiFactory().create_payment()
hfp.pay(100) # 花呗支付了100元!
缺点是每增加一个具体产品类,就必须增加一个相应的具体方法:
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_huabei = use_huabei
def pay(self, money):
if self.use_huabei == True:
print("花呗支付了{0}元!".format(money))
else:
print("支付宝余额支付了{0}元!".format(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 WechatPayPayFactory(PaymentFactory):
def create_payment(self):
return Alipay()
# 工厂类
class HuabeiPayFactory(PaymentFactory):
def create_payment(self):
return Alipay(use_huabei=True)
# 新增加银行支付的工厂类
class BankPayFactory(PaymentFactory):
def create_payment(self):
return BankPay()
bfp = BankPayFactory().create_payment()
bfp.pay(100) # 银行支付了100元!
抽象工厂模式
定义一个工厂类的接口让工厂的子类来创建一系列相关或者相互依赖的对象。
相比工厂方法模式,抽象工厂模式中的每一个具体工厂都生产一套产品。
下面是生产厂商生产一部手机的例子:生产一部手机如果说只需要手机壳、CPU 和操作系统这三个类对象,其中每个类对象都有不同的种类。对每个具体工厂,分别生产一部手机需要的三个对象。
from abc import ABCMeta, abstractmethod
# ------抽象的产品------
class PhoneShell(metaclass=ABCMeta):
@abstractmethod
def show_shell(self):
pass
class PhoneCPU(metaclass=ABCMeta):
@abstractmethod
def show_cpu(self):
pass
class PhoneOS(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(PhoneCPU):
def show_cpu(self):
print('骁龙CPU')
class HuaweiCPU(PhoneCPU):
def show_cpu(self):
print('化为CPU')
class AppleCPU(PhoneCPU):
def show_cpu(self):
print('苹果CPU')
class AndroidOS(PhoneOS):
def show_os(self):
print('IOS系统')
class AppleOS(PhoneOS):
def show_os(self):
print('安卓系统')
# ------抽象的工厂------
class PhoneFactory(metaclass=ABCMeta):
@abstractmethod
def make_shell(self):
pass
@abstractmethod
def make_cpu(self):
pass
@abstractmethod
def make_os(self):
pass
# ------具体的工厂------
class HuaweiFactory(PhoneFactory):
def make_shell(self):
return SmallShell()
def make_cpu(self):
return HuaweiCPU()
def make_os(self):
return AndroidOS()
class AppleFactory(PhoneFactory):
def make_shell(self):
return AppleShell()
def make_cpu(self):
return AppleCPU()
def make_os(self):
return AppleOS()
# ------客户端------
class Phone:
def __init__(self, shell, cpu, os):
self.shell = shell
self.cpu = cpu
self.os = os
def show_info(self):
print('手机信息:')
self.shell.show_shell()
self.cpu.show_cpu()
self.os.show_os()
def make_phone(factory):
shell = factory.make_shell()
cpu = factory.make_cpu()
os = factory.make_os()
return Phone(shell, cpu, os)
p = make_phone(HuaweiFactory())
p.show_info()
"""
手机信息:
普通手机小手机壳
华为CPU
IOS系统
"""
抽象工厂模式的角色有:抽象工厂角色、具体工厂角色、抽象产品角色、具体产品角色和客户端。
优点
- 将客户端和类的具体实现相分离;
- 每个工厂创建了一个完整的产品系列,使得易于交换产品系列;
- 有利于产品的一致性,即产品之间的约束关系。
缺点
- 难以支持新种类抽象产品。
建造者模式
建造者模式是将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
角色
- 抽象创建者
- 具体创建者
- 指挥者
- 产品
建造者模式与抽象工厂模式相似,也用来创建复杂的对象。主要区别是 建造者模式着重一步步构造一个复杂对象(控制顺序),而抽象工厂模式着重于多个系列的产品对象
from abc import ABCMeta, abstractmethod
# ------产品------
class Player:
def __init__(self, face=None, body=None, arms=None, legs=None):
self.face = face
self.body = body
self.arms = arms
self.legs = legs
def __str__(self):
return '%s,%s,%s,%s' % (self.face, self.body, self.arms, self.legs)
# ------抽象建造者------
class PlayerBuilder(metaclass=ABCMeta):
@abstractmethod
def build_face(self):
pass
@abstractmethod
def build_body(self):
pass
@abstractmethod
def build_arms(self):
pass
@abstractmethod
def build_legs(self):
pass
# ------具体建造者,隐藏了一个产品的内部结构------
class GirlBuilder(PlayerBuilder):
def __init__(self):
self.player = Player()
def build_face(self):
self.player.face = '漂亮的脸蛋'
def build_body(self):
self.player.body = '苗条的身材'
def build_arms(self):
self.player.arms = '细细的胳膊'
def build_legs(self):
self.player.legs = '大长腿'
# ------具体建造者,表示代码------
class MonsterBuilder(PlayerBuilder):
def __init__(self):
self.player = Player()
def build_face(self):
self.player.face = '绿脸'
def build_body(self):
self.player.body = '魁梧的身体'
def build_arms(self):
self.player.arms = '粗壮的胳膊'
def build_legs(self):
self.player.legs = '粗壮的大腿'
# ------指挥者,构造代码(构造代码和表示代码分开),可以对构造过程进行更加精细地控制------
class PlayerDirectory():
def builder_player(self, builder):
"""
隐藏了装配过程
:param builder:
:return:
"""
builder.build_face()
builder.build_body()
builder.build_arms()
builder.build_legs()
return builder.player
# ------客户端------
builder = GirlBuilder()
director = PlayerDirectory()
p = director.builder_player(builder)
print(p) # 漂亮的脸蛋,苗条的身材,细细的胳膊,大长腿
单例模式
单例模式保证一个类只有一个实例,并提供一个访问它的全局访问点。
优点是对唯一实例的受控访问(只有一个实例),单例相当于全局变量,但防止了命名空间被污染(变量命名不会有冲突)。
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
ms1 = MyClass(1)
ms2 = MyClass(2)
print(ms1.a, ms2.a)
print(id(ms1), id(ms2))
"""
2 2
139843914173312 139843914173312
"""
如果实例只出现一次,如:
- 日志系统中只需要创建一个日志对象(否则两个日志对象同时操作一个文件就会造成操作冲突);
- 数据库连接池只需要创建一个对象来操作数据库(否则增加系统开销,浪费系统资源);
- 操作系统只需要创建一个文件系统对象来操作文件系统。
创建型模式概述
抽象工厂模式和建造者模式相比于简单工厂模式和工厂方法模式而言更加灵活也更加复杂。
通常情况下,软件设计以简单工厂模式或工厂方法模式开始,当发现设计需要更大的灵活性的时候,则向更加复杂的设计模式演化。
结构型模式
适配器模式
将一个类的接口转换成客户希望的另外一个接口,适配器使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。实现适配器的两种方式,类适配器使用多继承,对象适配器使用组合
。组合就是一个类中放入另一类的对象。
先来看下组合:
class A:
pass
class B:
def __init__():
self.a = A()
类适配器模式使用示例:
# 类适配器模式使用示例:
from abc import ABCMeta, abstractmethod
# 目标接口
class Payment(object, metaclass=ABCMeta):
@abstractmethod
def pay(self, money):
pass
class Alipay(Payment):
def pay(self, money):
print('支付了%d' % money)
# 待适配的类
class BankPay():
def cost(self, money):
print('银联支付了%d' % money)
# 类适配器
class PaymentAdapter(Payment, BankPay):
"""
把不兼容cost转换成pay
"""
def pay(self, money):
self.cost(money)
p = PaymentAdapter()
p.pay(100)
"""
银联支付了100
"""
对象适配器模式使用示例:
# 类适配器模式使用示例:
from abc import ABCMeta, abstractmethod
# 目标接口
class Payment(object, metaclass=ABCMeta):
@abstractmethod
def pay(self, money):
pass
class Alipay(Payment):
def pay(self, money):
print('支付了%d' % money)
# 待适配的类
class BankPay():
def cost(self, money):
print('银联支付了%d' % money)
# 待适配的类
class ApplePay():
def cost(self, money):
print('