问题 模型对象应该有接口吗?


我正在我的系统中创建域模型。在设计模型对象时,我应该为每个实体对象创建接口吗?人们告诉我,我们的Web层不应该关心实体的实现,我们应该能够交换实现,但我不确定是否会发生。

例如,如果我们有一个维护学生列表的Teacher类,则getStudents方法可以是:

public List<Student> getStudents() {
      return this.students;
}

或这个:

public List<Student> getStudents() { 
     return someExternalService.retrieveStudents();
}

我理解这个好处,但一般做法是什么?


11390
2018-03-09 22:40


起源

“一般实践”不一定与“良好实践”相同,特别是在OO设计方面:) - skaffman
我不明白你的榜样。你的问题是教师是应该实现一些接口,还是通过接口使用依赖?我对这两种情况有不同的答案。你的文字让我觉得你在想前者,但这个例子让我觉得你想要后者。它是什么? - R. Martinho Fernandes
Martinho,我的问题是教师是否应该实现一个接口并生成TeacherImpl类。 - sma
但你的例子似乎在问“教师应该保留数据还是只调用其他类?” - matt b


答案:


除非你有充分的理由认为你需要换掉模型,否则我不会担心它。基于YAGNI原则(你不需要它


7
2018-03-09 23:00



感谢你的回答。我同意这两点。我实际上有一个跟进,我将作为一个单独的问题发布。它涉及建模关联。 - sma
在我看来,YAGNI比设计更多地应用于功能。例如,接口不会导致代码膨胀或测试中的复杂性;根据我的经验,为了方便起见,缺少界面是问题所在。 - Noel Ang
@Noel - 我认为你的起源是对的,但我认为避免过度工程可能是有用的。我发现有几次对某人说“你不会需要那个”,他们可以反驳你的主张并为加法做出好的论据,或者他们发现他们无法证明这一点并决定/意识到它的存在不是真正的要求。我同意在这种情况下添加接口没有太大的危害,但它可能导致更多的代码被添加“以防万一”。 - David
与诺埃尔完全一致,使用这个界面比KISS更多!YAGNI。 - orbfish
如果您打算在您的域模型中放置逻辑或行为,那么您可能需要一个接口。如果不这样做,只要某些东西使用该行为,您就必须对结果进行状态测试,而不仅仅是测试该方法是否被调用。 - Dan Kaufman


我见过或参与的项目都没有域实体的接口。

但在我的一个项目中,我有界面 NamedEntity

public interface NamedEntity {
   int getId ();
   String getName ();
}

所有域实体都实现了此接口。它让我有可能不为不同的实体创建不同的jsf转换器,但创建一个使用此接口而不是具体域类的转换器。


2
2018-03-09 22:46



很好的例子。 Nitpick:你说你见过或参与的项目都没有这种现象,然后你快速提供一个例子...... - R. Martinho Fernandes
这个upvote揭示了SO信誉计数中的错误(或简单地说不清楚的行为)。 - Roman
我同意。我真的不希望任何人有一个界面,但其他人不同意。 - sma
@Roman:基于upvotes的声望上限为200 /天。此后只会添加赏金和接受的答案。另见faq和meta。 - BalusC
@BalusC:看到这篇文章 meta.stackexchange.com/questions/41683/... 经过一番思考后,我认为这不是一个但是。 - Roman


我不能说一般的做法,但这不仅仅是隐藏实施。

它是将接口正式化为文档和合同的基础。

在将模型抽象为接口时,您将为服务客户端开发人员创建文档。根据您的开发风格,您可能已经或可能没有正式的服务描述(例如,基于WSDL的描述)。接口可以满足这种需求。


1
2018-03-09 23:15



实际上这有点适用于我的情况。有服务客户端开发人员基于我的模型对象提供服务,因此接口方法可能不是坏事。 - sma


我认为具有检索对象的服务接口和模型对象本身之间存在重要区别。

在运行时,加载模型对象的服务可能会根据您请求数据的位置而有所不同。但无论您如何创建此对象,预期的数据对象的格式和类型都不会更改。因此,携带状态的模型对象不需要接口,但是创建和加载这些模型对象的服务类需要接口。

如果模型对象不仅仅是一个简单的bean类型的对象,而是一个暴露某种行为的对象,那么模型对象拥有接口的唯一原因就有意义。


1
2018-03-10 00:18





我的一般观点是,我愿意  为域模型创建一对一的接口集,因为这除了复制域模型的类结构之外什么都不做。它没有添加任何有用的东西。

这两个案例我可以立刻想到我  考虑引入接口是:

  1. 一个接口描述了域模型中某些类集合的契约/行为,其中对于实现它的类的独立模型很有用。
  2. 您需要将您的域模型公开给外部客户端,并希望隐藏它们的实现细节

换句话说,添加接口,如果它们可以帮助您描述行为或帮助您隐藏客户端的实现细节。其他原因可能是有效的,但这些是我想到的两个。


0
2018-03-09 23:55



是的,我完全同意。这实际上从第2位开始,我们将域模型暴露给外部客户端。从那时起,这种方法发生了变化,现在我已经陷入了每个模型对象的界面。我考虑过摆脱它,但想先与社区核实。 - sma


提取接口是最简单的重构之一;最坏的情况是它是一个类重命名 - >移动方法。一旦你找到理由,改变主意的努力是微乎其微的。我总是发现,如果一个决定很容易改变,它会更加强化YAGNI和KISS。相反的论点是,最初创建一个接口并不是很费力,这是正确的,但如果做出多次决定,维护会随着时间的推移而增加 - 通常不会被使用。


0
2017-07-09 02:17