在这个例子中如何避免破坏LSP? C#

时间:2017-02-05 18:24:11

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

我有一个名为消息的基类,如下所示:

public abstract class Message 
{
    protected int m_id;
    protected bool m_localized;
    protected string m_metaData;

    public int GetID() { return m_id; }
    public bool GetLocalized() { return m_localized; }
    public string GetMetadata() { return m_metaData; }
}

然后,我还有两个继承自消息的类,例如:

public class ClassicMessage : Message
{
     private string m_title;
     private string m_content;

     public void SetTitle(string title) { m_title = title; }
     public void SetContent(string content) { m_content = content; }
     public string GetTitle() { return m_title; }
     public string GetContent() { return m_content; }
}

public class MessageWithCustomContent : Message
{
     private List<CustomContent> m_content;

     public MessageWithCustomContent() 
     {
          m_content = new List<CustomContent>();
     }

     public List<CustomContent> GetContent()
     {
          return m_content;
     }

     public CustomContent GetContentEntry(int id) 
     {
          return m_content.find(x => x.ID.Equals(id));
     }
}

public class CustomContent
{
     private int m_id;
     public int ID { get; set { m_id = value; } }
     private string m_body;
     public string Body { get { return m_body; } set { m_body = value; }
     private Image m_image; 
     public Image Image { get { return m_image; } set { m_image = value; } }
}

在这样的情况下,如果派生类具有类似的方法但这些方法具有不同的返回类型,我如何统一app界面? (即使方法试图做同样的事情)

我知道,通过我打破Liskov替代原则和开放/封闭原则的例子,解决这个问题的最佳方法是什么?

感谢您的帮助!

修改

为了更清楚,我想要实现的是创建一个通用接口来管理所有可能的消息作为基础“消息”,因为我想避免在消费者类中使用typeof。

例如:

if(message is MessageWithCustomContent) 
{
         // do something with the contents.
}
else if(message is MessageWithCustomContent) 
{
       // do another thing with the contents.
}
etc...

3 个答案:

答案 0 :(得分:2)

您可以将Message更改为通用,并且T将指定Content返回类型。见下面的例子。

修改 您可以使用“IMessage”和“Message:IMessage”作为基础。 然后,您就可以创建一个类似的IMessage列表

var messages = new List<IMessage>
{
    new ClassicMessage(),
    new MessageWithCustomContent()
};
foreach (var message in messages)
{
    message.GetContent();
}

以下是如何完成IMessage的实现。

public interface IMessage
{
    int GetID();
    bool GetLocalized();
    string GetMetadata();
    object GetContent();
}

public abstract class Message<T> : IMessage
{
    protected int m_id;
    protected bool m_localized;
    protected string m_metaData;

    public int GetID() { return m_id; }
    public bool GetLocalized() { return m_localized; }
    public string GetMetadata() { return m_metaData; }
    object IMessage.GetContent()
    {
        return GetContent();
    }
    public abstract T GetContent();
}

public class ClassicMessage : Message<string>
{
    private string m_title;
    private string m_content;

    public void SetTitle(string title) { m_title = title; }
    public void SetContent(string content) { m_content = content; }
    public string GetTitle() { return m_title; }
    public override string GetContent()
    {
        return m_content;
    }
}

public class MessageWithCustomContent : Message<List<CustomContent>>
{
    private List<CustomContent> m_content;

    public MessageWithCustomContent()
    {
        m_content = new List<CustomContent>();
    }

    public CustomContent GetCustomContent(int id)
    {
        return null;
    }

    public override List<CustomContent> GetContent()
    {
        return m_content;
    }
}

public class CustomContent
{
    private int m_id;
    public int ID { get; set; }
    private string m_body;

    public string Body
    {
        get { return m_body; }
        set { m_body = value; }
    }
}

答案 1 :(得分:2)

我将解释你如何破坏LSP,但在我这样做之前,你并没有真正做任何继承。是的,你声明你的类是继承的,但你不是真的继承任何东西。所以在学习LSP之前,或许你需要首先掌握继承。

我如何知道我是否在打破LSP?

请注意,您的Message课程是这样的,请注意虚拟抽象方法:

public abstract class Message 
{
    protected int m_id;
    protected bool m_localized;
    protected string m_metaData;

    public virtual int GetID() { return m_id; }
    public virtual bool GetLocalized() { return m_localized; }
    public abstract string GetMetadata();
}

创建一个这样的列表:

var messages = new List<Message>();

然后将具体类型添加到所有继承类型的列表中。然后这样做:

foreach(var thisMessage in messages)
{
    var id = thisMessage.GetID();
    var loc = GetLocalized();
    var meta = GetMetadata();
}

如果没有抛出异常,因为其中一个继承类决定它不需要其中一个方法,那么你就没有破坏LSP。这个想法是,如果某些东西继承Message,那么它应该继承一切。否则,我们无法安全而有把握地替换继承的那个为父母。

这个原则很重要的原因是因为可能存在使用Message的现有代码,如上面的foreach所示,它正在处理所有类型多态和开发人员决定像这样继承它:

public abstract class BadMessage 
{    
    public override int GetID() 
    { 
        throw new InvalidOperationException
           ("This method is not needed for BadMessage and should not be called"); 
    }
    public override bool GetLocalized() { ... }
    public override string GetMetadata() { ... }
}

您会发现这会破坏现有代码。最糟糕的是,编译器甚至无法捕获它,直到它在生产中表现得像一个丑陋的bug。

答案 2 :(得分:0)

好吧,你在de基类中缺少接口方法。抽象函数,在派生类中实现。如果您收到消息,不知道它是什么类型,您将如何请求其内容? 您可以将基于派生的特定方法添加到基础中,但是您必须在基类中的虚拟实现中实现not_implemented异常,以补偿未实现它的所有派生,并添加异常处理。但是你应该问问自己:&#34;这个类真的是衍生物吗?我想要达到什么目的。&#34;