2007-05-17

Niclas Nilsson hos Avega: Dependency Injection och AOP med Spring.NET

Dryga månaden efter Jimmy Nilssons besök på Avega förärades vi i slutet av mars med ett besök av dennes kollega från factor10 - Niclas Nilsson. Ämnen för kvällen var Dependency Injection och aspektorienterad programmering (AOP) med Spring.NET.

Dependency injection, även känt som Inversion of Control innan Martin Fowler m.fl. myntade det mer specifika begreppet, handlar i grund och botten om att minska beroenden mellan olika objekt genom att injicera de objekt man behöver. Det traditionella sättet att använda ett objekt från ett annat är ju att instantiera det direkt på det här sättet:

public class SalesController
{
   // Code...

  ItemRepository items = new ItemRepository();

  // More code...
}

Med dependency injection kan det istället se ut så här:

public class SalesController
{
   // Code...
   IItemRepository items;

   public SalesController(IItemRepository items)
   {
      this.items = items;
   }

   // More code....
}

I det första, traditionella, fallet har SalesController ett beroende av ItemRepository och måste anpassa sig efter ändringar i denna klass. I varianten med dependency injection förser man istället SalesController med en abstraktion (ett Interface i exemplet) av klassen och gör det därmed möjligt att ändra och byta ut implementationen av ItemRepository utan att SalesController påverkas.

En nackdel med dependency injection är att man nu måste skapa objekten som ska injiceras någon annanstans än i objektet som ska använda dem. Här kan Spring.NET hjälpa oss genom att låta oss konfigurera olika injections i XML och samla dem på ett ställe. Vi kan t.ex. ange vilken klass i vilken assembly vi avser när vi frågar efter "ItemRepository" och sedan ange att ItemRepository ska vara en konstruktorparameter för SalesController i samma eller en annan assembly. När vi sedan ber Spring.NET om ett SalesController-objekt levereras det initierat och klart med en referens till den ItemRepository vi angav. Spring.NET har stöd för en rad olika sätt att skapa objekt, t.ex. via Factory Method, och kan även utifrån typ eller namn själv lista ut vilka objekt som ska injiceras.

Spring.NET är också ett ramverk för aspektorienterad programmering och design. Ett vanligt sätt att beskriva AOP är att lyfta fram hur man kan använda det för att hantera "cross-cutting concerns" som loggning, säkerhet och transaktioner samtidigt som man håller fast vid principen "separation of concerns" och inte blandar in dessa aspekter där de egentligen inte hör hemma, t.ex. i domänobjekt. Säg att man vill logga varje metodanrop i t.ex. ett Service Layer. Principen om separation of concerns säger oss att vi helst inte bör blanda in koden för loggning i varje metod som kan anropas. Vi vill dessutom gärna slippa upprepa denna kod om och om igen i varje metod.

Med AOP definierar vi helt enkelt vår "cross-cutting concern", i detta fall loggningen, i vad som med AOP-terminologi kallas för ett "advice". Sedan anger vi på vilka platser i koden - "pointcuts" - vårt advice ska tillämpas. En pointcut kan anges med Regular Expression och vi kan till exempel ange att alla metoder i en assembly som börjar med "Save" ska omfattas av vårt advice "LoggingAdvice". Så här skulle det sedan kunna se ut i koden:

class LoggingAdvice : IMethodInterceptor
{
   public object Invoke(IMethodInvocation invocation)
   {
      Console.WriteLine("Calling " + invocation.Method.Name +
      " " + DateTime.Now);

      object result = invocation.Proceed();

      Console.WriteLine("Finished calling " + invocation.Method.Name +
      " " + DateTime.Now);

      return result;
   }
}

Nu kommer alla våra Save-metoder att meddela till Console när de anropas och när anropet är klart. Man behöver inte lägga till särskilt mycket kod till det här enkla exemplet för att få fram bra statistik på vilka metoder som tar längst tid att exekvera och därigenom använda AOP för att hitta flaskhalsar i ett system.

Transaktioner är en annan cross-cutting concern där AOP kommer väl till pass och faktum är att transaktionsmodellen i Enterprise Services/COM+ använder sig av AOP. När man sätter attribut som [Transaction(TransactionOption.Required)] på en klass för att få den att delta i transaktioner är det i själva verket en pointcut för ett transaktions-advice man skapar, även om Microsoft inte använder den terminologin.

Jag har tidigare stiftat bekantskap med dependency injection och AOP, Jimmy Nilsson har t.ex. ett kapitel om just detta i sin senaste bok (för övrigt delvis författat av Eric Doernenbourg som kommer till Developer Summit i nästa vecka), men jag har aldrig använt Spring.NET. Det var därför en nyhet för mig att ramverket innehåller mycket mer än stöd för dessa tekniker. Förutom en rad "nyckelfärdiga" advice för loggning, säkerhet med mera finns det ramverk för dependency injection för ASP.NET; klassbibliotek med olika nyttiga funktioner för datahantering med ADO.NET, collections, threading med mera; en objektorienterad modell för databasoperationer som mappar resultatset till objekt och mycket annat intressant.

En rolig sak som händer under kvällen var att vi under middagspausen fick besök av Arjen Poutsma från Interface21, företaget bakom Java-ramverket Spring som Spring.NET är inspirerat av. Poutsma var i Stockholm för att delta i grundandet av en Spring User Group nästa dag och passade då på att träffa Niclas Nilsson. Konsekvensen av detta blev att andra halvan av kvällen avhandlades på engelska och Poutsma flikade ständigt in i (för att inte säga tog över) föredraget. Han sammanfattade kärnfullt syftet med AOP: att hålla sina sockor torra. "DRY SOCS" - Don't Repeat Yourself (samla koden i advice) och Separation Of ConcernS (använd aspekter istället för att besudla klasser med sådant som inte hör dit). Han berättade också att Interface21 numera har en utvecklare av Spring.NET på sin lönelista vilket troligtvis innebär ännu bättre utveckling av och support för detta ramverk i framtiden.

Det kan också vara värt att nämna Microsofts satsningar inom dessa områden även om det inte var något som togs upp under kvällen på Avega. Microsoft Patterns and Practices Team skapade Object Builder som ett ramverk för dependency injection (och ett ramverk för att bygga ramverk/dependency injection containers) för sitt Enterprise Library och nu lever projektet ett eget liv på Codeplex. Jag har inte tittat närmare på det själv, men det verkar inte ha någon större spridning med bara drygt 1200 nedladdningar (källkod och binärkod) sedan i januari. Spring.NET har som jämförelse drygt 2 500 nedladdningar i månaden.

I Enterprise Library 3.0 som släpptes i april ingår något som heter Policy Injection Application Block och som utvecklare kan använda "to specify crosscutting behavior of objects in terms of a set of policies". Det handlar alltså om AOP, men ingenstans i beskrivningen av applikationsblocket nämns vare sig aspekter, AOP, pointcuts eller advice. Man kan fråga sig varför Microsoft väljer att skapa en helt ny terminologi, men det är i alla fall troligt att aspektorienterad programmering med .NET Framework får ett uppsving i och med detta.

0 kommentar(er):