Я создаю приложение WinForms с пользовательским интерфейсом, который состоит только из NotifyIcon
и его динамически заполненного ContextMenuStrip
. Существует MainForm
, чтобы удерживать приложение вместе, но это никогда не отображается.
Я решил построить это как SOLIDly, насколько это возможно (используя Autofac для обработки графа объектов), и я очень доволен своим успехом, в основном хорошо справляясь даже с O-частью. С расширением, которое я в настоящее время реализую, кажется, я обнаружил недостаток в моем дизайне и немного нуждаюсь в переделке; Я думаю, знаю, как мне нужно идти, но немного неясно, как точно определить зависимости.
Как уже упоминалось выше, меню запускается динамически динамически после запуска приложения. Для этой цели я определил интерфейс IToolStripPopulator
:
public interface IToolStripPopulator
{
System.Windows.Forms.ToolStrip PopulateToolStrip(System.Windows.Forms.ToolStrip toolstrip, EventHandler itemclick);
}
Реализация этого вводится в MainForm
, а метод Load()
вызывает PopulateToolStrip()
с помощью ContextMenuStrip
и обработчика, определенного в форме. Зависимости между populator связаны только с получением данных, которые будут использоваться для элементов меню.
Эта абстракция хорошо работала через несколько этапов эволюции, но этого недостаточно, когда мне нужно больше одного обработчика события, например. потому что я создаю несколько разных групп элементов меню, которые все еще скрыты за единственным интерфейсом IToolStripPopulator
, потому что форма не должна касаться этого вообще.
Как я уже сказал, я думаю, что знаю, какова должна быть общая структура - я переименовал интерфейс IToolStripPopulator
в нечто более конкретное * и создал новый, метод PopulateToolStrip()
не принимает параметр EventHandler
который вместо этого вводится в объект (что также обеспечивает гораздо большую гибкость в отношении количества обработчиков, необходимых для реализации и т.д.). Таким образом, мой "передовой" IToolStripPopulator
может быть легко адаптером для любого числа конкретных.
Теперь я не понимаю, как мне разрешить зависимости EventHandler. Я думаю, что обработчики должны быть определены в MainForm
, потому что у этого есть все другие зависимости, необходимые для правильного реагирования на события меню, а также "владеет" меню. Это означало бы, что мои зависимости для объектов IToolStripPopulator
, которые в конечном итоге были введены в MainForm, должны были бы зависеть от самого объекта MainForm
, используя Lazy<T>
.
Моя первая мысль заключалась в определении интерфейса IClickHandlerSource
:
public interface IClickHandlerSource
{
EventHandler GetClickHandler();
}
Это было реализовано моим MainForm
, и моя конкретная реализация IToolStripPopulator
зависела от Lazy<IClickHandlerSource>
. Хотя это работает, оно негибкое. Я либо должен был бы определить отдельные интерфейсы для потенциально растущего числа обработчиков (строго нарушая OCP с классом MainForm
), либо постоянно расширяя IClickHandlerSource
(в первую очередь, нарушая ISP).
Непосредственное использование зависимостей от обработчиков событий выглядит как хорошая идея со стороны потребителей, но индивидуальная проводка конструкторов через свойства ленивого экземпляра (или тому подобного) кажется довольно грязной - если возможно вообще.
Моя лучшая ставка в настоящее время выглядит так:
public interface IEventHandlerSource
{
EventHandler Get(EventHandlerType type);
}
Интерфейс по-прежнему будет реализован MainForm
и вводится как ленивый синглтон, а EventHandlerType
будет настраиваемым перечислением с различными типами, которые мне нужны. Это все равно не будет очень совместимым с OCP, но достаточно гибким. EventHandlerType
, очевидно, будет иметь изменение для каждого нового типа обработчика событий, равно как и логику разрешения в MainForm
, в дополнение к самому новому обработчику событий и (возможно) вновь написанную дополнительную реализацию IToolStripPopulator
.
Или.... отдельная реализация IEventHandlerSource
, которая (как единственный объект) принимает зависимость от Lazy<MainForm>
и разрешает опции EventHandlerType
для определенных обработчиков, определенных в MainForm
?
Я пытаюсь думать о способе фактического получения обработчиков событий из MainForm
допустимым способом, но сейчас это не совсем похоже.
Каков мой лучший вариант здесь, обеспечивающий самое слабое соединение и наиболее элегантное разрешение различных обработчиков событий?
[* Да, я, вероятно, должен был оставить только имя, чтобы действительно соответствовать OCP, но это выглядело лучше.]