程序员文章、书籍推荐和程序员创业信息与资源分享平台

网站首页 > 技术文章 正文

Python开发工程师必会的3个设计模式(工厂、单例、适配器)

hfteth 2025-05-22 10:31:24 技术文章 3 ℃

点赞、收藏、加关注,下次找我不迷路

刚学 Python ,是不是一听到 "设计模式" 就头大?觉得这是大佬才需要掌握的高深知识?别害怕!今天咱们就用通俗易懂的语言,把 Python 开发中最常用的 3 个设计模式 —— 工厂模式、单例模式、适配器模式,讲明白!学会它们,你的代码不仅会更规范、更灵活,还能直接提升一个档次,离成为高薪 Python 开发工程师又近了一步!



一、工厂模式:对象的 "智能工厂",让创建对象更简单

1. 什么是工厂模式?

咱们先打个比方,假如你开了一家奶茶店,顾客可能会点珍珠奶茶、草莓奶昔、芒果冰沙等不同的饮品。如果每次顾客点单,你都要亲自去准备材料、制作饮品,那效率可太低了。而且如果将来要新增一种饮品,比如杨枝甘露,你又得重新修改制作流程,非常麻烦。

工厂模式就相当于开了一个 "饮品制造工厂",你只需要告诉工厂你想要什么饮品,工厂就会按照既定的流程帮你生产出来。你不用关心具体的制作细节,只需要和工厂打交道就行。在编程中,工厂模式就是用来创建对象的一种模式,它把对象的创建和使用分离开来,让代码更灵活、更易于维护。

2. 工厂模式的三种类型

工厂模式主要有三种类型:简单工厂模式、工厂方法模式和抽象工厂模式。咱们一个个来看看,为了方便理解,还是用奶茶店的例子来讲解。

(1)简单工厂模式

简单工厂模式是最简单的工厂模式,它有一个专门的工厂类,负责创建不同的产品对象。就像咱们的奶茶店,有一个总的饮品工厂,当顾客点单时,工厂根据订单类型生产对应的饮品。

代码示例

class MilkTea:
    def drink(self):
        print("喝珍珠奶茶")

class StrawberryShake:
    def drink(self):
        print("喝草莓奶昔")

class MangoSmoothie:
    def drink(self):
        print("喝芒果冰沙")

class DrinkFactory:
    @staticmethod
    def create_drink(drink_type):
        if drink_type == "milk_tea":
            return MilkTea()
        elif drink_type == "strawberry_shake":
            return StrawberryShake()
        elif drink_type == "mango_smoothie":
            return MangoSmoothie()
        else:
            raise ValueError("不支持的饮品类型")

# 使用工厂创建饮品
drink1 = DrinkFactory.create_drink("milk_tea")
drink1.drink()  # 输出:喝珍珠奶茶
drink2 = DrinkFactory.create_drink("strawberry_shake")
drink2.drink()  # 输出:喝草莓奶昔


优点:简单易用,客户端不需要知道具体的产品类,只需要知道产品类型即可创建对象。

缺点:工厂类承担了所有产品的创建逻辑,如果产品类型过多,工厂类会变得很臃肿,而且新增产品时需要修改工厂类的代码,违反了 "开闭原则"(对扩展开放,对修改关闭)。

(2)工厂方法模式

为了解决简单工厂模式的缺点,工厂方法模式把工厂类也进行了抽象,定义了一个抽象工厂类,具体的工厂类负责创建具体的产品类。比如咱们的奶茶店,不同的饮品可以有不同的工厂,比如珍珠奶茶工厂、草莓奶昔工厂等,每个工厂专门生产一种饮品。

代码示例

from abc import ABC, abstractmethod

class Drink(ABC):
    @abstractmethod
    def drink(self):
        pass

class MilkTea(Drink):
    def drink(self):
        print("喝珍珠奶茶")

class StrawberryShake(Drink):
    def drink(self):
        print("喝草莓奶昔")

class DrinkFactory(ABC):
    @abstractmethod
    def create_drink(self):
        pass

class MilkTeaFactory(DrinkFactory):
    def create_drink(self):
        return MilkTea()

class StrawberryShakeFactory(DrinkFactory):
    def create_drink(self):
        return StrawberryShake()

# 使用具体工厂创建饮品
factory1 = MilkTeaFactory()
drink1 = factory1.create_drink()
drink1.drink()  # 输出:喝珍珠奶茶

factory2 = StrawberryShakeFactory()
drink2 = factory2.create_drink()
drink2.drink()  # 输出:喝草莓奶昔

优点:符合 "开闭原则",新增产品时只需要新增对应的产品类和工厂类,不需要修改现有代码。

缺点:当产品类型较多时,需要创建大量的工厂类,增加了代码的复杂度。

(3)抽象工厂模式

抽象工厂模式是工厂方法模式的升级,它可以创建一系列相关或相互依赖的产品对象。比如咱们的奶茶店,不仅要生产饮品,还要生产对应的杯子、吸管等配件,抽象工厂模式就可以同时创建饮品和配件。

代码示例

from abc import ABC, abstractmethod

class Drink(ABC):
    @abstractmethod
    def drink(self):
        pass

class MilkTea(Drink):
    def drink(self):
        print("喝珍珠奶茶")

class StrawberryShake(Drink):
    def drink(self):
        print("喝草莓奶昔")

class Cup(ABC):
    @abstractmethod
    def use(self):
        pass

class SmallCup(Cup):
    def use(self):
        print("使用小杯子")

class LargeCup(Cup):
    def use(self):
        print("使用大杯子")

class AbstractFactory(ABC):
    @abstractmethod
    def create_drink(self):
        pass

    @abstractmethod
    def create_cup(self):
        pass

class MilkTeaFactory(AbstractFactory):
    def create_drink(self):
        return MilkTea()

    def create_cup(self):
        return SmallCup()

class StrawberryShakeFactory(AbstractFactory):
    def create_drink(self):
        return StrawberryShake()

    def create_cup(self):
        return LargeCup()

# 使用抽象工厂创建饮品和杯子
factory1 = MilkTeaFactory()
drink1 = factory1.create_drink()
cup1 = factory1.create_cup()
drink1.drink()  # 输出:喝珍珠奶茶
cup1.use()  # 输出:使用小杯子

factory2 = StrawberryShakeFactory()
drink2 = factory2.create_drink()
cup2 = factory2.create_cup()
drink2.drink()  # 输出:喝草莓奶昔
cup2.use()  # 输出:使用大杯子

优点:可以创建一系列相关的产品对象,减少了客户端需要处理的对象数量。

缺点:结构比较复杂,当需要新增一种产品类型时,需要修改所有的工厂类,违反了 "开闭原则"。

3. 工厂模式口诀

"工厂模式像工厂,对象创建它来管,

简单工厂易扩展,工厂方法更灵活,

抽象工厂管家族,按需选择别搞错。"

4. 适用场景

  • 当需要创建多个相关或相似的对象时。
  • 当客户端不需要知道具体的产品类,只需要知道产品的类型时。
  • 当希望将对象的创建和使用分离开来,提高代码的可维护性和扩展性时。

二、单例模式:保证对象唯一,全局共享数据

1. 什么是单例模式?

单例模式是一种确保一个类只有一个实例,并提供一个全局访问点的设计模式。简单来说,就是这个类在整个程序中只能创建一个对象,而且这个对象可以被全局访问。比如咱们电脑上的任务管理器,不管你打开多少次,始终只有一个实例在运行;还有打印机,全班同学共用一台打印机,这就是单例模式的应用。

在编程中,单例模式通常用于需要全局共享一个对象的场景,比如配置管理器、日志记录器、数据库连接池等。

2. 单例模式的实现方法

在 Python 中,实现单例模式有多种方法,下面介绍几种常用的方式。

(1)重写__new__方法

__new__方法是在类实例化时首先被调用的方法,它负责创建类的实例。我们可以通过重写__new__方法,来确保只创建一个实例。

代码示例

class Singleton:
    _instance = None

    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            cls._instance = super().__new__(cls, *args, **kwargs)
        return cls._instance

# 创建单例对象
obj1 = Singleton()
obj2 = Singleton()
print(obj1 is obj2)  # 输出:True,说明两个对象是同一个实例


(2)使用装饰器

装饰器的内容可以参考我的另一篇文章《Python的装饰器还是不会?来看看这篇文章(建议收藏)》

装饰器是 Python 中一种强大的特性,我们可以用装饰器来装饰类,使其成为单例类。

代码示例

def singleton(cls):
    instances = {}

    def get_instance(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]

    return get_instance

@singleton
class MySingleton:
    def __init__(self, name):
        self.name = name

# 创建单例对象
obj1 = MySingleton("张三")
obj2 = MySingleton("李四")
print(obj1.name)  # 输出:张三
print(obj2.name)  # 输出:张三,说明后面的赋值不会改变实例的属性,因为对象是同一个
print(obj1 is obj2)  # 输出:True


(3)基于模块

Python 中的模块本身就是单例的,当我们导入一个模块时,模块中的代码只会执行一次,模块中的变量、类等都是单例的。所以我们可以把需要作为单例的类定义在一个模块中,然后通过导入模块来使用单例对象。

代码示例

创建一个模块singleton_module.py,内容如下:

class Singleton:
    def __init__(self, name):
        self.name = name

instance = Singleton("单例对象")


在其他文件中使用:

from singleton_module import instance

obj1 = instance
obj2 = instance
print(obj1 is obj2)  # 输出:True

3. 单例模式口诀

"单例模式保唯一,全局使用很方便,

__new__方法来重写,装饰模块也能行,

使用场景要牢记,配置日志数据库。"

4. 适用场景

  • 当类需要有且只有一个实例,并且该实例需要被全局访问时。
  • 当需要节省系统资源,避免重复创建和销毁对象时,比如数据库连接池。
  • 当需要确保数据的一致性,避免多个实例之间的数据不一致时。

三、适配器模式:解决接口不兼容问题,让旧代码焕发新生

1. 什么是适配器模式?

适配器模式就像我们日常生活中的充电器转换头。比如你去国外旅行,当地的插座接口和你的充电器接口不兼容,这时候你就需要一个转换头,把当地的插座接口转换成你充电器能使用的接口。在编程中,适配器模式就是用来解决两个接口不兼容的问题,它通过创建一个适配器类,将一个类的接口转换成另一个类所期望的接口,从而让原本不兼容的类可以一起工作。

2. 适配器模式的实现

适配器模式有两种类型:对象适配器和类适配器。在 Python 中,由于支持多重继承,类适配器也可以实现,但更常用的是对象适配器,因为它更灵活,不需要继承原类。

(1)对象适配器

对象适配器通过组合的方式,将被适配的对象作为适配器类的一个成员变量,然后在适配器类中实现目标接口。

代码示例

假设我们有一个旧的支付接口OldPayment,它有一个pay_old方法,而新的支付系统需要使用NewPayment接口,它有一个pay_new方法。为了让旧的支付接口能在新的系统中使用,我们需要创建一个适配器类PaymentAdapter。

class OldPayment:
    def pay_old(self, amount):
        print(f"旧支付方式支付了{amount}元")

class NewPayment:
    def pay_new(self, payment, amount):
        payment.pay_old(amount)  # 新系统需要调用pay_new方法,传入一个具有pay_old方法的对象

class PaymentAdapter:
    def __init__(self, old_payment):
        self.old_payment = old_payment

    def pay_new(self, amount):  # 实现新接口的pay_new方法
        self.old_payment.pay_old(amount)

# 使用适配器
old_payment = OldPayment()
adapter = PaymentAdapter(old_payment)
new_payment = NewPayment()
new_payment.pay_new(adapter, 100)  # 输出:旧支付方式支付了100元


(2)类适配器(使用多重继承)

类适配器通过继承被适配的类和实现目标接口来实现。

代码示例

class OldPayment:
    def pay_old(self, amount):
        print(f"旧支付方式支付了{amount}元")

class NewPaymentInterface:
    def pay_new(self, amount):
        pass

class PaymentAdapter(OldPayment, NewPaymentInterface):
    def pay_new(self, amount):
        self.pay_old(amount)

# 使用适配器
adapter = PaymentAdapter()
adapter.pay_new(100)  # 输出:旧支付方式支付了100元


3. 适配器模式口诀

"适配器像转换头,接口不符它来救,对象适配用组合,类适配用继承,新旧代码连一连,和谐工作乐无边。"

4. 适用场景

  • 当需要使用一个已经存在的类,而它的接口不符合你的需求时。
  • 当你想创建一个可以复用的类,该类可以与其他接口不兼容的类一起工作时。
  • 当你需要在不修改原类代码的情况下,扩展类的功能时。

3 个设计模式的核心要点对比

设计模式

核心作用

典型场景

实现关键

口诀记忆

工厂模式

统一管理对象创建

对象创建逻辑复杂 / 需解耦

工厂类封装创建逻辑

工厂模式像工厂,对象创建它来管

单例模式

保证全局唯一实例

全局共享资源 / 配置

重写__new__或用装饰器

单例模式保唯一,全局使用很方便

适配器模式

转换不兼容接口

新旧系统对接 / 接口适配

创建适配器类转换接口

适配器像转换头,接口不符它来救

设计模式是编程经验的总结,不是万能的银弹。在实际开发中,要根据具体的场景选择合适的设计模式,不要为了使用设计模式而使用设计模式。

最近发表
标签列表