是什么让静态初始化函数变好,坏或其他?

时间:2010-04-07 01:57:19

标签: design-patterns testing

假设你有这样的代码:

_READERS = None
_WRITERS = None

def Init(num_readers, reader_params, num_writers, writer_params, ...args...):
  ...logic...
  _READERS = new ReaderPool(num_readers, reader_params)
  _WRITERS = new WriterPool(num_writers, writer_params)
  ...more logic...

class Doer:
  def __init__(...args...):
    ...
  def Read(self, ...args...):
    c = _READERS.get()
    try:
      ...work with conn
    finally:
      _READERS.put(c)
  def Writer(...):
    ...similar to Read()...

对我而言,这是一个糟糕的模式,有些缺点:

  1. Doer可以在不满足前提条件的情况下创建
  2. 代码不易测试,因为无法直接模拟ConnPool。
  3. 第一次必须正确调用
  4. Init。如果它被更改以便可以多次调用,则必须添加额外的逻辑来检查变量是否已经定义,并且必须传递大量NULL值以跳过重新初始化。
  5. 如果是线程,通过添加锁定
  6. 会使上述内容变得更加复杂
  7. 全球不被用来传达状态(这不是严格的坏事,而是代码味道)
  8. 另一方面,一些专业人士:

    1. 调用Init(5, "user/pass", 2, "user/pass")
    2. 非常方便
    3. 简单而“干净”
    4. 就个人而言,我认为缺点超过了专业人士,即可测试性和有保证的前提条件超过了简单性和便利性。

1 个答案:

答案 0 :(得分:2)

在我看来,这个例子的唯一问题是使用全局状态。所以不要这样做。

说真的 - 你担心的问题可以通过在Doer出现时将适当的上下文传递给Doer来解决。在某些情况下,“适当的上下文”可能是一些简单的参数(例如读者列表和编写者列表),或者它可能是一个更复杂的聚合对象(“连接管理器”可能有连接添加和删除外部任何引用它的人。)

明确解决您的缺点:

  1. 如果Doer有先决条件,请验证它们。如果不符合,则提出异常。
  2. (如果将ConnPool作为参数传递给ctor或worker函数,则解决。)
  3. 让Init创建你传递给Doer的东西,而不是创建全局数据。对于嘲弄,还要传递要构建的类?基本上,使用某种工厂。
  4. 如果共享,您只需担心您所在州的线程安全。如果每个线程都有自己的连接管理器(例如),则没有什么可以锁定在这个级别。
  5. (如果不使用全局变量,则解决,natch。)
  6. 所以这样做并不是特别便宜:

    class ConnPool:
       def __init__(self, numReaders, readerParams, numWriters, writerParams):
          (your InitFunction with a bunch of self. prepending)
    
    class Doer:
       def __init__(self, connPool, ...):
          if not preconditions:
             raise DoerPreconditionsNotMetError()
          self.connections = connPool
    
       def Read(self):
          readers, writers = self.connections._READERS, self.connections._WRITERS
          ...
    

    所以,我不知道。我没有看到它比你的例子更简洁或更不易读。 (或者,您可以将连接管理器传递给Read函数,这显然符合您的要求。)