【通俗说设计模式】一、简单工厂模式 & Python示例(创建型模式开篇)

专业介绍:

         简单工厂模式是属于创建型模式,又叫做静态工厂方法(Static Factory Method)模式,但不属于23种GOF设计模式之一。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式,可以理解为是不同于工厂模式的一个特殊实现。引用自-百度百科

关键词:一个工厂、简单实用

通俗介绍:

         所谓简单工厂模式,既然有【简单】二字,那它的实现必然是简洁明了的,专业化的介绍中阐述了这种模式是由一个统一的工厂对象(可以是类/函数)决定创建出哪种最终被需要的产品类(对象)的实例。即是说,在某些场景中,某一种对象(可理解为某个配置/变量/服务)存在不止一种情况,可根据需要来决定调用哪一种对象。

简单例子1:现在你要提供两套配置供程序调用,程序会根据当前运行环境来决定使用哪一套配置,那么这个场景你就可以使用简单工厂模式(实际情况中也许你只是简单通过if--else来实现的)。

简单例子2:现在Leader要你为程序开发一个统一的日志组件(程序之前完全没有日志组件),告诉你日志既要记录到标准输出(sys.stdout),同时要记录到本地文件,并且告诉你不久之后还要记录到一个日志服务器上面(需要配置服务器地址等),这个时候使用简单工厂模式可能是一个最佳方式了。

模式细节:

简单工厂模式包含三个角色:

    A. 工厂--Creator:模式的核心,负责创建产品,直接被外部调用。可以是类/函数,外部可以调用类中不同的方法或调用函数(传入不同参数)来获得指定的创建好的产品(对象)。

    B. 抽象产品--Product:工厂对象创建的所有对象的父类(Java/Python有这个术语),它负责描述所有实例所共有的公共接口,也可以换句话说:预先定义实例对象的签名方法(也称模板方法),所有的"产品"必须按照签名(模板)来实现。

    C. 具体产品--Concrete Product: 工厂的创建目标,所有创建的对象都是充当这个角色的某个具体产品(类/函数/变量...)的实例。

代码示例:

         场景:现在以上面例子2为假设场景,来看看如何使用简单工厂模式优雅的实现它:

import abc, six
import sys
import logging


# 利用python中的抽象类模块来实现抽象产品类,其只能被继承,不能被实例化(Python2.7&3.x兼容)
@six.add_metaclass(abc.ABCMeta)
class BaseLogger:
    @abc.abstractmethod
    def info(self, msg: str, no_prefix=False, *args, **kwargs) -> None:
        """
        an abstract method need to be implemented
        the args listed are just as a reference, it's not necessary.
        """
        pass

    @abc.abstractmethod
    def debug(self, msg: str, no_prefix=False, *args, **kwargs) -> None:
        """
        an abstract method need to be implemented
        the args listed are just as a reference, it's not necessary.
        """
        pass


# 具体的产品类
class PrintLogger(BaseLogger):
    """
    本地打印日志
    """

    def __init__(self, logger_name, prefix, print_level=logging.INFO):
        self.pre = prefix
        self.logger = logging.getLogger(logger_name)
        self.logger.setLevel(print_level)
        formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s',
                                      datefmt='%Y-%m-%d %H:%M:%S')
        stream_handler = logging.StreamHandler(sys.stdout)
        stream_handler.setFormatter(formatter)
        self.logger.addHandler(stream_handler)

    def info(self, msg: str, no_prefix=False, *args, **kwargs):
        if not no_prefix:
            msg = f"{self.pre} {msg}"
        self.logger.info(msg)

    def debug(self, msg, no_prefix=False, *args, **kwargs): pass  # 省略实现


# 具体的产品类
class WriteLocalFileLogger(BaseLogger):
    """
    远程打印日志
    """
    addr = ("117.223.122.11", "6779")

    def __init__(self, logger_name, prefix, print_level=logging.INFO):
        self.pre = prefix
        self.logger = logging.getLogger(logger_name)
        self.logger.setLevel(print_level)
        formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s',
                                      datefmt='%Y-%m-%d %H:%M:%S')
        file_handler = logging.FileHandler("log.txt", "a", "utf-8", delay=2)
        file_handler.setFormatter(formatter)
        self.logger.addHandler(file_handler)

    def info(self, msg: str, no_prefix=False, *args, **kwargs):
        if not no_prefix:
            msg = f"{self.pre} {msg}"

        self.logger.info(msg)

    def debug(self, msg, no_prefix=False, *args, **kwargs): pass  # 省略实现


# Logger的唯一工厂类,需要哪个logger就调用类下的哪个方法
class FactoryLogger:
    """
    工厂类
    @1. 不需要__init__,仅仅是统一管理logger的创建
    @2. 静态方法,不需要实例化,因为没有类变量或实例变量需要调用,省去实例化开销
    """

    @staticmethod
    def get_print_logger(logger_name, prefix, print_level) -> BaseLogger:
        return PrintLogger(logger_name, prefix, print_level)

    @staticmethod
    def get_remote_logger(logger_name, prefix, print_level) -> BaseLogger:
        return WriteLocalFileLogger(logger_name, prefix, print_level)


if __name__ == '__main__':
    global_print_logger = FactoryLogger.get_print_logger("print_logger", "[chaseSpace-SYS]", logging.INFO)
    global_print_logger.info("This is a <print> msg")

    global_remote_logger = FactoryLogger.get_remote_logger("remote_logger", "[chaseSpace-SYS]", logging.INFO)
    global_remote_logger.info("This is a <remote> msg")

写在后面

           文章开头说了,简单工厂模式并不属于标准的设计模式中的一种,为什么呢,因为它这里不太符合开闭原则(对内修改关闭,对外扩展开放),当需要增加新的产品时,显然需要对现有的工厂类进行修改以支持新对象。那为什么这里还要写它?

简单工厂模式在这里仍然给我们提供了以下三点好处:

                 1. 统一了logger对象的使用入口

                 2. 间接隐藏了真实logger的实现

                 3. 后来的开发者如果要新增logger类,只需参考BaseLogger就知道需要实现什么方法了,能够保持代码结构的统一

这里要提的一点是,部分读者可能对代码中使用的Python的abc库比较陌生,简单介绍一下:

    ABC,Abstract Base Class(抽象基类),主要定义了基本类和最基本的抽象方法,可以为子类定义共有的API,不需要具体实现。相当于是Java中的接口或者是抽象类,Go中的接口类似。

    抽象基类(或接口)可以不实现具体的方法(当然也可以实现,只不过python中子类如果想调用抽象基类中定义的方法需要使用super())而是将其留给派生类实现。

    抽象基类提供了逻辑和实现解耦的能力,即在不同的模块中通过抽象基类来调用,可以用最精简的方式展示出代码之间的逻辑关系,让模块之间的依赖清晰简单。同时,一个抽象类可以有多个实现,让系统的运转更加灵活。而针对抽象类的编程,让每个人可以关注当前抽象类,只关注其方法和描述,而不需要考虑过多的其他逻辑,这对协同开发有很大意义。极简版的抽象类实现,也让代码可读性更高。

 

参考:

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值