Krzysztof Olszewski
Dyrektor Technologii i Architektury Oprogramowania
Krzysztof Olszewski
Dyrektor Technologii i Architektury Oprogramowania
Nie genetycznym, ale w tym bardziej mi bliskim, kodzie aplikacji. Czy zachodzi? Jakie jest tempo zmian? Jakie stymulanty płynące z czynników zewnętrznych ją napędzają? Aby udzielić odpowiedzi, prześledźmy przykład ewolucji pewnego aspektu kodu zwanego powszechnie przydzielaniem pamięci czy też tworzeniem instancji klas, a wg mnie bardziej precyzyjnie „stwarzaniem”.
Na początku, w czasach prehistorycznych, było boskie new. …
linia (9) pokazuje „boski” moment stworzenia pewnej części pewnej całości, jednak taki styl tworzenia bywa zbyt sztywny, narzuca twórcy zbyt wiele, zatem szybko wyewoluował styl taki:
postęp ewolucyjny widoczny w linii (6) doprowadził do tego, że linia (9) potencjalnie może zacząć inaczej wyglądać, zaczyna się pojawiać pewna szansa, jak to się właściwie stało, otóż dawniej tak:
ale już później i bardziej „trendy” tak:
Dalej mamy klasyczne new, ale już czujemy, że pojawia się szansa. Gdyby nasz kod miał być gotowy na zmiany, ale miał nie wymagać przy tych zmianach modyfikacji, to mamy wielki problem, bo linia (9) cały czas z uporem twierdzi, że „mainPlanet” to koniecznie „Earth”. Co zatem można tu usprawnić, zacznijmy od uznania, że przyczyną wszelkiego zła jest „new”, i pozbądźmy się go.
Nie mamy „new”, uwaga nie mamy także „Earth”, to jaka będzie „mainPlanet” nie jest już zależne od nas, tak naprawdę abstrahujemy od tego, jaka ona będzie i dobrze nam to w kodzie robi. Jednak trzeba przyznać, że trochę się nam sytuacja skomplikowała, mamy nowego gracza „Factory”, musimy umieć z nim rozmawiać i zaczynamy od niego mocno zależeć. Dodatkowo zaczynamy się bać, że gdyby pojawiła się w otoczeniu inna lepsza fabryka … a my już z tą jedną na stałe jesteśmy związani bardzo silnymi więzami, to nie mamy możliwości zareagowania na zamianę bez zmiany kodu. Pączkowanie tego etapu rewolucji w twory w rodzaju „UniverseFactoryProvider” czy „UniverseFactoryFactory” wydaje się rozwiązaniem tymczasowym, pełną wolność przynosi nam kolejny etap ewolucji, czyli:
oczywiście to działać nie będzie, chyba że dodamy:
Razem w komplecie to zadziała, w dodatku mamy pełną elastyczność, to co i jak ma być stwarzane, określamy poza kodem, gdzieś w warstwach wyższych. Jest jednak „ale”, projekt się znacznie zagmatwał, mamy już dwa kody źródłowe i w dodatku ten drugi to nie jest nasz ulubiony język. Nadchodzi zatem kolejny etap ewolucji i otrzymujemy:
Przenosimy konfigurację aplikacji … z powrotem do naszych klas, pozbywamy się zawartości pliku XML, mamy wyłącznie nasz ulubiony język. Ewolucja jest już na wysokim etapie rozwoju i wydaje się, że osiągamy cel. Ale! Nasze początkowe założenie, że mamy być gotowi na zmiany i aby przy tych zmianach nie zmieniać kodu, nie jest spełnione, to jaka fabryka jest domyślną mamy zaszyte w kodzie sic!.
Zatem dlaczego ewolucja doprowadziła nas właśnie do tego miejsca? Przyczyn jest wiele, tak naprawdę „@Inject”, „@Named”, „@Default”, itd. nie zastępują wprost „new” a robią znacznie większą robotę, o której przy „new” nawet nam się nie śniło. Możliwości, jakie dają mechanizmy „dependency injection (DI)”, „interception (AOP)” przyprawione „contexts Injection (CI)” wykraczają daleko poza sam dobór odpowiednich w danej chwili implementacji.
Przy okazji nie można także nie zauważyć ewolucji ze stylu imperatywnego do stylu deklaratywnego, której to przejawem jest nowy byt @Adnotacji pełniącej funkcję meta znacznika. Imperatywne „new” zastępujemy deklaratywnym @Inject, a tkwiąc w „boskiej nieświadomości” nie wiemy nawet, kto spełni kontrakt za tą adnotacją stojący i jest nam z tym dobrze.
Jest jeszcze jedna kwestia, ewolucja „słucha” rzeczywistości, to rzeczywistość jest jej stymulantem, a na pytanie „jak często zmieniasz implementację interfejsów w swoich aplikacjach, lub masz więcej niż dwie implementacje jednego interfejsu?” rzeczywistość odpowiada o dziwo: „ja? … bardzo rzadko”.