4

我制作了一个通用接口,例如:

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

如果我有两个仅使用上述方法的元素(Person 和 Store),那么什么是最佳实践?

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();

什么被认为是最佳实践?

4

1 回答 1

7

您所调用的内容有一个已知的设计模式IDatabaseElement<T>;它被称为存储库模式。所以首先重命名IDatabaseElement<T>为:

public interface IRepository<TEntity> { ... }

此外,由于您定义了IPerson接口,似乎您正在为Person实体定义一个接口,而不是存储库。

将实体隐藏在接口后面是不好的做法,因为您的实体是数据对象,而接口仅用于抽象行为。

因此,不要调用接口IPerson,而是先调用它IPersonRepository

另一方面,如果您的Person类实际上包含数据(例如FirstNameLastNameAge等),那么在这种情况下您就是在混合职责。您的实体不应该知道如何从数据库中检索自己(或其他实例!!!)。从数据库中检索数据和保存数据是两个不同的职责,您应该将它们分开(为每个职责赋予自己的类)。如果您违反单一职责原则,您的系统很快就会变得无法维护。

现在,为每个存储库类型(例如IPersonRepository)创建一个特定的接口是一个坏主意。拥有泛型抽象的主要原因是因为这使得添加额外的行为(例如横切关注点)变得更加容易,因为这允许您定义单个泛型装饰器,例如:AuditTrailingRepositoryDecorator<T>. 但是,当您让您的 person 存储库实现继承自 时IPersonRepository,您不能再用通用装饰器包装它,因为您在IPersonRepository其自身上定义的所有方法都将不再可访问。这也使编写单元测试变得更加容易,因为在您的测试套件中,您只需创建一个通用的IRepository<T>.

如果您对添加横切关注点和轻松测试代码库的能力不感兴趣,则可以使用特定(非通用)接口,例如IPersonRepositoryIStoreRepository

于 2013-05-12T13:44:23.700 回答