2017-02-05 2 views
1

У меня есть базовый класс под названием сообщение так:Как я могу избежать нарушения LSP в этом примере? C#

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; } } 
} 

В таком случае, как это , как я могу унифицировать интерфейс приложения, если производные классы имеют похожие методы, но эти методы имеют разные типы возвращаемых данных? (даже если методы пытаются сделать то же самое)

Я знаю, что с примером я нарушаю принцип замещения Лискова и принцип Open/Closed, какой лучший подход для этого?

Благодарим за помощь!

Edit:

Для большей ясности, что я пытаюсь добиться того, чтобы создать общий интерфейс для управления всех возможных сообщений в качестве базового «Message», потому что я хочу, чтобы избежать использования TypeOf в потребительский класс.

, например:

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

ответ

2

Вы можете изменить сообщение, чтобы быть общим, а T бы указать Content тип возвращаемого значения. См. Пример ниже.

Редактировать Вы можете использовать "IMessage" и "Message: IMessage" в качестве основы. Затем вы сможете создать список IMessage как так

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

Ниже, как реализация IMessagecould быть сделано.

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; } 
    } 
} 
+0

Это хорошая идея, но я не могу создать единый список сообщений для их управления, потому что я должен указать общий тип в объявлении – cmszc

+0

Я отредактировал свой ответ выше – Coolio

+0

Nice! Я сам пришел к очень похожей реализации. Но похоже, что подход, который я использую для этой проблемы, неправильно разработан, потому что тогда невозможно обработать контент, не проверяя, является ли это списком или строкой. Я всегда буду отмечать ваш ответ. Большое спасибо за Вашу помощь! – cmszc

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, как показано на Еогеасп выше, где он обрабатывает все типы полиморфно и разработчик решает наследовать это так:

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() { ... } 
} 

Вы видите, что это нарушит существующий код. И хуже всего то, что компилятор даже не сможет его поймать, пока он не станет похож на уродливую ошибку в производстве.

+0

спасибо за ваше объяснение о LSP! – cmszc

0

Ну, вам не хватает методов интерфейса в базовом классе. Абстрактные функции, которые реализуются в производных классах. Если вы получите сообщение, не зная, что это такое, как бы вы запросили его содержимое? Вы можете добавить к своей базе методы, специфичные для производных, но вам придется реализовать исключение not_implemented в виртуальной реализации базового класса, чтобы компенсировать все производные, не реализующие его, и добавить обработку исключений. Но тогда вы должны спросить себя: «Этот класс действительно является производным? Чего я хочу достичь».

Смежные вопросы