#c# #visual-studio #design-patterns
#c# #visual-studio #шаблоны проектирования
Вопрос:
В моем решении у меня есть несколько проектов:
- Основной проект. Этот проект содержит все мои классы и интерфейсы
- Проект Winforms
- Веб-проект
Ключевой особенностью приложения является динамическая генерация элементов управления. У меня есть класс с перечислением, и на основе значения перечисления должен быть возвращен определенный элемент управления. Итак, я создал фабрику в основном проекте следующим образом:
public IControl Create(Enum enum)
{
switch(enum)
{
case "enum.A":
return new CheckBoxControl();
case "enum.B":
return new RadioControl();
}
}
Как вы могли догадаться, у меня есть пользовательские элементы управления. Все они наследуются от интерфейса (IControl). Но в этом случае CheckBoxControl и RadioControl являются элементами управления WinFroms.
Теперь эти элементы управления находятся в основном проекте. Я не хочу этого, потому что это базовая библиотека, и я хочу, чтобы эта библиотека была независимой от платформы.
Я хочу, чтобы элементы управления winforms были в проекте WinCore, а WebControls — в проекте WebCore. Проблема в том, как я могу вернуть правильные элементы управления из основного проекта?
И WebCore, и WinCore project нуждаются в ссылке на основной проект, потому что основной проект содержит мои объекты. Это означает, что я не могу установить ссылку из ядра веб- или wincore-проекта.
Как это решить?
Ответ №1:
Я бы сделал это:
-
должна быть сборка, содержащая business .Core.
-
.Shared должна быть сборкой, содержащей инфраструктуру, базовые классы и интерфейсы, не зависящие от платформы и технологии — так что это позволит вам ссылаться на нее в любом проекте, избегая нежелательных зависимостей -.
-
.Web должна быть сборкой, содержащей веб-ориентированную функциональность
-
должна быть сборка, содержащая только веб-формы и веб-элементы управления .Web.Forms.
-
.Windows должна быть сборкой, поддерживающей функциональность, ориентированную на Windows.
-
.Windows.Формы должны быть сборкой, содержащей только Windows Forms и элементы управления.
Имея такую структуру сборки, интерфейс IControl должен быть в .Общая сборка, на которую должно ссылаться .Core, что позволяет коду добавлять экземпляры IControl без необходимости использования зависимостей, зависящих от конкретной технологии.
Кроме того, этого решения достаточно, чтобы позволить .Windows, .Windows.Ссылки на формы, .Web и .Web.Forms.Core и / или .Shared.
Обновить:
Это всего лишь предложение, но почему вы используете перечисление на своей фабрике? Вы могли бы это сделать:
public TControl Create<TControl>() where TControl : IControl, new()
{
IControl control = new TControl();
// Control initialization work
return control;
}
Что вы думаете?
Неважно, это было просто предложение, потому что, возможно, с некоторым рефакторингом вы могли бы использовать такой фабричный метод, но я понимаю, что это далеко от вашей текущей реализации. Сосредоточьтесь лучше на моем ответе 🙂
ОБНОВЛЕНИЕ 2:
Как инверсия управления может помочь вам в вашей проблеме?
Фактически, если вы зарегистрируете инверсию компонентов управления, вы получите доступ к ним по типу или по некоторому уникальному идентификатору.
Допустим, у вас есть IControl
интерфейс (сервис), и оба реализуют WindowsTextBox и WebFormsTextBox IControl
. В вашем файле конфигурации у вас будет два компонента, которые имеют одинаковый тип сервиса (интерфейс) и две разные реализации, и они будут помечены двумя разными идентификаторами:
- WindowsTextBox — это IControl с идентификатором WindowsTextBox.
- WebFormsTextBox — это IControl с идентификатором WebFormsTextBox.
Теперь у вас оба элемента управления зарегистрированы в вашем контейнере inversion of control, и вы можете использовать фабрику извлечения компонентов собственного API следующим образом:
-
IoCFactory.GetComponents<IControl>()
. Вам нужны все реализации IControl в текущей инверсии контейнера элемента управления — в вашем случае не рекомендуется! -. -
IoCFactory.GetComponent<IControl>("WindowsTextBox")
. Вам нужна реализация IControl под названием «WindowsTextBox».
Во втором случае, на котором мы сейчас сосредоточимся, вы получаете объект, набранный как IControl, что означает, что вы можете получить доступ к его общим элементам (свойствам, методам, событиям …).
В чем смысл инверсии управления в вашей проблеме? Обратите внимание, что вы используете класс factory, который будет размещен в общей библиотеке, чтобы вы могли использовать его где угодно, и то же самое для интерфейса IControl.
Верно, я думаю, вам нужны конкретные экземпляры управления. Вы можете сделать две вещи:
-
Просто поиграйте с экземплярами IControl и где угодно, в логике, зависящей от конкретной технологии, приведите его к конкретному типу — например, WindowsTextBox — и продолжайте.
-
Создайте как можно больше интерфейсов, которые конкретизируют больше IControl, чтобы некоторая логика могла получить доступ к некоторым свойствам и методам, которые ближе к конкретному набору функций управления. Примером этого может быть интерфейс ITextBox, и получить любую инверсию управляющего компонента с помощью этого более конкретного интерфейса таким же образом, как я объяснил выше.
Я полагаю, что дал достаточно подсказок, чтобы получить представление о том, как структура моего решения и организация сборки, плюс инверсия управления, могут дать вам такую технологическую независимость, что вы смотрите между слоями вашего проекта и внутри них ! 😉
Комментарии:
1. И где мне разместить свою фабрику, которая возвращает мои элементы управления?
2. Если ваша фабрика создает экземпляры IControl, вы можете поместить его в . Shared или .Core , но я предпочту сказать это окончательно . Shared — лучшее место, потому что вам не нужна зависимость «core» в вашем пользовательском интерфейсе.
3. Ваше обновление: я не настолько знаком с дженериками. Я не совсем понимаю это.
4. Моя фабрика создает конкретные классы. Итак, мне нужно (я думаю) 2 фабрики: фабрика wincore и фабрика webcore, которые оба возвращают конкретные элементы управления win- или web
5. Я объявляю фабричный метод, который вернет реализацию IControl, потому что где указано, что TControl будет принадлежать IControl и у него должен быть общедоступный конструктор без параметров.
Ответ №2:
Добавьте Controlmanager в ядро с помощью метода RegisterControls. Webcore вызывает Controlmanager.Метод RegisterControls. Основное приложение взаимодействует только с core
Комментарии:
1. Мне нравится идея такого ControlManager. Не думал об этом, но где я должен зарегистрировать эти элементы управления?
2. main выполняет webcore. Регистр, который вызывает core.register
Ответ №3:
Вы должны использовать другую фабрику в проекте WinForms и в веб-проекте. Создайте IControlFactory
интерфейс, который реализован на обеих фабриках, и создайте экземпляр фабрики соответствующего типа в каждом проекте.
Комментарии:
1. Я думал об этом, но это не сработает, потому что конкретные фабрики находятся в wincore и webcore, поэтому основной проект должен знать об этих конкретных классах, что означает, что я должен установить ссылку из ядра на web и wincore, а это невозможно.
2. Нет, это не нужно знать. Основной проект не должен знать о конкретных реализациях, он должен знать только об интерфейсе. Конкретная реализация должна быть создана в проектах web и winforms и передана в основной проект
3. Хорошо, но у меня есть несколько конкретных классов, которые реализуют один и тот же интерфейс. Если на заводе есть интерфейс, как я должен узнать, какой конкретный класс создавать?
4. Веб-проект создаст WebControlFactory, а проект WinForms создаст WinFormsControlFactory. В обоих случаях фабрика будет передана в основной проект как IControlFactory.
5. Я думаю, что понимаю, что вы имеете в виду, но в моей ситуации это не сработает. У меня есть объект-посредник с вызываемым методом
GetControls()
. Этот посредник размещен в основном проекте. В этом методе я создаю объект factory и вызываюCreate
метод с параметром (чтобы фабрика могла создать объект на основе параметра). Когда фабрика создала экземпляр, метод возвращает этот объект. Итак, я (думаю) не могу воспользоваться вашим предложением, или могу?