анти-паттерн “беспорядочный полиморфизм”
-
23-08-2019 - |
Вопрос
В довольно большом приложении Ruby мы сталкиваемся с ситуацией, когда данный объект идентифицируется несколькими вещами:скажем, имя и удостоверение личности.Каждый из этих типов значений служит несколько иной цели и поэтому не совсем эквивалентен (идентификатор и имя сохраняются в разных местах).Таким образом, мы получаем множество значений, передаваемых по всему приложению (идентификаторы, имена и объекты).Эта ситуация кажется проблемой, по крайней мере, в некоторой степени, поскольку мы уже сталкивались с ошибками, из-за которых было неясно, какой тип следует передавать данной функции.Я действительно припоминаю, что видел подобный проблемный код в ряде приложений на протяжении многих лет, хотя я опять-таки никогда не давал ему конкретного названия.
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
Распространение этого кода по нашей кодовой базе (а не только по инициализаторам) приводит к тому, что я бы назвал "беспорядочным полиморфизмом".Это часто срабатывает, но иногда создает очень запутанные ситуации.
У меня есть три вопроса по этому поводу.
- Есть ли официальное название для этого как анти-шаблон?"Беспорядочный интерфейс?", "Беспорядочный полиморфизм?" или что-то еще?
- Насколько плохо, по мнению людей, это делать?
- Существует ли систематический способ рефакторинга это?Проблема, с которой мы сталкиваемся при обычном рефакторинге многие тесты, которые мы создаем, используют это свободная типизация, и поэтому нам пришлось бы изменить оба тесты и реализации одновременно и, следовательно, не будет иметь эффекта каркаса обычного тестирования на основе рефакторинг.Я думаю, что на самом деле можно было бы "усилить" этот свободный полиморфизм, и абстрагировать код в функцию, а не сразу его удалять.Но будет ли это хорошей идеей?
Решение
Разве вы не можете определить какой-нибудь метод get_id, чтобы идентификатор возвращал сам себя, объект возвращал идентификатор, а имя делало все, что ему нужно для получения идентификатора?Тогда вы всегда можете канонизировать все, что, как вы знаете, будет одним из трех.Аналогично с методами get_name и get_object, если они необходимы.
То есть вы определили неявный интерфейс ThingWhatHasAnID и сделали его утиным типом параметра вашей функции.
Если я чего-то не упускаю, я бы назвал этот антишаблон "упущенной возможностью создать абстракцию".
Другие советы
Почти каждый раз, когда вы обнаруживаете, что включаете класс объекта, это подсказка к тому, что поведение должно быть методом самого объекта.Отправка сообщения является полиморфный на основе приемника.В этом случае это должно быть что-то вроде:
def initialize starting_value
@starting_id = starting_value.id
end
Определить id
делать все, что угодно из различных get_id_from_*
методы, используемые для этого.Случай незаконного типа уже будет поднят, потому что вы получите NoMethodError .
Что касается того, как это назвать, я бы назвал это "процедурным программированием на языке OO".