我制作了一个通用界面,如:
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();
什么是最佳做法?
答案 0 :(得分:7)
您所谓的IDatabaseElement<T>
有一种已知的设计模式;它被称为Repository Pattern。首先,将IDatabaseElement<T>
重命名为:
public interface IRepository<TEntity> { ... }
此外,由于您定义了IPerson
接口,因此您似乎正在为Person
实体而不是存储库定义接口。
将您的实体隐藏在接口之后是不好的做法,因为您的实体是数据对象,只需要接口来抽象行为。
因此,不要调用接口IPerson
,而是先调用它IPersonRepository
。
另一方面,如果您的Person
课程实际上包含数据(例如FirstName
,LastName
,Age
等),那么您就是混合职责。您的实体不应该知道如何从数据库中检索自己(或其他实例!!!)。从数据库中检索数据并保存数据是两个不同的职责,您应该将它们分开(将每个职责分配给自己的类)。如果您违反Single Responsibility Principle,您的系统很快就会无法维护。
现在,为每个存储库类型(例如IPersonRepository
)创建一个特定的接口是个坏主意。具有通用抽象的主要原因是因为这使得添加额外行为(例如横切关注点)变得更加容易,因为这允许您定义单个通用装饰器,例如:AuditTrailingRepositoryDecorator<T>
。但是当你让你的人员存储库实现继承自IPersonRepository
时,你不能再用通用装饰器包装它,因为你在IPersonRepository
上定义的所有方法本身都不再可访问。这也使编写单元测试变得更加容易,因为在测试套件中,您只需要创建一个IRepository<T>
的通用虚假实现。
如果您对添加横切关注点以及轻松测试代码库的能力不感兴趣,可以使用特定(非通用)界面,例如IPersonRepository
和IStoreRepository
。