2

WPFアプリケーション用の単純なメディエーターを作成しました(これは、ここにあるコードの派生物です)。

public class Messenger
{
    private IDictionary<Type, IList<MessageObserver>> observers;

    public Messenger()
    {
        observers = new Dictionary<Type, IList<MessageObserver>>();
    }

    public IDisposeObserver AddObserver<TMessageType>(Action<TMessageType> handler) 
        where TMessageType : Message { }

    protected abstract void RemoveObserver<TMessageType>(Action<TMessageType> handler) 
        where TMessageType : Message { }

    public abstract void PostMessage<TMessageType>(TMessageType message) 
        where TMessageType : Message { }
}

Messageは、を介して投稿されるすべてのメッセージの基本クラスを提供する単なる空のクラスですMessenger。このクラスから派生して、関連データを含む特定のメッセージクラスを作成するという考え方です(少し似ていますEventArgs)。

私の実装が異なるのは、オブザーバーを削除したい方法です。次の行に沿って、宣言的な方法でいつ削除する必要があるかを指定できるようにしたいと思います。

MessengerInstance.AddObserver<LoginMessage>(HandleLogin)
    .RemoveObserverWhen(message => message.LoginResult == LoginResult.Successful);

RemoveObserverWhenメソッドは、メソッドで指定されたメッセージタイプである必要がある場所をPredicate<T>取ります。TAddObserver

ここでの考え方は、オブザーバー自体を登録するのと同じ場所でオブザーバーを削除するロジックを指定できるということです。次に、Messengerクラスは、メッセージハンドラーの実行後に述語をチェックし、述語がtrueと評価された場合は、メッセージハンドラーと削除ハンドラーの両方を削除します。

各メッセージハンドラーは任意の数の削除ハンドラーを持つことができるので、これをと呼ばれるクラスにパッケージ化しましたMessageObserver

public class MessageObserver : IDisposeObserver
{
    private IList<object> disposalHandlers;

    public MessageObserver(object observer)
    {
        Observer = observer;
        disposalHandlers = new List<object>();
    }

    public object Observer { get; private set; }

    public IList<object> DisposalHandlers
    {
        get { return disposalHandlers; }
    }

    public IDisposeObserver RemoveObserverWhen<T>(Predicate<T> predicate) 
        where T : Message
    {
        disposalHandlers.Add(predicate);
        return this;
    }
}

メソッドを提供するMessageObserver実装。IDisposeObserverRemoveObserverWhen

public interface IDisposeObserver
{
    IDisposeObserver RemoveObserverWhen<T>(Predicate<T> predicate) 
        where T : Message;
}

ここでの考え方は、IDisposeObserverメソッドをチェーンできるようにインスタンスを返すことができるということです。

これはすべて機能し、viewModelに次のコードがあります。

MessengerInstance.AddObserver<LoginMessage>(HandleLogin)
    .RemoveObserverWhen<LoginMessage>(message => message.LoginResult == LoginResult.Successful)
    .RemoveObserverWhen<LoginMessage>(SecondDisposalHandler);

私が抱えている問題は、メソッドを呼び出すときにジェネリックパラメーター(LoginMessageこの場合)を指定した場合にのみこれが機能することです。RemoveObserver投稿の冒頭で説明した方法でメソッドを呼び出せるようにしたいと思います。

どういうわけかジェネリックを返す必要があると思いますIDisposeObserverが、この変更を行う場合はジェネリックにする必要があり、クラスが非ジェネリックでMessageObserverあるため、制約を指定できません。Messenger

だから私の質問は、メソッドを呼び出すときにメッセージタイプを指定する必要がないようにコードを更新できますか、RemoveObserverWhenそれとも現在のソリューションを使用する必要がありますか?

注:これには他にも利用可能な実装があることは承知していますが、c#のオブジェクト指向設計の原則とジェネリックスを理解するためにこれを行っているので、他の実装の方向に向けないでください。

4

1 に答える 1

3

どういうわけかジェネリックIDisposeObserverを返す必要があると思いますが、この変更を行う場合は、MessageObserverをジェネリックにする必要があり、Messengerクラスは非ジェネリックであるため、制約を指定できません。

MessageObserverおそらく、すべての型中立部分を含む非ジェネリック基本クラスと、ジェネリックを実装するジェネリック基本クラスが必要になるでしょうIDisposeObserver<T>AddObserver次に、ジェネリックを返すように変更できますがIDisposeObserver<T>、辞書はMessengerとして保持しIDictionary<Type, IList<MessageObserver>>ます。したがって、明確にするために、次のようになります。

public interface IDisposeObserver<T>
{
    IDisposeObserver<T> RemoveObserverWhen(Predicate<T> predicate) 
        where T : Message;
}

public abstract class MessageObserver { ... }

public class MessageObserver<T> : MessageObserver, IDisposeObserver<T> { ... }

この場合、メソッドを実行する必要があるときに問題が発生します。IDiposeObserver<T>.RemoveObserverWhen辞書のリスト内の各項目をにキャストするだけでよい場合がありますIObserver<T>Tその時点で、リストに適切にデータを入力したことがわかります。

もちろん、それがオブザーバーで行う唯一のことである場合は、代わりにそのままにして、2つの異なるクラスIDictionary<Type, IList<object>>をわざわざ持たないようにすることができます。MessageObserverそれは本当に、非ジェネリックなものから何らかの価値を得るかどうかに依存します。

于 2012-09-30T22:26:51.853 に答える