在本例中,如何避免破坏LSP?C#
我有一个名为Message的基类,如下所示:在本例中,如何避免破坏LSP?C#,c#,oop,design-patterns,liskov-substitution-principle,C#,Oop,Design Patterns,Liskov Substitution Principle,我有一个名为Message的基类,如下所示: 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; } publi
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; }
}
var messages = new List<Message>();
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() { ... }
}
然后,我又有两个类继承自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; } }
}
if(message is MessageWithCustomContent)
{
// do something with the contents.
}
else if(message is MessageWithCustomContent)
{
// do another thing with the contents.
}
etc...
您可以将消息更改为泛型,T将指定内容返回类型。见下面的例子
编辑
您可以使用“IMessage”和“Message:IMessage”作为基础。
然后您就可以创建这样的IMessage列表
var messages = new List<IMessage>
{
new ClassicMessage(),
new MessageWithCustomContent()
};
foreach (var message in messages)
{
message.GetContent();
}
var messages=新列表
{
新ClassicMessage(),
新消息WithCustomContent()
};
foreach(消息中的var消息)
{
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; }
}
}
公共接口IMessage
{
int GetID();
bool GetLocalized();
字符串GetMetadata();
对象GetContent();
}
公共抽象类消息:IMessage
{
受保护的int m_id;
受保护的布尔m_本地化;
受保护的字符串m_元数据;
public int GetID(){return m_id;}
public bool GetLocalized(){return m_localized;}
公共字符串GetMetadata(){return m_metaData;}
对象IMessage.GetContent()
{
返回GetContent();
}
公共摘要T GetContent();
}
公共类ClassicMessage:消息
{
私有字符串m_title;
私有字符串m_内容;
public void SetTitle(字符串标题){m_title=title;}
public void SetContent(字符串内容){m_content=content;}
公共字符串GetTitle(){return m_title;}
公共重写字符串GetContent()
{
返回m_内容;
}
}
公共类MessageWithCustomContent:Message
{
私有列表m_内容;
公共消息WithCustomContent()
{
m_content=新列表();
}
公共CustomContent GetCustomContent(int-id)
{
返回null;
}
公共覆盖列表GetContent()
{
返回m_内容;
}
}
公共类自定义内容
{
私人国际货币单位id;
公共int ID{get;set;}
私有字符串m_体;
公共字符串体
{
获取{return m_body;}
集合{m_body=value;}
}
}
您可以将消息更改为通用消息,T将指定内容返回类型。见下面的例子
编辑
您可以使用“IMessage”和“Message:IMessage”作为基础。
然后您就可以创建这样的IMessage列表
var messages = new List<IMessage>
{
new ClassicMessage(),
new MessageWithCustomContent()
};
foreach (var message in messages)
{
message.GetContent();
}
var messages=新列表
{
新ClassicMessage(),
新消息WithCustomContent()
};
foreach(消息中的var消息)
{
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; }
}
}
公共接口IMessage
{
int GetID();
bool GetLocalized();
字符串GetMetadata();
对象GetContent();
}
公共抽象类消息:IMessage
{
受保护的int m_id;
受保护的布尔m_本地化;
受保护的字符串m_元数据;
public int GetID(){return m_id;}
public bool GetLocalized(){return m_localized;}
公共字符串GetMetadata(){return m_metaData;}
对象IMessage.GetContent()
{
返回GetContent();
}
公共摘要T GetContent();
}
公共类ClassicMessage:消息
{
私有字符串m_title;
私有字符串m_内容;
public void SetTitle(字符串标题){m_title=title;}
public void SetContent(字符串内容){m_content=content;}
公共字符串GetTitle(){return m_title;}
公共重写字符串GetContent()
{
返回m_内容;
}
}
公共类MessageWithCustomContent:Message
{
私有列表m_内容;
公共消息WithCustomContent()
{
m_content=新列表();
}
公共CustomContent GetCustomContent(int-id)
{
返回null;
}
公共覆盖列表GetContent()
{
返回m_内容;
}
}
公共类自定义内容
{
私人国际货币单位id;
公共int ID{get;set;}
私有字符串m_体;
公共字符串体
{
获取{return m_body;}
集合{m_body=value;}
}
}
我将在下面解释您如何打破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();
}
创建如下列表:
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; }
}
var messages = new List<Message>();
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() { ... }
}
如果由于一个继承类决定它不需要这些方法中的一个而没有引发异常,那么您就没有破坏LSP。其思想是,如果某个内容继承了消息
,那么它应该继承所有内容。否则,我们就不能安全而自信地用继承的替代父代
这一原则之所以重要,是因为可能存在使用消息的现有代码,如上面的foreach所示,它以多态方式处理所有类型,开发人员决定这样继承它:
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; }
}
var messages = new List<Message>();
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一样出现在生产环境中。我将在下面解释如何打破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();
}
创建如下列表:
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; }
}
var messages = new List<Message>();
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() { ... }
}
如果由于一个继承类决定它不需要这些方法中的一个而没有引发异常,那么您就没有破坏LSP。其思想是,如果某个东西继承了消息
,那么它应该继承所有内容