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

动态代理可用于实现适用于整个系统或子系统的行为的各个方面。如果您有 15 个服务,每个服务有 20 种方法,这些方法需要应用相同的行为(安全检查、远程处理、日志记录、...或嗅探 - 如您的示例所示),那么可以使用动态代理来实现该行为一次。使用委托,您可以获得相同的结果,但使用更多样板代码(使用我的数字是 300 倍。)

重复的样板代码增加了进行更改所涉及的维护工作量,并使某些类型的结构更改更加昂贵(并且不太可能发生)。

动态代理也有一些缺点——代码比(单次使用)委托更复杂。性能特征也可能存在差异,这意味着在性能开销很大的极少数情况下,需要在性能和可维护性之间进行权衡。

于 2012-01-02T18:09:57.957 回答
0

我认为动态代理的想法是它非常适合横切关注点。底层对象图不会影响您尝试插入的行为的事物。每个功能的日志记录或策略执行之类的东西。

于 2012-01-02T18:11:10.293 回答