Était-ce utile?

La solution

Disons que votre classe a besoin de faire 3 choses pour une certaine opération:

  1. Effectuer un contrôle de sécurité;
  2. Connectez l'appel de méthode;
  3. Cache le résultat.

Supposons en outre que votre classe ne sait rien sur la façon spécifique vous avez configuré votre sécurité, l'exploitation ou la mise en cache. Vous devez compter sur des abstractions de ces choses.

Il y a quelques façons de s'y prendre. Un moyen serait de mettre en place un groupe d'interfaces et utiliser l'injection de constructeur:

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

Ce n'est pas un code horrible, mais penser aux problèmes nous présentons:

  • La classe OrderService ne peut pas fonctionner sans les trois dépendances. Si nous voulons le faire afin qu'il puisse, nous devons commencer émaillant le code avec des contrôles nuls partout.

  • Nous écrivons un ton de code supplémentaire pour effectuer une opération relativement simple (regardant un ordre).

  • Tout ce code est boilerplate à répéter dans tous méthode, ce qui rend pour une très grande, laide, la mise en œuvre de sujettes aux bugs.

Voici une classe qui est beaucoup plus facile à maintenir:

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

Programmation Orientée Aspect, ces attributs sont appelés Rejoindre Points , l'ensemble complet qui est appelé Cut point.

au lieu du code de dépendance en train d'écrire, encore et encore, nous laissons « trucs » que certaines opérations supplémentaires sont censées être effectuées pour cette méthode.

Bien sûr, ces attributs doivent se transformer en code quelque temps , mais vous pouvez reporter que tout le chemin jusqu'à votre principal code d'application, en créant un proxy pour le OrderService (notez que la méthode GetOrder a été virtual car il doit être remplacé pour le service), et interceptant la méthode GetOrder.

Ecrire l'intercepteur peut être aussi simple que cela:

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

Et la création du proxy serait:

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

Ceci est non seulement beaucoup de code moins répétitif, mais il supprime complètement la dépendance réelle , parce que regardez ce que nous avons fait - nous ne même pas Vous un autorisation ou d'un système de mise en cache encore, mais le système fonctionne toujours. Nous pouvons simplement insérer l'autorisation et la logique de la mise en cache plus tard en enregistrant un autre intercepteur et la vérification des AuthorizeAttribute ou CacheAttribute.

Si tout va bien ce qui explique le "pourquoi".

Barre latérale: Comme commentaires Krzysztof Kozmic, ce n'est pas un DP « meilleure pratique » d'utiliser un intercepteur dynamique comme celui-ci. Dans le code de production, vous ne voulez pas avoir l'intercepteur en cours d'exécution pour les méthodes inutiles, donc utiliser IInterceptorSelector à la place.

Autres conseils

La raison pour laquelle vous utiliseriez Château-DynamicProxy est pour ce qu'on appelle la programmation Orientée Aspect. Il vous permet de code Intervenir dans le flux de fonctionnement standard de votre code sans qu'il soit nécessaire de devenir dépendant du code lui-même.

Un exemple simple est, comme toujours, l'exploitation forestière. Que vous créer un DynamicProxy autour d'une classe que vous avez des erreurs de ce qu'il enregistre les données entrant dans la méthode et les captures des exceptions et enregistre l'exception.

Utilisation du intercepteur votre code actuel n'a aucune idée qu'il existe (en supposant que vous avez votre logiciel construit de manière découplée avec des interfaces correctement) et vous pouvez modifier l'enregistrement de vos classes avec une inversion du conteneur de contrôle à utiliser la classe à la place approximé sans avoir à changer une seule ligne d'autre où dans le code. Ensuite, lorsque vous résoudre le bug que vous pouvez désactiver le mandatement.

Plus d'une utilisation avancée de mandatement peut être vu avec NHibernate où tout le chargement paresseux est géré par des procurations.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top