我有一个名为消息的基类,如下所示:
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...
答案 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;