0

正しく名前を付けているかどうかわからない(つまり、ネストされたインターフェイスの実装)。ただし、ネストされたインターフェイスの実装よりも動的プロキシを使用する利点がわかりません。以下のサンプルコードよりも動的プロキシの方が優れていますか?次のサンプルコードは、DynamicProxyで使用されるインターセプターパターンよりも何らかの方法で制限されていますか?

更新 横断的関心事とは何か、DynamicProxyがこれらの状況の維持をどのように容易にするかを理解しています。例外のロギングのようなものは、実行されている実際のコードが実行していることとは無関係です。この例は、ロギングの例のように本質的に普遍的ではありません。食べるとは、クッキーを食べる方法です。いつ食べるかは気にしないでください。あまり工夫されていない例は、ローカルストレージを使用する実装を呼び出すか、特定のクエリに対してネットワーク呼び出しを行う実装を呼び出すかを決定するクエリサービスです。ローカルストレージに含まれるアイテムの更新に関するメッセージをバスで受信したかどうかに基づきます。このような場合にDynamicProxyインターセプターを使用すると、コードのメンテナンスにどのように有利になりますか?

using System;
using Castle.Windsor;
using Castle.MicroKernel.Registration;

 namespace ConsoleApplication19 {
public enum SmellsLike { Poo, YummyCookie }

public class Cookie {
    public SmellsLike SmellsLike { get; set; }
    public int Size { get; set; }
}

public interface IHaveCookies { 
    Cookie Eat(Cookie c);
    void OtherOperation(Cookie c);
}

// this would be the interceptor if implemented using DynamicProxy
// e.g. interceptor or decorator pattern
public class SmellService : IHaveCookies {
    IHaveCookies _;
    public SmellService(IHaveCookies implementation) {
        _ = implementation;
    }
    public Cookie Eat(Cookie c) {
        Console.WriteLine("Smelling cookie");
        // intercept call to Eat and don't call it if it smells like poo
        return c.SmellsLike == SmellsLike.Poo
            ? c
            : _.Eat(c);
    }
    // shows that i'm not intercepting this call
    void OtherOperation(Cookie c) {
        // do nothing
        _.OtherOperation(c);
    }
}

//This is the actual service implementation
public class EatService : IHaveCookies {

    public Cookie Eat(Cookie c) {
        Console.WriteLine("Eating cookie");
        var whatsLeft = NomNomNom(c);
        return whatsLeft;
    }

    Cookie NomNomNom(Cookie c) {
        c.Size--;
        return c;
    }

    public void OtherOperation(Cookie c) {
        // do something else
    }
}

   // shor program that uses windsor to wire up the interfaces
class Program {
    static void Main(string[] args) {
        var container = new WindsorContainer();
        container.Register(

            // interface implementation that is actually given when
            // container.Resolve is called
            Component.For<IHaveCookies>().ImplementedBy<SmellService>().Named("Smell"),

            // wiring up actual service implementation      
            Component.For<IHaveCookies>().ImplementedBy<EatService>().Named("Eat"),

            // this injects the interceptor into the actual service implementation
            Component.For<SmellService>().ServiceOverrides(ServiceOverride.ForKey("implementation").Eq("Eat")));


        // example usage

        var yummy = new Cookie { Size = 2, SmellsLike = SmellsLike.YummyCookie };
        var poo = new Cookie { Size = 2, SmellsLike = SmellsLike.Poo };
        var svc = container.Resolve<IHaveCookies>();
        Console.WriteLine("eating yummy");

        // EatService.Eat gets called, as expected
        svc.Eat(yummy);
        Console.WriteLine("eating poo");

        // EatService.Eat does not get called, as expected
        svc.Eat(poo);
        Console.WriteLine("DONE");
        Console.ReadLine();
    }
}
}
4

3 に答える 3

4

動的プロキシ-インターセプター-任意のインターフェイスへの呼び出しをインターセプトできます。したがって、任意のインターフェースの任意の実装で任意の呼び出しを傍受したいとします。実装する必要のあるデコレータはいくつですか?そして、それらはすべて同じコードを含みますが、コンストラクターで異なるインターフェースを受け取ります。そして、彼らは装飾されたインターフェースのすべてのメソッドを実装する必要があります。非常に多くの反復コード。退屈で乾かない。

Castleを使用すると、IInterceptorを一度実装して、それを使用して上記のことを実行できます。

public class LoggingInterceptor : IInterceptor  
{
    public void Intercept(IInvocation invocation)
    {
        // log method call and parameters
        try                                           
        {                                             
            invocation.Proceed();   
        }                                             
        catch (Exception e)              
        {                                             
            // log exception
            throw; // or sth else                
        }
    }                                             
}

あなたのアップデートへの回答:それは有利ではないでしょう。そしておそらく間違っています。または多分私はあなたが何を意味するのか理解していません。多分これ?

public class ServiceSelectingWhatCallToMake : IHaveCookies
{
  public ServiceSelectingWhatCallToMake(IHaveCookies localCalls, IHaveCookies networkCalls)
  {
    // save in member variables
  }
  public SomeMethod()
  {
    if (somethingDescribingIShouldMakeLocalCall)
       this.localCalls.SomeMethod();
    else
       this.networkCalls.SomeMethod();
  }
}

それなら本当にこれだろう

public class ServiceThatDoesntKnowWhatCallItMakes
{
  public ServiceSelectingWhatCallToMake(IHaveCookiesFactory factory)
  {
    // save in member variables
  }
  public SomeMethod()
  {
    var localOrNetwork = this.factory.Create(somethingDescribingWhatCallToMake)
    localCalOrNetwork.SomeMethod();
  }
}

したがって、デコレータ/インターセプタの場所はありません。他の説明と同様に、横断的関心事(ロギング、監査、キャッシング、セキュリティなど)にインターセプターを使用します。

于 2012-01-02T18:42:07.747 に答える
0

動的プロキシは、システム全体またはサブシステム全体に適用される動作の側面を実装するのに役立ちます。同じ動作(セキュリティチェック、リモート処理、ロギング、...、またはスニッフィング-例のように)を適用する必要がある20のメソッドを持つ15のサービスがある場合、動的プロキシを使用して動作を1回実装できます。委任を使用すると、同じ結果を得ることができますが、ボイラープレートコードがはるかに多くなります(私の番号を使用すると300倍になります)。

繰り返しのボイラープレートコードは、変更を行うために必要なメンテナンス作業の量を増やし、特定のタイプの構造変更をはるかに高価にします(そして発生する可能性が低くなります)。

動的プロキシにもいくつかの欠点があります。コードは、委任(の1回の使用)よりも複雑です。また、パフォーマンスのオーバーヘッドが大きいまれなケースでは、パフォーマンスと保守性の間でトレードオフを行う必要があることを意味するパフォーマンス特性の違いが存在する可能性があります。

于 2012-01-02T18:09:57.957 に答える
0

Dynamic Proxyのアイデアは、横断的関心事に非常に適しているということだと思います。基になるオブジェクトグラフが、挿入しようとしている動作に影響を与えないもの。すべての機能のロギングやポリシー施行のようなもの。

于 2012-01-02T18:11:10.293 に答える