2017-02-05 1 views
1

は、私はこのように呼ばれるメッセージ基底クラスを持っています。このような場合にはこの例でLSPを壊すのを避けるにはどうしたらいいですか? C#の

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

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

その後、私は、例えばメッセージから継承し、さらに2つのクラスを持っています派生クラスに似たメソッドがあるが、これらのメソッドの戻り値の型が異なる場合は、どうすればアプリケーションインターフェイスを統一できますか? (たとえメソッドが同じことをしようとしても)

私は、Liskov Substitution PrincipleとOpen/Closedの原理を破っていることを知っていますが、それを回避するにはどうすればよいでしょうか?

ありがとうございました!

編集:私はtypeofを使用しないようにしたいので、より明確にするために

、私は何を達成しようとしていることは、ベースの「メッセージ」として、可能なすべてのメッセージを管理する共通のインタフェースを作成することです消費者階級。例えば

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

答えて

2

あなたは、汎用されるメッセージを変更することができ、そしてTは、コンテンツの戻り値の型を指定します。以下の例を参照してください。

編集 あなたは "IMessageが" と "メッセージ: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

を編集しました。私は自分で非常に似た実装になった。しかし、この問題のために私が使っているアプローチは間違って設計されているようです。リストや文字列かどうかをチェックせずにコンテンツを処理する方法がないからです。とにかくあなたの答えを正しいものとしてマークします。ご助力ありがとうございます! – cmszc

2

私はあなたが下のLSPをどのように壊すかを説明しますが、それを行う前に、あなたは本当に継承していません。はい、クラスを継承すると宣言していますが、は実際にはを継承していません。だから、LSPを学ぶ前に、おそらく最初に継承を把握する必要があります。


LSPを壊しているかどうかはどうすればわかりますか?このようなリストを作成します

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>(); 

そして、それに具体的なタイプを追加

ないよう仮想抽象方法に気づく、あなたの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() { ... } 
} 

これは既存のコードを破ることがわかります。そして、最悪の部分は、コンパイラは生産に醜いバグのようなものになるまで、それをキャッチすることさえできません。

+0

LSPについてのご説明いただきありがとうございます! – cmszc

0

まあ、あなたは、基本クラスのインターフェイスメソッドがありません。デリバティブクラスで実装される抽象関数。あなたがどのような種類であるかを知らないメッセージを受け取った場合、その内容をどのように要求しますか? デリバリ固有のメソッドをベースに追加することもできますが、実装していないすべての派生を補うために、ベースクラスの仮想実装にnot_implemented例外を実装し、例外処理を追加する必要があります。しかし、あなたは自分自身に尋ねるべきです: "このクラスは本当に派生的なものなのですか?達成したいものは何ですか?"

関連する問題