Как избежать сумасшедшего количества интерфейсов в пользовательском интерфейсе с внедрением зависимостей?

8

Проблема В
последнее время я много читал о том, что Singletons - это плохо, и как лучше внедрить зависимости (что я понимаю как «использование интерфейсов»). Когда я реализовал часть этого с callbacks / interfaces / DI и придерживаясь принципа разделения интерфейса, я оказался в полном беспорядке.

Зависимости родителя пользовательского интерфейса, где в основном объединены родительские элементы всех его дочерних элементов, поэтому чем дальше вверх по иерархии был элемент пользовательского интерфейса, тем более раздутым был его конструктор.

На вершине иерархии пользовательского интерфейса находился класс Application, содержащий информацию о текущем выборе и ссылку на 3d-модель, которая должна отражать изменения. Класс приложения реализовывал 8 интерфейсов, и это была только пятая часть продуктов (/ интерфейсов)!

В настоящее время я работаю с одноэлементным объектом, содержащим текущий выбор, и элементами пользовательского интерфейса, имеющими функцию для обновления самих себя. Эта функция стекает по дереву UI и элементам UI, а затем обращается к текущему выделенному синглтону по мере необходимости. Код кажется мне чище таким образом.

Вопрос
Может ли синглтон подойти для этого проекта?
Если нет, есть ли фундаментальный недостаток в моем мышлении и / или реализации DI, который делает его таким громоздким?

Дополнительная информация о проекте
Тип: корзина для покупок с квартирами, со свистками
Размер: 2 человеко-месяца для кода и пользовательского интерфейса.
Обслуживание: обновлений нет, но может быть «версия 2.0» позже.
Среда: Использование C # в Unity, который использует Entity Компонентная система

Практически во всех случаях взаимодействие с пользователем запускает несколько действий. Например, когда пользователь выбирает элемент

  • часть пользовательского интерфейса, показывающая этот элемент и его описание, должна быть обновлена. Для этого также необходимо получить некоторую информацию от 3d-модели, чтобы рассчитать цену.
  • дальше пользовательский интерфейс, общая общая стоимость должна быть обновлена
  • соответствующая функция в классе на 3d-модели должна быть вызвана для отображения изменений
Р. Шмитц
источник
DI - это не только использование интерфейсов, это возможность поменять конкременты, что особенно полезно в сфере юнит-тестирования ...
Робби Ди,
1
Кроме того, я еще не видел проблему, где синглтон является предпочтительным решением. Канонический пример, который люди, кажется, всегда утверждают, - это средство записи журналов, но даже здесь вы можете записывать в различные журналы (ошибки, отладки и т. Д.).
Робби Ди
@RobbieDee Да, DI больше, но я не вижу ситуации, когда использование интерфейса не DI. И если синглтон не является предпочтительным решением - что я также предполагаю - тогда почему предпочтительное решение так грязно? Это мой главный вопрос, но я хотел оставить его открытым, так как иногда общее правило не применяется.
Р. Шмитц
Согласование интерфейса с интерфейсом также является особенностью фреймворков. Они также были на ОО-языках в течение добрых нескольких десятилетий ...
Робби Ди
Я не понимаю вашу точку зрения.
Р. Шмитц

Ответы:

4

Я думаю, что вопрос является симптомом, а не решением.

Недавно я много читал о том, что Singleton плох, и как лучше внедрить зависимости (что я понимаю как «использование интерфейсов»). Когда я реализовал часть этого с callbacks / interfaces / DI и придерживаясь принципа разделения интерфейса, я оказался в полном беспорядке.

Решение ищет проблему; это и неправильное понимание этого, вероятно, портят ваш дизайн. Вы читали этот ТАК вопрос о DI против Singleton, разные концепции или нет? Я прочитал это как простую упаковку синглтона, чтобы клиенту не приходилось иметь дело с синглтоном. It.is.just.good.old.encapsulation. Я думаю, что это место.


чем дальше вверх по иерархии был элемент пользовательского интерфейса, тем более раздутым был его конструктор.

Сначала создайте меньшие биты, затем передайте их в конструктор того, к чему они принадлежат, и передайте эту более крупную вещь в конструктор следующей, более крупной вещи ...

Если у вас есть сложные строительные проблемы, используйте шаблон фабрики или строителя. Суть в том, что сложная конструкция включена в свой класс, чтобы другие классы были аккуратными, чистыми, понятными и т. Д.


Класс приложения реализовывал 8 интерфейсов, и это была только пятая часть продуктов (/ интерфейсов)!

Это звучит как отсутствие способности к расширению. Основной дизайн отсутствует, и все забивается сверху. Должно быть больше восходящего построения, композиции и наследования.

Интересно, твой дизайн "тяжелый". Похоже, мы пытаемся сделать один класс всем или чем угодно. И тот факт, что это класс пользовательского интерфейса, а не класса бизнес-домена, заставляет меня задуматься о разделении интересов.

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


... 3d модель, которая должна отражать изменения.

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

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


Дизайн и DI

90% всех внедрений зависимостей, которые вы когда-либо будете делать, - это передача параметров конструктора. Так говорит тот, кто написал книгу . Хорошо спроектируйте свои классы и избегайте загрязнения этого мыслительного процесса некоторыми смутными представлениями о необходимости использования DI-контейнера. Если вам это нужно, ваш дизайн предложит вам это, так сказать.


Сосредоточьтесь на моделировании торговых домов.

Избегайте подхода Джессики Симпсон к дизайну : «Я совершенно не знаю, что это значит, но я хочу этого».

Следующее просто неправильно:

  • Я должен использовать интерфейсы
  • Я не должен использовать синглтон
  • Мне нужен DI (что бы это ни было)
  • Я должен использовать композицию, а не наследование
  • Я должен избежать наследства
  • Мне нужно использовать шаблоны
radarbob
источник
Я пытался избавиться от всех синглетов, которые я использовал, и некоторые из вещей, которые вы сказали, происходили более или менее автоматически. Остальное тоже имеет смысл, и я приму ваш совет и прочту информацию о посетителях и т. Д. В общем, хорошо написанный ответ, спасибо!
Р. Шмитц
Всего лишь одно замечание, и я не пытаюсь быть здесь вампирским помощником, но это было своего рода частью первоначального вопроса: общее мнение (и оно основано на веских причинах), похоже, заключается в том, что вы действительно не должны использовать синглтон. Вы пишете: «Я не должен использовать синглтон, это неправильно» - в какой ситуации было бы целесообразно использовать его тогда?
Р. Шмитц
Все, что я говорю, это «это зависит». Слишком часто я вижу принципы, взятые буквально.
радаробоб
3

«Классовая иерархия» - это немного красного флага. Гипотетический: веб-страница с 5 виджетами. Страница не является предком ни одного из виджетов. Может содержать ссылку на эти виджеты. Но это не предок. Вместо класса heirarchy, рассмотрите возможность использования композиции. Каждый из 5 виджетов может быть создан самостоятельно, без ссылки на другие виджеты или верхнюю страницу. Затем создается верхняя страница с достаточным количеством информации для построения базовой страницы и разметки объектов виджетов (Коллекция), которые передаются ей. Страница отвечает за макет и т. Д., Но не за конструкцию и логику виджетов.

Как только вы используете композицию, DI становится вашим лучшим другом. DI позволяет вам менять каждый виджет на любую версию или тип виджета, который вы определяете в DI. Конструкция виджетов фиксируется в DI и отделена от верхней страницы. Возможно, даже состав Коллекции может быть определен в вашем DI. Верхняя страница выполняет макет на основе переданных ей виджетов, как и должно быть. Никаких изменений в конструкторе верхней страницы не требуется. Верхняя страница нуждается только в логике для выполнения компоновки виджетов и передачи информации в / из виджета на основе определенных интерфейсов.

Вместо того, чтобы передавать слушателей вверх и вниз по цепочке композиции, вставьте коллекцию слушателей тем виджетам, которые в этом нуждаются. Вставьте коллекцию в издателя, чтобы они могли опубликовать в коллекции. Вставьте коллекцию в слушателей, чтобы они могли добавить себя в коллекцию. Коллекция охватывает все объекты в цепочке композиции.

scorpdaddy
источник
Извините, «иерархия классов» была неправильной формулировкой здесь; на самом деле это была не иерархия классов, а иерархия пользовательского интерфейса. «Виджеты» не являются подклассами класса приложения, но «страница» содержит ссылки на них для публикации обновлений пользовательского интерфейса. С этой структурой это было очень тестируемо, но было также много накладных расходов, особенно в конструкторах, просто передавая много слушателей для своих (UI) потомков.
Р. Шмитц
@ R.Schmitz: были ли накладные расходы? Был ли вялый пользовательский интерфейс и был ли виноват дизайн, который вы описали?
Роберт Харви
@ R.Schmitz Можете ли вы рассказать о том, как "передать много слушателей"? Возможно, мой опыт работы с DI в C # низок, но что-то подсказывает мне, что в правильном дизайне не будет необходимости (по крайней мере, в конструкторе).
Katana314
@RobertHarvey Без потери производительности, речь идет только о читабельности.
Р. Шмитц
@ Katana314 Например, когда элемент добавляется, 3d-модель необходимо обновить, и она не принадлежит ни к одной из категорий продуктов, поэтому на нее ссылается класс приложения, который прослушивает добавляемые / удаляемые / измененные элементы. В конце концов, это будет практически одинаково для каждой категории продуктов. Другие части пользовательского интерфейса также реагируют (и слушают), но сообщение должно перемещаться к вершине, потому что там находится ссылка на 3d-модель и фактические данные (которые затем также обновляются).
Р. Шмитц