在此示例中如何避免破坏 LSP? C#
How can i avoid breaking LSP in this example? C#
我有一个名为 Message 的基础 class,如下所示:
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; }
}
然后,我还有两个 class 继承自 Message 例如:
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; } }
}
在这种情况下,如果派生的 classes 具有相似的方法但这些方法具有不同的 return 类型,我该如何统一应用程序界面? (即使方法试图做同样的事情)
我知道这个例子违反了里氏替换原则和 Open/Closed 原则,解决这个问题的最佳方法是什么?
感谢您的帮助!
编辑:
为了更清楚,我想要实现的是创建一个通用接口来管理所有可能的消息作为基础 "Message",因为我想避免在消费者中使用 typeof class.
例如:
if(message is MessageWithCustomContent)
{
// do something with the contents.
}
else if(message is MessageWithCustomContent)
{
// do another thing with the contents.
}
etc...
您可以将 Message 更改为通用的,T 将指定 Content return 类型。请参阅下面的示例。
编辑
您可以使用 "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; }
}
}
我将在下面解释你如何破坏 LSP,但在我这样做之前,你并没有真正进行任何继承。是的,您是在声明您的 class 继承,但您 并没有真正 继承任何东西。所以在学习 LSP 之前,或许你需要先了解一下继承。
我怎么知道我是否破坏了 LSP?
为了避免你的 Message
class 是这样,请注意 virtual 和 abstract 方法:
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();
}
如果您没有因为其中一个继承 classes 决定它不需要这些方法之一而抛出异常,那么您没有破坏 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() { ... }
}
你看这会破坏现有代码。最糟糕的是,编译器甚至无法捕捉到它,直到它像生产中的丑陋错误一样出现。
好吧,您缺少基础 class 中的接口方法。抽象函数,在派生 classes 中实现。如果你收到一条消息,不知道它是什么类型,你将如何请求它的内容?
您可以将 derivative-specific 方法添加到您的基础中,但您必须在基础 class 的虚拟实现中实现一个 not_implemented 异常,以补偿所有未实现它的派生,并添加异常处理。但是你应该问问自己:“这个 class 真的是一个导数吗?我想达到什么目的。”
我有一个名为 Message 的基础 class,如下所示:
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; }
}
然后,我还有两个 class 继承自 Message 例如:
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; } }
}
在这种情况下,如果派生的 classes 具有相似的方法但这些方法具有不同的 return 类型,我该如何统一应用程序界面? (即使方法试图做同样的事情)
我知道这个例子违反了里氏替换原则和 Open/Closed 原则,解决这个问题的最佳方法是什么?
感谢您的帮助!
编辑:
为了更清楚,我想要实现的是创建一个通用接口来管理所有可能的消息作为基础 "Message",因为我想避免在消费者中使用 typeof class.
例如:
if(message is MessageWithCustomContent)
{
// do something with the contents.
}
else if(message is MessageWithCustomContent)
{
// do another thing with the contents.
}
etc...
您可以将 Message 更改为通用的,T 将指定 Content return 类型。请参阅下面的示例。
编辑 您可以使用 "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; }
}
}
我将在下面解释你如何破坏 LSP,但在我这样做之前,你并没有真正进行任何继承。是的,您是在声明您的 class 继承,但您 并没有真正 继承任何东西。所以在学习 LSP 之前,或许你需要先了解一下继承。
我怎么知道我是否破坏了 LSP?
为了避免你的 Message
class 是这样,请注意 virtual 和 abstract 方法:
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();
}
如果您没有因为其中一个继承 classes 决定它不需要这些方法之一而抛出异常,那么您没有破坏 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() { ... }
}
你看这会破坏现有代码。最糟糕的是,编译器甚至无法捕捉到它,直到它像生产中的丑陋错误一样出现。
好吧,您缺少基础 class 中的接口方法。抽象函数,在派生 classes 中实现。如果你收到一条消息,不知道它是什么类型,你将如何请求它的内容? 您可以将 derivative-specific 方法添加到您的基础中,但您必须在基础 class 的虚拟实现中实现一个 not_implemented 异常,以补偿所有未实现它的派生,并添加异常处理。但是你应该问问自己:“这个 class 真的是一个导数吗?我想达到什么目的。”