Корисник:LuleIzOrbite/песак
° SOLID je akronim koju označava pet osnovnih principa dizajna softvera, osmišljenih kako bi objektno-orijentisani dizajn bio razumljiviji, fleksibilniji i lakši za održavanje. Ovi principi su podskup mnogih principa promovisanih od strane američkog softverskog inženjera i instruktora Roberta S. Martina, poznatijeg kao "Ujka Bob" (engl. "Uncle Bob") [1][2][3], prvi put predstavljene u njegovom radu iz 2000. godine pod nazivom Principi dizajna i dizajnerski obrasci koji se bave softverskom buđi.
Akronim SOLID je kasnije uveden oko 2004. godine od strane Majkla Federsa[4].
Iako se SOLID principi odnose na bilo koji objektno-orijentisani dizajn, takođe mogu da predstavljaju osnovnu filozofiju za metodologije poput agilnog razvoja ili adaptivnog softverskog razvoja.
Ideje koje SOLID obuhvata su:
Princip jedinstvene odgovornosti
[уреди | уреди извор](engl. Single Responsibility Principle): "Nikada ne bi trebalo da postoji više od jednog razloga za promenu klase."[5] Drugim rečima, svaka klasa treba da ima samo jednu odgovornost.
U ovom primeru, napisanom u javi programerskom jeziku, klasa Kalkulator
u krši principe jedinstvene odgovornosti jer osim toga što se bavi logikom sabiranja dva broja, takođe se brine o unosu i ispisu podataka.
public class Kalkulator {
public void saberi() {
Scanner scanner = new Scanner(System.in);
System.out.println("Unesite broj a");
int a = scanner.nextInt();
System.out.println("Unesite broj b");
int b = scanner.nextInt();
System.out.println("Vrednost sabiranja broja " + a + " i broja b " + b + " je " + (a + b));
}
}
Da bi se klasa više pridržavala principu, može se refaktorisati u nešto nalik ovome
public class Kalkulator {
public int saberi(int a, int b) {
return a+b;
}
}
Sada klasa Kalkulator
je odgovorna samo za datu logiku sabiranja dva broja, dok bi se za: unos, ispis itd... brinula neka druga klasa.
Otvoreno/Zatvoren Princip
[уреди | уреди извор](engl. Open/Closed Principle): "Softverski entiteti bi trebalo da budu zatvoreni za modifikaciju, ali otvoreni za proširivanje."[6]
U ovom primeru, u slučaju da postoji potreba za dodavanjem novog načina plaćanja gotovinom, kod bi morao da se doda direktno u klasu ProcesorPlacanja
čime se rizikuje promena već definisanog ponašanja, tj. uvođenje baga.
public class ProcesorPlacanja {
public void obradiPlacanje(String tipPlacanja, double iznos) {
switch (tipPlacanja) {
case "KreditnaKartica":
obradiPlacanjeKreditnomKarticom(iznos);
break;
case "PayPal":
obradiPlacanjePayPalom(iznos);
break;
case "Bitcoin":
obradiPlacanjeBitcoinom(iznos);
break;
}
}
private void obradiPlacanjeKreditnomKarticom(double iznos) {
// Logika za plaćanje kreditnom karticom
}
private void obradiPlacanjePayPalom(double iznos) {
// Logika za plaćanje PejPalom
}
private void obradiPlacanjeBitcoinom(double iznos) {
// Logika za plaćanje Bitkoinom
}
}
Izvlačenjem interfejsa ProcesorPlacanja
i klasa koje ga nasleđuju PlacanjeKreditnomKarticomPlacanjePayPalom
PlacanjeBitcoinom
omogućava se lakše proširenje funkcionalnosti plaćanja, bez ikakvog uticaja na ostale implementacije.
public interface ProcesorPlacanja {
void obradiPlacanje(double iznos);
}
class PlacanjeKreditnomKarticom implements ProcesorPlacanja {
@Override
public void obradiPlacanje(double iznos) {
// Logika za plaćanje kreditnom karticom
}
}
class PlacanjePayPalom implements ProcesorPlacanja {
@Override
public void obradiPlacanje(double iznos) {
// Logika za plaćanje PejPalom
}
}
class PlacanjeBitcoinom implements ProcesorPlacanja {
@Override
public void obradiPlacanje(double iznos) {
// Logika za plaćanje Bitkoin
}
}
Liskov princip zamene
[уреди | уреди извор]
(engl. Liskov Substitution Principle): "Ako je klasa B potklasa klase A, prosleđivanje klase B programu koji očekuje klasu A ne sme izazvati neočekivano ili nedefinisano ponašanje"[7]. Dakle u programu koji rukovodi klasama tipa B koje nasleđuju klasu A, ne sme se zadesiti da se nadja klasa koja nasleđuje klasu A, a ne ispunjava sve njene funckionalnosti.
U narednom primeru postoji abstraktnu klasa Vozilo
koju nasleđuju klase Auto
i Bicikl
i abstraktne funkcije zaustaviMotor()
i pokreniMotor()
. Pošto Bicikl
nema motor, to dovodi do neočekivanog ponašanja sa programom koji bi koristio funkcije zaustaviMotor()
i pokreniMotor()
od klase Vozilo
.
abstract class Vozilo {
abstract public void idiNapred();
abstract public void pokreniMotor();
abstract public void zaustaviMotor();
abstract public int brojTockova();
}
class Auto extends Vozilo {
@Override
public void idiNapred() {
//Logika za vožnju
}
@Override
public void pokreniMotor() {
// Logika za pokretanje motora auta
}
@Override
public void zaustaviMotor() {
// Logika za zaustavljanje motora auta
}
@Override
public int brojTockova() {
return 4;
}
}
class Bicikl extends Vozilo {
@Override
public void idiNapred() {
//Logika za vožnju
}
@Override
public void pokreniMotor() {
throw new RuntimeException("Bicikl nema motor");
}
@Override
public void zaustaviMotor() {
throw new RuntimeException("Bicikl nema motor");
}
@Override
public int brojTockova() {
return 2;
}
}
Jedno od mogućih rešenja je da se od klase Vozilo
izdvoji druga abstraktna klasaVoziloSaMotorom
koja će nasleđivati klasu Vozilo
i da se tu dodaju funckije pokreniMotor()
i zaustaviMotor()
, dok bi se u Vozilo
nalazile funkcije idiNapred()
i brojTockova()
. Klasu Vozilo
bi nasleđivao Bicikl
, dok bi klasu VoziloSaMotorom
nasleđivao Auto
.
abstract class Vozilo {
abstract public void idiNapred();
abstract public int brojTockova();
}
abstract class VoziloSaMotorom extends Vozilo{
abstract public void pokreniMotor();
abstract public void zaustaviMotor();
}
class Auto extends VoziloSaMotorom {
@Override
public void idiNapred() {
//Logika za vožnju
}
@Override
public void pokreniMotor() {
// Logika za pokretanje motora auta
}
@Override
public void zaustaviMotor() {
// Logika za zaustavljanje motora auta
}
@Override
public int brojTockova() {
return 4;
}
}
class Bicikl extends Vozilo {
@Override
public void idiNapred() {
//Logika za vožnju
}
@Override
public int brojTockova() {
return 2;
}
}
Princip segregacije interfejsa
[уреди | уреди извор](eng.l Interface Segregation Principle): "Klijenti ne bi trebalo da budu prisiljeni da zavise od interfejsa koje ne koriste."[8]
U ovom primeru KorisnickiServis
je primoran da implementira i funkcije izvrsiUplatu()
izvrsiIsplatu()
promeniPin(int noviPIN)
čak iako klijenta zanima samo trenutno stanje računa.
interface BankomatServis {
void izvrsiUplatu(double iznos);
void izvrsiIsplatu(double iznos);
void prikaziStanjeRacuna();
void promeniPIN(int noviPIN);
}
class KorisnickiServis implements BankomatServis {
@Override
public void izvrsiUplatu(double iznos) {
}
@Override
public void izvrsiIsplatu(double iznos) {
}
@Override
public void prikaziStanjeRacuna() {
// Logika za prikazivanje računa
}
@Override
public void promeniPIN(int noviPIN) {
}
}
class UpravljackiServus implements BankomatServis {
@Override
public void izvrsiUplatu(double iznos) {
// Logika za uplatu
}
@Override
public void izvrsiIsplatu(double iznos) {
// Logika za isplatu
}
@Override
public void prikaziStanjeRacuna() {
// Logika za prikaz stanja računa
}
@Override
public void promeniPIN(int noviPIN) {
// Logika za PIN
}
}
Da bi ovaj primer bio prilagoðen principu segregacije interfejsa, može se izdvojiti još jedan interfejs od BankomatServis
, tako da klijenti koriste samo ono što im je potrebno.
interface UpravljackiBankomatServis {
void izvrsiUplatu(double iznos);
void izvrsiIsplatu(double iznos);
void promeniPIN(int noviPIN);
}
interface InformacionaiBankomatServis {
void prikaziStanjeRacuna();
}
class KorisnickiServis implements InformacionaiBankomatServis {
@Override
public void prikaziStanjeRacuna() {
// Logika za prikazivanje računa
}
}
class UpravljackiServis implements UpravljackiBankomatServis, InformacionaiBankomatServis {
@Override
public void izvrsiUplatu(double iznos) {
// Logika za uplatu
}
@Override
public void izvrsiIsplatu(double iznos) {
// Logika za isplatu
}
@Override
public void promeniPIN(int noviPIN) {
// Logika za PIN
}
@Override
public void prikaziStanjeRacuna() {
// Logika za prikazivanje stanja računa
}
}
Princip inverzije zavisnosti
[уреди | уреди извор](engl. Dependency Inversion Principle): "Klijenti treba da zavise od apstrakcija, ne od konkretnih implementacija." [9] Ako klasa A u sebi sadrži klasu B, klasa A zavisi od klase B.
Ovaj princip govori o tome da klasa B ne bi trebalo da bude konkretno neka implementacija, već neka vrste apstrakcije. Što omogućava veću modularnost programa i lakšu zamenu implementacija u slučaju da dođe do potrebe za tim. Ovaj princip je poznat po tome što se često koristi u potrebi testiranja softvera, jer omogućava lakšu zamenu pravih implementacija sa lažnim, za potrebe testiranja.
U ovom primeru ako bi programer želeo da zameni izvor korisnika, iz baze podataka u udaljeni izvor, to bi mu bilo otežano činjenicom da klasa KorisnikServis
u sebi sadrzi konkretnu implementaciju KorisnikBazaPodataka
.
class Korisnik {
private String ime;
private String prezime;
long id;
public Korisnik(String ime, String prezime, int id) {
this.ime = ime;
this.prezime = prezime;
this.id = id;
}
@Override
public String toString() {
return "Korisnik{" +
"ime='" + ime + '\'' +
", prezime='" + prezime + '\'' +
", id=" + id +
'}';
}
}
class KorisnikServis {
KorisnikBazaPodataka korisnikBazaPodataka = new KorisnikBazaPodataka();
public void iscitajKorisnika() {
System.out.println(korisnikBazaPodataka.dobijKorisnika(1234).toString());
}
}
class KorisnikBazaPodataka {
public Korisnik dobijKorisnika(int id) {
// Logika za čitanje korisnika iz baze
}
}
class KorisnikUdaljeniIzvoraPodataka {
public Korisnik dobijKorisnika(int id) {
// Logika za dobijanje korisnika sa nekog udaljenog izvora podataka
}
}
Da bi se to izbeglo može da se uvede interfejs KorisnikIzvorPodataka
koji sadrži funkciju dobijKorisnika(int id)
koje će klase KorisnikUdaljeniIzvoraPodataka
i KorisnikBazaPodataka
da implementiraju, a klasa KorisnikServis
da zavisi od njega.
class Korisnik {
private String ime;
private String prezime;
long id;
public Korisnik(String ime, String prezime, int id) {
this.ime = ime;
this.prezime = prezime;
this.id = id;
}
@Override
public String toString() {
return "Korisnik{" +
"ime='" + ime + '\'' +
", prezime='" + prezime + '\'' +
", id=" + id +
'}';
}
}
class KorisnikServis {
KorisnikIzvorPodataka korisnikBazaPodataka;
public KorisnikServis(KorisnikIzvorPodataka korisnikIzvorPodataka) {
this.korisnikBazaPodataka = korisnikIzvorPodataka;
}
public void iscitajKorisnika() {
System.out.println(korisnikBazaPodataka.dobijKorisnika(1234).toString());
}
}
interface KorisnikIzvorPodataka {
public Korisnik dobijKorisnika(int id);
}
class KorisnikBazaPodataka implements KorisnikIzvorPodataka {
public Korisnik dobijKorisnika(int id) {
// Logika za čitanje korisnika iz baze
}
}
class KorisnikUdaljeniIzvoraPodataka implements KorisnikIzvorPodataka {
public Korisnik dobijKorisnika(int id) {
// Logika za dobijanje korisnika sa nekog udaljenog izvora podataka
}
}
Ovim postupkom se može jednostavno zameniti izvor podataka u klasi KorisnikServis
, i dodati drugi ako ima potrebe za tim.
Vidi takođe
[уреди | уреди извор]
Reference
[уреди | уреди извор]- ^ Martin, Robert C. „Principles Of OOD”. ButUncleBob.com. Архивирано из оригинала 10. 9. 2014. г. Приступљено 2014-07-17. . (Referenca se odnosi na "the first five principles", akronim SOLID nije korišćen u ovom izvoru.) Datira jos
- ^ Martin, Robert C. (13. 2. 2009). „Getting a SOLID start”. Uncle Bob Consulting LLC (Google Sites). Архивирано из оригинала 17. 9. 2013. г. Приступљено 2013-08-19.
- ^ Metz, Sandi (мај 2009). „SOLID Object-Oriented Design”. YouTube. Архивирано из оригинала 2021-12-21. г. Приступљено 2019-08-13. Govor održan 2009. godine na Gotam Ruby Konferenciji.
- ^ Martin, Robert (2018). Clean Architecture: A Craftsman's Guide to Software Structure and Design. стр. 58. ISBN 9780134494166.
- ^ „Single Responsibility Principle” (PDF). objectmentor.com. Архивирано из оригинала 2. 2. 2015. г.
- ^ https://www.cs.utexas.edu/users/downing/papers/OCP-1996.pdf
- ^ https://www.cs.utexas.edu/users/downing/papers/LSP-1996.pdf
- ^ https://condor.depaul.edu/dmumaugh/OOT/Design-Principles/isp.pdf
- ^ https://www.engr.mun.ca/~theo/Courses/sd/5895-downloads/sd-principles-4.ppt.pdf