【问题标题】:Interface inheritance - How to not break Liskov's Substitution Principle and the Single Responsibility Pattern?接口继承 - 如何不破坏 Liskov 替换原则和单一职责模式?
【发布时间】:2014-05-04 11:33:24
【问题描述】:

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

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

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

解决这个问题的正确方法是什么?接口继承真的是要走的路,还是有其他理想的符合 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);
}

【问题讨论】:

  • 为什么你认为 ICustomerRepository 继承形式 IRepository 违背了 Liskov 的替换原则?
  • "子类型必须可以替代它们的基本类型。"如果向 ICustomerRepository 添加新方法 GetNextAvailableCustomer,它仍然可以被 IRepository 替换。
  • 因为 ICustomerRepository 的实现不能替代 IRepository,因为该实现将具有 GetNextAvailableCustomerNumber 方法。 IRepository 的实现不会有这个。
  • @Maritim 这不是liskovs 的意思。 “程序中的对象应该可以替换为其子类型的实例,而不会改变该程序的正确性”
  • ICustomerRepository 继承 IRepository 而不是其他方式。

标签: c# design-patterns inheritance liskov-substitution-principle


【解决方案1】:

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

程序中的对象应该可以替换为它们的实例 子类型而不改变该程序的正确性

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

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

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

【讨论】:

  • 这肯定是一个很大的解脱。除了通过实施 ICustomerRepository 之外,是否可以拒绝实施 IRepository
  • 不使用“是否可以拒绝实施 IRepository”的意思?
【解决方案2】:

你不会破坏 LSP。

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

如果向 ICustomerRepository 添加新方法 GetNextAvailableCustomer,它仍然可以被 IRepository 替换。

这是一篇关于存储库模式的好文章Entity Framework, Repository and Specification Pattern

source code for different .net versions

【讨论】:

    【解决方案3】:

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

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

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

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

    【讨论】:

      猜你喜欢
      • 2014-06-05
      • 2022-11-09
      • 2019-10-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-18
      • 1970-01-01
      相关资源
      最近更新 更多