Меня попросили реализовать замок динамический прокси в моем веб-приложении asp.net, и я проходил несколько статей, которые я получил из Проекта Проекта и Кода Замка о замке динамический прокси в веб-приложении asp.net....
Обе статьи delt с созданием перехватчиков, но я не могу получить идею, почему перехватчики используются с классами.... Почему я должен прервать свой класс, который ведет себя правильно?
Допустим, вашему классу необходимо выполнить 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 .
Причина, по которой вы должны использовать Castle-DynamicProxy, - это так называемое аспектно-ориентированное программирование. Это позволяет вам вставлять код в стандартный поток операций вашего кода без необходимости попадать в зависимость от самого кода.
Простым примером, как всегда, является ведение журнала. Вы должны создать DynamicProxy вокруг класса, из-за которого у вас есть ошибки, поскольку он регистрирует данные, поступающие в метод, и улавливает любые исключения, а затем регистрирует исключение.
Используя перехватчик, ваш текущий код не знает, что он существует (при условии, что ваше программное обеспечение правильно построено с развязкой с интерфейсами), и вы можете изменить регистрацию своих классов с помощью инверсии контейнера управления, чтобы вместо этого использовать прокси-класс. без необходимости изменять ни одной строчки в другом месте кода. Затем, когда вы исправите ошибку, вы можете отключить проксирование.
Более продвинутое использование прокси можно увидеть в NHibernate, где вся отложенная загрузка выполняется через прокси.