AOP (Aspect Oriented Programming), czyli Programowanie Zorientowane Apektowo, więcej informacji co to jest i z czym to się je możecie znaleźć sobie w googlach ;), lecz całość opiera się na czymś takim:
// Pseudokod
main()
{
Wypisz("Cześć Wszystkim! :)");
}
Wypisz(string text)
{
Console.Writeln(text);
}
Wynik: Cześć Wszystkim! :)
Teraz chcemy by zaraz np. przed wywołaniem metody Wypisz() coś się wydarzyło czyli:
AspektPrzed
{
Console.Writeln("AOP mówi:");
}
main()
{
Wypisz();
}
[AspektPrzed]
Wypisz(string text)
{
Console.Writeln(text);
}
Wynik: AOP mówi: Cześć Wszystkim! :)
Tak czy siak zapraszam na:
http://www.postsharp.org/about/documentation/
http://www.postsharp.org/about/video/default.aspx <-- obowiązkowo! :)
Okey, teraz przejdźmy do naszego głównego problemu. Bardzo często nam się zdarza, że podczas korzystania z DAO, ręcznie tworzymy połączenia, zamykamy je, commitujemy transakcję, etc. Z odsieczą przychodzi właśnie AOP. Spójrzmy na przykład:
1: using (ConnectionManager cm = ConnectionManager.Create)
2: {
3: osoba.Save(cm);
4:
5: cm.CommitTransaction();
6: }
Wstawiliśmy rekord do tabeli, wszystko ok, lecz musieliśmy utworzyć połączenie i dać commita, a takiego kodu możemy mieć baaardzo dużo. Jeszcze możemy pokusić się o inne rozwiązanie projektu:
W klasie Osoba piszemy metodę Save(), lecz tak czy tak w niej musimy otworzyć to połączenie i zatwierdzić transakcję.
Zobaczmy przykład przy użyciu AOP, trochę będzie tego kodu ;):
1: osoba.Save(null);
Gotowe :)
A teraz krótkie wyjaśnienie jak to działa. Na samym początku tworzymy aspekt ConnectionAttribute : PostSharp.Laos.OnMethodInvocationAspect, OnMethodInvocationAspect znaczy, że będziemy "panować" nad metodami. Przeciążamy metodę OnInvocation i piszemy coś takiego:
public override void OnInvocation(PostSharp.Laos.MethodInvocationEventArgs eventArgs)
{
// Przechwytujemy wpisane parametry metody
object[] args = eventArgs.GetArgumentArray();
// Na pierwszym miejscu tablicy oczekujemy ConnectionManager
if (args[0] != null)
{
// Jesli istnieje, to wywolujemy metode z wczesniej wpisanymi parametrami
eventArgs.ReturnValue = eventArgs.Delegate.DynamicInvoke(args);
return;
}
using (ConnectionManager cm = ConnectionManager.Create)
{
// Jesli nie istniaje w args[0] ConnectioManager, to go wstawiamy
args[0] = cm;
// Wywolujemy metode (args[0] ma juz ConnectionManager'a)
eventArgs.ReturnValue = eventArgs.Delegate.DynamicInvoke(args);
// Commitujemy transakcje
cm.CommitTransaction();
}
}
Myślę, że tutaj dodatkowy komentarz jest zbędny ;)
Aby dana metoda "korzystała" z aspektu musimy dodać do niej atrybut:
1: [Connection] // Jest to nasz ConnectionAttribute, można podać jego całą nazwę jeśli chcemy
2: public void Save(ConnectionManager cm)
W zasadzie to by było na tyle, zapraszam do ściągnięcia pełnego przykładu, są tam umieszczone przeze mnie komentarze by można się było w tym połapać, ale na samym początku polecam użycie debugera i obserwowanie krok po kroku co się dzieje na ekranie.
Co do tego rozwiązania to nie wiem czy czasem nie da się go ulepszyć (zresztą znając życie na pewno się da ;)). Szukałem po sieci jakiegoś przykładu/wzorca AOP, który rozwiązuje ten problem, lecz nie znalazłem. Dlatego chętnie wysłucham krytykę (konstruktywną ;)) oraz to w jaki sposób można ulepszyć ten Aspekcik (i czy w ogóle to dobry kierunek). Jak macie jakieś uwagi zapraszam do wypowiedzi.
Brak komentarzy:
Prześlij komentarz