AOPCzęść NR.1

Programowanie aspektowe – to brzmi skomplikowanie, ale w rzeczywistości tak nie jest. Programowanie aspektowe - w skrócie AOP, pomaga spędzić mniej czasu na kopiowaniu i wklejaniu tego samego kodu.

Te powtarzające fragmenty kodu fachowo nazywają się “boilerplate”.

Redukując powtarzalność kodu możesz poświęcić więcej czasu na ważniejsze części projektu i dodać do niego, i do innych projektów wartość dodaną.

AOP w .NET może zostać uzyskane na wiele sposobów. Najpopularniejszymi sposobami są techniki związane z:

  • Castle Windsor i Intercepotrami
  • PostSharpem i Aspektach

Co to jest Aspect_oriented programming

AOP jest dosyć nowym konceptem programistycznym. W 1997 roku Gregory Kiczales zauważył, że wiele kodów w językach obiektowych się powtarza. Zwłaszcza w procesach gdzie występują:

  • zapisy akcji aplikacji i jej błędów
  • zapisywanie danych do ponownego użytku (“cashing”)
  • Transakcje

Gregory Kiczales wraz ze swoim zespołem zastanawiali się długo jak rozwiązać ten problem. Powtarzający kod zawsze musiał występować w wielu miejscach.

Na tym etapie dali fachową nazwę “rzeczom”, które powinny być wycięte i wydzielone.

http://en.wikipedia.org/wiki/Cross-cutting_concern

Zespół Gregorego przeanalizował wiele wzorców projektów i doszedł dlaczego ten problem w ogóle występuje, w językach obiektowych.

Ostatecznie ich przemyślenia zostały spisane.

http://www.cs.ubc.ca/~gregor/papers/kiczales-ECOOP1997-AOP.pdf

Opisali styl programowania zawierający się w OOP (Object-oriented programming). Ten styl programowania miał koncentrować się na aspektach – co to znaczy? . Ten styl programowania ma polegać na hermetyzowaniu sposobów działania pewnych rozwiązań w aplikacji tak, aby mogły być one później użyte ponownie.

Zalety : Wartość dodana

Główną zaletą AOP jest czysty kod, który jest łatwiejszy do przeczytania, łatwiejszy do zarządzania.

Kod łatwiej się czyta, to twoje oczy koncentrują się na aspektach biznesowej aplikacji, a nie na kodzie, który się powtarza schematycznie.

Kodem łatwiej się zarządza, ponieważ kod trzeba zmienić tylko w jednym miejscu, a nie wszędzie, gdzie użycie to występuje.

Cross-cutting concern

Cross-cutting concern jest niefunkcjonalnym wymaganiem systemu, który występuje w wielu miejscach.

Wymagania niefunkcjonalne zazwyczaj występują w wielu miejscach, w aplikacji.

Zapisywanie błędów i informacji na temat działania aplikacji jest takim przykładem.

Cross-cutting concern istnieje niezależnie od tego, czy stosujesz AOP.

Powiedzmy, że masz metodę “X” , którą chcesz wykonać, a nie chcesz zapisać przebiegu jej działania, czyli logowania. Nazwijmy logowanie metodą “L”.

Jeśli więc chcesz osiągnąć ten cel, musiałbyś dopisać do metody X wywołania metody L.

Problem jednak narasta, gdy do metody Y, Z, A, B też chcesz dodać Cross-Cutting-Corner. Metoda “L” powinna być umieszczona do tych metod inną metodą niż kopiuj-wklej.

Tutaj do akcji wkracza technika AOP.

Oto przykład z kodem:

public void AddGame(Game game)
{
    Log.Debug("Method AddGame was called");

    try
    {
        _gameRepository.Add(game);
    }
    catch (Exception ex)
    {
        Log.Error(ex,"AddGame raised exception");
        throw;
    }
    finally
    {
        Log.Debug("Method AddGame ended");
    } 
}

Widzimy tutaj, że w metodzie “AddGame” kod dotyczący logowania się powtarza. Jeśli chcemy dodać logowanie do innych klas, możemy zauważyć, że nasz kod CCC pojawia się w wielu miejscach. Będzie on też występował według pewnego wzoru.

public void AddPerson(Person person)
{
    Log.Debug("Method AddPerson was called");

    try
    {
        _personRepository.Add(person);
    }
    catch (Exception ex)
    {
        Log.Error(ex, "AddPerson raised exception");
        throw;
    }
    finally
    {
        Log.Debug("Method AddPerson ended");
    }
}

Taki kod łamie zasadę DRY. Powtarza się.

Co my tak naprawdę zaraz zrobimy. Pierwszym krokiem w programowaniu aspektowym jest stworzenie aspektu w postaci klasy.

Aspekt jest wrapper-em jakiejś funkcji biznesowej.

Aspekt musi spełniać zasadę Single Responsibility Principle, czyli zasadę pojedynczej odpowiedzialności. Mamy aspekt do logowania błędów i on nie powinien robić nic innego.

Mając już aspekt. Co robimy potem?

Używając wzorca projektowego dekorator dodajemy tę funkcjonalność do istniejącej klasy. Mówimy w ten sposób, że ten kawałek funkcjonalności opakowuje inną funkcjonalność.

AOP daje naszej aplikacji zasadę “Open Close” , czyli otwarte-zamknięte. Nasze klasy biznesowe są otwarte do rozszerzeń, ale są zamknięte na swoje zmiany.

Jak to wygląda w praktyce.

Bez AOP mamy klasę biznesową i logowanie jest do niej dodane bezpośrednio.

public class BussinesLogicClassMotherF
{
    public void Method1()
    {
        Log.Debug("Method 1 called");
        //bussines stuff
        Log.Debug("Method 1 ended");
    }
}

Czas jednak na cięcie.

public class BussinesLogicClassMotherF
{
    public void Method1()
    {
        //bussines stuff
    }
}

public class LogAspect
{
    public void BeginMethod()
    {
        //log
    }

    public void EndMethod()
    {
        //log
    }
}

..i tak powstała nasza klasa aspektowa. Ma ona dwie metody. Jedna z nich będzie się wywoływać na początku wywołania jakiejkolwiek metody w klasie biznesowej, druga na końcu.

Używając techniki dekoratora. Aspekt zostanie wstrzyknięty do klas biznesowych w wielu miejscach.

Super co nie. Przejdźmy więc do rzeczy bardziej praktycznych.

Gdzie kod wyciąć

Teraz, gdy wiemy, co chcemy wycinać. Powstaje jeszcze pytanie “gdzie”.

Patrząc na poprzedni przykład widzimy, że w łatwy sposób możemy dodawać kolejne funkcjonalności przed i po wywołaniu metody X.

Jak to wykorzystać.

Spójrzmy na poniższy kod.

public Game GameById(int id)
{
    if (Cashe.ObjectExist("Game", id))
    {
        return Cashe.GetObject<Game>("Game", id);
    }
    else
    {
        var game = LoadGameById(id);
        Cashe.AddObject("Game", game, id);
        return game;
    }
}

W tym przykładzie przed pobraniem gry sprawdzamy, czy ona istnieje w obiekcie Cashe.

Jeśli tak jest to ją zwracamy.

Skoro wczytanie gry jest elementem biznesowym, to nie jest ona aspektem.

Natomiast sprawdzanie, czy obiekt istnieje w obiekcie Cashe już nie.

Na początku przed wczytaniem gry z bazy danych w aspekcie sprawdzamy, czy obiekt tej gry istnieje w CASHE . Jeśli tak nie jest to wczytujemy grę z bazy.

image

Dodanie zwróconej wartości do CASHE też można byłoby zaliczyć do aspektu, który wykonałby się po wczytaniu gry z bazy.

image

Przed i po aspekty definitywnie mogą być użyte w ciekawy sposób.

Kiedy one jeszcze mogą się przydać. Gdy w metodzie wystąpi błąd – wtedy byśmy utworzyli aspekt, który wykonywałby się przy błędzie metody biznesowej.

public void AddGame(Game game)
{
    try
    {
        //do stuff
    }
    catch (Exception ex)
    {
        Log.Error(ex,"AddGame raised exception");
        throw;
    }
}

Jako aspekt wyglądałoby to tak.

public class LogErrorAspekt
{
    public void Intercept()
    {
        try
        {
            //BussinesMetod
        }
        catch(Exception ex)
        {
            Log.Error("Error", ex);
	    throw;
        }
    }
}

image

Analogicznie moglibyśmy mieć aspekt, który wykona powtarzający się kod w klauzurze finalny, jak zamkniecie połączenia do bazy lub wykonanie transakcji wzorem Unity of Work (n.p NHibernate).

image

AOP w .NET już istnieje

AOP istnieje też w samym frameworku .NET. Oto przykłady

  • ASP.NET Forms Authentication
  • implementacja IHTTPModule
  • ASP.NET MVC Authentication
  • implementacja IActionFilter w ASP.NET MVC

ASP.NET ma IHttpModule, który może być zaimplementowany w kodzie, a później dodany do web.config. Kiedy to zrobisz każdy moduł będzie uruchamiany, gdy zostanie wysłane zapytanie do twojej aplikacji internetowej.

public class CustomHeaderHttpModule : IHttpModule { ... }

Wewnątrz IHttpModule możesz zaimplementować kod, który się uruchomi na początku zapytania “BeginRequest” i na jego końcu “EndRequest”.

void context_BeginRequest(object sender, EventArgs e) { ... }

void context_EndRequest(object sender, EventArgs e) { ... }

Gdy to zrobisz w pewnym sensie stworzyłeś właśnie kod aspektowy, który uruchamia się przy każdym wywołaniu strony.

image

To samo tyczy się aplikacji ASP.NET MVC. Możliwość stworzenia klasy atrybutów, które implementują interfejs IActionFilter.

Te atrybuty mogą dodać kod przed wywołaniem akcji MVC “OnActionExecuting” oraz pod koniec “OnActionExecuted”.

Jeśli stworzyłeś kiedyś projekt MVC, to zapewne widziałeś w domyślnym kontrolerze atrybut “AuthorizeAttribute”.

Jest to wbudowana implantacja interfejsu IActionFilter, która zajmuje się sprawdzaniem, czy użytkownik jest już zalogowany. Jest to bardzo pomocne ponieważ nie musimy w ten sposób dodawać tego kodu autoryzującego do każdej akcji w naszym kontrolerze.

Dodawanie aspektów

Mamy więc aspekty i klasy z metodami biznesowymi. Jednak jak w praktyce połączyć jedno z drugim.

Są dwa typy technik AOP

  • Interceptory
  • IL Code Weaving

Interceptory wywołują metody klasy lub ich właściwości. Ich użycie polega na kontenerze odwrócenia zależności Castle.Windosor lub na generatorze proxy. W przeciwieństwie do następnej techniki po kompilacji nie zmienia ona bibliotek.

IL Code Weaving polega na uruchomieniu dodatkowego procesu kompilacji . Kod aspektowy jest mieszany z klasami biznesowymi na podstawie klas informacyjnych, jak ten proces ma zostać wykonany. Pod koniec tego dodatkowego procesu kompilacji mamy więc programowanie aspektowe.

Początek techniki IL Code Weaing już pokazałem przy wpisie zmiany kodu, przy użyciu kompilatora Roslyn

Kurs XAML : Wstęp i Elementy i Atrybuty

Tak ja wspomniałem na początku wpisu przejrzymy rozwiązanie Castle.Windsor z dynamicznymi dekoratorami oraz rozwiązanie dodatkowo kompilujące kod, czyli PostSharp.

W następnym wpisie zobaczymy jak technika AOP działa z PostSharp.