Istnieją takie ramy, które pomagam zaprojektować. Istnieje kilka typowych zadań, które należy wykonać przy użyciu niektórych typowych komponentów: w szczególności rejestrowanie, buforowanie i wywoływanie zdarzeń.
Nie jestem pewien, czy lepiej jest użyć wstrzykiwania zależności i wprowadzić wszystkie te składniki do każdej usługi (na przykład właściwości), czy też powinienem umieścić jakieś metadane nad każdą metodą moich usług i użyć przechwytywania do wykonania tych typowych zadań ?
Oto przykład obu:
Iniekcja:
public class MyService
{
public ILoggingService Logger { get; set; }
public IEventBroker EventBroker { get; set; }
public ICacheService Cache { get; set; }
public void DoSomething()
{
Logger.Log(myMessage);
EventBroker.Publish<EventType>();
Cache.Add(myObject);
}
}
a oto inna wersja:
Przechwycenie:
public class MyService
{
[Log("My message")]
[PublishEvent(typeof(EventType))]
public void DoSomething()
{
}
}
Oto moje pytania:
- Które rozwiązanie jest najlepsze w przypadku skomplikowanych ram?
- Jeśli przechwytywanie wygra, jakie są moje opcje interakcji z wewnętrznymi wartościami metody (na przykład w przypadku usługi pamięci podręcznej?)? Czy mogę zastosować inne sposoby niż atrybuty do wdrożenia tego zachowania?
- A może mogą istnieć inne rozwiązania problemu?
c#
dependency-injection
Beatlesi 1692
źródło
źródło
Odpowiedzi:
Zagadnienia przekrojowe, takie jak rejestrowanie, buforowanie itp., Nie są zależnościami, dlatego nie należy ich wprowadzać do usług. Jednak podczas gdy większość ludzi wydaje się sięgać po pełną ramkę przeplatania AOP, istnieje ładny wzór dla tego: Dekorator .
W powyższym przykładzie pozwól MyService zaimplementować interfejs IMyService:
Dzięki temu klasa MyService jest całkowicie wolna od obaw przekrojowych, a zatem jest zgodna z zasadą pojedynczej odpowiedzialności (SRP).
Aby zastosować rejestrowanie, możesz dodać dekorator rejestrowania:
Możesz zaimplementować buforowanie, pomiary, zdarzenia itp. W ten sam sposób. Każdy Dekorator robi dokładnie jedną rzecz, dlatego też postępuje zgodnie z SRP i możesz komponować je w dowolnie złożone sposoby. Na przykład
źródło
Jeśli chodzi o garść usług, myślę, że odpowiedź Marka jest dobra: nie będziesz musiał uczyć się ani wprowadzać żadnych nowych zależności stron trzecich i nadal będziesz przestrzegać dobrych zasad SOLID.
W przypadku dużej liczby usług poleciłbym narzędzie AOP, takie jak PostSharp lub Castle DynamicProxy. PostSharp ma darmową (jak w piwie) wersję, a niedawno wypuścił PostSharp Toolkit do diagnostyki (darmowy jak w piwie ORAZ mowy), który zapewni kilka funkcji logowania po wyjęciu z pudełka.
źródło
Uważam, że konstrukcja frameworka jest w dużej mierze ortogonalna w stosunku do tego pytania - powinieneś najpierw skupić się na interfejsie frameworka, a być może jako proces mentalny w tle zastanowić się, w jaki sposób ktoś może go faktycznie wykorzystać. Nie chcesz robić czegoś, co uniemożliwia wykorzystanie go w sprytny sposób, ale powinno to być jedynie wkładem w twój projekt frameworka; jeden z wielu.
źródło
Wiele razy napotykałem ten problem i myślę, że wpadłem na proste rozwiązanie.
Początkowo poszedłem ze wzorem dekoratora i ręcznie wdrożyłem każdą metodę, gdy masz setki metod, staje się to bardzo nudne.
Potem zdecydowałem się użyć PostSharp, ale nie podobał mi się pomysł włączenia całej biblioteki tylko po to, aby zrobić coś, co można osiągnąć za pomocą (dużo) prostego kodu.
Następnie wybrałem przezroczystą ścieżkę proxy, co było zabawne, ale wymagało dynamicznego emitowania IL w czasie wykonywania i nie byłoby czymś, co chciałbym robić w środowisku produkcyjnym.
Niedawno postanowiłem użyć szablonów T4 do automatycznej implementacji wzoru dekoratora w czasie projektowania, okazuje się, że szablony T4 są naprawdę dość trudne do pracy i potrzebowałem tego szybko, więc utworzyłem poniższy kod. Jest szybki i brudny (i nie obsługuje właściwości), ale mam nadzieję, że ktoś uzna to za przydatne.
Oto kod:
Oto przykład:
Następnie utwórz klasę o nazwie LoggingTestAdapter, która implementuje ITestAdapter, poproś Visual Studio, aby automatycznie zaimplementowało wszystkie metody, a następnie uruchom go przez powyższy kod. Powinieneś mieć coś takiego:
To jest z kodem pomocniczym:
źródło