Python常用的设计模式(上)

本文涉及的产品
云拨测,每月3000次拨测额度
简介: Python常用的设计模式(上)

文章目录


1. 设计模式

2. 面向对象设计原则

3. 创建型模式

3.1. 简单工厂模式

3.2. 工厂方法模式

3.3. 抽象工厂模式

3.4. 建造者模式

3.5. 单例模式

3.6. 创建型模式概述

4. 结构型模式

4.1. 适配器模式

4.2. 桥模式

4.3. 组合模式

4.4. 外观模式

4.5. 代理模式

5. 行为型模式

5.1. 责任链模式

5.2. 观察者模式

5.3. 策略模式

5.4. 模板方法模式


正文


1. 设计模式


设计模式是对软件设计中普遍存在(反复出现)的各种问题,所提出的解决方案。每一个设计模式系统地命名、解释和评价了面向对象系统中一个重要的和重复出现的设计。


设计模式的分类

创建型模式:工厂方法模式、抽象工厂模式、创建者模式、原型模式、单例模式。隐藏底层模块的逻辑,关注怎么创建对象。

结构型模式:适配器模式、桥模式、组合模式、装饰模式、外观模式、享元模式、代理模式。类之间如何协同工作,应该组成什么结构。


行为型模式:解释器模式、责任链模式、命令模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式、策略模式、访问者模式、模板方法模式。关注行为,也就是方法,应该怎样某些行为。


面向对象

设计模式解决的就是面向对象中的问题。需要指导面向对象的三大特性是 封装、继承和多态 ,封装是把数据和方法封装到类中,继承是类之间复用代码,多态在Python中默认支持的,Python是一种多态的语言。


接口

接口是若干抽象方法的集合。接口的作用是限制实现接口的类必须按照接口给定的调用方式实现这些方法,对高层模块隐藏了类的内部实现。下面通过一个简单的例子来加强对接口的理解:

from abc import ABCMeta, abstractmethod
# 具有抽象方法的类就是接口类,
class Payment(metaclass=ABCMeta):
    @abstractmethod
    def pay(self, money):
        """
        抽象方法,在实现的类中必须实现的方法。限制实现接口的类必须按照接口给定的调用方式实现这些方法
        :param money: 
        :return: 
        """
        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)


2. 面向对象设计原则


开放封闭原则

一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。即软件实体应该在不修改原代码的情况下进行修改。

里氏替换原则

所有引用父类的地方必须能透明地使用其子类的对象,即能调用父类就一定能调用其子类,一个简单的例子加强理解

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()
# 无论是实现User()还是VipUser()都是可以的
u = User()
# u = VipUser()
print_name(u)


  • 依赖倒置原则
    高层模块不应该依赖底层模块,二者都应该依赖抽象。抽象不应该依赖细节,细节应该应该依赖抽象。要针对接口编程,而不是针对实现变成。通过例子加强理解:
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


  • 单一职责原则
    不要存在多于一个导致类变更的原因,一个类只负责一项职责,一个类只做一件事。把面向过程的代码放到类中,虽然用到了类,但不是面向对象


3. 创建型模式


3.1. 简单工厂模式


简单工厂模式不是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 ctreate_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.ctreate_payment('HuabeiPay')
p.pay(100)


3.2. 工厂方法模式


简单工厂模式只创建一个工厂类,当有新的产品时,需要修改工厂类代码。而 工厂方法模式的每个具体产品对应一个具体的工厂类,不需要修改工厂类代码,并且同时也能满足隐藏对象创建的细节。但是工厂方法模式也是有缺点的,就是 每增加一个具体产品类,就必须增加一个相应的具体方法。

工厂模式方法模式的概念是定义了一个用于创建对象的接口(工厂接口),让子类决定实例化那一个产品类。角色有抽象工厂角色、具体工厂角色、抽象产品角色和具体产品角色。通过例子来加深理解,每个具体产品对应一个具体的工厂类:


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元!


3.3. 抽象工厂模式


抽象工厂模式:定义一个工厂类的接口让工厂子类来创建一系列相关或者相互依赖的对象。相比工厂方法模式,抽象工厂模式中的每一个具体工厂都生产一套产品。下面是生产厂商生产一部手机的例子:生产一部手机如果说只需要手机壳、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系统
"""

抽象工厂模式的角色有:抽象工厂角色、具体工厂角色、抽象产品角色、具体产品角色和客户端。抽象工厂模式的优点是:将客户端和类的具体实现相分离;每个工厂创建了一个完整的产品系列,使得易于交换产品系列;有利于产品的一致性,即产品之间的约束关系。缺点是:难以支持新种类抽象产品


3.4. 建造者模式


建造者模式是将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。角色有抽象创建者、具体创建者、指挥者和产品。建造者模式与抽象工厂模式相似,也用来创建复杂的对象。主要区别是 建造者模式着重一步步构造一个复杂对象(控制顺序)。而抽象工厂模式着重于多个系列的产品对象,写个例子来加强理解:

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)  # 漂亮的脸蛋,苗条的身材,细细的胳膊,大长腿


3.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
ms1 = MyClass(1)
ms2 = MyClass(2)
print(ms1.a, ms2.a)
print(id(ms1), id(ms2))
"""
2 2
139843914173312 139843914173312
"""

如果实例只出现一次,如日志系统中只需要创建一个日志对象(否则两个日志对象同时操作一个文件就会造成操作冲突);数据库连接池只需要创建一个对象来操作数据库(否则增加系统开销,浪费系统资源);操作系统只需要创建一个文件系统对象来操作文件系统。

相关文章
|
19天前
|
设计模式 算法 程序员
Python从入门到精通:2.1.3深入学习面向对象编程——设计模式的学习与实践
Python从入门到精通:2.1.3深入学习面向对象编程——设计模式的学习与实践
|
3月前
|
设计模式 数据挖掘 开发者
Python中的面向对象编程与设计模式
【2月更文挑战第1天】Python作为一种动态、面向对象的高级编程语言,广泛应用于Web开发、数据分析等领域。本文将介绍Python中的面向对象编程特性,并结合常用的设计模式,探讨如何在Python中实现灵活、可维护的代码结构。
17 2
|
8月前
|
设计模式 分布式计算 算法
Python后端技术栈(三)--设计模式
Python后端技术栈(三)--设计模式
|
9月前
|
设计模式 存储 Python
【从零学习python 】46. Python中的__new__和__init__方法解析及单例设计模式
【从零学习python 】46. Python中的__new__和__init__方法解析及单例设计模式
42 0
|
4月前
|
设计模式 缓存 测试技术
python的装饰器与设计模式中的装饰器模式
python的装饰器与设计模式中的装饰器模式
|
8月前
|
设计模式 API Python
Python 外观设计模式讲解和代码示例
Python 外观设计模式讲解和代码示例
33 0
|
12月前
|
设计模式 算法 数据库
Python常用的设计模式(下)
Python常用的设计模式(下)
|
设计模式 Python
Python:设计模式之设计模式简介
Python:设计模式之设计模式简介
101 0
|
设计模式 Python
Python:设计模式之反模式
Python:设计模式之反模式
56 0
|
设计模式 存储 前端开发
Python:设计模式之模型-视图-控制器-MVC复合模式
Python:设计模式之模型-视图-控制器-MVC复合模式
75 0