1

操り人形の達人がひもを引っ張るのと同じように、下の層を上の層で制御する必要があるシナリオがあります。

一部の内部イベントが時々生成されるため、下位層も上位層にコールバックします。

SimpleInjectorを使用しており、ILowerをUpperコンストラクターに注入します。循環参照が発生するため、アッパーをロワーに注入できません。

代わりに、2つのレイヤーをリンクするためのレジスタコールバック関数があります。ただし、nullチェックを使用してコードを分散させる必要があります。

このオブジェクトのリンクを実現するためのより良い方法や異なるアーキテクチャはありますか?

// an interface that transport can callback from transport to client
public interface ILowerToUpperCallback
{
    void ReplyA();
    void ReplyB();
}

// transport interface that client calls
public interface ILower
{
    void Test1();
    void Test2();
    void RegisterCallback(ILowerToUpperCallback callback);
}

public class Upper : ILowerToUpperCallback
{
    private readonly ILower lower;

    public Upper(ILower lower)
    {
        this.lower = lower;
        this.lower.RegisterCallback(this);
    }

    void ReplyA()
    {
    }

    void ReplyB()
    {
    }
}

public class Lower : ILower
{
    private ILowerToUpperCallback callback;

    /* this is not possible, would cause a circular reference
    public Lower(ILowerToUpperCallback callback)
    {
        this.callback = callback;
    }
    */

    // set by different method instead, what happens if this is never set?!
    void RegisterCallback(ILowerToUpperCallback callback)
    {
        this.callback = callback;
    }

    void OnTimer()
    {
        // some timer function

        if(this.callback != null) // these null checks are everywhere :(
            this.callback.ReplyA();
    }
}
4

4 に答える 4

1

あなたの例は少し抽象的で、アーキテクチャの感触をつかむのは難しいですが、ドメインイベントの概念からおそらく利益を得ることができるようです。あなたのコールバックのアイデアも同じ線に沿っているようです。最近、ドメインイベントパターンの単純なバージョンを実装しました。これにより、コンポーネントが相互に参照することなく、主要なビジネスレイヤーコンポーネントを連携させることができます。あなたが言ったように、UpperはILowerを参照できますが、Lower参照IUpperを使用すると循環参照が発生し、問題が発生する可能性があります。

これが私の解決策に向けて取り組むのに役立ったSOの質問です。

ビジネスレイヤーファサードと混合ビジネスコンポーネント

また、これら2つのリンクは、ドメインイベントの概念と実装を理解するのに役立ちます。

http://jasondentler.com/blog/2009/11/simple-domain-events/

http://blog.robustsoftware.co.uk/2009/08/better-domain-event-raiser.html

于 2012-11-19T19:54:27.263 に答える
1

設定が最も簡単ではないことにすでに気づいていますが、設計に問題はないと思います。問題はDIフレームワークの制限ではなく、精神体操で実行する必要があります。

ここにアイデアがあります。クラスを次のように変更します。

public class Upper : IUpper, ILowerToUpperCallback
{
   public Upper(/* all depedencies except ILower */) { }

    // Promote ILower to property dependency
    public ILower Lower { get; set; }
}

public class Lower : ILower
{
    // Use the Null Object Pattern for default implementation to prevent
    // null checks.
    private ILowerToUpperCallback callback = new NullCallback();

    public Upper(/* all dependencies except ILowerToUpperCallback */)
    {
        this.callback = callback;
    }

    // Allow overriding the default implementation using a method, just
    // as you are already did.
    public SetCallback(ILowerToUpperCallback callback)
    {
        if (callback == null) throw new ArgumentNullException("callback");
        this.callback = callback;
    }
}

この設計では、次のようにすべてを配線できます。

container.Register<ILower, Lower>();
container.Register<IUpper, Upper>();

container.RegisterInitializer<Upper>(upper =>
{
    var lower = (Lower)container.GetInstance<ILower>();
    lower.SetCallback(upper);
    upper.Lower = lower;
});

Lowerおよびは通常のサービスであるためUpper、通常どおりに解決できます。のイニシャライザデリゲートを登録することによりUpper、コンテナの作成後に追加の初期化を行うことができますUpper。この初期化子は配線LowerUpperます。コンポジションルートはとを知っているので、ILowerルールを破ることなく、からにLower安全にキャストできます。この設計の最も優れている点は、インターフェイスがクリーンに保たれ、実際には実装の詳細であるを認識しないことです。ILowerLowerILowerILowerToUpperCallback

于 2012-11-19T20:03:33.163 に答える
1

まず第一に、あなたのコードは私にはうまく見えます。

ただし、これはObserverパターンのように見えます。

2番目の懸念事項(nullチェック)は、コールバック/オブザーバーフィールドをNullオブジェクトIObserver( /の何もしない実装ILowerToUpperCallback)で初期化することで処理できます。

于 2012-11-19T19:56:21.790 に答える
1

考えられるアプローチの1つは、最下位層の1つにメッセージングサブシステムを配置して、下位層と上位層の両方がpub/subモデルに参加できるようにすることです。

メッセージングサブシステムは、たとえばEventAggregator。これは、パブリッシャーをサブスクライバーから切り離すのに役立ちます。両方のレイヤーからアクセスできる専用のイベントモデルを使用すると、さらに別のオブジェクトを使用して、ほぼすべてのオブジェクトを制御できます。

しかし、私はあなたのアプローチに反対していません。循環依存は何も悪いことではありません。たとえば、MVP(Model-View-Presenter)は、ビューとプレゼンターの間の依存関係に基づいています。

于 2012-11-19T19:48:40.630 に答える