Dio 1 – CBSE, odlike, interfejsi
Dio 2 – Principi, smjernice, provođenje
Dopunska praksa – Paket sa verzijom
Materijale priredio Erkan Islamović
erkan.islamovic@eubd.edu.ba · erkan.islamovic@gmail.com
Deset predavanja: dva časa teorije i jedan čas dopunske prakse. Danas je poglavlje 8. Isti primjer kroz knjigu: DeliveryApp 2.0.
| Pred. | Dio 1 | Dio 2 | Praksa |
|---|---|---|---|
| 1 | Projekat i proizvod, trendovi, etika | Inženjerski pristup, alati, automatizacija | Repozitorij i vizija proizvoda |
| 2 | Modeli, uloge, planiranje | Rizici, tim, kvalitet, poboljšanje procesa | Plan izdanja, registar rizika |
| 3 | Konceptualni model, UML, SOLID | Prototip, UX, specifikacija dizajna | Wireframe i SDD |
| 4 | IDE, organizacija koda, OOP, okviri | Podaci, MVC/MVVM, čisti kod, refaktoring | Refaktoring funkcije |
| 5 | Git, pull request, CI | Testovi, CD, kontejneri | GitHub Actions |
Deset predavanja: dva časa teorije i jedan čas dopunske prakse. Danas: poglavlje 8 (označeno). Isti primjer kroz knjigu: DeliveryApp 2.0.
| Pred. | Dio 1 | Dio 2 | Praksa |
|---|---|---|---|
| 6 | Klasifikacija, mjerljivost | Taktike i redizajn | Mjerenje i keš |
| 7 | Odluke, pogledi, stilovi | Troslojna i čista arhitektura, predlošci | ADR i C4 |
| 8 | CBSE, odlike, interfejsi | Principi, smjernice, provođenje | Paket sa verzijom |
| 9 | SaaS, SOA, IoT, proizvodne linije | Aspekti, reaktivno programiranje | Dekorator i tok događaja |
| 10 | Vrijednost, troškovi, razvoj ili kupnja, TCO | Metrike, procjena, NPV, povrat | NPV u Pythonu |
Na istoj konferenciji u Garmischu 1968. godine, o kojoj smo čitali u poglavlju 1, programer Douglas McIlroy iz Bell Labsa održao je izlaganje pod naslovom „Masovno proizvedene softverske komponente”. Pitao je zašto softverska industrija nema ono što ima svaka druga industrija: katalog standardnih dijelova. Proizvođač automobila ne pravi sam svaki vijak; kupi ga, po standardu, iz kataloga. McIlroy je zamišljao da će programeri birati gotove rutine iz porodica, prema preciznosti, robusnosti i brzini, i od njih sklapati programe. Skoro pola vijeka kasnije, 22. marta 2016, ta vizija se ostvarila na način koji niko nije očekivao.
Programer Azer Koçulu povukao je sve svoje pakete iz registra npm, u kojem programeri JavaScripta dijele komponente. Među njima je bio i left-pad, funkcija od jedanaest redova koja dopunjava tekst razmacima s lijeve strane. Ispostavilo se da od nje, preko lanca drugih paketa, zavise popularni alati, i gradnja hiljada projekata širom svijeta odjednom je počela padati. Registar je paket vratio za nekoliko sati i promijenio pravila povlačenja. Katalog dijelova je stvaran, ali je donio i novo pitanje: koliko zaista znamo o dijelovima od kojih je naš proizvod sastavljen?
U poglavlju 7 izabrali smo arhitekturu DeliveryApp 2.0: modularni monolit s jasnim granicama između narudžbi, dostave, plaćanja i obavještenja. Ovo poglavlje uzima te dijelove ozbiljno i pravi od njih komponente: dijelove s ugovorom, verzijom i vlastitim životom. Prvi dio objašnjava ideju programskog inženjerstva temeljenog na komponentama, odlike komponente, interfejse, koheziju i povezanost. Drugi dio daje principe i smjernice za dizajn komponenata, pokazuje kako se komponenta pravi kao paket sa semantičkom verzijom, i govori o rizicima ponovne upotrebe, od Ariane 5 do left-pada.
Nakon poglavlja možete:
objasniti ideju CBSE i njene koristi; navesti odlike komponente po Szyperskom; razlikovati ponuđeni i zahtijevani interfejs i nacrtati UML dijagram komponenti; objasniti koheziju i povezanost; objasniti principe REP, CCP, CRP, ADP, SDP i SAP prostim riječima; primijeniti smjernice za dizajn komponente; napraviti Python paket sa semantičkom verzijom i reći kad se mijenja koji broj; objasniti rizike ponovne upotrebe i šta je SBOM.
| Čas | Sadržaj | Na času |
|---|---|---|
| Teorija 1 | 8.1–8.4 CBSE, odlike, interfejsi, kohezija i povezanost | Lego kocka i utičnica; dijagram komponenti DeliveryApp |
| Teorija 2 | 8.5–8.8 Principi, smjernice, provođenje, rizici | Koja je sljedeća verzija: 1.1.0 ili 2.0.0? |
| Dopunska praksa | Paket deliveryapp-dostava sa semantičkom verzijom | pip install . i tri testa |
Lego kocke su najbolja slika komponente. Svaka kocka ima standardne čepiće (interfejs), pa se svaka može spojiti sa svakom, bez obzira na to ko ju je napravio i kada. Iz istih kocki prave se kuća, auto i svemirski brod. Niko ne pravi kocke iznova za svaku igračku.
Programsko inženjerstvo temeljeno na komponentama (CBSE) prenosi tu ideju u softver: proizvod se sklapa od komponenata, a ne piše od nule. Dio komponenata tim pravi sam (komponenta za obračun dostave), a dio uzima gotove (biblioteka za heširanje lozinki, okvir za web, klijent za sistem plaćanja).
Koristi:
Danas je CBSE svakodnevica: tipična web aplikacija sadrži više stotina paketa iz registara, a vlastitog koda je često manje od tuđeg.
Clemens Szyperski je u knjizi Component Software dao definiciju koja se najčešće navodi: komponenta je jedinica sastavljanja s ugovorom definisanim interfejsima i samo eksplicitnim zavisnostima od okoline; može se isporučiti nezavisno i sklapati s drugim komponentama, i to od strane trećih lica.
Prevedeno na pet odlika:
| Odlika | Šta znači | deliveryapp-dostava |
|---|---|---|
| standardizovana | prati dogovoren model (npr. Python paket, .NET biblioteka) | Python paket s pyproject.toml |
| nezavisna | ne traži skrivene stvari od okoline; sve zavisnosti su zapisane | nema zavisnosti osim Pythona |
| sastavljiva | sav pristup ide kroz javni interfejs | obracunaj(grad, km, iznos) |
| isporučiva | može se instalirati i nadograditi posebno | pip install deliveryapp-dostava==2.0.0 |
| dokumentovana | interfejs je opisan tako da ga koristi neko ko ne vidi kod | opis u modulu i README |
Komponenta nije isto što i klasa. Klasa je jedinica koda; komponenta je jedinica isporuke. Komponenta obično sadrži više klasa, od kojih je većina skrivena, a vani se vidi samo interfejs.
Komponenta ima dvije vrste interfejsa. Analogija je električni aparat:
Fen radi u bilo kojoj kući koja ima tu utičnicu; kući je svejedno koji fen je uključen. Tako i komponenta deliveryapp-narudzbe zahtijeva interfejse obracunaj(), naplati(), posalji() i udaljenost(), a ne zna i ne zanima je ko ih pruža.
flowchart LR
N["«komponenta»<br/>deliveryapp-narudzbe"]
D["«komponenta»<br/>deliveryapp-dostava 2.0.0"]
iD(("obracunaj()"))
D --- iD
N -.->|"zahtijeva"| iD
Ugovor je precizan opis interfejsa. Za obracunaj(grad, km, iznos) ugovor kaže:
Obracun s gradom, iznosom dostave u centima i kratkim opisom;KeyError; udaljenost izvan zone ili iznos ispod 5,00 € → ValueError;Ugovor se ne mijenja „usput”. Ako se mora promijeniti, mijenja se glavna verzija (8.6).
Dva pojma odlučuju da li je podjela na komponente dobra:
razno u kojoj su obračun dostave, slanje SMS-a i izvoz u Excel.Cilj je uvijek isti: visoka kohezija, niska povezanost. Analogija: dobro organizovana kuhinja ima ladicu za pribor i ladicu za začine (visoka kohezija), a da biste uzeli kašiku ne morate izvaditi začine (niska povezanost).

Tim predlaže komponentu deliveryapp-alati u kojoj bi bili formatiranje cijene (24,50 €), provjera e-mail adrese, računanje udaljenosti i slanje push obavještenja. Šta nije u redu i kako biste je razdvojili?
Odgovori: tipka R ili klik na zamućeno polje.
Robert C. Martin je opisao šest principa za komponente. Tri govore šta ide zajedno u komponentu, a tri kako komponente zavise jedna od druge.
Šta ide zajedno (kohezija):
| Princip | Prostim riječima | U DeliveryApp |
|---|---|---|
| REP – ponovna upotreba = izdanje | ono što se ponovo koristi mora imati verziju i izdanje | deliveryapp-dostava ima verziju 2.0.0 i bilješke o izdanju |
| CCP – zajedničko zatvaranje | u istu komponentu ide ono što se mijenja iz istog razloga i u isto vrijeme | tarife i obračun dostave mijenjaju se zajedno, pa su zajedno |
| CRP – zajednička ponovna upotreba | ne tjeraj korisnika da zavisi od onoga što ne koristi | dostava ne nosi kod za SMS, pa ko koristi dostavu ne mora instalirati SMS |
Kako zavise (povezanost):
| Princip | Prostim riječima | U DeliveryApp |
|---|---|---|
| ADP – bez ciklusa | zavisnosti ne smiju praviti krug | narudžbe → dostava, ali dostava ne zavisi od narudžbi |
| SDP – zavisi od stabilnog | zavisi od onoga što se rjeđe mijenja od tebe | ekran zavisi od narudžbi, narudžbe od tarife |
| SAP – stabilno je apstraktno | komponente od kojih mnogi zavise neka budu apstraktne (interfejsi) | ServisMapa je interfejs, a konkretna firma je zamjenjiva |
Zašto je ciklus opasan? Ako narudžbe zavise od dostave, a dostava od narudžbi, nijedna se ne može izgraditi, testirati ni isporučiti bez druge: dvije komponente su zapravo jedna, samo s dva imena. Ciklus se prekida tako što se zajednički dio izdvoji ili se uvede interfejs (inverzija zavisnosti).

Praktične smjernice koje tim DeliveryApp koristi:
deliveryapp-dostava izvozi tri imena: Obracun, obracunaj, gradovi. Sve ostalo (npr. _Tarifa, _TARIFE) počinje donjom crtom i nije dio ugovora.frozen=True), da pozivalac ne pokvari stanje komponente.| Promjena | Koji broj raste | Primjer za deliveryapp-dostava |
|---|---|---|
| ispravka greške, ugovor isti | zakrpa | 1.1.0 → 1.1.1: ispravljeno zaokruživanje na 8,0 km |
| nova mogućnost, stari pozivi i dalje rade | sporedna | 1.0.0 → 1.1.0: dodane tarife po gradu, stari poziv dostava(km, iznos) radi za Grad A |
| promjena koja kvari stare pozive | glavna | 1.1.0 → 2.0.0: obracunaj() vraća Obracun umjesto broja; stari poziv uklonjen |
Kad DeliveryApp kaže da zavisi od deliveryapp-dostava>=2.0,<3, zna da će dobiti sve ispravke i nove mogućnosti verzije 2, a da ga ništa neće pokvariti bez upozorenja. Pravila semantičkog verzioniranja zapisana su na stranici semver.org.
„Verzija je samo broj za marketing.” U svijetu komponenata verzija je obećanje. Ako komponenta u zakrpi promijeni ugovor, hiljade projekata koji su vjerovali obećanju mogu se pokvariti preko noći.
Kako tim DeliveryApp od dijela koda pravi komponentu, korak po korak:
obracunaj), zahtijevani (nijedan), greške, garancije.pyproject.toml s imenom i verzijom; C#: projekat biblioteke (.csproj) i NuGet paket.Paket deliveryapp-dostava (primjeri/paket/deliveryapp-dostava) ima ovu strukturu:
deliveryapp-dostava/
├── pyproject.toml
├── src/deliveryapp_dostava/__init__.py
└── tests/test_komponenta.pypyproject.toml opisuje paket:
[project]
name = "deliveryapp-dostava"
version = "2.0.0"
description = "Obračun dostave po tarifi grada (komponenta proizvoda DeliveryApp 2.0)"
requires-python = ">=3.10"A ponuđeni interfejs izgleda ovako:
@dataclass(frozen=True)
class Obracun:
grad: str
dostava: int # centi
opis: str
def obracunaj(grad: str, km: float, iznos: int) -> Obracun:
t = _TARIFE.get(grad)
if t is None:
raise KeyError(f"nepoznat grad: {grad}")
if km < 0 or km > t.max_km:
raise ValueError("adresa je izvan zone dostave")
if iznos < MIN_NARUDZBA:
raise ValueError("iznos je manji od najmanje narudžbe")
if iznos >= BESPLATNA_OD:
return Obracun(grad, 0, "besplatna dostava od 30,00 €")
dodatni = math.ceil(max(0, km - t.ukljuceno_km))
return Obracun(grad, t.osnovna + t.po_km * dodatni, f"osnovna + {dodatni} km")Nakon pip install . u čistom virtuelnom okruženju, poziv obracunaj("B", 4.2, 2150) vraća Obracun(grad='B', dostava=380, opis='osnovna + 3 km'), a tri testa komponente prolaze.
U C# isti posao radi projekat biblioteke: dotnet new classlib -n DeliveryApp.Dostava, verzija u .csproj (<Version>2.0.0</Version>), pakovanje sa dotnet pack i objava u NuGet registar. Javne su samo klase i metode označene sa public; ostalo je internal i ne vidi se izvan komponente.
pyproject.toml; C# komponenta je biblioteka i NuGet paket.internal u C#).Ponovna upotreba štedi vrijeme, ali komponenta koja je radila u jednom okruženju ne mora raditi u drugom, a svaka tuđa komponenta je i tuđi rizik.
Raketa Ariane 5 eksplodirala je na prvom letu, 4. juna 1996, oko četrdeset sekundi nakon polijetanja. Softver za inercijalni navigacioni sistem preuzet je s rakete Ariane 4, gdje je godinama radio bez greške. Ariane 5 je, međutim, brža, pa je jedna vrijednost horizontalne brzine postala prevelika za pretvaranje iz 64-bitnog decimalnog broja u 16-bitni cijeli broj. Pretvaranje nije bilo zaštićeno, sistem je prijavio grešku, i raketa je skrenula s putanje i raspala se. Istražna komisija je utvrdila da funkcija koja je izazvala grešku Ariani 5 nakon polijetanja nije ni bila potrebna. Komponenta je bila ispravna za svoj stari ugovor, a niko nije provjerio da li taj ugovor važi u novom okruženju.
Rizici ponovne upotrebe i odgovori:
| Rizik | Primjer | Odgovor |
|---|---|---|
| komponenta ne odgovara novom okruženju | Ariane 5 | provjeriti ugovor i granice u novom okruženju; testovi s novim vrijednostima |
| komponenta nestane | left-pad, 2016 | interni registar ili keš paketa; zaključane verzije |
| ranjivost u komponenti | ranjivost u biblioteci Log4j otkrivena u decembru 2021, koja je pogodila ogroman broj Java sistema | praćenje ranjivosti (npr. Dependabot), brza nadogradnja |
| zlonamjerna komponenta | paket s imenom sličnim popularnom, s ubačenim kodom | provjera izvora i imena, potpisani paketi |
| tranzitivne zavisnosti | moja zavisnost zavisi od stotinu drugih | spisak svih zavisnosti, redovna provjera |
| licenca | biblioteka s licencom koja traži objavu vlastitog koda | provjera licenci prije uključivanja |
Zaključavanje verzija. Fajl s tačnim verzijama svih zavisnosti, uključujući tranzitivne (npr. requirements.txt s ==, poetry.lock, packages.lock.json u .NET-u), garantuje da se proizvod sutra gradi od istih dijelova kao danas.
SBOM (software bill of materials) je deklaracija sastava proizvoda, kao spisak sastojaka na pakovanju hrane: sve komponente, njihove verzije i licence. Kad se objavi nova ranjivost, firma sa SBOM-om za minut zna da li je pogođena. Zakoni o sajber sigurnosti (npr. evropski Akt o sajber otpornosti) sve češće traže takav spisak za proizvode koji se prodaju.
Odgovori: tipka R ili klik na zamućeno polje.
deliveryapp-dostavaCilj je napraviti komponentu kao Python paket, instalirati je i promijeniti verziju po pravilima. Trajanje: jedan čas.
Kopirajte folder primjeri/paket/deliveryapp-dostava u svoj repozitorij. Napravite virtuelno okruženje i instalirajte paket:
python -m venv .venv
.venv\Scripts\activate (Linux/macOS: source .venv/bin/activate)
pip install -e .
python -c "import deliveryapp_dostava as z; print(z.__version__, z.obracunaj('B', 4.2, 2150))"
pip install pytest
pytest testsPronađite ili zamislite ispravku koja ne mijenja ugovor (npr. bolja poruka greške). Napravite je, promijenite verziju u pyproject.toml i __init__.py na 2.0.1 i dopišite stavku u CHANGELOG.md.
Dodajte Grad D (2,00 € do 4 km, 0,40 € po km, najdalje 15 km) i test za njega. Stari pozivi rade, pa je nova verzija 2.1.0. Provjerite: koliko je dostava za N-1001 (21,50 €, 4,2 km) u Gradu D?
U CHANGELOG.md opišite promjenu koja bi tražila 3.0.0 (npr. iznos se vraća kao Decimal u eurima umjesto int u centima) i šta korisnici komponente moraju promijeniti.
Pokrenite pip list --format=freeze u virtuelnom okruženju i sačuvajte izlaz kao docs/sastav.txt. To je najjednostavniji spisak sastava; profesionalni alati prave SBOM u standardnim formatima (CycloneDX, SPDX).
Rezultat: paket koji se instalira, testovi koji prolaze, verzija 2.1.0 i CHANGELOG.md s tri stavke.
Rješenja su sakrivena – tipka R ili klik na okvir.
Komponenta deliveryapp-placanje je u verziji 3.4.2. Koja je sljedeća verzija za: a) ispravku greške u zaokruživanju; b) novi način plaćanja (stari rade); c) uklanjanje starog načina plaćanja „pouzećem”; d) ispravku pravopisne greške u poruci?
a) 3.4.3; b) 3.5.0; c) 4.0.0 (stari pozivi prestaju raditi); d) 3.4.3 ako se poruka ne smatra dijelom ugovora (ako je pozivaoci porede kao tekst, sigurnije je smatrati je promjenom ugovora).
Komponenta deliveryapp-narudzbe direktno čita tabelu tarifa iz baze da bi izračunala dostavu, umjesto da pozove deliveryapp-dostava. Šta je loše i šta predlažete?
Povezanost je visoka i skrivena: narudžbe zavise od unutrašnje strukture podataka dostave, pa svaka promjena tabele kvari narudžbe, a pravilo obračuna postoji na dva mjesta (krši DRY i CCP). Prijedlog: narudžbe pozivaju samo ponuđeni interfejs obracunaj(); tabela tarifa je privatna stvar komponente dostave.
deliveryapp-narudzbe zavisi od deliveryapp-obavjestenja (šalje poruku o promjeni stanja), a deliveryapp-obavjestenja zavisi od deliveryapp-narudzbe (čita detalje narudžbe za tekst poruke). Kako prekinuti ciklus?
Npr. narudžbe objavljuju događaj „stanje promijenjeno” sa svim podacima potrebnim za poruku (broj, novo stanje, telefon), preko reda poruka ili interfejsa koji pripada narudžbama. Obavještenja reaguju na događaj i više ne zovu narudžbe. Zavisnost ide samo u jednom smjeru (ADP).
Tim želi u DeliveryApp uključiti besplatnu biblioteku za crtanje mape koju je napisao jedan programer i koja nije ažurirana tri godine. Napišite pet pitanja koja tim treba postaviti prije odluke.
1) Kakva je licenca i da li dozvoljava upotrebu u komercijalnom proizvodu? 2) Ima li poznatih ranjivosti i ko ih ispravlja? 3) Od čega ona zavisi (tranzitivne zavisnosti)? 4) Šta radimo ako nestane ili prestane raditi – možemo li je držati u internom registru ili zamijeniti? 5) Da li njen ugovor odgovara našim uslovima (broj tačaka na mapi, mobilni uređaji, brzina)?
obracunaj.