Efektywność

Doświadczeni programiści mogą łatwo odróżnić dobrą architekturę od złej, ale poproszeni o opisanie jej w kilku słowach raczej nie będą w stanie tego zrobić. Nie ma jednego kryterium dobrej architektury i jednej definicji.

Jednak jeśli się nad tym zastanowić, można napisać szereg kryteriów, jakie powinna spełniać dobra architektura. Dobra architektura to przede wszystkim architektura logiczna, dzięki której proces tworzenia i utrzymania programu jest prostszy i wydajniejszy.

Gdy program ma dobrą architekturę, zawsze łatwo jest zrozumieć, jak działa i gdzie pisać kod. Dobrze zaprojektowany program jest łatwiejszy do zmiany, testowania, debugowania i rozwijania. Mądrzy ludzie sformułowali następujące kryteria dobrej architektury:

  • Efektywność;
  • Elastyczność;
  • Możliwość rozbudowy;
  • Skalowalność;
  • testowalność;
  • Łatwość utrzymania kodu.

Wydajność systemu. Program oczywiście musi rozwiązywać postawione zadania i wykonywać swoje funkcje dobrze iw różnych warunkach. Wydaje się, że każdy program robi to, co powinien (jeśli jest napisany), ale często wcale tak nie jest.

Ciągle będziesz natykać się na programy, które nie robią tego, co deklarują.

  • Libre Office to pełny zamiennik pakietu Microsoft Office (nie do końca);
  • Przeglądarka Edge obsługuje wszystkie standardy sieciowe (nie do końca);
  • Bank dba o bezpieczeństwo danych osobowych swoich użytkowników (właściwie nie).

I nie poruszyliśmy jeszcze kwestii wydajności, niezawodności, terminowych poprawek błędów ani publikacji informacji o znanych lukach w zabezpieczeniach.

Oczywiste jest, że nikt nie jest doskonały, ale program musi rozwiązywać swoje podstawowe zadania. Dlatego bez wydajności nigdzie.

Elastyczność

Jedyną rzeczą ważniejszą od wydajności jest moim zdaniem elastyczność. Każda aplikacja musi się zmieniać w czasie, ponieważ zmieniają się wymagania, dodawane są nowe. Im szybsze i wygodniejsze jest wprowadzanie zmian w istniejącej funkcjonalności, tym mniej problemów i błędów powoduje, tym bardziej elastyczna jest architektura systemu.

Bardzo często początkującym programistom/architektom wydaje się, że potrzebują idealnej architektury do bieżących zadań. NIE. Potrzebujesz idealnej architektury do zadań, które zostaną Ci ogłoszone za rok. Ty, nie znając już przyszłych zadań, powinieneś wiedzieć, jakie one będą.

Nie ma sensu próbować ich przewidywać, ponieważ zawsze znajdzie się coś nieoczekiwanego. Ale musisz wziąć pod uwagę, że takie zadania będą się pojawiać. Dlatego w procesie rozwoju spróbuj ocenić, co jest uzyskiwane pod kątem tego, jak trzeba będzie to zmienić.

Zadaj sobie pytania: „Co się stanie, jeśli obecna decyzja architektoniczna okaże się błędna?”, „Ile kodu zostanie zmienione?”. Zmiana jednego fragmentu systemu nie powinna mieć wpływu na pozostałe jego fragmenty.

W miarę możliwości decyzje architektoniczne nie powinny być osadzane w kamieniu, a konsekwencje błędów architektonicznych powinny być rozsądnie ograniczane. „Dobra architektura pozwala OPÓŹNIĆ kluczowe decyzje” (Bob Martin) i minimalizuje „koszt” błędów.

Jednym z takich podejść jest podział aplikacji na mikroserwisy: łatwo jest rozbić już istniejącą logikę na osobne części. Ale największym problemem jest wprowadzanie przyszłych zmian w kilkunastu usługach jednocześnie, aby zaimplementować jedną małą funkcję.

Skalowalność

Skalowalność to możliwość skrócenia czasu rozwoju poprzez dodanie nowych osób do projektu. Architektura powinna umożliwiać zrównoleglenie procesu programowania, tak aby wiele osób mogło pracować nad programem w tym samym czasie.

Wydaje się, że ta zasada jest wykonywana sama, ale w praktyce wszystko jest dokładnie odwrotnie. Istnieje nawet bardzo popularna książka, The Mythical Man-Month , która wyjaśnia, dlaczego po dodaniu nowych osób do projektu wydłuża się czas opracowywania.

Możliwość rozbudowy

Rozszerzalność to możliwość dodawania nowych funkcji i jednostek do systemu bez naruszania jego podstawowej struktury. Na początkowym etapie warto umieścić w systemie tylko podstawowe i najbardziej potrzebne funkcjonalności.

To jest tak zwana zasada YAGNI – nie będziesz tego potrzebować , „nie będziesz tego potrzebować”. Jednocześnie architektura powinna umożliwiać łatwe zwiększanie dodatkowych funkcjonalności w miarę potrzeb. I tak, aby wprowadzenie najbardziej prawdopodobnych zmian wymagało jak najmniejszego wysiłku.

Wymóg, aby architektura systemu była elastyczna i rozszerzalna (to znaczy zdolna do zmian i ewolucji) jest tak ważny, że sformułowano go nawet jako odrębną zasadę – „zasadę otwartego/zamkniętego . Zasada otwarte-zamknięte jest drugą z pięciu zasad SOLID: jednostki oprogramowania (klasy, moduły, funkcje) powinny być otwarte na rozbudowę, ale zamknięte na modyfikację .

Innymi słowy: powinna istnieć możliwość zmiany i rozszerzenia zachowania systemu bez przepisywania istniejących części systemu .

Oznacza to, że aplikacja powinna być zaprojektowana w taki sposób, aby zmiana jej zachowania i dodanie nowych funkcjonalności odbywało się poprzez napisanie nowego kodu (rozszerzeń), bez konieczności zmiany istniejącego kodu.

W tym przypadku pojawienie się nowych wymagań nie pociągnie za sobą modyfikacji istniejącej logiki, ale może być realizowane przede wszystkim poprzez jej rozbudowę. Ta zasada jest podstawą „architektury wtyczek” (Plugin Architecture). Techniki, dzięki którym można to osiągnąć, zostaną omówione później.

Pamiętasz serwlety i filtry? Po co potrzebne były filtry, nawet z oddzielnymi interfejsami, skoro w rzeczywistości całą tę samą logikę można było zaimplementować za pomocą serwletów?

Dopiero wynalezienie koncepcji filtrów (servletów serwisowych) umożliwiło przeniesienie różnych funkcji serwisowych do osobnej warstwy. A w przyszłości przy zmianie zachowania filtrów zmiana serwletów nie była konieczna.

Przed wynalezieniem filtrów cała logika usługi odpowiedzialna za przekierowywanie żądań znajdowała się w samych serwletach. I często jedna mała zmiana w logice prowadziła do konieczności przejrzenia wszystkich serwletów i wprowadzenia różnych zmian we wszystkich.

Testowalność

Jeśli jesteś programistą Java Backend, twoje aplikacje serwerowe często udostępniają zestaw metod jako interfejs API REST. A żeby sprawdzić, czy wszystkie Twoje metody działają zgodnie z założeniami, trzeba je objąć testami.

Ogólnie rzecz biorąc, pokrycie testów API to dobry styl. Pozwala upewnić się, że Twój interfejs API naprawdę robi to, do czego został przeznaczony. A co ważniejsze, możesz wprowadzić zmiany w logice serwera i łatwo sprawdzić, czy niczego przypadkowo nie zepsułeś .

Gdy tylko zaczniesz pisać testy, zdasz sobie sprawę, że większości kodu w ogóle nie da się przetestować: metody prywatne, silne sprzężenie, statyczne klasy i zmienne.

„Po co nam testy, skoro kod działa?”, zapyta początkujący.

„Po co nam działający kod, skoro nie można go przetestować?”, zapyta profesjonalista.

Kod, który jest łatwy do przetestowania, będzie zawierał mniej błędów i będzie bardziej niezawodny. Ale testy nie tylko poprawiają jakość kodu. Prawie wszyscy programiści ostatecznie dochodzą do wniosku, że wymóg „dobrej testowalności” jest również siłą przewodnią, która automatycznie prowadzi do dobrego projektu.

Oto cytat z książki Ideal Architecture: „Użyj zasady „testowalności” klasy jako „papierka lakmusowego” dobrego projektu klasy. Nawet jeśli nie napiszesz ani jednej linijki kodu testowego, odpowiadając na to pytanie w 90 % przypadków pomoże zrozumieć, jak wszystko, co dobre” lub „złe” w jego projekcie”.

Istnieje cała metodologia tworzenia programów w oparciu o testy, która nazywa się Test-Driven Development (TDD). Jest to oczywiście druga skrajność: pisz kod przed napisaniem kodu.

Łatwość utrzymania kodu

Z reguły nad programem pracuje bardzo dużo osób – jedni odchodzą, przychodzą nowi. Średni czas pracy programisty w firmie informatycznej to półtora roku. Jeśli więc przyszedłeś do projektu, który ma 5 lat, to tylko 20% twoich współpracowników pracowało nad nim od samego początku.

Utrzymanie i rozwijanie programu napisanego przez innych jest bardzo trudne. Nawet jeśli program jest już napisany, często konieczne jest jego dalsze utrzymywanie: naprawianie błędów i wprowadzanie drobnych poprawek. I często muszą to robić ludzie, którzy nie brali udziału w jej pisaniu.

Dlatego dobra architektura powinna umożliwiać nowym osobom stosunkowo łatwe i szybkie zrozumienie systemu . Projekt musi być:

  • Dobrze ustrukturyzowane.
  • Nie zawieraj duplikatów.
  • Mieć dobrze sformatowany kod.
  • Pożądane jest dołączenie dokumentacji.
  • Konieczne jest zastosowanie standardowych i znanych programistom rozwiązań.

Możesz łatwo ocenić projekt, nad którym pracujesz w systemie 5-punktowym . Po prostu policz dwa punkty za każde z tych wymagań . A jeśli zdobędziesz 5 lub więcej, masz szczęście.

Programiści mają nawet zasadę najmniejszego zaskoczenia : im bardziej egzotyczny system, tym trudniej jest go zrozumieć innym. Zwykle jest używany w odniesieniu do interfejsu użytkownika, ale ma również zastosowanie do pisania kodu.