Что делает статические функции инициализации хорошими, плохими или нет? - PullRequest
1 голос
/ 07 апреля 2010

Предположим, у вас был такой код:

_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 s можно создать безего предварительные условия выполняются
  2. Код не легко тестируется, поскольку ConnPool не может быть напрямую смоделирован.
  3. Init должен вызываться правильно с первого раза.Если его изменили, чтобы его можно было вызывать несколько раз, необходимо добавить дополнительную логику, чтобы проверить, определены ли уже переменные, и необходимо пропустить множество значений NULL, чтобы пропустить повторную инициализацию.
  4. В событииЧто касается потоков, вышеприведенное становится более сложным, добавляя блокировку
  5. Глобальные переменные не используются для сообщения о состоянии (что не совсем плохо, но запах кода)

НаС другой стороны, некоторые плюсы:

  1. очень удобно звонить Init(5, "user/pass", 2, "user/pass")
  2. Это просто и "чисто"

Лично я считаю минусыперевешивают плюсы, то есть тестируемость и гарантированные предварительные условия перевешивают простоту и удобство.

1 Ответ

2 голосов
/ 07 апреля 2010

Мне кажется, что единственной проблемой в этом примере является использование глобального состояния.Так что не делайте этого.

Серьезно - проблемы, о которых вы беспокоитесь, решаются путем передачи соответствующего контекста Делателю, когда он появляется.В некоторых случаях «соответствующий контекст» может представлять собой пару простых аргументов (например, список читателей и список авторов) или это может быть более сложный агрегатный объект («менеджер соединений», в который могут добавляться и удаляться внешние соединения).любому, кто ссылается на него).

Чтобы явно указать ваши минусы:

  1. , если у Doer есть предварительные условия, проверьте их.Если они не выполнены, выведите исключение.
  2. (Решено, если ConnPool передается в качестве параметра в ctor или рабочую функцию.)
  3. Сделайте, чтобы Init создал вещь, которую вы передадите Doerвместо создания глобальных данных.Для издевательства, передать класс, который будет построен?По сути, используйте какую-то фабрику.
  4. Вам нужно беспокоиться только о безопасности потоков в вашем состоянии, если они являются общими.Если у каждого потока есть собственный менеджер соединений (например), то на этом уровне нечего блокировать.
  5. (Решено, если вы не используете глобальные переменные, естественно.)

Так что это не особенно удобно:

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, что, очевидно, будет соответствовать вашим требованиям.)

...