【问题标题】:How to handle generic classes / interfaces in a modular application?如何在模块化应用程序中处理泛型类/接口?
【发布时间】:2015-04-24 10:51:57
【问题描述】:

我是 Java 新手,我正在编写一个这样的模块化应用程序:

*************          *******         ***********
*           *          *     *         *   Data  *
* Front-end * -------- * API * ------- * Handler *
*           *          *     *         *         *
*************          *******         ***********

本质上,我希望能够定义一个包含 N 个类的 API,然后让一个“数据处理程序”模块处理将对象存储在某个地方(例如数据库),而前端不需要知道任何事情是如何实现的。

假设我的 API 中定义了两个类:Contract 和 Company。 我希望前端能够执行以下操作:

myContract = new Contract();
myCompany = new Company();
myContract.setCompany(myCompany);
myContract.save();
myCompany.save();

这样,将来我可以更改存储数据的方式(数据处理模块),而无需更改前端的任何代码。

为此,我在 API 模块中为 Contract 和 Company 编写了两个接口。 然后,在数据处理模块中,我编写了实现两个接口的类:DbContract 和 DbCompany。

现在,我遇到了问题,因为我在 Contract 接口中定义了 getter/setter 方法:

public interface Contract {
    public Company getCompany();
    public void setCompany(Company company);
}

并在 DbContract 中将它们实现为:

public class DbContract implements Contract {
    private DbCompany company;

    @Override
    public Company getCompany() {
        return this.company;
    }

    @Override
    public void setCompany(Company company) {
        this.company = company;
    }
}

但是,我到处都遇到类类型不匹配错误... 我完全错过了重点吗?我应该怎么做呢?

提前感谢您的帮助。

【问题讨论】:

  • setCompany 中,您不能在不进行强制转换的情况下将Company 分配给DBCompany 引用。您何时/如何创建DBCompany
  • 我的想法是在 API 中有一个名为 Manager 的类,它具有实例化和返回对象的方法。像public Contract createContract() { return new DbContract(); } 这样的东西。演员阵容是解决这个问题的正确方法吗?
  • API 的主要目的是隐藏实现细节,使Frontend 只对接口进行操作。通过在那里创建对象,你就打破了这个规则。
  • 谢谢@SashaSalauyou,你会怎么做?
  • 您能举个例子吗?正如我所说,我上周确实开始使用 Java 进行编码,但我仍然有点迷茫。感谢您的帮助!

标签: java modularity decoupling


【解决方案1】:

改变

    private DbCompany company;

    private Company company;

每件事都应该是甜蜜的。

否则,您将需要前端了解您正在处理的公司类型并通用。 这还没有编译但是类似:

public interface Contract<T extends Company>{
    public T getCompany();
    public void setCompany(T company);
}

public class DbContract implements Contract<DbCompany> {
    private DbCompany company;

    @Override
    public DbCompany getCompany() {
        return this.company;
    }

    @Override
    public void setCompany(DbCompany company) {
        this.company = company;
    } 
}

【讨论】:

  • 感谢您的回复。第一种方法是我最初遵循的方法,但是我使用 JPA 和 EclipseLink 将对象存储在数据库中,所以我需要正确的类型(因为它们被定义为实体)。
  • 您需要稍微了解一下您的架构,因为我同意@Thomas 的观点,即前端最好不知道它正在使用什么类型的对象。如果您的数据处理程序使用 createNewCompany 和 createNewContract 方法进行了增强,则第一个选项有效。
【解决方案2】:
myContract = new Contract();

这行不通,因为您无法创建接口的实例。

您似乎有正确的想法,但似乎对“实施”一词也存在误解。

如果前端需要创建这些接口的实例,它需要知道实现或提供自己的实现。另一方面,如果前端根本不应该知道实现,它应该调用后端来创建一个新实例。

此外,制作ContractCompany 数据容器并将实际的保存逻辑移动到服务可能是更好的设计,例如您的代码如下所示:

class Contract { ... } //as before, but classes not interfaces
class Company { ... } //as before, but classes not interfaces

创建新合同:

Contract myContract = new Contract();
Company myCompany = new Company();
myContract.setCompany(myCompany);

contractService.save( myContract ); //assuming this would save the company as well

您可以查找服务或让它被注入。对于后者,请查看 Spring 依赖注入。

更新:

在您的评论中,您声明 CompanyContract 将是 JPA 实体,如果我理解正确,您不希望前端有任何使用 JPA 的概念。

在这种情况下,您有两个选择:

  1. 使您的对象成为普通 POJO,并为后端的 JPA 映射添加 XML。然后,您将从前端传递的每个实例视为一个分离的实体。

  2. 如果您想为映射使用注释并且不希望前端知道您需要提供一个中间层(称为数据传输对象 = DTO),供API。然后后端将在内部在实体和 DTO 之间进行转换。

【讨论】:

  • 感谢您的回复。这种方法的问题是我想将 JPA 与 EclipseLink 一起使用来存储数据,因此每个类都需要定义为一个实体。如果我使用这种方法,那么前端将不再是“与存储无关”。
  • @NiccolòZapponi 这取决于。如果前端可能知道 JPA,它就不需要知道正确的实现(它可能是 Eclipse Link 或 Hibernate)。不过我会扩展我的答案。
  • 非常感谢!
  • 感谢您的更新。如果我想遵循方法 #2,我是否应该在这一点上废弃 API 中的接口,使用普通对象,并让它们将数据中继到适当的后端 JPA 实体?
  • @NiccolòZapponi 如果您不需要这些接口来做其他事情,那么可以。基本上这就是我们所做的。过去有人认为我们的架构需要这些 DTO 的接口,但我们很快发现它们根本没有被使用,因此将它们全部删除。
猜你喜欢
  • 2016-10-03
  • 2011-11-03
  • 1970-01-01
  • 2013-10-19
  • 1970-01-01
  • 1970-01-01
  • 2023-03-15
  • 1970-01-01
  • 2014-09-25
相关资源
最近更新 更多