【问题标题】:Factory method of a python class returning implementation based on system and design issues基于系统和设计问题的python类返回实现的工厂方法
【发布时间】:2013-11-30 00:40:23
【问题描述】:

1) 简介

我已经开始在 Python 中实施一个工具,该工具收集多个系统指标(例如 cpu 利用率、cpu 饱和度、内存错误等)并将它们呈现给最终用户。此工具应理想地支持尽可能多的平台(Linux、FreeBSD、Windows 等)。

我已经针对一些我认为重要的指标为 Linux 系统完成了此工具的实现,并且我刚刚开始为 FreeBSD 系统实现相同的指标。此工具的设计方式必须允许支持更多系统指标和未来更多的平台。此外,很快将添加一个网络界面,并将接收来自我的工具的数据

2) 到目前为止我的设计决策

由于上述原因,我决定在 Python 中实现该工具(方便在许多系统上从不同来源读取数据,而且,我对它有点熟悉 :))和我为每个系统指标遵循一个类结构(继承很重要,因为某些系统共享功能,因此无需重写代码)。此外,我已经确定我有一个使用工厂方法的有效用例。

2.1) 类结构

这是 CPU Metrics 的示例类图(为了问题而进行了简化):

                  CpuMetrics        (Abstract Base Class)
                 /      |    \
                /       |     \
               /        |      \
              /         |       \
             /          |        \
            /           |         \
LinuxCpuMetrics  FreeBSDCpuMetrics WindowsCPUMetrics   (per OS)
           /  \ 
          /    \
         /      \
        /        \
       /          \
      /            \
     /              \
ArchLinuxCpuMetrics  DebianLinuxCpuMetrics             (sometimes important per Distro or Version)

2.2) 工厂方法

在名为CpuMetrics的抽象基类中定义了一些抽象方法,应通过继承类和一个名为get_impl()的工厂方法来实现。我对何时应该使用工厂方法进行了一些研究(例如,this 之类的答案),我认为在我的情况下使用它是有效的。

例如,我希望客户端(例如我的 Web 界面)调用我的工具来获取 CPU 利用率指标,如下所示:

cpu_metrics = CpuMetrics.get_impl() # factory method as an alternative constructor
cpu_metrics.get_cpu_util() # It is completely transparent for the client which get_cpu_util() is returned.

3) 我的担忧和我的问题(最后)

按照上面分析的设计,我的工厂方法知道我们现在在哪个系统上是非常重要的(“这是Linux,Windows?我现在应该带哪个实现?”)。因此,我必须高度依赖诸如platform.system() 之类的函数或其替代品。所以我的工厂方法所做的是(大致再次):

def get_impl():
    """Factory method returning the appropriate implementation depending on system."""
    try:
        system = platform.system() # This returns: { Linux, Windows, FreeBSD, ... }
        system_class = getattr("cpu", system + "CpuMetrics" )
    except AttributeError, attr_err:
        print ("Error: No class named " + system + "CpuMetrics in module cpu. ")
        raise attr_err
    return system_class()

我对此感到非常不舒服,原因有两个:

1) 我强迫未来的程序员(甚至我自己)遵循他的类的命名约定。例如,如果有人决定扩展我的系统,比如说,对于 Solaris,他绝对必须将他的类命名为 SolarisCpuMetrics

2) 如果在 Python 的未来版本中修改了 platform.system() 的值(或我将选择使用的其他替代方法),那么我必须更改我的命名约定并大量修改我的工厂方法。

所以我的问题:是否有解决我问题的方法?我的代码会变得不可读或我的担忧无效吗?如果您认为有一种解决方法,我需要多少修改/重构我的代码和更改我的设计?

我没有从头开始设计项目的经验,所以我可以参考任何建议。另外,我在 Java 方面有更多经验。在编写 Python 时,我尝试以尽可能多的 Python 方式思考,但有时无法在两者之间进行适当的分离。您的建设性批评非常可取。

【问题讨论】:

    标签: python


    【解决方案1】:

    使用类装饰器枚举类。并覆盖allocator

    sysmap = {}
    
    class metric:
      def __init__(self, system):
        self.system = system
      def __call__(self, cls):
        sysmap[self.system] = cls
        return cls
    
    class CpuMetrics:
      def __new__(self):
        cls = sysmap.get(platform.system)
        if not cls:
          raise RuntimeError('No metric class found!')
        else:
          return cls()
       ...
    

    ...

    @metric('Linux')
    class SomeLinuxMetrics(CpuMetrics):
       ...
    

    ...

    metrics = CpuMetrics()
    

    【讨论】:

    • 感谢您的回答。它工作得很好。我只想补充一点,子类(例如 SomeLinuxMetrics )还必须覆盖分配器 __new__ 以避免无限递归。我猜你可能是认真的:)
    • @ArtemTsikiridis:是的,我忘了检查cls is self
    猜你喜欢
    • 2018-02-10
    • 2011-07-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多