Я проектирую простой механизм создания форм, в котором пользователи могут создавать новые формы, используя различные типовые поля полей (Date, Text, DropDown)
Я решил смоделировать объект домена (поле формы) независимо от объектов, которые будут использоваться для визуализации этих полей в пользовательском интерфейсе.
Вот интерфейс, который определяет контракт для домена и некоторые его специализации:
namespace Acme.Core.Domain{
public interface IFormField
{
bool Visible { get; set; }
string Key { get; set; }
event EventHandler<FieldVisibilityChangedEventArgs> VisibilityChanged;
FieldType Type{get;}
void Validate(IEnumerable<ValidationError> errors);
int DataId {get;set;}
}
public interface IDropDownField:IFormField{
IDictionary<string, string> Items { get; set; }
KeyValuePair<string, string> SelectedValue { get; set; }
}
public interface IDateField:IFormField{
DateTime? SelectedDate{get;set}
}
}
Для пользовательского интерфейса я построил иерархию параллельных типов. Это сохраняет объект домена, который связан с бизнес-правилами вокруг проверки данных, отдельно от вопросов пользовательского интерфейса, а именно, как визуализировать данное поле (MVC HtmlHelper против WebForm WebControl):
namespace Acme.UI{
public interface IControl
{
//parallel to IFormField
bool Visible { get; set; }
string ID { get; set; }
}
public interface IDropListControl:IControl
{
//parallel to IDropDownField
}
public interface IDatePickerControl: IControl
{
//parallel to IDateField
}
public interface IControlFactory {
IControl CreateControl(IFormField field);
}
}
Хотя этот дизайн дает мне свободу в разработке модели предметной области независимо от пользовательского интерфейса, я не нашел чистого способа соединения и управления двумя иерархиями. Я чувствую, что должен иметь возможность использовать универсальные шаблоны для соединения параллельных классов друг с другом, но я не совсем понимаю, как это будет выглядеть. Существует ли шаблон, который решает проблему ассоциации или вообще устраняет необходимость в параллельных иерархиях классов?
РЕДАКТИРОВАТЬ: Мой уровень пользовательского интерфейса ссылается на уровень моего бизнеса (Core.csproj). Вот несколько примеров того, как я подключаю иерархию классов пользовательского интерфейса к иерархии классов домена. Эти типы в настоящее время не используют дженерики, но я чувствую, что они должны.
// create concrete instances of IControl based on the the domain object passed in
public interface IControlFactory {
IControl CreateControl(IFormField field);
}
// scrape values form the UI controls and apply them to the appropriate domain object
public interface IFormFieldDataBinder{
void Bind(IFormField field, IControl control);
}