анти-паттерн "грязный полиморфизм" - PullRequest
5 голосов
/ 03 апреля 2009

В довольно большом приложении на Ruby возникает ситуация, когда данный объект идентифицируется с помощью нескольких вещей: name и id, скажем. Каждый из этих типов значений служит несколько разной цели и поэтому не является абсолютно эквивалентным (идентификатор и имя сохраняются в разных местах). Таким образом, мы получаем множество значений, передаваемых приложению (идентификаторы, имена и объекты). Эта ситуация кажется проблемой, по крайней мере, в некоторой степени, так как нас уже укусили ошибки, связанные с непонятностью, какой тип должен быть передан данной функции. Я на самом деле помню, как видел код проблемы в ряде приложений на протяжении многих лет, хотя я снова никогда не давал ему конкретного имени.

Ruby, как язык без типов, не допускает классических полиморфных функций на основе типов, как это делает C ++. В качестве обходного пути коллега часто прибегает к коду такого рода:

  def initialize starting_value
    if starting_post.kindof? Foo
      @starting_id = get_id_from_foo starting_value
    elsif starting_post.kindof? Bar
      @starting_id = get_id_from_bar starting_value
    else
      raise "illegal type"
    end
  end

Распространение этого кода вокруг нашей кодовой базы (не только инициализаторов) приводит к тому, что я бы назвал "грязным полиморфизмом". Это часто работает, но иногда создает очень загадочные ситуации.

У меня есть три вопроса по этому поводу.

  • Есть ли официальное название для этого как анти-модель? «Грязный интерфейс?», «Грязный полиморфизм?» или что-то еще?
  • Насколько плохо люди думают, что это так?
  • Есть ли систематический способ рефакторинга? этот? Задача, которую мы имеем с обычный рефакторинг, что многие тесты, которые мы создали, используют это свободная печать, и поэтому мы должны были бы изменить оба тесты и реализации одновременно и поэтому не будет иметь эффект эшафот от обычного теста на основе рефакторинг. Я думаю, что на самом деле можно «усилить» этот свободный полиморфизм, и абстрагируйте код в функцию, а не немедленно вырвите его. Но бы это хорошая идея?

Ответы [ 2 ]

7 голосов
/ 03 апреля 2009

Разве вы не можете определить какой-то метод get_id, чтобы идентификатор возвращал себя, объект возвращает идентификатор, а имя делает все, что нужно для получения идентификатора? Тогда вы всегда можете канонизировать все, что вы знаете, будет одним из трех. Аналогично с методами get_name и get_object, если они необходимы.

То есть, вы определили неявный интерфейс ThingWhatHasAnID и сделали тип утилитой вашего параметра функции.

Если бы я что-то не упустил, я бы назвал этот анти-шаблон "упущенной возможностью создать абстракцию".

4 голосов
/ 03 апреля 2009

Почти каждый раз, когда вы переключаетесь на класс объекта, это подсказка, что поведение должно быть методом самого объекта. Отправка сообщения является полиморфной в зависимости от получателя. В этом случае это должно быть что-то вроде:

def initialize starting_value
  @starting_id = starting_value.id
end

Определите id, чтобы использовать любые методы get_id_from_*, используемые для этого. Случай с недопустимым типом уже возникнет, потому что вы получите NoMethodError.

Что касается того, как это назвать, я бы назвал это «процедурным программированием на языке ОО».

...