前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >python多种创建工厂模式场景

python多种创建工厂模式场景

原创
作者头像
MicLon
发布2023-03-03 22:46:39
4840
发布2023-03-03 22:46:39
举报
文章被收录于专栏:python-进阶

工厂模式使用场景

  • 不清楚用户需要创建什么对象
  • 使用方法来代替new实例化对象的过程

它可以是用户自定义输入,也可以是通过接口或配置文件传入。如输入"Message",可以创建Message类的实例。

工厂模式指的是程序传入一个输入参数,自动创建所对应的对象。调用端并不需要关心类实例化的过程。基于工厂模式,可以实现可扩展、易维护的代码。

当你想扩展新增一个子类的时候,只需要关注于类本身。这样就可以遵守代码的"开闭原则",对扩展开放,对修改封闭。

场景举例

下面演示一个需求场景,我们需要开发一个消息通知功能,这个消息通知需要支持多种通知方式,比如邮件、短信、微信、钉钉等。

传统的工厂模式

传统的工厂模式就是if-else多重判断,指定传参匹配指定类。

代码语言:python
代码运行次数:0
复制
# main.py
from factory import factory_sender
from config import message_config

def send_message(message: dict):
    
    for msg_cfg in message_config:
        for name, config in msg_cfg.items():
            sender = factory_sender(name, config)
            sender.send_message(**message)

if __name__ == '__main__':

    send_message(
        {
            "title": "警告消息",
            "body": "这是一条警告消息",
            "type": "warning"
        }
    )
代码语言:python
代码运行次数:0
复制
# factory.py
from channels import Ding, WeChat, Email

def factory_sender(name, config):
    if name == "ding":
        return Ding(config)
    elif name == "wechat":
        return WeChat(config)
    elif name == "email":
        return Email(config)
    else:
        raise Exception("Unknown message type")
代码语言:python
代码运行次数:0
复制
# channels.py
class Notification:

    def send_message(self, title, body, *args, **kwargs):
        raise NotImplementedError()

class Ding(Notification):

    def __init__(self, config):
        self.token = config['token']
    
    def send_message(self, title, body, *args, **kwargs):
        print(f"Ding send message: {title} {body}")

class WeChat(Notification):

    def __init__(self, config):
        self.key = config['key']
        self.secret = config['secret']

    def send_message(self, title, body, *args, **kwargs):
        print(f"WeChat send message: {title} {body}")


class Email(Notification):

    def __init__(self, config):
        self.username = config['username']
        self.password = config['password']
    
    def send_message(self, title, body, *args, **kwargs):
        print(f"Email send message: {title} {body}")
代码语言:python
代码运行次数:0
复制
# config.py
message_config = [
    {
        "ding": {
            "token": "Ding Token",
        },
        "wechat": {
            "key": "WeChat Key",
            "secret": "WeChat secret"
        },
        'email': {
            'username': 'Email username',
            'password': 'Email password'
        }
    }
]
  • main.py是程序的入口,在不更改需求的前提下我们是不会去修改这个入口文件的。
  • factory.pyfactory_sender负责实例化对象。
  • channels.py就是我们开发的消息发送渠道,未来大部分工作应该专注于渠道的开发。
  • config.py是消息发送的配置项,因为不同的消息渠道有各自不同的配置。

传统模式下的一点小弊端:

我们的工作不仅限于渠道的开发,新增渠道后我们需要在factory_sender中进行手动匹配。

比如新增短信渠道就需要:elif name == "sms":

自动导入工厂模式

在自动导入模式中,我们依然会保持上面的channels.pyconfig.py文件不变。

代码语言:python
代码运行次数:0
复制
# main.py
from util import import_object
from config import message_config

def send_message(message: dict):
    
    for cfg in message_config:
        for key, value in cfg.items():
            Obj = import_object(f'channels.{key}')
            Obj(value).send_message(**message)

if __name__ == '__main__':

    send_message(
        {
            "title": "警告消息",
            "body": "这是一条警告消息",
            "type": "warning"
        }
    )
代码语言:python
代码运行次数:0
复制
# util.py
def import_object(name: str):
    """字符串导入模块方法"""
    if name.count(".") == 0:
        return __import__(name)
    parts = name.split(".")
    obj = __import__(".".join(parts[:-1]), fromlist=[parts[-1]])
    try:
        return getattr(obj, parts[-1])
    except AttributeError:
        raise ImportError("No module named %s" % parts[-1])

main.py入口文件中可以发现,原来的factory_sender方法变成了import_object

它的作用是根据传参字符串自动导入包:

"channels.Ding"即导入channels包下面的Ding类

那么事情就变得相对简单,我们依然只需要把重心放在渠道扩展上,当新增了一个渠道Sms,自动就拥有了此类的功能。

代码语言:python
代码运行次数:0
复制
# channels.py
……

class Sms(Notification):

    def __init__(self, config):
        ...
    
    def send_message(self, title, body, *args, **kwargs):
        print(f"Sms send message: {title} {body}")

不需要改变main.py文件,直接运行main.py主程序。

代码语言:shell
复制
Ding send message: 警告消息 这是一条警告消息
WeChat send message: 警告消息 这是一条警告消息
Email send message: 警告消息 这是一条警告消息
Sms send message: 警告消息 这是一条警告消息

注册中心工厂模式

上述方式是通过动态导入类来实现的工厂模式,接下来继续使用动态语言特性,通过字典映射关系来注册类。

同样的,我们无需更改channels.pyconfig.py中的代码。

代码语言:python
代码运行次数:0
复制
# main.py
from config import message_config
import channels
import factory


factory.register('Ding', channels.Ding)
factory.register('WeChat', channels.WeChat)
factory.register('Email', channels.Email)

def send_message(message: dict):
    
    for cfg in message_config:
        for key, value in cfg.items():
            Obj = factory.run(key)
            Obj(value).send_message(**message)

if __name__ == '__main__':
    send_message(
        {
            "title": "警告消息",
            "body": "这是一条警告消息",
            "type": "warning"
        }
    )
代码语言:python
代码运行次数:0
复制
# factory.py
services = {}

def register(name, service):
    services[name] = service

def unregister(name):
    services.pop(name, None)

def run(name):
    return services[name]

不难发现,所谓注册中心,其实就是字典的关系映射,将所有渠道注册到字典中,通过字典key来调用类value。

聪明的同学会说:那我岂不是还是需要手动一个个注册这些渠道?

于是我们可以在注册中心中做个自动注册

代码语言:python
代码运行次数:0
复制
# main.py

from config import message_config
import factory

factory.auto_register()

def send_message(message: dict):
    
    for cfg in message_config:
        for key, value in cfg.items():
            Obj = factory.run(key)
            Obj(value).send_message(**message)

if __name__ == '__main__':
    send_message(
        {
            "title": "警告消息",
            "body": "这是一条警告消息",
            "type": "warning"
        }
    )
代码语言:python
代码运行次数:0
复制
# factory.py
services = {}

def register(name, service):
    services[name] = service

def unregister(name):
    services.pop(name, None)

def run(name):
    return services[name]

def auto_register():
  import channels
  for name in channels.__dict__:
    if name.startswith('__'):
      continue
    register(name, getattr(channels, name))

总结

利用python动态语言的特性,可以开发出不一样的工厂模式来解耦实际场景。本次就分享两种工程模式的设计实现。

以上案例笔者已在一项目中体现:GitHub-simple-notify

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 工厂模式使用场景
  • 场景举例
  • 传统的工厂模式
  • 自动导入工厂模式
  • 注册中心工厂模式
  • 总结
相关产品与服务
微服务引擎 TSE
微服务引擎(Tencent Cloud Service Engine)提供开箱即用的云上全场景微服务解决方案。支持开源增强的云原生注册配置中心(Zookeeper、Nacos 和 Apollo),北极星网格(腾讯自研并开源的 PolarisMesh)、云原生 API 网关(Kong)以及微服务应用托管的弹性微服务平台。微服务引擎完全兼容开源版本的使用方式,在功能、可用性和可运维性等多个方面进行增强。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档