如何管理(或消除)并行的类层次结构?
我正在设计一个简单的表单创建引擎,用户可以在其中使用各种样板字段类型(日期、文本、DropDown)编写新表单
我决定独立于用于渲染的对象对域对象(表单字段)进行建模这些字段到 UI。
下面是定义域契约及其一些专业化的接口:
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}
}
}
对于 UI 方面,我构建了一个并行类型层次结构。这使得与数据验证相关的业务规则相关的域对象与 UI 问题分开,即如何呈现给定的字段(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);
}
}
虽然这种设计使我可以自由地设计独立于UI,我还没有找到一种干净的方法来连接和管理这两个层次结构。我觉得我应该能够利用泛型将并行类相互连接,但我不太明白那会是什么样子。是否有一种模式可以解决关联问题或完全消除对并行类层次结构的需要?
编辑:我的 UI 层引用我的业务层 (Core.csproj)。以下是我如何将 UI 类层次结构连接到域类层次结构的几个示例。这些类型目前不使用泛型,但我认为它们应该使用。
// 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);
}
I'm designing a simple form creation engine, in which users may compose new forms using various boilerplate field types (Date, Text, DropDown)
I decided to model the domain object (Form Field) independently of the objects that would be used to render these fields to a UI.
Here's the interface that defines the contract for the domain and some of it's specializations:
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}
}
}
For the UI side of things, I constructed a parallel type hierarchy. This keeps the domain object, which is concerned with business rules around data validation separate from UI concerns, namely how to render a given field (MVC HtmlHelper vs 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);
}
}
While this design gives me the freedom to design the domain model independently of the UI, I haven't come across a clean way to connect and manage the two hierarchies. I feel like I should be able to leverage generics to connect parallel classes to one another, but I can't quite grok how that would look. Is there a pattern that solves the association problem or eliminates the need for a parallel class hierarchies altogether?
EDIT: My UI tier references the my business layer (Core.csproj). Here are a few examples of how I connect the UI class hierarchy to the domain class hierarchy. These types don't currently use generics, but I feel that they ought to.
// 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);
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我认为这两个层次结构之间的差异非常小,您确实应该考虑价值或区别是什么。例如,如果您考虑的是拥有多个具有不同呈现形式的下拉列表控件,那么问问自己,如果不在设计器中,您将在哪里选择具体的控件?
也许您的 IDropdownListControl 可以是具有抽象“Render”方法的基类?
您的
IFormField
和IControl
非常相似,我不明白您同时拥有这两者会买什么?特别是
IDropDownField
看起来确实像 MVC 术语中的模型对象,它与实例化表单时字段将保存的数据有关。这与表单的形成方式无关(您说过它是域模型)。也许所有
IDropListControls
都支持模型IDropDownField
? (在这种情况下,我实际上只需删除IDropDownField
并直接在IDropListControl
上声明属性)。考虑重用抽象原则。对于您创建的每个接口,您能否想到两种实现,或者它们实际上只是具体的类?
I think the difference between the two hierarchies are so small you really should consider what the value -or- distinction is. If all you are considering is having, for instance, multiple dropdownlist controls that have a different rendering forms then ask yourself, if not in the designer, where are you going to pick concrete controls?
Perhaps your
IDropdownListControl
can be a base class with an abstract 'Render' method?Your
IFormField
andIControl
are so similar, I don't see what you buy with having both?The
IDropDownField
in particular really looks like a model object in MVC terms, it's about data the field would hold when a form is instantiated. It's not about how a form is shaped (you stated it was a domain model).Should all
IDropListControls
support the modelIDropDownField
perhaps? (in which case I would really just drop theIDropDownField
and declare the properties directly onIDropListControl
).Consider the Reused Abstraction Principle. For every interface you create, can you think of two implementations or are they really just concrete classes?