最佳实践:通用接口

4
我已经创建了一个通用接口,如下所示:

public interface IDatabaseElement<T>
{
  IList<T> GetAll();
  T Get(id);
  void Save(T element);
  void Delete(int id);
}

如果我有两个元素(人和商店),只使用上述方法,那么最佳实践是什么?
A:是否需要为每个元素制作新的接口,例如:
public interface IPerson : IDatabaseElement<Person> { }
public interface IStore : IDatabaseElement<Store> { }

然后我的类像这样:

public class Person : IPerson { .... }
public class Store : IStore { .... }

当实例化变量时:

IPerson person = new Person();
IStore store = new Store();

或者 B:直接使用通用接口,例如:

public class Person : IDatabaseElement<Person> { .... }
public class Store : IDatabaseElement<Store> { .... }

在实例化变量时:

IDatabaseElement<Person> person = new Person();
IDatabaseElement<Store> store = new Store();

什么被认为是最佳实践?


这个问题不属于本主题。 - Daniel A. White
@olf 请查看SO的常见问题解答,这个问题不是关于特定问题的。 - Candide
1
你的实体为什么一开始就知道数据库的存在呢?而且你的方法对于实体来说完全没有意义,它们适合放在一个存储库中。 - CodesInChaos
1个回答

7

对于你所称的IDatabaseElement<T>,已经有一个已知的设计模式;它被称为仓储模式。因此,请从重命名IDatabaseElement<T>开始:

public interface IRepository<TEntity> { ... }

此外,由于你定义了 IPerson 接口,似乎你正在为 Person 实体定义一个接口,而不是仓库。
将实体隐藏在接口后面是不好的做法,因为实体是数据对象,而接口只需要抽象行为。
因此,不要称其为 IPerson,而应该从 IPersonRepository 开始命名接口。
另一方面,如果你的 Person 类实际上包含数据(例如 FirstNameLastNameAge 等),那么你就是在混合责任。你的实体不应该知道如何从数据库中检索自己(或其他实例!)。从数据库检索数据和持有数据是两个不同的职责,你应该将它们分开(给每个职责一个类)。如果你违反了单一职责原则,你的系统很快就会变得难以维护。
现在,为每个存储库类型(例如IPersonRepository)制作特定的接口是一个不好的想法。拥有通用抽象的主要原因是因为这使得添加额外行为(例如横切关注点)更加容易,因为这允许您定义单个通用装饰器,例如:AuditTrailingRepositoryDecorator<T>。但是当您让您的人员存储库实现继承IPersonRepository时,您将无法再使用通用装饰器进行包装,因为您在IPersonRepository上定义的所有方法都将不再可访问。这也使编写单元测试更加容易,因为在您的测试套件中,您只需要创建一个单一的IRepository<T>通用虚拟实现。
如果您不想添加横切关注点和轻松测试代码库的功能,则可以使用特定的(非通用)接口,例如IPersonRepositoryIStoreRepository

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接