Repository模式中方法的命名约定

本文关键字:命名约定 方法 模式 Repository | 更新日期: 2023-09-27 18:06:57

我正在努力学习更好的软件设计,最近发现了存储库和服务层模式。根据我的理解,存储库基本上包含数据访问代码,服务层调用存储库来获取数据,然后对数据执行一些逻辑和处理。

到目前为止,存储库通常还没有一系列方法。然而,存储库通常有以下方法:

  • 列出/获取/读取等
  • 创建
  • 保存
  • 更新
  • 删除

我正在努力理解存储库的命名约定。我应该怎么称呼"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);
}

Repository模式中方法的命名约定

我将使用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实例进行查询,而从不进行写入。将其分解为两个接口所增加的复杂性是否值得解决这个问题,取决于您的软件的预期生命周期,以及许多其他系统是否可能依赖于您的代码。