繁体   English   中英

继承和Liskov替换原则

[英]Inheritance and Liskov substitution principle

在创建我的类结构时,我正在努力遵守Liskov替换原则。 我希望在Day类中存储一组日历项。 需要有几种不同类型的CalendarItem,例如:

AppointmentItem
NoteItem
RotaItem

它们都共享一些在抽象基类CalendarItem中存在的常用功能:

public abstract class CalendarBaseItem
{
  public string Description { get; private set; }
  public List<string> Notes { get; private set; }
  public TimeSpan StartTime { get; private set; }
  public TimeSpan EndTime { get; private set; }
  public int ID { get; private set; }
  public DateTime date { get; private set; }

  code omitted...
}

但是后来例如RotaItem有一些额外的功能:

public class RotaItem : CalendarBaseItem
{
    public string RotaName { get; private set; }
    private bool spansTwoDays;

    public bool spanTwoDays()
    {
        return this.spansTwoDays;
    }

}

其他类也添加了自己的逻辑等。

我有一个日历类的CalendarBaseItem集合:

List<CalendarBaseItem> calendarItems;

但是在回顾这个问题时,我可以看到我正在破坏LSP原则,因为我必须检查并转换每个具体类型以获得我希望每个子类的功能。

如果有人可以建议如何避免这个问题,我将不胜感激。 我应该使用合成方法并为每个最终类添加CalendarItem类,例如

 public class RotaItem
{
    private CalendarBaseItem baseItem;
    public string RotaName { get; private set; }
    private bool spansTwoDays;

    public RotaItem(baseArgs,rotaArgs)
    {
       baseItem = new CalendarBaseItem(baseArgs);

    }

    public bool spanTwoDays()
    {
        return this.spansTwoDays;
    }

}

这里唯一的问题是,我将在我的Day类中为每个Concrete CalendarItem需要一个单独的集合?

我认为你遇到的不是Liskov替换原则违规,因为你在大多数语言中遇到了多态限制。

使用类似List<CalendarBaseItem>的东西,编译器推断你只处理CalendarBaseItem ,如果CalendarBaseItem是抽象的,那么显然不可能是真的 - 但这就是强类型语言的作用:它只被告知CalendarBaseItem所以它是是什么限制了使用。

有些模式可以让您处理这种限制。 最流行的是双分派模式:多分派的特化,它将方法调用分派给运行时类型。 这可以通过提供覆盖来实现,该覆盖在调度时调度预期的方法。 (即“双重派遣”)。 由于缺乏细节,很难准确地与您的情况相关联。 但是,如果你想根据某种其他类型进行一些处理,例如:

public abstract class CalendarBaseItem
{
    abstract void Process(SomeData somedata);
//...
}

public class RotaItem : CalendarBaseItem
{
    public override void Process(SomeData somedata)
    {
        // now we know we're dealing with a `RotaItem` instance,
        // and the specialized ProcessItem can be called
        someData.ProcessItem(this);
    }
//...
}

public class SomeData
{
    public void ProcessItem(RotaItem item)
    {
        //...
    }
    public void ProcessItem(NoteItem item)
    {
        //...
    }
}

这会取代像:

var someData = new SomeData();
foreach(var item in calendarItems)
    someData.ProcessItem(item);

现在,这是C#中“经典”的做法 - 跨越所有版本的C#。 使用C#4引入了dynamic关键字以允许运行时类型评估。 所以,你可以做你想做的事,而不必简​​单地通过将你的项目转换为dynamic来自己编写双重调度。 这迫使方法评估在运行时发生,因此将选择专门的覆盖:

var someData = new SomeData();
foreach(var item in calendarItems)
    someData.ProcessItem((dynamic)item);

这引入了您可能想要捕获和处理的潜在运行时异常 - 这就是为什么有些人不喜欢这样的原因。 相比之下它目前也很慢,所以不建议在性能敏感的紧密循环中使用它。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM