1

我有一个通用存储库模式,我现在看到我需要一个自定义方法来实现这个模式的一个特定实现,让我们调用实现 CustomerRepository 和方法 GetNextAvailableCustomerNumber。我有一些想法,但它们不符合面向对象设计的 SOLID 原则。

我首先考虑为该实现创建一个自定义存储库模式(ICustomerRepository),但这并不是很可行。经验告诉我,肯定有其他一些我目前还没有考虑甚至不知道的方法。此外,我认为为道路上的每一个颠簸发明一个新的存储库接口不应该如此轻松。

然后我考虑让 ICustomerRepository 继承 IRepository<Customer> 并为 GetNextAvailableCustomer 添加方法签名,但这非常违反 Liskov 的替换原则,我相信它也略微违反了单一责任模式。即使我只想使用 ICustomerRepository,我仍然可以实现基于 IRepository 的客户存储库。我最终会得到两种选择,并且客户端应该实现哪个接口不再是显而易见的。在这种情况下,我希望它只能实现 ICustomerRepository,而不是 IRepository<Customer>

解决这个问题的正确方法是什么?接口继承真的是要走的路,还是有其他理想的符合 LSP 的首选方法?

这是我的通用存储库接口:

public interface IRepository<T>
    where T : IEntity
{
    T GetById(int id);

    IList<T> GetAll();

    IEnumerable<T> Query(Func<T, bool> filter);

    int Add(T entity);

    void Remove(T entity);

    void Update(T entity);
}
4

3 回答 3

4

您实际上并没有违反 Liskov 的替代原则。利斯科夫的说

程序中的对象应该可以用其子类型的实例替换,而不会改变该程序的正确性

在你的情况下,你可以。根据您对 Liskov 的解释,几乎不允许继承和扩展类。

我认为从 IRepository “继承”的 ICustomerRepository 就可以了。我仍然可以在使用 IRepostory 的任何地方替换 ICustomerRepository(给定 ICustomerRepository:IRepostory)

Liskov 防范子类的意外行为。最常用的(尽管不一定是最好的)示例似乎是正方形从矩形继承的示例。这里我们有一个被 Square 覆盖的 SetWidth 方法,但是 Square 也设置了高度,因为它是一个正方形。因此,原始方法定义在子类中发生了更改,因此违反了原则。

于 2014-03-26T08:09:43.910 回答
1

Liskov 并不打算取消任何扩展程序的方法。替换原则是关于当您将基类型替换为子类型时程序的正确性。

但是,向子类型添加附加方法是完全有效的:任何需要基类型的地方都不知道也不使用附加方法。如果你用一个子类替换那些地方使用的实现,你的代码中的那些地方仍然可以完美地工作。

破坏 LSP 的一个示例是,如果您创建一个在调用“Query”时引发异常的实现,或者“Remove”方法添加一个元素而“add”方法删除一个元素。

据我所知,创建一个ICustomerRepository继承IRepository<Customer>并添加客户特定方法的方法正是存储库模式的含义。

于 2014-03-26T08:17:43.823 回答
1

您不会破坏 LSP。

Subtypes must be substitutable for their base types.(LSP 来自 Agile.Principles.Patterns.and Practices.In.C#[Robert.C.Martin] 书)

如果将新方法 GetNextAvailableCustomer 添加到 ICustomerRepository,它仍然可以被 IRepository 替代。

这是存储库模式实体框架,存储库和规范模式的好文章

不同.net版本的源代码

于 2014-03-26T08:08:36.730 回答