我正在努力学习更好的软件设计,最近发现了存储库和服务层模式。根据我的理解,存储库基本上包含数据访问代码,服务层调用存储库来获取数据,然后对数据执行一些逻辑和处理。
到目前为止,存储库通常还没有一系列方法。然而,存储库通常有以下方法:
- 列出/获取/读取等
- 创建
- 保存
- 更新
- 删除
我正在努力理解存储库的命名约定。我应该怎么称呼"List/Get/Read/etc"方法?我举一个例子。
我目前正在做一个项目,该项目将从一堆目录和文件中读取。这些文件表示由一个完全独立且已经存在的系统生成的传感器读数。
方法名称应该特定于特定类型的存储库,还是应该使用听起来更通用的名称?例如:
通用名称:
interface ISensorRepository
{
IEnumerable<Sensor> GetAll(); / IEnumerable<Sensor> ListAll(); / etc.
Sensor GetByID(int id);
}
实体特定名称:
interface ISensorRepository
{
IEnumerable<Sensor> GetAllSensors(); / IEnumerable<Sensor> ListAllSensors(); / etc.
Sensor GetSensorByID(int id);
}
我将使用OP的第一个选项"通用命名";
为什么?这是为了避免smurf命名约定(这是一种编程术语(,当你不断重复自己时,你会这样做。
您已经命名了存储库所代表的实体类型,为什么要重复所有的工作呢?如果你坚持单一责任原则,你就不必担心你的抽象会漏洞百出。
以下是我尽量避免的:
Employee
- EmployeeID
- EmployeeFirstName
- EmployeeLastName
- EmployeeAddress
- EmployeeNumber
- EmployeeAge
- EmployeeEmail
- EmployeeHiringDate
- EmployeeThis
- EmployeeThat
- EmployeeBlabla
- Employee...
- Employee...
这有点疯狂,对吧?
Martin Fowler在POEAA中将Repository
定义为域和数据映射层之间的中介,它的作用类似于内存域中的对象集合。
由于它的行为应该像内存中的集合,所以我喜欢按以下方式命名我的方法。
public interface IRepository<T> where T : class {
T Create(T entity);
void Create(IList<T> entities);
T Update(T entity);
T FirstOrDefault(Expression<Func<T, bool>> clause);
IEnumerable<T> Where(Expression<Func<T, bool>> clause);
IEnumerable<TResult> Select<TResult>(Expression<Func<T, TResult>> selector);
T First();
IEnumerable<T> All();
}
请参阅http://martinfowler.com/eaaCatalog/repository.html关于模式的简短讨论
考虑消费者可能会命名哪些存储库变量。我会写这样的东西:
public void DoSomethingWithSensors(ISensorRepository sensors)
{
//Which looks better?
foreach (var sensor in sensors.All)
//Or:
foreach (var sensor in sensors.GetAllSensors())
}
我通常喜欢尽可能避免裁员。人们不太可能忘记他们在哪个存储库上调用All
,所以我会这样命名,并对其他操作使用类似的通用名称。这也为您提供了一个优势,即您可以将基本接口抽象为一种通用类型:
public interface IRepository<T>
{
IQueryable<T> All { get; }
void Update(T entity);
T Get(int id); //If you have a standard type for IDs
//etc.
}
我会避免这样的方法(不要选择3dd的好答案(:
T FirstOrDefault(Expression<Func<T, bool>> clause);
List<TResult> Select<TResult>(Expression<Func<T, TResult>> selector);
如果你沿着这条路走下去,你可能会发现你很快就在接口中声明了所有的LINQ。只需公开一个IQueryable<T>
并依靠LINQ来提供所有这些选项。如果您使用EntityFramework,我会坚持使用IQueryable
而不是IEnumerable
。
另一个考虑因素是,这种模式可能会打破接口隔离原则。您的许多消费者将只获取IRepository
实例进行查询,而从不进行写入。将其分解为两个接口所增加的复杂性是否值得解决这个问题,取决于您的软件的预期生命周期,以及许多其他系统是否可能依赖于您的代码。