ABONAMENTE VIDEO REDACȚIA
RO
EN
NOU
Numărul 150
Numărul 149 Numărul 148 Numărul 147 Numărul 146 Numărul 145 Numărul 144 Numărul 143 Numărul 142 Numărul 141 Numărul 140 Numărul 139 Numărul 138 Numărul 137 Numărul 136 Numărul 135 Numărul 134 Numărul 133 Numărul 132 Numărul 131 Numărul 130 Numărul 129 Numărul 128 Numărul 127 Numărul 126 Numărul 125 Numărul 124 Numărul 123 Numărul 122 Numărul 121 Numărul 120 Numărul 119 Numărul 118 Numărul 117 Numărul 116 Numărul 115 Numărul 114 Numărul 113 Numărul 112 Numărul 111 Numărul 110 Numărul 109 Numărul 108 Numărul 107 Numărul 106 Numărul 105 Numărul 104 Numărul 103 Numărul 102 Numărul 101 Numărul 100 Numărul 99 Numărul 98 Numărul 97 Numărul 96 Numărul 95 Numărul 94 Numărul 93 Numărul 92 Numărul 91 Numărul 90 Numărul 89 Numărul 88 Numărul 87 Numărul 86 Numărul 85 Numărul 84 Numărul 83 Numărul 82 Numărul 81 Numărul 80 Numărul 79 Numărul 78 Numărul 77 Numărul 76 Numărul 75 Numărul 74 Numărul 73 Numărul 72 Numărul 71 Numărul 70 Numărul 69 Numărul 68 Numărul 67 Numărul 66 Numărul 65 Numărul 64 Numărul 63 Numărul 62 Numărul 61 Numărul 60 Numărul 59 Numărul 58 Numărul 57 Numărul 56 Numărul 55 Numărul 54 Numărul 53 Numărul 52 Numărul 51 Numărul 50 Numărul 49 Numărul 48 Numărul 47 Numărul 46 Numărul 45 Numărul 44 Numărul 43 Numărul 42 Numărul 41 Numărul 40 Numărul 39 Numărul 38 Numărul 37 Numărul 36 Numărul 35 Numărul 34 Numărul 33 Numărul 32 Numărul 31 Numărul 30 Numărul 29 Numărul 28 Numărul 27 Numărul 26 Numărul 25 Numărul 24 Numărul 23 Numărul 22 Numărul 21 Numărul 20 Numărul 19 Numărul 18 Numărul 17 Numărul 16 Numărul 15 Numărul 14 Numărul 13 Numărul 12 Numărul 11 Numărul 10 Numărul 9 Numărul 8 Numărul 7 Numărul 6 Numărul 5 Numărul 4 Numărul 3 Numărul 2 Numărul 1
×
▼ LISTĂ EDIȚII ▼
Numărul 25
Abonament PDF

AOP și LinFu

Radu Vunvulea
Solution Architect
@iQuest



PROGRAMARE

În ultimele trei numere ale TSM, am descoperit lumea AOP folosind trei stack-uri diferite - .NET, Unity și PostSharp. Dacă ne uităm la aceste framework-uri cu un ochi critic, putem să observăm că PostSharp este un framework extrem de robust care face magia AOP-ului în momentul compilării. Pe când Unity sau .NET stack inserează toată această funcționalitate la runtime.

Run-time vs Build-Time

Este foarte important momentul în care această funcționalitate este injectată deoarece poate să ne afecteze performanța și modul în care aplicația noastră funcționează. În graficul de mai jos putem să vedem o listă cu aceste framework-uri în funcție de momentul în care AOP este injectat în sistem.

După cum putem observa în diagrama de mai sus, cel mai robust framework și flexibil este PostSharp. Acesta injectează în momentul compilării tot ce este nevoie în cod pentru a putea adăuga funcționalitatea de care avem nevoie. Pe când un framework ca Unity nu va modifica codul generat de compilator, iar la runtime va injecta prin reflection și alte mecanisme cu această funcționalitate. Din punct de vedere a performanței, injectarea la runtime a funcționalități are un preț – performanța.

În acest articol vom analiza caracteristicile LinFu-ului. Un framework de AOP este un framework hibrid, o combinație între tipul static și dinamic. Dar înainte să facem acest lucru, haideți să ne aducem puțin aminte ce este AOP.

Recapitulare

AOP este o paradigmă de programare care are drept scop principal creșterea modularității unei aplicații. AOP încearcă să atingă acest scop prin permiterea separării aspectelor secante (cross-cutting concerns) – utilizând interceptarea diferitelor comenzi sau cereri.

În ultimele articole am descoperit cum putem utiliza AOP folosind Unity, PostSharp și proprietăți .NET (RealProxy). Unity ne oferă posibilitatea de a înregistra acțiunile care pot fi executate înainte și după o acțiune specifică. Clasa RealProxy este clasa principală în jurul tuturor acestor proprietăți, care este utilizată de către framework-uri precum Unity pentru a oferi această proprietate.

Cea mai mare diferență dintre RealProxy și un stack care ne oferă AOP este din perspectiva proprietăților. Utilizarea RealProxy în mod direct ne va cere să scriem toată funcționalitatea de care avem nevoie – aceasta se poate traduce în timp, bani și mai mult cod care necesită mentenanță (în cele din urmă, nu dorim să reinventăm roata).

Ce este LinFu

Este un set de librării ce ne oferă suport de AOP plus alte câteva funcționalități precum. Mai jos găsiți lista cu toate aceste funcționalități (am lăsat denumirile în engleză, deoarece traducerea în română suna extrem de ciudat):

Am putea spune că LinFu este o colecție de funcționalități de care am avut nevoie mereu în .NET, dar nu le-am avut niciodată suportate de către .NET stack. Din această cauză am apelat la framework-uri precum LinFu sau Castle (dacă nu ne-am scris propiile noastre framework-uri).

Caracteristica cea mai importantă a LinFu este ușurința cu care poate să fie învățat. Acesta este un framework extrem de simplu, care poate să fie învățat și integrat doar în câteva minute în aplicația noastră.

Deși LinFu are extrem de multe funcționalități, noi ne vom concentra doar asupra celor care sunt legate de AOP. Cât despre celelalte funcționalități, vă las pe voi să le descoperiți.

De unde vine numele

De unde credeți că vine numele de LinFu? Un nume asiatic, al unui programator care a inițiat acest stack? Nu.

Language INdependent Features Underneath [.NET]

La urma urmei LinFu este exact ce spune și numele său. O colecție de assembly-uri care extinde funcționalitatea pe care .NET o are. Această funcționalitate este extinsă folosind librării, fără să modifice sintaxa limbajului, precum alte framework-uri de AOP.

DynamicProxy

DynamicProxy ne ajută să injectăm la runtime cod și funcționalitate în codul nostru fără să modificăm aplicația deja existentă. De exemplu, dacă dorim să injectăm un sistem de trace-ing sau audit fară să fim nevoiți să poluăm codul aplicației noastre.

LinFu ne permite să interceptăm orice metodă la runtime, atâta timp cât este virtuală. Din păcate aceasta este o limitare pe care o are LinFu și alte framework-uri de AOP. Metodele pe care le interceptăm trebuie să fie metode virtuale.

Interceptarea unei metode se poate face implementând una din cele două interfețe pe care DynamicProxy ni le pune la dispoziție IInterceptor sau IInvokeWrapper.

public interface IInterceptor
{
    object Intercept(InvocationInfo info);
}

public interface IInvokeWrapper
{
    void BeforeInvoke(InvocationInfo info);
    object DoInvoke(InvocationInfo info);
    void AfterInvoke(InvocationInfo info, 
    object returnValue);
}

După cum putem observa din definiția celor doua interfețe, putem doar să interceptăm apelul sau să avem un control direct înainte de apel, după apel sau chiar în momentul apelului și să apelăm cu totul altă metodă.

În exemplul de mai jos interceptăm metoda Do din clasa Foo și logăm apelul, fară să apelăm o altă metodă, decât cea originală din clasa Foo.

public class Foo
{
    public virtual int Do(int a, int b)
    {
        return a+b;
    }
}
public class FooInterceptor : IInvokeWrapper
{
    private Foo _target;
    public FooInterceptor(Foo target)
    {
        _target = target;
    }
    public void BeforeInvoke(InvocationInfo info)
    {
        Trace.WriteLine(„Before Do() called”);
    }

    public object DoInvoke(InvocationInfo info)
    {
        object result = null;
        result = info.TargetMethod.Invoke(_target, info.Arguments);
        return result;
    }

    public void AfterInvoke(InvocationInfo info, 
    object returnValue)
    {
        Trace.WriteLine(„After Do() called”);
    }
}

Dacă dorim să apelăm o altă metodă sau să facem ceva specific în momentul apelului, trebuie să adăugăm cod în metoda DoInvoke. Putem chiar să apelăm cu totul o altă metodă, fără să mai apelăm metoda de baza din Foo. Acest lucru se poate face dacă ștergem linia de cod care face apelul propriu zis info.TargetMethod.Invoke(…).

Clasa InvocationInfo conține toată informația de care avem nevoie:

O altă caracteristică a acestui framework este constructorul la interceptor, care primește ca parametru o referință la obiectul propriu zis. Acest lucru se întâmplă deoarece LinFu are nevoie de această referință pentru a putea intercepta apelul și a suprascrie metoda virtuală pe care noi vrem să o interceptăm. Până la urma, LinFu doar redirectează apelurile spre un proxy intern.

Tot ce ne-a mai rămas de făcut este să facem legătura între cele două - clasa Foo și interceptor. Acest lucru realizându-se în felul următor:

ProxyFactory factory = new ProxyFactory();
Foo foo = new Foo();
FooInterceptor fooInterceptor = new FooInterceptor(foo);
Foo customFoo = factory.CreateProxy(interceptor);

Da, codul de mai sus nu arată tocmai foarte frumos, dar poate foarte să fie foarte ușor pus într-o metodă generică și să nu mai fie nevoie să ne batem capul cu acest setup. Odată ce avem o referință la customFoo putem să apelăm fără nici o problemă metoda Do.

Se poate observa foarte clar diferența majoră între PostSharp și LinFu. PostSharp nu cere folosirea unui proxy, deoarece face totul în momentul compilării generând un cod IL care are deja acest hook. Pe când LinFu are nevoie de o configurare din cod.

IProxy

Un lucru foarte interesant la LinFu este interfața IProxy și în special proprietatea "Interceptor". În momentul în care generăm un proxy avem o referință la această interfață. Folosind proprietatea amintită mai sus, putem să schimbăm la runtime interceptorul, fără să fim nevoiți să generăm un nou proxy sau să schimbăm ceva.

IProxy proxy = (IProxy) customFoo;
proxy.Interceptor = fooInterceptor2;

Acest lucru se întâmplă deorece CreateProxy ne returnează o instanță a obiectului nostru ușor modificată. Aceasta implementează interfața IProxy și extinde clasa noastră (în cazul nostru Foo).

Ceea ce se generează de CreateProxy arată asemănator cu:

public class FooProxy : Foo, IProxy
{
    …
    public ovverite int Do(int a, int b)
    {
…
} 
    …
}

Performanță

Din punct de vedere a performanței, LinFu este mult mai rapid ca Castle sau Unity, dar nu se poate compara cu PostSharp. LinFu este extrem de folositor în momentul în care avem foarte multe metode pe care dorim să le controlăm prin intermediul unui AOP framework.

Limitări

LinFu poate să ofere suport de AOP doar pentru metodele virtuale. O metodă care nu este marcată ca virtuală sau face parte dintr-o clasă sealed, nu poate să fie controlată.

Licențiere

Acesta este un framework sub licența GNU. Putem să îl folosim și să îl modificăm fără nici un fel de probleme.

Concluzie

Acesta este ultimul articol despre AOP. În cele patru articole pe care le-am citit până acuma despre AOP, am descoperit diferite mecanisme prin care putem să avem suport AOP în aplicația noastră. Fiecare framework are avantaje și dezavantaje. Putem spune că PostSharp este cel mai complet, având cele mai multe funcționalități și cea mai bună performanță, doar că acest lucru vine cu un cost. PostSharp nu este gratis.

În tabelul de mai jos putem să vedem funcționalitățile suportate de fiecare framework de AOP:

NUMĂRUL 149 - Development with AI

Sponsori

  • Accenture
  • BT Code Crafters
  • Accesa
  • Bosch
  • Betfair
  • MHP
  • BoatyardX
  • .msg systems
  • P3 group
  • Ing Hubs
  • Cognizant Softvision
  • Colors in projects