Вероятно, я бы посчитал запахом кода или даже анти-паттерном иметь класс, реализующий 23 интерфейса. Если это действительно анти-паттерн, как бы вы это назвали? Или просто не следует принципу Единой ответственности?
object-oriented
anti-patterns
solid
Йонас Эльфстрём
источник
источник
Ответы:
Королевская семья: они не делают ничего особенно сумасшедшего, но имеют миллиард титулов и так или иначе связаны с большинством других королевских особ.
источник
somehow
Я утверждаю, что этот анти-паттерн будет назван «Мастер на все руки» или, может быть, «Слишком много шляп»
источник
Если бы мне пришлось дать ему имя, я бы назвал его Гидрой :
Особое значение имеет тот факт, что у него не только много голов, но и растет их все больше и из-за этого невозможно убить. Это был мой опыт с этими видами конструкций; разработчики просто вбивают в него все больше и больше интерфейсов, пока их не будет слишком много, чтобы поместиться на экране, и к тому времени она настолько глубоко укоренится в дизайне программы и предположениях, что ее разделение - безнадежная перспектива (если вы попытаетесь, вы на самом деле часто нужно больше интерфейсов для преодоления разрыва).
Одним из ранних признаков надвигающейся гибели из-за «Гидры» является частое приведение одного интерфейса к другому, без особой проверки работоспособности, и часто к цели, которая не имеет смысла, как в:
Очевидно, когда вырвано из контекста, в этом коде есть что-то подозрительное, но вероятность того, что это произойдет, возрастает с увеличением числа «голов» объекта, потому что разработчики интуитивно знают, что они всегда имеют дело с одним и тем же существом.
Успехи когда - либо делать какое - либо техническое обслуживание на объекте , который реализует 23 интерфейсов. Вероятность того, что вы не нанесете побочный ущерб в процессе, невелика.
источник
Бог Объект приходит на ум; один объект, который знает, как делать все. Это происходит из-за низкой приверженности требованиям «сплоченности» двух основных методологий проектирования; если у вас есть объект с 23 интерфейсами, у вас есть объект, который знает, как быть 23 разными вещами для своих пользователей, и эти 23 разные вещи, вероятно, не соответствуют линии одной задачи или области системы.
В SOLID, хотя создатель этого объекта, очевидно, пытался следовать принципу разделения интерфейса, он нарушил предыдущее правило; Принцип единой ответственности. Есть причина, по которой это «ТВЕРДЫЙ»; S всегда стоит на первом месте, думая о дизайне, и все остальные правила следуют.
В GRASP создатель такого класса пренебрег правилом «Высокой сплоченности»; GRASP, в отличие от SOLID, учит, что объект не ДОЛЖЕН иметь единственную ответственность, но в большинстве случаев он должен иметь две или три очень тесно связанные обязанности.
источник
23 - это просто число! В наиболее вероятном сценарии это достаточно высоко, чтобы встревожить. Однако, если мы спросим, каково наибольшее количество методов / интерфейсов, прежде чем он сможет получить тег для вызова в качестве «анти-паттерна»? Это 5 или 10 или 25? Вы понимаете, что число на самом деле не является ответом, потому что если 10 хорошо, 11 также может быть - и затем любое целое число после этого.
Настоящий вопрос о сложности. И мы должны отметить, что длинный код, количество методов или большой размер класса по любым показателям на самом деле НЕ являются определением сложности. Да, все больший и больший код (большее количество методов) затрудняет чтение и восприятие нового новичка. Он также обрабатывает потенциально различную функциональность, большое количество исключений и довольно развитые алгоритмы для различных сценариев. Это не значит, что это сложно - трудно только переварить.
С другой стороны, код сравнительно небольшого размера, который можно надеяться прочитать через несколько часов, может быть сложным. Вот когда я думаю, что код (излишне) сложен.
Любая мудрость объектно-ориентированного дизайна может быть заложена здесь, чтобы определить «сложный», но я бы ограничился здесь, чтобы показать, когда «так много методов» является признаком сложности.
Взаимное Знание. (иначе говоря) Во многих случаях, когда вещи пишутся как классы, мы все думаем, что это «хороший» объектно-ориентированный код. Но предположение о другом классе существенно нарушает действительно необходимую инкапсуляцию. Когда у вас есть методы, которые «пропускают» глубокие подробности о внутреннем состоянии алгоритмов, - и приложение строится с ключевым предположением о внутреннем состоянии класса обслуживания.
Слишком много повторений (взлом) Когда методы, которые имеют схожие имена, но выполняют противоречивую работу - или противоречат именам со схожей функциональностью. Много раз коды развиваются, чтобы поддерживать немного разные интерфейсы для разных приложений.
Слишком много ролей Когда класс продолжает добавлять побочные функции и продолжает расширяться по мере того, как людям это нравится, нужно только знать, что класс действительно теперь является двумя классами. Удивительно, но все это начинается с подлинноготребования и никакой другой класс не существует для этого. Учтите это, есть класс Transaction, который сообщает подробности транзакции. Пока все выглядит хорошо, теперь кому-то требуется преобразование формата в «время транзакции» (между UTC и т. Д.), Позже люди добавляют правила, чтобы проверить, была ли определенная вещь в определенные даты, чтобы проверить недействительные транзакции. - Я не буду писать всю историю, но в конце концов класс транзакций будет включать в себя весь календарь, и тогда люди начнут использовать его только из одного календаря! Это очень сложно (представить), почему я буду создавать экземпляр "класса транзакции", чтобы иметь функциональность, которую мне предоставил бы "календарь"!
(В) согласованность API Когда я делаю book_a_ticket () - я бронирую билет! Это очень просто, независимо от того, сколько проверок и процессов идет, чтобы это произошло. Теперь это становится сложным, когда поток времени начинает влиять на это. Как правило, можно было бы разрешить «поиск» и возможный доступный / недоступный статус, затем, чтобы уменьшить количество возвратов, вы начинаете сохранять некоторые контекстные указатели внутри заявки, а затем ссылаетесь на нее , чтобы забронировать билет. Поиск - не единственная функциональность - после многих таких «побочных функций» дела ухудшаются. В процессе значение book_a_ticket () подразумевает book_that_ticket ()! и это может быть невообразимо сложным.
Вероятно, есть много таких ситуаций, которые вы бы увидели в очень развитом коде, и я уверен, что многие люди могут добавлять сценарии, где «так много методов» просто не имеет смысла или не делает то, что вы, очевидно, думаете. Это анти-паттерн.
Мой личный опыт показывает, что, когда проекты, которые начинаются разумно снизу вверх, многие вещи, для которых должны были существовать подлинные классы сами по себе - остаются похороненными или, что еще хуже, остаются разделенными между различными классами, и увеличивается сцепление. Чаще всего то, что заслуживало бы 10 классов, но имеет только 4, вполне вероятно, что многие из них имеют многоцелевые запутанные и большое количество методов. Назовите это БОГОМ и ДРАКОНОМ, это то, что ПЛОХО.
НО вы встречаете ОЧЕНЬ БОЛЬШИЕ классы, которые аккуратно согласованы, у них есть 30 методов - и все еще очень ЧИСТЫЕ. Они могут быть хорошими.
источник
Классы должны иметь точно правильное количество интерфейсов; Ни больше ни меньше.
Сказать «слишком много» было бы невозможно без рассмотрения того, служат ли все эти интерфейсы полезной цели в этом классе. Объявление о том, что объект реализует интерфейс, означает, что вы должны реализовать его методы, если ожидаете, что класс скомпилируется. (Я предполагаю, что класс, на который вы смотрите, это делает.) Если все в интерфейсе реализовано, и эти реализации делают что-то относительно внутренностей класса, было бы трудно сказать, что реализации не должно быть. Единственный случай, когда я могу вспомнить, где интерфейс, отвечающий этим критериям, не будет там, - это внешний: когда его никто не использует.
Некоторые языки позволяют «подклассить» интерфейсы с помощью механизма, подобного
extends
ключевому слову Java , и тот, кто его написал, может не знать об этом. Возможно также, что все 23 достаточно обширны, и их объединение не имело смысла.источник
Звучит так, как будто у них есть пара «getter» / «setter» для каждого свойства, как обычно для типичного «bean» и продвигает все эти методы в интерфейс. Так как насчет того, чтобы называть это " бобом "? Или более рабелевская «метеоризм» после хорошо известного воздействия слишком большого количества бобов.
источник
Количество интерфейсов часто растет, когда универсальный объект используется в разных средах. В C # варианты IComparable, IEqualityComparer и IComparer допускают сортировку в разных установках, поэтому вы можете в конечном итоге реализовать все из них, некоторые из них могут быть несколько раз, поскольку вы можете реализовать универсальные строго типизированные версии, а также неуниверсальные версии. Кроме того, вы можете реализовать несколько обобщений.
Давайте рассмотрим пример сценария, скажем, интернет-магазин, в котором вы можете купить кредиты, с помощью которых вы можете купить что-то другое (стоковые фото-сайты часто используют эту схему). У вас может быть класс "Valuta" и класс "Credits", которые наследуют одну и ту же базу. Valuta имеет несколько изящных перегрузок операторов и процедур сравнения, которые позволяют выполнять вычисления, не беспокоясь о свойстве «Валюта» (например, добавление фунтов к долларам). Кредиты намного проще, но имеют другое отличное поведение. Желая иметь возможность сравнивать их друг с другом, вы можете в конечном итоге реализовать IComparable, а также IComparable и другие варианты интерфейсов сравнения на обоих (хотя они используют общую реализацию, будь то в базовом классе или где-то еще).
При реализации сериализации реализуются ISerializable, IDeserializationCallback. Затем реализуем стеки отмены и повторения: добавлен IClonable. Функциональность IsDirty: IObservable, INotifyPropertyChanged. Разрешение пользователям редактировать значения, используя строки: IConvertable ... Список можно продолжать и продолжать ...
В современных языках мы видим другую тенденцию, которая помогает разделить эти аспекты и поместить их в свои собственные классы, вне основного класса. Внешний класс или аспект затем связывается с целевым классом с помощью аннотации (атрибутов). Часто можно сделать классы внешних аспектов более или менее общими.
Использование атрибутов (аннотации) подлежит рефлексии. Один недостаток - незначительная (начальная) потеря производительности. Недостаток (часто эмоциональный) заключается в том, что такие принципы, как инкапсуляция, должны быть смягчены.
Всегда есть другие решения, но для каждого изящного решения есть компромисс или подвох. Например, использование решения ORM может потребовать объявления всех свойств виртуальными. Решения сериализации могут потребовать конструкторов по умолчанию на ваших классах. Если вы используете внедрение зависимостей, вы можете реализовать 23 интерфейса в одном классе.
На мой взгляд, 23 интерфейса не должны быть плохими по определению. За этим может стоять хорошо продуманная схема или какое-то принципиальное определение, например, избегать использования рефлексии или крайних инкапсуляционных убеждений.
Всякий раз, когда вы переключаете работу или вынуждены строить на существующей архитектуре. Мой совет - сначала полностью познакомиться, не пытайтесь все слишком быстро реорганизовать. Послушайте оригинального разработчика (если он все еще там) и постарайтесь выяснить мысли и идеи, стоящие за тем, что вы видите. Задавая вопросы, делайте это не ради того, чтобы разбить его, а чтобы узнать ... Да, у каждого есть свои золотые молотки, но чем больше молотков вы сможете собрать, тем легче вам будет ладить с коллегами.
источник
«Слишком много» субъективно: стиль программирования? спектакль? соответствие стандартам? Прецеденты? просто чувство комфорта / уверенности?
Пока ваш код ведет себя правильно и не вызывает проблем с обслуживаемостью, 23 может даже стать новой нормой. Однажды я мог бы сказать: «Вы можете сделать отличную работу с 23 интерфейсами, см. Jonas Elfström».
источник
Я бы сказал, что предел должен быть меньше, чем 23 - больше как 5 или 7,
однако это не включает в себя любое количество интерфейсов, которые наследуют эти интерфейсы, или любое количество интерфейсов, реализованных базовыми классами.
(Таким образом, всего N + любое количество унаследованных интерфейсов, где N <= 7.)
Если ваш класс реализует слишком много интерфейсов, это, вероятно, божественный класс .
источник