Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/344.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 设计:当域对象和服务对象之间的界限为';不清楚_Java_Oop_Domain Driven Design - Fatal编程技术网

Java 设计:当域对象和服务对象之间的界限为';不清楚

Java 设计:当域对象和服务对象之间的界限为';不清楚,java,oop,domain-driven-design,Java,Oop,Domain Driven Design,这个问题的核心是一个设计问题。我将使用Java/JavaEE示例来说明这个问题 考虑一个web邮件应用程序,它使用JPA构建持久性,使用EJB构建服务层。假设我们的EJB中有一个服务方法,如下所示: public void incomingMail(String destination, Message message) { Mailbox mb = findMailBox(destination); // who cares how this works mb.addMessa

这个问题的核心是一个设计问题。我将使用Java/JavaEE示例来说明这个问题

考虑一个web邮件应用程序,它使用JPA构建持久性,使用EJB构建服务层。假设我们的EJB中有一个服务方法,如下所示:

public void incomingMail(String destination, Message message) {
    Mailbox mb = findMailBox(destination); // who cares how this works
    mb.addMessage(message);
}
public void incomingMail(String destination, Message message) {
    Mailbox mb = findMailBox(destination); // who cares how this works
    if (mb instanceof AutoRespondingMailbox) {
        String response = ((AutoRespondingMailbox)mb).getAutoResponse();
        // now we can access the container services to send the mail
    } else if (mb instanceof HelpDeskMailbox) {
        // ...
    } else {
        mb.addMessage(message);
    }
}
public class AutoRespondingMailbox {
    private IEmailSender _sender;

    public AutoRespondingMailbox(IEmailSender sender){
        _sender = sender;
    }

    public void addMessage(Message message){
        String response = getAutoResponse();
        _sender.Send(response);
}
这似乎是一种合理的商业方法。据推测,邮箱对象仍将被附加,并将无缝地将更改保存回数据库。毕竟,这是透明持久性的承诺

邮箱对象将具有以下方法:

public void addMessage(Message message) {
    messages.add(message);
}
public void addMessage(Message message) {
    String response = getAutoResponse();
    // do something magic here to send the response automatically
}
这里是它变得复杂的地方——假设我们想要其他邮箱类型。假设我们有一个自动回复邮箱,它会自动回复发件人,还有一个helpdesk邮箱,它会在收到每封电子邮件时自动打开一个helpdesk票证

自然要做的事情是扩展邮箱,其中AutoRespondingMailbox具有以下方法:

public void addMessage(Message message) {
    messages.add(message);
}
public void addMessage(Message message) {
    String response = getAutoResponse();
    // do something magic here to send the response automatically
}
问题是我们的Maibox对象及其子类是“域对象”(在本例中,也是JPA实体)。Hibernate的家伙(和许多其他人)鼓吹一个非依赖域模型——也就是说,一个不依赖于容器/运行时提供的服务的域模型。这种模型的问题是AutoRespondingMailbox.addMessage()方法无法发送电子邮件,因为它无法访问例如JavaMail

HelpDeskMailbox也会出现同样的问题,因为它无法访问Web服务或JNDI注入来与HelpDesk系统通信

因此,您必须将此功能放在服务层中,如下所示:

public void incomingMail(String destination, Message message) {
    Mailbox mb = findMailBox(destination); // who cares how this works
    mb.addMessage(message);
}
public void incomingMail(String destination, Message message) {
    Mailbox mb = findMailBox(destination); // who cares how this works
    if (mb instanceof AutoRespondingMailbox) {
        String response = ((AutoRespondingMailbox)mb).getAutoResponse();
        // now we can access the container services to send the mail
    } else if (mb instanceof HelpDeskMailbox) {
        // ...
    } else {
        mb.addMessage(message);
    }
}
public class AutoRespondingMailbox {
    private IEmailSender _sender;

    public AutoRespondingMailbox(IEmailSender sender){
        _sender = sender;
    }

    public void addMessage(Message message){
        String response = getAutoResponse();
        _sender.Send(response);
}
必须以这种方式使用instanceof是出现问题的第一个迹象。每次您想要对邮箱进行子类化时,都必须修改此服务类,这是问题的另一个迹象

有人有处理这些情况的最佳实践吗?有人会说,邮箱对象应该可以访问容器服务,这可以通过一些捏造来实现,但这样做肯定会与JPA的预期用途相冲突,因为容器在除实体之外的任何地方都提供依赖项注入,这清楚地表明这不是预期的用例

那么,我们应该做些什么呢?提升我们的服务方式,放弃多态性?我们的对象自动降级为C风格的结构,我们失去了OO的大部分好处


Hibernate团队会说,我们应该在域层和服务层之间划分业务逻辑,将所有不依赖于容器的逻辑放入域实体,并将所有依赖于容器的逻辑放入服务层。我可以接受这一点,如果有人能给我一个例子,说明如何做到这一点,而不必完全放弃多态性,不必求助于instanceof和其他类似的肮脏行为,一个选项(可能不是最好的选项)就是将对象包装在“executor”对象中。executor对象将包含服务层信息,而内部化的数据对象将包含域信息。然后,您可以使用工厂来创建这些对象,从而限制“instanceof”方法或类似元素的范围,然后不同的对象将具有某种通用接口,以便可以在其数据对象上执行。它是命令模式(您将命令对象作为执行者)和状态模式(状态是数据对象的当前状态)的混合,尽管两者都不完全匹配。

邮箱就是邮箱

…但自动响应邮箱是附加了一些规则的邮箱;这可以说不是mailbox的子类,而是控制一个或多个邮箱和一组规则的MailAgent


警告:我在DDD方面的经验有限,但这个例子给我的印象是基于一个错误的假设,例如,应用规则的行为属于邮箱。我认为对邮件应用规则独立于邮箱,即收件人邮箱可能只是筛选/路由规则使用的标准之一。因此,在这种情况下,ApplyRules(message)或ApplyRules(mailbox,message)服务对我来说更有意义。

您遗漏了一些东西:邮箱对象完全可以依赖于运行时提供的接口。“不依赖于运行时服务”是正确的,因为您不应该有编译时依赖关系

由于唯一的依赖项是接口,所以可以使用IoC容器(如等)为对象提供测试实例,而不是运行时实例

最后,自动响应邮箱的代码可能如下所示:

public void incomingMail(String destination, Message message) {
    Mailbox mb = findMailBox(destination); // who cares how this works
    mb.addMessage(message);
}
public void incomingMail(String destination, Message message) {
    Mailbox mb = findMailBox(destination); // who cares how this works
    if (mb instanceof AutoRespondingMailbox) {
        String response = ((AutoRespondingMailbox)mb).getAutoResponse();
        // now we can access the container services to send the mail
    } else if (mb instanceof HelpDeskMailbox) {
        // ...
    } else {
        mb.addMessage(message);
    }
}
public class AutoRespondingMailbox {
    private IEmailSender _sender;

    public AutoRespondingMailbox(IEmailSender sender){
        _sender = sender;
    }

    public void addMessage(Message message){
        String response = getAutoResponse();
        _sender.Send(response);
}

请注意,这个类确实依赖于某些东西,但它不一定由运行时提供-对于单元测试,您可以轻松地提供一个虚拟的IEmailSender来写入控制台等。此外,如果您的平台更改或需求更改,您可以很容易地提供一个不同的IEmailSender,该构建使用了与原始构建不同的方法这就是“限制依赖性”态度的原因。

我对DDD不太有经验,但我有一个建议如何解决这个问题

我会将MailBox类抽象,然后将MailBox作为其超类进行3个实现

我认为方法addMessage(…)的命名可以做得更好。这个name-add建议只将提供的消息添加到邮箱中,就像setter一样,但它不替换现有值,而是将提供的消息添加到某种存储中

但你所寻找的是一种行为。如果抽象邮箱强制所有子类实现方法
public void handleincommmingmessage(Message Message),该怎么办