Factory method of a python class returning implementation based on system and design issues
我已经开始在python中实现一个工具,它收集了几个系统指标(例如CPU利用率、CPU饱和、内存错误等),并将它们呈现给最终用户。这个工具应该理想地支持尽可能多的平台(Linux、FreeBSD、Windows等)。
我已经为Linux系统完成了这个工具的实现,其中有一些我认为很重要的指标,我刚刚开始为FreeBSD系统实现相同的指标。该工具的设计必须能够支持更多的系统度量和未来更多的平台。此外,很快将添加一个Web界面,并从我的工具接收数据。
2)我目前的设计决策出于上述原因,我决定在python中实现该工具(方便在许多系统上从不同的源读取数据,而且我对它比较熟悉:),并且我对每个系统度量都遵循一个类结构(继承很重要,因为一些系统共享特性,所以不需要重写代码)。此外,我已经决定使用工厂方法有一个有效的用例。
2.1)阶级结构下面是一个CPU度量的类图示例(为了这个问题简化了):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | CpuMetrics (Abstract Base Class) / | \ / | \ / | \ / | \ / | \ / | \ LinuxCpuMetrics FreeBSDCpuMetrics WindowsCPUMetrics (per OS) / \ / \ / \ / \ / \ / \ / \ ArchLinuxCpuMetrics DebianLinuxCpuMetrics (sometimes important per Distro or Version) |
2.2)工厂法
在名为cpummetrics的抽象基类中,定义了一些抽象方法,这些方法应通过继承类和名为get_impl()的工厂方法来实现。我做了一些关于何时应该使用工厂方法的研究(例如,这样的答案),我相信在我的案例中使用工厂方法是有效的。
例如,我希望一个客户机(例如我的Web界面)调用我的工具来获取CPU利用率指标,如下所示:
1 2 | 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吗?"我现在应该带来哪种实现呢?")因此,我不得不严重依赖诸如
1 2 3 4 5 6 7 8 9 | 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,他必须将类命名为
2)如果在未来的python版本中修改了
所以我的问题是:我是否有解决办法?我的代码会变得不可读还是我的问题无效?如果您认为有一个解决方法,我需要修改/重构代码和更改设计多少?
我没有从零开始设计项目的经验,所以我可以使用任何建议。另外,我在Java方面有更多的经验。在编写Python时,我尽量用Python式的方式进行思考,但有时无法在两者之间进行适当的分离。你的建设性批评是非常可取的。
使用类修饰器枚举类。并重写分配器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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() ... |
…
1 2 3 | @metric('Linux') class SomeLinuxMetrics(CpuMetrics): ... |
…
1 | metrics = CpuMetrics() |