🏗️ Šta su Design Patterns?
Za razliku od pravih biblioteka koda (poput jQuery i Bootstrap-a) koje možete samo ugraditi i koristiti, Dizajn Obrasci (Design Patterns) su opisana i teoretski dokazana rješenja za softverske probleme koji se stalno ponavljaju. Oni su nastali iz višedecenijskog iskustva najboljih arhitekata softvera u svijetu (na bazi knjige "Gang of Four" iz 1994).
Državne institucije poput ministarstava, općina i poreskih uprava obično razvijaju gigantske platforme koje istovremeno moraju rukovati porezima, radnicima, građanima i budžetima. Dizajn obrasci su jezik koji omogućava inženjerima da kažu: "Koristit ćemo Singleton za bazu" umjesto objašnjavanja koda 15 minuta.
📊 Klasifikacija Obrazaca
| Kategorija | Svrha u Javnoj Upravi | Patterni |
|---|---|---|
| Creational (Kreativni) | Kako ispravno instancirati i "stvoriti" kompleksne državne biltene, forme i jedinstvene servise. | Singleton, Factory, Builder |
| Structural (Strukturni) | Kako svezati "prastari" IT sistem katastra i novi web portal zajedno kroz standardne "konektore". | Adapter, Decorator, Facade |
| Behavioral (Ponašajni) | Kako obavijestiti sto različitih odsjeka (bolnice, poreznu, sudove) kad se desi promjena adrese. | Observer, Strategy, Command |
✨ Creational Patterns - Kako kreirati strukture
Osnovna namjena im je optimizacija i sigurnost procesa instanciranja klasa klasičnom
new Klasa() sintaksom.
1. Singleton Pattern - Vladina Konfiguracija
Problem: Vaša aplikacija za e-Katastar mora komunicirati s CIK-ovom bazom. Server prima
hiljade zahtjeva. Ako za svakog korisnika aplikacija ponovo kreira proces otvaranja, očitavanja
konfiguracije o taksama, radnom vremenu servisa i API ključevima, server će usporiti i srušiti se.
Rješenje: Singleton garantuje da postoji APSOLUTNO JEDNA (i jedina) instanca objekta
koja se dijeli svim korisnicima aplikacije u memoriji na cijelom serveru.
public sealed class EUpravaKonfiguracija
{
private static EUpravaKonfiguracija _instance = null;
private static readonly object _lock = new object();
// Ključni podaci sistema (Učitani iz baze SAA)
public string GlavniTrekorRacun { get; private set; }
public decimal TaksaZaElektronskeUsluge { get; private set; }
public bool SistemURezimOdrzavanja { get; private set; }
// Privatni konstruktor - niko izvana ne može napisati 'new EUpravaKonfiguracija()'
private EUpravaKonfiguracija()
{
// Simulacija učitavanja skupe baze (Treba sekunda vremena)
GlavniTrekorRacun = "338-900-220-449-1122";
TaksaZaElektronskeUsluge = 5.00m;
SistemURezimOdrzavanja = false;
}
// Jedna globalna ulazna tačka za sve građane i kontrolere servera!
public static EUpravaKonfiguracija Instance
{
get
{
if (_instance == null)
{
lock (_lock) // Blokira paralelan pristup iz različitih requestova
{
if (_instance == null)
_instance = new EUpravaKonfiguracija();
}
}
return _instance;
}
}
}
// U svakom kontroleru e-uprave pozivamo jednostavno:
var config = EUpravaKonfiguracija.Instance;
Console.WriteLine(config.GlavniTrekorRacun); // Neće se nikad dupla u memoriji kreirati.
2. Factory Method Pattern - Mašina za Štampanje Dokumenata
Problem: Građanin putem portala može zatražiti Rodni List, Vjenčani
list ili Uvjerenje o nekažnjavanju. Svaki od ovih zahtijeva potpuno različitu
proceduru popunjavanja i konektovanja na druge državne baze podataka. Kôd bi bio noćna mora ispunjena
ogromnim if-else blokovima prilikom generisanja onog pravog fajla na kraju.
// 1. Interfejs općenitog rješenja:
public interface IZvanicniDokument
{
void GenerisiPdf(string jmbg);
}
// 2. Specifične produkcione klase
public class RodniList : IZvanicniDokument
{
public void GenerisiPdf(string jmbg)
{
// Logika pristupa matičnim knjigama rođenih i IDDEEA bazi
Console.WriteLine($"Kreiranje izvatka iz matične knjige rođenih za {jmbg}");
}
}
public class UvjerenjeKrivicno : IZvanicniDokument
{
public void GenerisiPdf(string jmbg)
{
// Logika pristupa bazi MUP-a i sudskim registrima
Console.WriteLine($"Provjera krivične evidencije sudova za {jmbg}");
}
}
// 3. FACTORY - "Šalterski" sistem koji odlučuje i dispečeruje zahtjev!
public class DokumentFactory
{
public static IZvanicniDokument KreirajDokument(string vrstaDokumenta)
{
return vrstaDokumenta.ToUpper() switch
{
"RODNI_LIST" => new RodniList(),
"KRIVICNO_UVJERENJE" => new UvjerenjeKrivicno(),
"VJENCANI_LIST" => new VjencaniList(),
_ => throw new ArgumentException("Općina ne izdaje ovaj dokument.")
};
}
}
// KORIŠTENJE: API endpoint samo primi string i JMBG, i zove factory!
IZvanicniDokument dokument = DokumentFactory.KreirajDokument("RODNI_LIST");
dokument.GenerisiPdf("1208990150011");
3. Builder Pattern - Javne Nabavke
Problem: Dosje o Javnoj Nabavci je kompleksan. Može, ali ne mora imati
komisije, bankovne garancije, certifikate, žalbe, i tehničku specifikaciju na preko 100 stranica.
Praviti new JavnaNabavka(..., ..., ..., ..., null, null, null, ...) je potpuno haotično.
public class JavnaNabavkaTender
{
public string NazivProjekta { get; set; }
public decimal ProcijenjenaVrijednost { get; set; }
public bool TraziBankovnuGaranciju { get; set; }
public string Komisija { get; set; }
public int RokIsporukeDani { get; set; }
}
// BUILDER
public class TenderBuilder
{
private JavnaNabavkaTender _tender = new JavnaNabavkaTender();
public TenderBuilder PostaviNaziv(string naziv)
{
_tender.NazivProjekta = naziv;
return this; // fluent interface
}
public TenderBuilder SaBudzetom(decimal budzet)
{
_tender.ProcijenjenaVrijednost = budzet;
return this;
}
public TenderBuilder DodajGaranciju()
{
_tender.TraziBankovnuGaranciju = true;
return this;
}
public JavnaNabavkaTender IzgradiTender()
{
if(string.IsNullOrEmpty(_tender.NazivProjekta))
throw new Exception("Tender nema naziv!");
return _tender;
}
}
// Lako čitanje za inženjera, potpuno jasno šta se kreira
var tenderZaAsfalt = new TenderBuilder()
.PostaviNaziv("Asfaltiranje Ulice Maršala Tita 2026")
.SaBudzetom(300000m)
.DodajGaranciju() // Jasno na prvu!
.IzgradiTender();
🧱 Structural Patterns - Konekcije Sistema
1. Adapter Pattern - Modernizacija starih Uprava
Problem: Katastarske knjige (zemljišno knjižni izvadci) su na starom Mainframe IBM serveru iz 1998. godine. Vraćaju podatke isključivo u XML formatu. Novi web portal za građane i investitore očekuje isključivo brzoleteći JSON API format. Moramo napraviti spregu ove dvije strane.
// Stari katastar daje prastari XML
public class StariZkKatastar
{
public string PreuzmiSveParceleXml(int opstinaId)
{
return "<parcele><parcela kbr='345'><vlasnik>Država BiH</vlasnik></parcela></parcele>";
}
}
// Novi Portal želi JSON
public interface INoviKatastarskiPortal
{
string DohvatiParceleJson(int opstinaId);
}
// ADAPTER: Stoji na sredini
public class KatastarAdapter : INoviKatastarskiPortal
{
private readonly StariZkKatastar _stariSistem;
public KatastarAdapter(StariZkKatastar stari)
{
_stariSistem = stari;
}
public string DohvatiParceleJson(int opstinaId)
{
string siroviXml = _stariSistem.PreuzmiSveParceleXml(opstinaId);
// Logika pretvaranja u lagani REST JS objekat
var xmlElementi = XDocument.Parse(siroviXml);
// ... C# konverzija ...
return JsonSerializer.Serialize(new { Vlasnik = "Država BiH", Broj = "345" });
}
}
2. Decorator Pattern - Nadodavanje sigurnosti i zaštita JMBG
Omogućava dinamičko lijepljenje novih mogućnosti funkcionalnosti. U slučaju pristupa ličnim podacima građana: Ili pravite pristup, Ili pristup sa zapisivanjem pristupa, Ili pristup sa snažnom dvostrukom provjerom!
public interface IGrađanskiServis
{
string DohvatiPodatkeOsobe(string jmbg);
}
// Osnova - Samo ide u bazu i čita.
public class BazniNacionalniServis : IGrađanskiServis
{
public string DohvatiPodatkeOsobe(string jmbg) => "{ ime: 'Sanja', prebivaliste: 'Sarajevo' }";
}
// Decorator 1: Praćenje i LOGOVANJE zbog Zakona o zaštiti ličnih podataka
public class AuditLogDecorator : IGrađanskiServis
{
private readonly IGrađanskiServis _inner;
public AuditLogDecorator(IGrađanskiServis inner) { _inner = inner; }
public string DohvatiPodatkeOsobe(string jmbg)
{
File.AppendAllText("pristupi.log", $"[!] U {DateTime.Now} pristupljeno dosijeu JMBG: {jmbg}\n");
return _inner.DohvatiPodatkeOsobe(jmbg); // PROSJEĐUJE POZIV!
}
}
// Decorator 2: Autorizacija - ima li korisnik pristup?
public class SecurityCheckDecorator : IGrađanskiServis
{
private readonly IGrađanskiServis _inner;
public SecurityCheckDecorator(IGrađanskiServis inner) { _inner = inner; }
public string DohvatiPodatkeOsobe(string jmbg)
{
if(!TrenutniKorisnik.ImaDozvolu("TAJNI_PODACI"))
throw new Exception("Neovlašten pristup bazi CIPS-a!");
return _inner.DohvatiPodatkeOsobe(jmbg);
}
}
// Kada pokrećemo web aplikaciju, dekorišemo je kao BABUŠKE!
IGrađanskiServis cipsAPI = new BazniNacionalniServis();
cipsAPI = new AuditLogDecorator(cipsAPI);
cipsAPI = new SecurityCheckDecorator(cipsAPI);
// Kada pozovemo DohvatiPodatke, sistem ĆE PRVO ići u Security, pa u AuditLog, pa tek u Bazu!
string info = cipsAPI.DohvatiPodatkeOsobe("1234");
3. Facade Pattern - Objedinjeni Šalter
Tipičan problem BiH administracije je kada građanin otvara kompaniju. Registracija te firme radi putem
Poreske Uprave (ID broj), Sudskog Registra (Rješenje), Zavoda za Statistiku i raznih obavijesti. Da
građanin (ili web portal) to
ne mora rješavati u 20 koraka koda, kreira se FACADE (Fasada) koja "krije" haos pozadi
u vidu samo jedne jednostavne funkcije:
OtvoriPreduzece("Moja Firma doo");.
🚦 Behavioral Patterns - Slanje poruka i algoritmi
1. Observer Pattern - Registar Promjena Adrese
Kada se građanin putem e-Servisa preseli iz općine Ilidža u općinu Novi Grad (MUP CIPS), u trenutku spremanja te jedne promjene aplikacija mora poslati okidač mnogim institucijama da ga uključe/skinu s budžeta i evidencija.
// 1. Interfejs promatrača
public interface IPrebivalistePromatrac
{
void NaPromjenuAdreseEvent(string jmbg, string novaOpstina);
}
// 2. Državne Službe koje ČEKAJU događaj
public class PoreznaUpravaObserver : IPrebivalistePromatrac
{
public void NaPromjenuAdreseEvent(string jmbg, string novaO) =>
Console.WriteLine($"POREZNA: Premještam poreski karton u poslovnicu {novaO}");
}
public class CikIzboriObserver : IPrebivalistePromatrac
{
public void NaPromjenuAdreseEvent(string jmbg, string novaO) =>
Console.WriteLine($"CIK: Biračko mjesto građanina {jmbg} promijenjeno!");
}
// 3. Glavni Sistemski MUP HUB (Izdavač poruke)
public class SistemPrebivaliste
{
private List<IPrebivalistePromatrac> _pretplatnici = new();
public void DodajOsluskivaca(IPrebivalistePromatrac o) => _pretplatnici.Add(o);
public void ZavediPromjenuMup(string jmbg, string opstina)
{
Console.WriteLine($"-- MUP Ažurirao Centralnu Bazu za {jmbg} --");
// OKIDAČ: Sve zainteresovane obavjesti!
foreach(var sluzba in _pretplatnici)
sluzba.NaPromjenuAdreseEvent(jmbg, opstina);
}
}
2. Strategy Pattern - Plaćanje komunalija i zakupa
Građanin koji ima obavezu rente od općine može to platiti na desetine načina - klasičnim e-bankingom, kreditnom karticom direktno na portalu države, ili klasičnom papirnom uplatnicom sa barkodom kojeg ponese na šalter pošte.
public interface INacinPlacanjaTakse
{
bool IzvrsiPlacanje(decimal iznos, string referenca);
}
public class VizaKarticaNaplata : INacinPlacanjaTakse
{
public bool IzvrsiPlacanje(decimal iznos, string r) => true; // Spaja se na Stripe Ccard
}
public class GenerisiUplatnicuBarkod : INacinPlacanjaTakse
{
public bool IzvrsiPlacanje(decimal iznos, string r)
{
Console.WriteLine("Kreiran PDF sa barkodom. Idi u banku.");
return true;
}
}
// Web Aplikacija tokom 'Kase' samo proslijedi state. Niti jedan IF!
public class SalterKasa
{
public void NaplatiDug(decimal ukupanDug, string ugovor, INacinPlacanjaTakse strategijaPlacanja)
{
strategijaPlacanja.IzvrsiPlacanje(ukupanDug, ugovor);
}
}
3. Command Pattern - Praćenje Administrativnih Rješenja
Kada sud ili visoko vijeće donesu nalog za smjenu ministra ili odobrenje velikog ugovora, u bazi to nije jednostavna `UPDATE` izmjena. Potez može biti predmet sumnje za tri mjeseca zašto je to odobreno. Command obrazac generiše puni entitet te administrativne narědbe koji se pohranjuje u Bazi, sadrži ko ga je izdao, kako glasi, a po rješenju Suda omogućava momentalni UNDO efekt na prošlost.
🎯 Zaključak: Kako u Upravi?
- Zakon prvenstveno - Ako zakon nalaže da postoji jedna agencija s unikatnim PIB-om (Jedinstvenim brojem), onda se arhitektura softvera MORA na to ugledati (Singleton/Factory).
- Zaštitite Podatke u Arhitekturi - Enkapsulacija spriječava miješanje ličnih podataka, a Decorator obrasci nam jamče autorizacijsku strogost.