Design patterns 与域实体使用一对一接口是好的还是坏的做法?为什么?

Design patterns 与域实体使用一对一接口是好的还是坏的做法?为什么?,design-patterns,dns,interface,domain-driven-design,Design Patterns,Dns,Interface,Domain Driven Design,在我所使用的一些DDD企业应用程序中,我看到的一件事是使用了与域实体相同的接口,并对属性和函数进行了一对一的映射。实际上,域对象总是通过它的一对一接口来使用,并且所有域实体都有这种风格的一对一接口 例如: public class Account : IAccount { ... } // Usual account, persistent public class MockAccount : IAccount { ... } // Test mock object public

在我所使用的一些DDD企业应用程序中,我看到的一件事是使用了与域实体相同的接口,并对属性和函数进行了一对一的映射。实际上,域对象总是通过它的一对一接口来使用,并且所有域实体都有这种风格的一对一接口

例如:

public class Account : IAccount { ... }       // Usual account, persistent
public class MockAccount : IAccount { ... }   // Test mock object
public class TransAccount : IAccount { ... }  // Account, not persistent
public class SimAccount : IAccount { ... }    // Account in a performance sim
域对象帐户:

public class Account : IAccount
{
     public string Name {get;set;}
     //...some more fields that are also in IAccount
     public decimal Balance {get;set;}
}
它是匹配的接口

public interface IAccount
{
   string Name {get;set;}
   //... all the fields in Account
   decimal Balance {get;set;}
}
但最近我越来越相信这实际上是一种反模式。
我是由开源社区的一些架构师运行的,他们说这是基于设计链上的某个地方的设计错误或缺陷

所以我告诉我的同事,他们应该停止为域对象创建接口。因为它们没有任何用途,并且在更新域实体时必须更新接口

首先,有人声称这些接口提供“解耦”,但我反驳说,由于接口与域实体之间存在一对一的关系,因此它们实际上不提供任何解耦,因此对接口的更改意味着域实体的更改,反之亦然

下一个声明是我们需要用于测试目的的接口。我的反对意见是Rhino mocks提供了对具体类的模拟和存根。但他们声称Rhino mocks在具体类方面存在问题。我不知道我是否相信这一点,即使rhino mocks在具体类方面有问题,但这并不一定意味着我们应该为域实体使用接口

所以我很好奇:

为什么要为域实体提供一对一接口

为什么不呢

为什么这是一个好的或坏的做法

谢谢你的阅读


编辑:我应该注意,我一直在使用接口,我相信如果需要,我会立即使用接口。但我特别指的是具有一对一接口的域实体。

如前所述,这是一种不好的做法,但是

没有具体的原因说明您的接口需要不同于您的域实体;有时它确实是正确的映射。但令人怀疑的是,事实总是如此。值得关注的问题是,这些接口是否是真正设计的,或者它们是否只是因为缺乏时间/惰性而被投入使用


使用您的示例,您描述的IAccount接口公开Account对象上的getter和setter;似乎有点奇怪,也不太可能所有使用帐户的人都需要在帐户上设置余额,并且隐含的权限是在该接口级别指定的。在您的系统中,是否没有一个地方您只想检查而不想设置帐户余额?

始终将域对象指定为接口而不是直接指定为类的最大原因是为您提供一定程度的实现自由度。在您的示例中,您只有一种IAccount,因此它有点不一致

但如果你有,例如:

public class Account : IAccount { ... }       // Usual account, persistent
public class MockAccount : IAccount { ... }   // Test mock object
public class TransAccount : IAccount { ... }  // Account, not persistent
public class SimAccount : IAccount { ... }    // Account in a performance sim
等等


通过将域对象定义为接口,您可以在不干扰域定义的情况下替换实现。

一般来说,如果我的类不是设计模式(如Strategy或Visitor)的一部分,我不会添加接口

添加接口对于Strategy和Visitor等设计模式非常有用,但在这些情况下,我不会复制域类的getter和setter。相反,我创建的接口特定于我创建的设计模式接口

interface SomeStrategy {
   void doSomething(StrategyData data);
}

interface StrategyData {
   String getProperty1();

   String getProperty2();
} 
这允许我让域类实现这些接口,或者使用适配器模式。我发现这是一种更干净的方法,它只是为了创建接口


设计应始终减少不确定性。为了它而创建接口并不能减少不确定性,事实上,它可能会增加混乱,因为它没有任何意义。

实体上的一对一接口是一种反模式


詹姆斯·格雷戈里说得比我好。

为什么这个失败了

正如Charlie Martin所说,我发现拥有一个域对象接口可以让我选择实现

一个基本的例子是对象的标识符(object.Id),这将根据您存储该对象的位置而有所不同,并且创建该对象的责任可能取决于以后数据的实现,也可能不取决于此。在SQL Server中,您可能会选择自动编号,但在Azure表存储中,您可能会选择Guid,但您不想更改应用程序的业务逻辑,因为您会更改数据存储的位置

我可能会也可能不会选择将我的域对象持久化,甚至在表示层中使用它——这取决于我的应用程序的最佳范围。但是,在公共层中添加一组公共域接口可以让我针对它们编写服务并一次又一次地重用它们

我们会重复同样的争论,如果没有IAddress,地址应该是什么,如果没有ICreditCard,新的程序员会重写信用卡的内容

反模式标签是对语言的一种不好的使用,它对于描述复杂多变任务的解决方案的价值来说过于简单


大多数模式都有一席之地,即使是被诽谤的单身汉,这意味着它不是一种“反模式”,至少就术语而言是这样。

我同意你的看法。接口应该充当契约,因此与域实体的一对一接口没有任何价值。如果您想抽象某个行为,它可能会很有用。但是,这在某些情况下会起作用。

是的,在很多领域,整个界面都没有使用。事实上,更多的情况是没有使用接口的一部分