How does Castle Project's DynamicProxy make code easier to maintain when the concern is not cross-cutting?

StackOverflow https://stackoverflow.com/questions/8703911

Domanda

Not sure if i'm naming it correctly (i.e. nested interface implementations). However I do not understand the benefit of using dynamic proxy over nested interface implementations. How is dynamic proxy better than doing what the sample code below does? Is the following sample code in some way more limiting that the interceptor pattern used in DynamicProxy?

UPDATE I understand what cross-cutting concerns are and how DynamicProxy makes maintaining these situations easier. Things like logging exceptions are independent of what the actual code being executed is doing. This example is not universal in nature like the logging example. Eat is how a cookie you eat a cookie. It should not be concerned with when you should eat it. A less contrived example would be a query service that determines if it should call an implementation that uses a local storage, or call an implementation that makes a network call for specific queries. Based on if it has received a message on the bus for an item update contained in the local storage. How would using a DynamicProxy interceptor in cases like these be advantageous for code maintenance?

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();
    }
}
}
È stato utile?

Soluzione

Dynamic proxy - interceptor - let's you intercept calls to any interface. So let's say you want intercept any cally in any implementation of any interface. How many decorators you would have to implement? And all of them would contain same code but receive different interface in constructor. And they would have to implement all methods of decorated interface. So much repetitive code. Boring and not DRY.

With Castle you can Implement IInterceptor once and use it to do what's described above.

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

Answer to your UPDATE: It wouldn't be advantageous. And probably wrong. Or maybe I don't understand what you mean. Maybe this?

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

Then it should really be this

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

So there is no place for decorator/interceptor. Like others described I would use interceptors for cross cutting concerns - logging, auditing, caching, security and so on.

Altri suggerimenti

A dynamic proxy can be useful for implementing aspects of behaviour that apply across a complete system or sub-system. If you had 15 services each with 20 methods that need the same behaviour applied to them (security check, remoting, logging, ..., or sniffing - as in your example) then a dynamic proxy can be used to implement the behaviour once. Using delegation you can achieve the same result but with much more boilerplate code (300 times more using my numbers.)

Repetitive boilerplate code increases the amount of maintenance work involved in making a change, and makes certain types of structural change much more expensive (and less likely to happen.)

Dynamic proxies also have some disadvantages - the code is more complicated than for (a single use of) delegation. There can also be differences in performance characteristics that mean a trade-off needs to be made between performance and maintainability in those rare cases where the performance overhead is significant.

I think the idea with Dynamic Proxy is that it is really good for cross cutting concerns. Things where the underlying object graph does not affect the behavior you are trying to insert. Something like logging or policy enforcement of every function.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top