Что действительно перехватчики делают с моим c# классом?

Меня попросили реализовать замок динамический прокси в моем веб-приложении asp.net, и я проходил несколько статей, которые я получил из Проекта Проекта и Кода Замка о замке динамический прокси в веб-приложении asp.net....

Обе статьи delt с созданием перехватчиков, но я не могу получить идею, почему перехватчики используются с классами.... Почему я должен прервать свой класс, который ведет себя правильно?

12
задан ACP 7 April 2010 в 13:37
поделиться

2 ответа

Допустим, вашему классу необходимо выполнить 3 действия для определенной операции:

  1. выполнить проверку безопасности;
  2. зарегистрировать вызов метода;
  3. кэшировать результат.

Предположим далее, что ваш класс ничего не знает о специфическом способе настройки безопасности, ведения журнала или кеширования. Вам нужно полагаться на абстракции этих вещей.

Есть несколько способов сделать это. Один из способов - настроить несколько интерфейсов и использовать внедрение конструктора:

public class OrderService : IOrderService
{
    private readonly IAuthorizationService auth;
    private readonly ILogger logger;
    private readonly ICache cache;

    public OrderService(IAuthorizationService auth, ILogger logger,
        ICache cache)
    {
        if (auth == null)
            throw new ArgumentNullException("auth");
        if (logger == null)
            throw new ArgumentNullException("logger");
        if (cache == null)
            throw new ArgumentNullException("cache");
        this.auth = auth;
        this.logger = logger;
        this.cache = cache;
    }

    public Order GetOrder(int orderID)
    {
        auth.AssertPermission("GetOrder");
        logger.LogInfo("GetOrder:{0}", orderID);
        string cacheKey = string.Format("GetOrder-{0}", orderID);
        if (cache.Contains(cacheKey))
            return (Order)cache[cacheKey];
        Order order = LookupOrderInDatabase(orderID);
        cache[cacheKey] = order;
        return order;
    }
}

Это не ужасный код, но подумайте о проблемах, которые мы вводим:

  • Класс OrderService может ' t без всех трех зависимостей. Если мы хотим сделать так, чтобы это было возможно, нам нужно начать приправлять код повсюду нулевыми проверками.

  • Мы пишем тонну дополнительного кода для выполнения относительно простой операции (поиска заказа).

  • Весь этот шаблонный код должен повторяться в каждом методе, что делает реализацию очень большой, уродливой и подверженной ошибкам.

Вот класс, который намного проще поддерживать:

public class OrderService : IOrderService
{
    [Authorize]
    [Log]
    [Cache("GetOrder-{0}")]
    public virtual Order GetOrder(int orderID)
    {
        return LookupOrderInDatabase(orderID);
    }
}

В Аспектно-ориентированном программировании эти атрибуты называются Точки соединения , полный набор которых называется ] Point Cut .

Вместо фактического написания кода зависимости снова и снова мы оставляем «подсказки», что для этого метода предполагается выполнение некоторых дополнительных операций.

Конечно, эти атрибуты должны быть преобразованы в код когда-нибудь , но вы можете отложить это до своего основного кода приложения, создав прокси для OrderService (обратите внимание, что метод GetOrder был сделан виртуальным , потому что его нужно переопределить для службы), и перехватывает ] GetOrder метод.

Написать перехватчик может быть так просто:

public class LoggingInterceptor : IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        if (Attribute.IsDefined(invocation.Method, typeof(LogAttribute))
        {
            Console.Writeline("Method called: "+ invocation.Method.Name);
        }
        invocation.Proceed();
    }
}

А создание прокси будет следующим:

var generator = new ProxyGenerator();
var orderService = (IOrderService)generator.CreateClassProxy(typeof(OrderService),
    new LoggingInterceptor());

Это не только намного менее повторяющийся код, но он полностью удаляет фактическую зависимость , потому что посмотрите, что мы сделали - у нас даже еще нет системы авторизации или кэширования, но система все еще работает. Мы можем просто вставить логику авторизации и кеширования позже, зарегистрировав другой перехватчик и проверив AuthorizeAttribute или CacheAttribute .

Надеюсь, это объясняет «почему».

Врезка: Как комментирует Кшиштоф Когмич, использование такого динамического перехватчика - не лучшая практика для DP. В производственном коде вы не хотите, чтобы перехватчик работал для ненужных методов, поэтому используйте вместо него IInterceptorSelector .

53
ответ дан 2 December 2019 в 03:01
поделиться

Причина, по которой вы должны использовать Castle-DynamicProxy, - это так называемое аспектно-ориентированное программирование. Это позволяет вам вставлять код в стандартный поток операций вашего кода без необходимости попадать в зависимость от самого кода.

Простым примером, как всегда, является ведение журнала. Вы должны создать DynamicProxy вокруг класса, из-за которого у вас есть ошибки, поскольку он регистрирует данные, поступающие в метод, и улавливает любые исключения, а затем регистрирует исключение.

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

Более продвинутое использование прокси можно увидеть в NHibernate, где вся отложенная загрузка выполняется через прокси.

3
ответ дан 2 December 2019 в 03:01
поделиться
Другие вопросы по тегам:

Похожие вопросы: