#autowire
Explore tagged Tumblr posts
nolifer-pl · 6 years ago
Text
[Design Patterns] Strategia z wykorzystaniem Symfony
Strategia to moim zdaniem jeden z ciekawszych wzorców projektowych. Często bywa pomijany, co prawie zawsze kończy się spaghetti code. Nie można mówić o wzorcach projektowych bez kilku zdań teorii. Każdy wzorzec projektowy składa się z 4 elementów. Nazwy, problemu który opiszę sytuację, w jakiej wzorzec powinien zostać użyty, rozwiązania tego problemu oraz konsekwencji jakie się wiążą z jego użycie. Skoro nazwę mamy już w tytule przejdźmy do kolejnych elementów.
Problem
Załóżmy, że mamy sytuacje gdzie chcielibyśmy, aby nasza aplikacja była bardziej uniwersalna. Czyli jakiś fragment naszego algorytmu ma być zmienny w zależności od sytuacji. Choć nie jest to wymagane, ale z doświadczenia wiem, że powinno to dotyczyć wymiany sposobu części kodu w ramach jednej odpowiedzialności.
Rozpatrzmy jakiś konkretny przypadek który powinien nam rozjaśnić sytuację. Załóżmy, że nasza aplikacja (napisana w Symfony) otrzymuje dane z różnych źródeł. Otrzymywane dane mogą być w różnym formacie danych na tą chwilę JSON i XML. Trzeba jednak wszystko sprowadzić do spójnej struktóry by zapisać dane w bazie lub zrobić z nimi cokolwiek innego.
Rozwiązanie
Skoro rozwiązanie dotyczyć ma konkretnie aplikacji napisanej z pomocą frameworka Symfony. To sama postać Strategii będzie różnić się od innych implementacji niezależnych od konkretnych frameworków. To sprawia jednocześnie, że temat może kogoś zainteresować.
Na początek potrzebujemy serwisu, który przyjmie po prostu obiekt Request a na wyjściu zwróci nam już znormalizowany model danych (nazwijmy go kreatywnie SomeModel). W konstruktorze będzie on przyjmował listę naszych tytułowych strategii, które będą mieć spójny interfejs ProcessorInterface.
<?php final class ProcessData { /** * @var ProcessorInterface[] dataProcessors */ private $dataProcessors; /** * @param ProcessorInterface[] $dataProcessors */ public function __constructor(iterable $dataProcessors) { $this->dataProcessors = $dataProcessors; } /** * @throws Exception */ public function process(Request $request): SomeModel { foreach ($this->dataProcessors as $processor) { if ($processor->canBeUsed($request)) { return $processor->process($request); } } throw new Exception('Unsupported data format'); } }
Powyższy serwis zdradza nam już jak powinien wyglądać ProcessorInterface. Dla formalności to jego definicja:
<?php interface ProcessorInterface { public function canBeUsed(Request $request): bool; public function process(Request $request): SomeModel; }
Zdefiniujmy teraz nasze strategie:
<?php final class JSONProcessor implements ProcessorInterface { public function canBeUsed(Request $request): bool { return $request->request->get('data-format') === 'json'; } public function process(Request $request): SomeModel { $content = json_decode($request->getContent()); return new SomeModel($content->id, $content->someData); } }
<?php final class XMLProcessor implements ProcessorInterface { public function canBeUsed(Request $request): bool { return $request->request->get('data-format') === 'xml'; } public function process(Request $request): SomeModel { $content = new SimpleXMLElement($request->getContent()); return new SomeModel($content->id, $content->someData); } }
Oczywiście nasze strategie to normalne serwisy, które mogą mieć swoje niezależne zależności, by rozwiązać swój problem. Autowire rozwiąże tu większość problemów, ale został nam jeden. Jak przypisać wszystkie strategie do naszego ProcessData. Można przekazać wszystkie strategie jawnie jako zależności. Niestety trzeba będzie zawsze o tym pamiętać znaleźć miejsce definicji i dopisać. Nalepiej ograniczać takie sytuacje. Na pomoc przychodzą nam tagi. Musimy więc zdefiniować nasz serwis:
# config/services.yaml services: # ... App\SomeNamespace\ProcessData: arguments: $dataProcessors: [!tagged app.data_processor] # podanie jawnie nazwy parametru pozwala nam mieć inne zależności w dowolnej kolejności
Teraz nasz serwis otrzyma wszystkie serwisy z tagiem app.data_processor zostało nam więc tylko je dodać do naszych strategii:
# config/services.yaml services: # ... App\SomeStrategiesNamespace\: resource: "%kernel.project_dir%/src/SomeStrategiesNamespace/*Processor.php" tags: ['app.data_processor']
Konsekwencje
Podsumujmy teraz nasze rozwiązanie i jakie to niesie konsekwencje. Oddzieliliśmy kod służący do przetwarzania każdego wymaganego formatu danych. Jako że tagi zdefiniowaliśmy na cały katalog, dodanie obsługi nowego formatu wymaga od nas jedynie stworzenia kolejnej klasy implementującej nasz interfejs. W razie, gdyby dane przyszły w nieoczekiwanym formacie nasz serwis rzuci wyjątek, który możemy obsłużyć w oczekiwany sposób. Wadą tego rozwiązania jest zwiększenie narzutu spowodowanego większą ilością serwisów. Oraz tym, że szukamy odpowiedniej strategii iteracyjnie. Jest on jednak raczej pomijalny i o ile poruszamy się w rozsądnej liczbie strategii trudno mi sobie wyobrazić przypadek, gdy stałoby się to problemem. Chcąc jednak być obiektywnym, zawsze trzeba wspomnieć również o wadach.
Zalety:
zdefiniowanie grupy serwisów rozwiązujących jeden problem
łatwe rozszeżanie kodu o nowe strategie odbywa się w wielu przypadkach tylko poprzez tworzenie pojedynczego serwisu/klasy
pozbycie się if-ów które zapewne znalazłyby się w alternatywnym rozwiązaniu
możliwość niezależnego testowania poszczególnych strategii
możliwość tworzenia w zespole kilku strategii jednocześnie bez obaw o późniejsze problemy z konfliktami trudnymi do rozwiązania
Wady:
zwiększenie liczby serwisów
dodatkowy narzut spowodowany dobraniem odpowiedniej strategii
0 notes
monieyang-blog · 7 years ago
Photo
Tumblr media
#autowire #automobile #cableassembly #wireharness ready shipping to France! Small order accepted!(在 Dongguan, Guangdong)
0 notes
spring-framework-guru · 10 years ago
Text
Advanced Autowire Features in Spring 4
Advanced Autowire Features in Spring 4
At the heart of the Spring Framework is its support of dependency injection through its Inversion of Control container. In this video, I look at using some of the advanced autowire features of Spring.
@Primary
By default, Spring will autowire by type. When you have more than one Spring Bean of a given type, you can use the @Primaryannotation to give a specific bean preference over the others. If…
View On WordPress
0 notes