我不确定是否应该直接在用户界面中使用域对象。例如,我希望为域实体用户用户设计用户界面,用户具有用户id、名称、密码和角色列表。实体的设计方式使其永远不会处于无效状态(例如无效密码或空userId)
public class User {
public User(String userId, String name, String password) {
//Initialization and validation
}
public String getUserId {
/*implementation*/
}
public void changePassword(String oldPassword, String newPassword) {
/*Set new password if it complies with the rules
and if the the old one is correct*/
}
public void setName(String Name) {
/*implementation*/
}
public String getName() {
/*implementation*/
}
public List<Role> getRoles() {
/*implementation*/
}
public void addRole(Role role) {
/*implementation*/
}
}
设计用户界面最合适的方式是什么?
1) 坚持领域模型:设计3个窗口。窗口"New user"创建一个具有给定userId、名称和密码的新用户。另一个窗口"更改密码"可以更改密码,另一个"修改用户"可以修改现有用户的名称和角色
2) 最好只使用一个窗口来创建具有给定userId、名称、密码和角色列表的用户。即使我还没有键入userId,我也应该能够添加角色。
选项1是最容易实现的,因为我可以直接在用户界面中使用域对象。但是用户界面使用起来可能会很痛苦。选项2是可取的,但是域对象不能直接使用。如果用户还没有创建,我不知道如何添加角色。我无法创建用户,因为当时我可能没有正确的信息,例如表示userId的临时空文本框。我怎样才能做到这一点?
我能想到的唯一干净的解决方案是创建在用户界面中使用的类,该类将模拟真实域对象中的信息。因此,我将能够创建一个空用户来添加角色,然后设置userId。用户界面可以使用该信息来创建真正的域对象。
有什么简单的方法可以解决这个问题吗?这通常是如何管理的?
这是很多问题,但你问它们是正确的,因为当你开始研究如何在层之间传递对象时,它们是至关重要的。
让我们从开始
"这通常是如何管理的?"
你正确地指出,你想向用户展示域对象的方式并不总是以原始形式——有时你想少显示一点,比如在用户示例中,添加角色可以在不同于用户创建/修改的UI中,有时你想多显示一点——同时并排显示两个对象,或者呈现例如来自2个对象的混合数据。
顺便说一句,稍微精确一点:你认为选项1(多个窗口)更多地坚持域模型,而我认为它倾向于更少地模仿模型,因为它没有在一个屏幕上显示整个域对象,但这是一个细节。
那么你通常是怎么处理的呢?正如你所说的,通过创建为你的视图量身定制的UI特定对象
我能想到的唯一干净的解决方案是创建要在中使用的类将模拟真实域中的信息的用户界面对象。因此,我将能够创建一个空用户来添加角色,然后然后设置userId。
这些对象通常被称为DTO、.NET/MVM世界中的ViewModel对象等。
用户界面可以使用该信息创建真实的域物镜。
通常,UI本身不负责从UI对象创建/更新域对象。映射在另一层中完成。
既然我们已经看到了基本的解决方案,让我们看看它是如何被异常地管理的;)
有几种选择。其中之一是裸对象模式,其中所有用户界面都是域对象的直接表示。这大大简化了问题,但这样的UI在工作流程和易用性方面是否总是最好的,还有待观察。
或者,您可以只在表示层中使用域对象。最近的这篇文章指出了当同一对象有多个表示时会出现的问题,并研究了在所有层中使用相同域对象的可能性。这里的问题是,您可能必须将一些数据强制输入到与域无关的域对象中,并且您可能还必须放弃一些不变量(例如,如果您将用户创建和角色分配用户界面分开,则"如果没有附加角色,用户就不可能存在")。
最后,回到用户/角色的例子,"如果用户还没有创建,我不知道如何添加角色":您的应用程序的最终用户不必有一个现有的user对象就可以选择角色。您可以在屏幕上选择角色,并保持它们直到用户对象的实际实例化完成,并且仍然强制执行所有必需的字段,如ID。
选项2有什么问题?您可以先创建一个新的用户对象,然后向其中添加规则。如果您没有创建新用户对象所需的所有信息(例如,空的userid文本框),只需通知用户需要填写此字段。