Inżynieria oprogramowania i projektowanie
Cykl życia oprogramowania, zwinne metodyki (Agile, Scrum), modelowanie systemów z wykorzystaniem UML (diagramy przypadków użycia, klas, sekwencji), wybrane wzorce projektowe (np. Singleton, MVC, Observer).
Wstęp: dział, w którym nie ma czego liczyć — trzeba rozumieć
W INF.04 to jedyny dział bez wzorów i bez składni języka. Egzamin sprawdza tu trzy rzeczy: czy potrafisz nazwać fazę cyklu życia po jej opisie, czy czytasz diagram UML (najczęściej klas albo przypadków użycia) i czy odróżniasz pojęcia, które brzmią podobnie — agregację od kompozycji, test jednostkowy od integracyjnego, Product Ownera od Scrum Mastera. Zadania są krótkie i bardzo konkretne: „na diagramie klas symbol wypełnionego rombu oznacza…", „ile trwa sprint w Scrumie", „który poziom testów wykonuje klient". Nauka tego działu to nauka precyzyjnych rozróżnień, a nie ogólnych opowieści o projektach.
📋 Spis treści
Proces wytwarzania
1
Cykl życia oprogramowania i wymagania
Cykl życia (SDLC, Software Development Life Cycle) to sześć faz, przez które przechodzi każdy program — niezależnie od tego, czy jest pisany kaskadowo, czy w sprintach. Zadanie egzaminacyjne zwykle opisuje czynność („programista pisze kod klasy Faktura", „klient sprawdza, czy system spełnia jego oczekiwania") i każe wskazać fazę. Naucz się kolejności i typowego produktu każdej fazy.
Analiza wymagań — rozmowy z klientem, wywiady, obserwacja obecnego systemu. Produkt: specyfikacja wymagań i najczęściej diagram przypadków użycia. Tu decyduje się, co system ma robić.
Projektowanie — architektura, podział na moduły, model bazy danych, diagramy klas i sekwencji, projekt interfejsu. Produkt: dokumentacja projektowa. Tu decyduje się, jak system ma to robić.
Implementacja — pisanie kodu w wybranym języku, testy jednostkowe pisane przez programistów, praca w repozytorium git.
Testowanie — integracyjne, systemowe i akceptacyjne; raportowanie i poprawianie błędów.
Wdrożenie — instalacja u klienta, migracja danych ze starego systemu, konfiguracja środowiska produkcyjnego, szkolenie użytkowników.
Utrzymanie (konserwacja) — poprawki błędów zgłoszonych przez użytkowników, aktualizacje, dostosowanie do zmian prawa i sprzętu, rozwój o nowe funkcje. To najdłuższa i najdroższa faza całego cyklu.
Opisują co system ma robić — konkretne funkcje widziane przez użytkownika. Zwykle da się je zapisać zdaniem „system umożliwia…".
- System umożliwia rejestrację konta przez e-mail.
- System generuje fakturę w formacie PDF.
- Administrator może zablokować konto użytkownika.
- System wysyła powiadomienie o zbliżającym się terminie płatności.
Opisują jak dobrze system ma działać — cechy jakościowe. Najczęściej zawierają liczbę, jednostkę albo nazwę normy.
- Wydajność — strona ładuje się poniżej 2 sekund przy 500 użytkownikach.
- Bezpieczeństwo — hasła przechowywane jako skróty, transmisja po HTTPS.
- Niezawodność — dostępność 99,9% w skali miesiąca.
- Użyteczność, przenośność, skalowalność — obsługa czytnika ekranu, działanie na Windows i Linux, praca na wielu serwerach.
Test na rozróżnienie wymagań
Zapytaj: „czy da się to wyłączyć i program nadal będzie robił to samo, tylko
gorzej?". Jeśli tak — to wymaganie niefunkcjonalne. „Logowanie do systemu" to
funkcja (funkcjonalne). „Logowanie ma trwać mniej niż sekundę" to jakość tej
funkcji (niefunkcjonalne). Uwaga na słowo „bezpieczeństwo": samo
szyfrowanie transmisji jest niefunkcjonalne, ale
nadawanie uprawnień przez administratora to już konkretna
funkcja systemu.
Im później wykryty błąd, tym droższa poprawka. Błąd w specyfikacji wymagań poprawiasz, zmieniając jedno zdanie w dokumencie. Ten sam błąd wykryty po wdrożeniu oznacza przeprojektowanie modułu, poprawę kodu, ponowne testy, nową wersję u wszystkich klientów i migrację danych. Dlatego w każdym modelu wytwarzania — także zwinnym — nacisk kładzie się na jak najwcześniejsze ustalenie i weryfikację wymagań.
2
Modele wytwarzania oprogramowania
Model wytwarzania mówi, w jakiej kolejności i ile razy wykonuje się fazy cyklu życia. Egzamin pyta o dobór modelu do sytuacji („wymagania są niepewne i będą się zmieniać — który model?") oraz o zalety i wady. Poniższa tabela to minimum, które trzeba znać.
| Model | Kiedy stosować | Zalety | Wady |
|---|---|---|---|
| Kaskadowy (waterfall) | wymagania pewne i niezmienne, projekt krótki, silne wymogi formalne i dokumentacyjne | prosty i czytelny, łatwo planować budżet i terminy, pełna dokumentacja każdej fazy | brak powrotu do poprzedniej fazy, klient widzi produkt dopiero na końcu, kosztowna zmiana wymagań |
| Iteracyjno-przyrostowy | duże systemy, które da się podzielić na moduły dostarczane po kolei | klient dostaje działającą część wcześnie, zmiany łatwiejsze, ryzyko rozłożone na iteracje | trudniejsze planowanie całości, wymaga dobrej architektury od początku, ryzyko rozrostu zakresu |
| Spiralny | projekty duże, kosztowne i obarczone dużym ryzykiem (systemy bankowe, medyczne) | w każdym cyklu jest jawna analiza ryzyka, wcześnie wykrywa kosztowne pomyłki | skomplikowany i drogi, wymaga ekspertów od oceny ryzyka, nieopłacalny w małych projektach |
| Prototypowy | klient nie potrafi określić wymagań, projekt mocno oparty na interfejsie użytkownika | szybkie doprecyzowanie wymagań, klient widzi i klika makietę, mniej nieporozumień | pokusa oddania prototypu jako produktu, prototyp bywa pisany „na skróty", trudno oszacować koszt |
| V-model | systemy o wysokich wymogach jakości i certyfikacji, gdzie testy muszą być zaplanowane z góry | każdej fazie projektowej odpowiada poziom testów, błędy wykrywane wcześniej niż w kaskadzie | nadal sztywny jak kaskada, słabo znosi zmiany wymagań, dużo dokumentacji |
Lewe ramię to fazy analizy i projektowania, prawe — odpowiadające im poziomy testów. Wymagania weryfikujesz testami akceptacyjnymi, projekt techniczny — testami jednostkowymi. Zapamiętaj te pary, bo to gotowa odpowiedź na pytanie „który poziom testów sprawdza specyfikację wymagań".
Analiza wymagan -------------------> Testy akceptacyjne
\ /
Specyfikacja systemu -----------> Testy systemowe
\ /
Projekt architektury ----> Testy integracyjne
\ /
Projekt szczegolowy -> Testy jednostkowe
\ /
IMPLEMENTACJA3
Agile, Scrum i Kanban
Manifest zwinnego wytwarzania oprogramowania (Agile, 2001) to cztery pary wartości. Kluczowe zdanie brzmi: doceniamy elementy po prawej, ale bardziej cenimy te po lewej. To nie znaczy „nie robimy dokumentacji", tylko „działający program jest ważniejszy niż jej objętość".
- Ludzie i interakcje ponad procesy i narzędzia.
- Działające oprogramowanie ponad obszerną dokumentację.
- Współpraca z klientem ponad formalne ustalenia umowy.
- Reagowanie na zmiany ponad realizację ustalonego planu.
Manifest uzupełnia 12 zasad — najczęściej cytowane to: częste dostarczanie działającego oprogramowania, akceptacja zmian wymagań nawet późno w projekcie, codzienna współpraca biznesu z programistami oraz regularna refleksja zespołu nad tym, jak pracować skuteczniej.
Scrum jest najpopularniejszą ramą postępowania zgodną z Agile. Praca dzieli się na sprinty — stałe okresy zwykle od 1 do 4 tygodni (najczęściej 2), zakończone potencjalnie gotowym do wydania przyrostem produktu. Długość sprintu raz ustalona nie zmienia się między sprintami.
| Element | Rodzaj | Odpowiada za / czas | Zapamiętaj |
|---|---|---|---|
| Product Owner | rola | wartość produktu, kolejność zadań w backlogu produktu | reprezentuje klienta, decyduje CO i w jakiej kolejności |
| Scrum Master | rola | przestrzeganie zasad Scruma, usuwanie przeszkód zespołu | nie jest kierownikiem ani szefem — jest opiekunem procesu |
| Zespół deweloperski | rola | wytworzenie przyrostu, zwykle kilka osób | samoorganizujący się i wielofunkcyjny, decyduje JAK |
| Backlog produktu | artefakt | uporządkowana lista wszystkiego, co może trafić do produktu | właścicielem jest Product Owner, lista żyje przez cały projekt |
| Backlog sprintu | artefakt | zadania wybrane na bieżący sprint plus plan ich wykonania | należy do zespołu, nie zmienia się dowolnie w trakcie sprintu |
| Planowanie sprintu | wydarzenie | na początku sprintu, kilka godzin | powstaje cel sprintu i backlog sprintu |
| Daily Scrum | wydarzenie | codziennie, maksymalnie 15 minut | co zrobiłem, co zrobię, co mnie blokuje — to nie raport dla szefa |
| Przegląd sprintu | wydarzenie | na końcu sprintu, z udziałem klienta | pokazujemy działający produkt i zbieramy uwagi |
| Retrospektywa | wydarzenie | po przeglądzie, przed kolejnym planowaniem | rozmawiamy o sposobie pracy zespołu, nie o produkcie |
Przegląd kontra retrospektywa — pewny punkt
Oba wydarzenia są na końcu sprintu i uczniowie je mylą. Różnica jest jedna:
przegląd dotyczy produktu (co powstało, czy klient to
akceptuje), a retrospektywa dotyczy procesu (co poprawić w
sposobie pracy zespołu). Na przeglądzie jest klient, na retrospektywie
zwykle tylko zespół.
Kanban nie ma sprintów, ról ani stałych wydarzeń. Ma tablicę z kolumnami odpowiadającymi etapom pracy i zadania przesuwane w prawo. Najważniejsze pojęcie to limit WIP (Work In Progress) — maksymalna liczba zadań, które mogą jednocześnie znajdować się w kolumnie. Gdy limit jest osiągnięty, nie wolno wciągnąć nowego zadania; trzeba najpierw dokończyć któreś z trwających. Dzięki temu widać wąskie gardło procesu.
+-------------+---------------+--------------+-------------+
| DO ZROBIENIA | W TOKU (3) | TESTY (2) | GOTOWE |
+-------------+---------------+--------------+-------------+
| [ Eksport ] | [ Logowanie ] | [ Koszyk ] | [ Rejestr. ]|
| [ Raporty ] | [ Platnosci ] | [ Filtry ] | [ Menu ]|
| [ Powiadom. ] | [ Wyszukiw. ] | | [ Stopka ]|
| [ Ustawien. ] | | | |
+-------------+---------------+--------------+-------------+
limit = 3 limit = 2
^ pelno! ^ mozna wciagnac 0 zadan
Liczba w naglowku kolumny to limit WIP.
Kolumna "W toku" jest pelna -> nikt nie zaczyna nowego zadania,
zespol pomaga dokonczyc to, co juz sie zaczelo.4
UML: przypadki użycia i diagram klas
UML (Unified Modeling Language) to język graficzny do opisu systemów. Na egzaminie liczą się dwa diagramy statyczne: przypadków użycia (powstaje w fazie analizy) i klas (powstaje w fazie projektowania). Pytania są zwykle o odczytanie symbolu albo o wskazanie, który diagram czemu służy.
Pokazuje, kto korzysta z systemu i co może w nim zrobić — nie pokazuje kolejności ani szczegółów technicznych. Aktor (ludzik) to rola użytkownika lub system zewnętrzny, przypadek użycia (elipsa) to funkcja systemu nazwana czasownikiem, a prostokąt to granica systemu.
SYSTEM SPRZEDAZY BILETOW
+--------------------------------------------+
| |
| ( Przegladaj repertuar ) |
| ^ |
| | |
| ( Rezerwuj miejsce ) |
| | | |
| <<include>>| |<<extend>> |
| v v |
| ( Zaloguj sie ) ( Wybierz znizke ) |
| |
| ( Generuj raport sprzedazy ) |
| ^ |
+---------------|----------------------------+
O | O
-|- -------+ -|- ------> raport
/ \ / \
KLIENT PRACOWNIK KASY
Legenda:
O/-|-/ \ aktor (rola uzytkownika lub system zewnetrzny)
( tekst ) przypadek uzycia (funkcja systemu)
------ asocjacja: aktor bierze udzial w przypadku uzycia
<<include>> przypadek ZAWSZE wlacza inny (obowiazkowy)
<<extend>> przypadek CZASEM rozszerza inny (opcjonalny)Kierunek strzałki: przy include strzałka biegnie od przypadku bazowego do włączanego („Rezerwuj miejsce" zawsze wymaga „Zaloguj się"). Przy extend strzałka biegnie od rozszerzenia do przypadku bazowego („Wybierz zniżkę" tylko czasami rozszerza rezerwację). To najczęściej mylona para w całym UML.
Klasa rysowana jest jako prostokąt podzielony na trzy części: nazwa, atrybuty (pola), operacje (metody). Przed każdym elementem stoi symbol widoczności. Podkreślenie oznacza element statyczny, kursywa — klasę abstrakcyjną lub metodę abstrakcyjną.
+---------------------------+
| Osoba | <- nazwa klasy
+---------------------------+
| # imie : string | <- atrybuty
| # nazwisko : string |
| - pesel : string |
+---------------------------+
| + getImie() : string | <- metody (operacje)
| + setPesel(p:string):void |
+---------------------------+
/_\ dziedziczenie
| (pusty trojkat)
+---------------------------+
| Klient |
+---------------------------+
| - idKlienta : int |
| - rabat : float |
+---------------------------+
| + zlozZamowienie() : void |
+---------------------------+
1 |
| posiada
* |
+---------------------------+ +------------------+
| Zamowienie |<>-------*| PozycjaKoszyka |
+---------------------------+ 1 +------------------+
| - numer : string | agregacja| - ilosc : int |
| - dataZlozenia : date | | - cena : float |
+---------------------------+ +------------------+
| + policzWartosc() : float |
+---------------------------+
1 |
<>| KOMPOZYCJA (romb wypelniony)
1..1
+---------------------------+
| Faktura |
+---------------------------+
| - nrFaktury : string |
+---------------------------+
Widocznosc:
+ public - dostepny wszedzie
- private - dostepny tylko wewnatrz klasy
# protected - dostepny w klasie i w klasach dziedziczacych
~ package - dostepny w obrebie pakietu| Relacja | Symbol | Znaczenie i przykład |
|---|---|---|
| Asocjacja | zwykła linia | obiekty po prostu o sobie „wiedzą" i współpracują. Nauczyciel — Uczeń. |
| Agregacja | romb pusty przy całości | „całość — część", ale część przetrwa usunięcie całości. Zespół i Zawodnik: rozwiązujesz zespół, zawodnicy istnieją dalej. |
| Kompozycja | romb wypełniony przy całości | silna „całość — część": część ginie razem z całością. Faktura i Pozycja faktury; Dom i Pokój. |
| Dziedziczenie (generalizacja) | linia ciągła + pusty trójkąt przy klasie bazowej | relacja „jest rodzajem". Klient jest rodzajem Osoby. Trójkąt zawsze wskazuje klasę nadrzędną. |
| Realizacja interfejsu | linia przerywana + pusty trójkąt | klasa implementuje interfejs. Klasa PlatnoscKarta realizuje interfejs Platnosc. |
| Zależność | linia przerywana + zwykła strzałka | jedna klasa chwilowo używa drugiej, np. jako parametru metody. |
1— dokładnie jeden.0..1— zero albo jeden (opcjonalny).*lub0..*— dowolnie wiele, także zero.1..*— co najmniej jeden.2..5— od dwóch do pięciu.
Liczebność czytasz zawsze po przeciwnej stronie linii: „jeden Klient — wiele Zamówień" to zapis 1 przy klasie Klient i * przy klasie Zamówienie.
Zadaj jedno pytanie: czy część ma sens bez całości?
Usuwasz Fakturę — jej pozycje przestają mieć jakikolwiek sens, nikt ich nie przypisze gdzie indziej. To kompozycja, romb wypełniony.
Usuwasz Playlistę — utwory zostają w bibliotece i mogą trafić do innej playlisty. To agregacja, romb pusty.
Skrót pamięciowy: wypełniony romb = wspólny los. Romb rysujesz zawsze przy klasie będącej całością.
5
UML: diagram sekwencji i czynności
Diagramy dynamiczne pokazują zachowanie w czasie. Sekwencji — kto do kogo wysyła komunikaty i w jakiej kolejności. Czynności — przepływ kroków algorytmu lub procesu biznesowego, z warunkami i rozgałęzieniami.
Uczestnicy stoją w rzędzie u góry. Od każdego w dół biegnie linia życia (pionowa linia przerywana). Prostokąt na linii to okres aktywności obiektu. Poziome strzałki to komunikaty: strzałka pełna z pełnym grotem — wywołanie synchroniczne, strzałka przerywana — odpowiedź (return). Czas płynie z góry na dół.
:Uzytkownik :FormularzLogowania :Autoryzacja :BazaDanych
| | | |
| wpiszDane() | | |
|------------------->| | |
| [#] | |
| | sprawdz(login,haslo) |
| |------------------>| |
| | [#] pobierzUzytkownika(login)
| | |--------------->|
| | | [#]
| | |<- - - - - - - -|
| | | rekord |
| |<- - - - - - - - - | |
| | true / false | |
|<- - - - - - - - - -| | |
| komunikat | | |
| | | |
----> komunikat synchroniczny (wywolanie metody)
- - > komunikat zwrotny (odpowiedz, return)
[#] prostokat aktywnosci - obiekt wykonuje operacje
| linia zycia obiektu; czas plynie w dolOdpowiednik schematu blokowego w UML. Wypełnione koło to węzeł początkowy, koło z obwódką — końcowy, zaokrąglony prostokąt to czynność, romb to węzeł decyzyjny z warunkiem w nawiasach kwadratowych. Gruba pozioma kreska (rozwidlenie) oznacza czynności wykonywane równolegle.
(*) <- wezel poczatkowy
|
v
+--------------------+
| Dodaj towar do |
| koszyka |
+--------------------+
|
v
/ \
< suma > [suma > 200 zl]
\ /--------------------+
| |
[suma <= 200 zl] v
| +--------------------+
v | Naliczen. rabatu |
+--------------------+ +--------------------+
| Naliczen. wysylki | |
+--------------------+ |
| |
+----------+-----------+
|
v
+------------------+
| Zlozenie zamow. |
+------------------+
|
v
(( )) <- wezel koncowyDiagram czynności można podzielić na tory (ang. swimlanes) — pionowe pasy przypisane do konkretnych osób, ról albo systemów. Wtedy od razu widać, kto wykonuje którą czynność.
6
Testowanie oprogramowania i TDD
Poziomy testów układają się od najmniejszego fragmentu do całego systemu u klienta. Egzamin pyta najczęściej o to, kto wykonuje dany poziom testów i co dokładnie sprawdza.
| Poziom | Co sprawdza | Kto wykonuje | Przykład |
|---|---|---|---|
| Jednostkowe | pojedynczą funkcję, metodę lub klasę w izolacji | programista | funkcja obliczBrutto(100, 23) zwraca 123 |
| Integracyjne | współpracę modułów i poprawność interfejsów między nimi | programista lub tester | moduł koszyka poprawnie zapisuje zamówienie w bazie danych |
| Systemowe | cały system jako całość, zgodność ze specyfikacją, także wydajność i bezpieczeństwo | zespół testerów | pełna ścieżka: rejestracja, zakup, płatność, faktura |
| Akceptacyjne | czy system spełnia oczekiwania zamawiającego | klient / użytkownik końcowy | odbiór systemu przez zamawiającego przed podpisaniem protokołu |
| Regresyjne | czy nowa zmiana nie zepsuła tego, co wcześniej działało | zwykle automat (CI) | po poprawce w module płatności uruchamiamy cały zestaw starych testów |
Tester nie zna kodu. Podaje dane wejściowe i sprawdza wyjście względem specyfikacji. Typowe techniki: klasy równoważności i wartości brzegowe. Dla pola „wiek od 18 do 65" sprawdzasz 17, 18, 65 i 66 — błędy najczęściej siedzą właśnie na granicach.
Tester widzi kod i projektuje przypadki tak, aby przejść wszystkie instrukcje, gałęzie warunków i ścieżki. Miarą jest pokrycie kodu (code coverage) w procentach. Odmiana pośrednia to szara skrzynka — częściowa znajomość wnętrza.
TDD (Test Driven Development) odwraca zwykłą kolejność: najpierw piszesz test, który musi się nie powieść, a dopiero potem kod, który go zaliczy. Cykl nazywa się „red — green — refactor".
1. RED napisz test dla nieistniejacej jeszcze funkcji
-> test NIE przechodzi (na czerwono)
2. GREEN napisz NAJPROSTSZY kod, ktory zalicza test
-> test przechodzi (na zielono)
3. REFACTOR popraw strukture kodu (nazwy, powtorzenia),
nie zmieniajac zachowania
-> testy nadal przechodza
-> wroc do punktu 1 dla kolejnej funkcji
Przyklad testu jednostkowego (skladnia w stylu PHPUnit):
class KalkulatorTest extends TestCase
{
public function testDodawanie()
{
$k = new Kalkulator();
$this->assertEquals(7, $k->dodaj(3, 4));
}
public function testDzieleniePrzezZero()
{
$this->expectException(DivisionByZeroError::class);
(new Kalkulator())->podziel(5, 0);
}
}Korzyści TDD: kod od początku ma komplet testów, łatwiej o refaktoryzację, a wymagania są doprecyzowane, zanim powstanie implementacja. Koszt: wolniejszy start i konieczność utrzymywania testów razem z kodem.
7
Dokumentacja i kontrola wersji (git)
Dokumentacja dzieli się na trzy rodzaje, a system kontroli wersji jest dziś standardowym narzędziem pracy zespołowej. Egzamin pyta o znaczenie podstawowych pojęć gita i o to, po co w ogóle repozytorium.
Instrukcja obsługi, opis funkcji krok po kroku, zrzuty ekranu, FAQ. Pisana językiem nietechnicznym, dla osoby, która ma tylko używać programu.
Architektura, diagramy UML, schemat bazy danych, opis API, komentarze w kodzie i dokumentacja generowana z nich (np. narzędziami typu phpDocumentor czy Javadoc). Dla programisty utrzymującego system.
Wymagania sprzętowe i systemowe, procedura instalacji, konfiguracja serwera, kopie zapasowe, procedura aktualizacji. Dla administratora.
- Pełna historia zmian — wiadomo, kto, kiedy i co zmienił w każdej linii kodu.
- Możliwość cofnięcia się do dowolnej wcześniejszej, działającej wersji.
- Praca równoległa wielu osób na tym samym projekcie bez nadpisywania cudzych plików.
- Rozwijanie nowych funkcji na gałęziach, bez psucia wersji produkcyjnej.
- Kopia repozytorium na serwerze (GitHub, GitLab) to jednocześnie zapasowa kopia projektu.
Git jest systemem rozproszonym — każdy programista ma pełną kopię repozytorium z całą historią i może pracować offline. Starsze systemy scentralizowane (np. SVN) trzymały historię wyłącznie na serwerze.
REPOZYTORIUM katalog projektu wraz z pelna historia zmian (folder .git)
COMMIT zatwierdzona migawka zmian; ma autora, date, opis i skrot SHA
BRANCH galaz - rownolegla linia rozwoju, np. na jedna nowa funkcje
MERGE scalenie galezi z powrotem do glownej linii (main / master)
KONFLIKT dwie osoby zmienily te sama linie - git nie wie, ktora zostawic;
czlowiek musi recznie wybrac i zatwierdzic wynik
git init utworzenie nowego repozytorium
git clone <adres> pobranie repozytorium zdalnego
git status co zostalo zmienione
git add plik.php dodanie pliku do poczekalni (staging)
git commit -m "opis zmiany" zatwierdzenie zmian z opisem
git log --oneline historia commitow
git branch nowa-funkcja utworzenie galezi
git checkout nowa-funkcja przelaczenie sie na galaz
git merge nowa-funkcja scalenie galezi do biezacej
git push wyslanie commitow na serwer
git pull pobranie zmian z serwera galaz "logowanie"
o---o---o
/ \ merge
main: o---o---o---o--o-----------o---o--->
A B C D scalenie
Kazde "o" to jeden commit. Praca nad nowa funkcja odbywa sie
na osobnej galezi, wiec main przez caly czas pozostaje sprawny.8
Wzorce projektowe
Wzorzec projektowy to sprawdzony schemat rozwiązania powtarzalnego problemu projektowego — nie gotowy kod, tylko sposób ułożenia klas. W INF.04 wystarczy znać kilka najpopularniejszych i umieć powiedzieć, jaki problem rozwiązuje każdy z nich.
Problem: w programie ma istnieć dokładnie jeden obiekt danej klasy — jedno połączenie z bazą danych, jeden obiekt konfiguracji, jeden rejestrator zdarzeń. Gdy każdy moduł tworzy własny obiekt, otwierasz dziesiątki połączeń i marnujesz zasoby.
Rozwiązanie: konstruktor staje się prywatny, więc nikt z zewnątrz nie zrobi „new". Klasa udostępnia statyczną metodę, która przy pierwszym wywołaniu tworzy obiekt, a przy każdym kolejnym zwraca ten sam.
class Baza
{
private static $instancja = null;
private $polaczenie;
private function __construct() // konstruktor PRYWATNY
{
$this->polaczenie = new PDO("mysql:host=localhost;dbname=sklep",
"user", "haslo");
}
public static function getInstancja(): Baza
{
if (self::$instancja === null) {
self::$instancja = new Baza(); // tworzone raz
}
return self::$instancja; // potem zawsze to samo
}
}
$a = Baza::getInstancja();
$b = Baza::getInstancja(); // $a i $b to DOKLADNIE ten sam obiektProblem: w jednym pliku mieszają się zapytania SQL, HTML i logika sterująca. Zmiana wyglądu strony wymaga grzebania w zapytaniach, a zmiana bazy — w kodzie widoku. Nie da się tego testować ani rozwijać w zespole.
Rozwiązanie: podział na trzy warstwy o rozdzielonych odpowiedzialnościach. Model — dane i logika biznesowa, dostęp do bazy. Widok — prezentacja, szablon HTML. Kontroler — przyjmuje żądanie użytkownika, prosi model o dane i wybiera widok. Na tym wzorcu opierają się frameworki webowe (Laravel, Symfony, ASP.NET MVC).
UZYTKOWNIK
| 1. zadanie: GET /produkty?kategoria=laptopy
v
+--------------+ 2. pobierz dane +-----------------+
| KONTROLER |-------------------->| MODEL |
| |<--------------------| (baza, logika) |
+--------------+ 3. lista obiektow +-----------------+
|
| 4. przekaz dane do szablonu
v
+--------------+
| WIDOK | 5. gotowy HTML -> przegladarka
+--------------+
Zasada: WIDOK nie odpytuje bazy, MODEL nie generuje HTML-a.
Wymiana szablonu nie dotyka logiki, wymiana bazy nie dotyka szablonu.Problem: gdy zmienia się stan jednego obiektu, kilka innych musi na to zareagować — po złożeniu zamówienia trzeba wysłać e-mail, zapisać log i zaktualizować stan magazynu. Wpisanie tych wywołań na sztywno w klasę Zamówienie oznacza, że przy każdej nowej reakcji trzeba ją modyfikować.
Rozwiązanie: obiekt obserwowany (subject) trzyma listę obserwatorów i przy zmianie stanu powiadamia wszystkich jedną pętlą. Dodanie nowej reakcji to dopisanie obserwatora — bez zmiany klasy obserwowanej. Ten sam mechanizm stoi za obsługą zdarzeń w JavaScripcie (metoda addEventListener).
interface Obserwator {
public function aktualizuj($zamowienie);
}
class Zamowienie // obiekt obserwowany
{
private $obserwatorzy = [];
public function dodajObserwatora(Obserwator $o)
{
$this->obserwatorzy[] = $o;
}
public function zloz()
{
// ... zapis zamowienia ...
foreach ($this->obserwatorzy as $o) {
$o->aktualizuj($this); // powiadomienie WSZYSTKICH
}
}
}
class WysylkaMaila implements Obserwator { /* wysyla potwierdzenie */ }
class Magazyn implements Obserwator { /* zdejmuje towar ze stanu */ }
class Logger implements Obserwator { /* zapisuje wpis do logu */ }Problem: w wielu miejscach programu decydujesz na podstawie parametru, jaki obiekt utworzyć — płatność kartą, przelewem albo BLIK-iem. Rozsiane po kodzie instrukcje warunkowe z „new" trzeba poprawiać w każdym miejscu, gdy dochodzi nowa metoda płatności.
Rozwiązanie: tworzenie obiektów przenosisz do jednej klasy fabryki. Reszta programu prosi fabrykę o obiekt i posługuje się wyłącznie wspólnym interfejsem, nie wiedząc, która konkretna klasa powstała. Nowa metoda płatności to nowa klasa i jedna linia w fabryce.
interface Platnosc {
public function zaplac(float $kwota): bool;
}
class PlatnoscKarta implements Platnosc { /* ... */ }
class PlatnoscPrzelew implements Platnosc { /* ... */ }
class PlatnoscBlik implements Platnosc { /* ... */ }
class FabrykaPlatnosci
{
public static function utworz(string $typ): Platnosc
{
switch ($typ) {
case "karta": return new PlatnoscKarta();
case "przelew": return new PlatnoscPrzelew();
case "blik": return new PlatnoscBlik();
default: throw new InvalidArgumentException("Nieznany typ");
}
}
}
// reszta programu nie wie, ktora klasa powstala:
$p = FabrykaPlatnosci::utworz($wyborUzytkownika);
$p->zaplac(199.00);Podział wzorców — jedno zdanie na kategorię
Kreacyjne odpowiadają za tworzenie obiektów (Singleton,
Factory, Builder, Prototype). Strukturalne układają klasy
w większe całości (Adapter, Dekorator, Fasada, Kompozyt).
Behawioralne opisują komunikację między obiektami (Observer,
Strategia, Iterator, Komenda). MVC bywa opisywany osobno jako wzorzec
architektoniczny, bo dotyczy struktury całej aplikacji, a nie kilku klas.
- Agregacja i kompozycja rysowane tym samym rombem. Pusty romb — część przeżyje całość (Playlista i Utwór). Wypełniony romb — część ginie z całością (Faktura i jej pozycja). Romb zawsze przy klasie będącej całością.
- Mylenie include z extend. „Include" to zależność obowiązkowa (zawsze wykonywana), „extend" opcjonalna (czasem rozszerza). Strzałki biegną w przeciwnych kierunkach.
- Scrum Master traktowany jak kierownik projektu. Nie przydziela zadań i nie rozlicza ludzi — pilnuje zasad Scruma i usuwa przeszkody. Kolejność zadań ustala Product Owner, sposób wykonania — zespół.
- Przegląd sprintu mylony z retrospektywą. Przegląd = produkt (z klientem), retrospektywa = sposób pracy zespołu.
- Testy akceptacyjne przypisane programiście. Wykonuje je zamawiający lub użytkownik końcowy. Programista robi testy jednostkowe.
- Czarna skrzynka pomylona z białą. Czarna — bez znajomości kodu, sprawdzasz wejście i wyjście. Biała — z kodem przed oczami, liczy się pokrycie ścieżek.
- Wymaganie „system ma być bezpieczny" jako funkcjonalne. To cecha jakościowa, czyli wymaganie niefunkcjonalne, podobnie jak czas odpowiedzi czy dostępność.
- Model spiralny mylony z iteracyjnym. Oba są cykliczne, ale wyróżnikiem spirali jest jawna analiza ryzyka w każdym obiegu.
Ten dział zdaje się na rozróżnieniach: romb pusty czy wypełniony, include czy extend, przegląd czy retrospektywa. Rozwiąż test i sprawdź, które pary nadal ci się zlewają.