我想要一些建议和批评与领域驱动的设计如下。我在下面包括了伪码。真正的代码会封装属性。
关注点
我唯一担心的是,它似乎是贫血。
步骤
类
enum UnitedStatesState {
ALABAMA,
//...
CALIFORNIA,
//...
MAINE,
//...
WASHINGTON
}
class License {
int id;
String name;
//enum of state that the license is applicable in
UnitedStatesState state;
}
class LicenseRequest {
//the name of the person making the request
String name;
//enum of state to which the user is requesting a license in
UnitedStatesState state;
LicenseResponse submit()
{
//TODO: move creation of the rules out of this class
RuleGroup<LicenseRequest> ruleGroup = new RuleGroup<>();
ruleGroup.add(new StateExclusionLicenseRequestRule(UnitedStatesState.MAINE));
boolean approved = ruleGroup.execute(this);
if(approved) {
License license = createLiscense(request);
return new ApprovedLicenseResponse(license);
} else {
DeniedLicenseResponse response = new DeniedLicenseResponse();
response.rules = newArrayList(ruleGroup);
return response;
}
}
//TODO: move create license out of Request. maybe a factory class?
private License createLicense()
{
License license = LicenseIdGenerator.generate(this.state);
license.name = this.name;
license.state = this.state;
save(license);
return license;
}
}
//visitor for the rule
interface Rule<T> {
public boolean execute(T o);
public List<String> getMessages();
}
//rule that auto denies when the request is made in an excluded state
class StateExclusionLicenseRequestRule : Rule<LicenseRequest> {
public List<String> getMessages();
UnitedStatesState excludedState;
public boolean execute(LicenseRequest request) {
if(request.state == excludedState)
{
messages.add("No license for " + request.state + " is available at this time.");
return false;
}
return true;
}
}
//rule that groups all other rules
class RuleGroup<T> : Rule<T> {
public void addRule(Rule<T> rule);
public List<Rule<T>> getFailedRules();
public List<String> getMessages() {
List<String> messages = new ArrayList<>();
for(Rule<T> rule : rules) {
messages.addAll(rule.getMessages());
}
return messages;
}
public boolean execute(T o) {
List<Rule<T>> failedRules = new ArrayList<>(rules.size());
for(Rule<T> rule : rules) {
boolean approve = rule.execute(o);
if(!approve) {
failedRules.add(rule);
}
}
return !failedRules.isEmpty();
}
}
interface LicenseResponse {
boolean approved;
}
class ApprovedLicenseResponse : LicenseResponse {
License license;
}
class DeniedLicenseResponse : LicenseResponse {
private List<Rule<LicenseRequest>> rules;
public List<String> getMessages()
{
List<String> messages = new ArrayList<>();
for(Rule<LicenseRequest> rule : rules) {
messages.addAll(rule.getMessages());
}
return messages;
}
}样本码
request = new Request(name: 'Test', state: UnitedStatesState.CALIFORNIA)
response = request.submit()
if(response.approved)
{
out('Your request is approved');
out('license id = ' + reponse.id);
}
else
{
out('Your request was denied');
for(String message : response.messages)
{
out(message);
}
}更新1:背景
这只是对我想要实现的东西的嘲弄。这是一个简单的系统,用户在表单中输入有关他们自己的信息,并批准或拒绝他们的许可。经批准后,证书可供打印。
例如,唯一的规则是拒绝缅因州的许可证请求。
更新2:重构规则和删除处理程序
我对上面的例子做了一些修改。移除处理程序并将所有代码移动到LicenseRequest。我还将批准/拒绝类的规则移到了实现vistor模式的类上。
发布于 2011-03-31 20:32:57
不幸的是,有些更相关的代码没有显示出来,但我想看看哪些代码可以被推入LicenseRequest。特别是,LicenseRequest可以创建License而不是处理程序(可能通过给它ID)。如果LicenseRequest的属性仅用于创建已批准的许可证,则尤其如此。这样就不必向getter公开这些信息了。
我还会让determineApproval (可能使用另一个名称)直接创建响应,而不是传递可写消息列表(仅用于失败)。
你应该找的气味是特征Envy。特别是,应该检查使用来自License或LicenseRequest的数据的任何计算,看看是否应该在这些类中进行计算。
数据对象(特别是不可变的数据对象)有其用途,但您应该关注。
发布于 2015-06-23 20:11:35
就可扩展性而言,状态应该是ValueObjects。例如,当您需要将一个州的缩写与其名称等同时,会发生什么?
看起来你的设计也没有真正的聚合根源。如果没有许可证,LicenseRequest是否毫无意义?在这种情况下,它应该通过许可证(或许可服务)来处理,而不是直接由调用代码(可以是web服务器、控制台应用等)来处理。
所以对于伪代码来说
var license = licenseFactory.NewLicense();
response = license.Request(name, state)更有意义吗?如果你有多种类型的驾照,比如商业驾驶执照,会发生什么?
另外,对于规则,而不是命令模式(执行),您可以通过将规则作为规范- 模式来获得更清晰的解决方案。
https://stackoverflow.com/questions/5502464
复制相似问题