简体   繁体   中英

Factory method of a python class returning implementation based on system and design issues

1) Introduction

I have started the implementation of a tool in Python that gathers several system metrics (eg cpu utilisation, cpu saturation, memory errors etc.) and presents them to the end-user. This tool should ideally support as many platforms as possible (Linux, FreeBSD, Windows etc.).

I have completed the implementation of this tool for a Linux system for a few metrics I consider important and I have just started to implement the same metrics for a FreeBSD system. This tool must be designed in a way that allows support for more system metrics and for more platforms in the future. Moreover, a web-interface will soon be added and will receive data from my tool.

2) My design decisions so far

For the above reasons, I have decided to implement the tool in Python (convenient to read data from different sources on many systems and ,well, I am somewhat more familiar with it :)) and I am following a class structure for each system metric (inheritance is important as some systems share features so there is no need to rewrite code). Moreover, I have decided that I have a valid use case for using a factory method .

2.1) Class structure

Here is an example class diagram for CPU Metrics (simplified for the sake of the question):

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

2.2) Factory Method

In the Abstract Base Class called CpuMetrics there are some abstract methods defined that should be implemented by inheriting classes and a factory method called get_impl(). I have done some research on when I should use a Factory Method (for example, answers such as this ) and I believe it is valid to use one in my case.

For example, I want a client (eg my web interface) to call my tool to get CPU Utilisation metrics like this:

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) My concern and my question (finally)

Following the above analyzed design, it is very important for my factory method to be informed about which system are we on now ("is this Linux, Windows? Which implementation should I bring now?"). Therefore, I have to heavily rely on functions such as platform.system() or its alternatives. So what my factory method does is (roughly again):

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()

I feel very uncomfortable with this for two reasons:

1) I force a future programmer (or even myself) to follow a naming convention for his class. For example, if someone decides to extend my system, say, for Solaris he absolutely has to name his class SolarisCpuMetrics .

2) If in a future version of Python the values of platform.system() (or an other alternative I will choose to use) are modifiled, then I have to change my naming convention and modify my factory method a lot.

So my question : is there a workaround for my concern ? Will my code become unreadable or my concern is not valid? If you believe there is one workaround, how much do I need to modify / refactor my code and change my design?

I don not have experience in designing projects from scratch, so I could use any advice. Also, I have some more experience in Java. I try to think in a as much pythonic way as possible when writing Python, but sometimes fail to do a proper seperation between the two. Your constructive criticism is very desirable.

Use a class decorator to enumerate classes. And override the 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()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM