Как DynamicProxy от Castle Project облегчает поддержку кода, если проблема не является сквозной? - PullRequest
0 голосов
/ 02 января 2012

Не уверен, правильно ли я его назвал (т. Е. Реализации вложенного интерфейса). Однако я не понимаю преимущества использования динамического прокси по сравнению с вложенными реализациями интерфейса. Чем динамический прокси лучше, чем делает приведенный ниже пример кода? Является ли следующий пример кода более ограничивающим, чем шаблон перехватчика, используемый в DynamicProxy?

UPDATE Я понимаю, что такое сквозные проблемы и как 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();
    }
}
}

Ответы [ 3 ]

3 голосов
/ 02 января 2012

Динамический прокси - перехватчик - позволяет перехватывать звонки на любой интерфейс. Допустим, вы хотите перехватить любую запись в любой реализации любого интерфейса. Сколько декораторов вам придется реализовать? И все они содержали бы один и тот же код, но получали бы другой интерфейс в конструкторе. И им придется реализовать все методы оформления интерфейса. Так много повторяющегося кода. Скучно и не СУХОЙ.

С помощью Castle вы можете один раз реализовать IInceptceptor и использовать его для выполнения описанного выше.

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

Так что нет места для декоратора / перехватчика. Как и другие описанные, я бы использовал перехватчики для сквозных задач - регистрация, аудит, кэширование, безопасность и т. Д.

0 голосов
/ 02 января 2012

Я думаю, что идея с Dynamic Proxy заключается в том, что он действительно хорош для сквозных задач.Вещи, где базовый граф объектов не влияет на поведение, которое вы пытаетесь вставить.Что-то вроде ведения журнала или применения политик для каждой функции.

0 голосов
/ 02 января 2012

Динамический прокси может быть полезен для реализации аспектов поведения, которые применяются ко всей системе или подсистеме.Если у вас было 15 сервисов, в каждом из которых по 20 методов, к которым применялось одинаковое поведение (проверка безопасности, удаленное взаимодействие, ведение журнала, ... или прослушивание - как в вашем примере), то динамический прокси-сервер можно использовать для реализации этого поведения один раз.Используя делегирование, вы можете достичь того же результата, но с гораздо большим количеством стандартного кода (в 300 раз больше, используя мои цифры).

Повторяющийся стандартный код увеличивает объем работ по обслуживанию, необходимых для внесения изменений, и создает определенные типы структурных элементов.изменение намного дороже (и менее вероятно).

Динамические прокси также имеют некоторые недостатки - код более сложный, чем для (одноразового использования) делегирования.Также могут быть различия в характеристиках производительности, которые означают необходимость компромисса между производительностью и ремонтопригодностью в тех редких случаях, когда накладные расходы производительности значительны.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...