Пређи на садржај

Корисник: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[уреди | уреди извор]

Barbara Liskov
Barbara Liskov 2010. godine, po kojoj je princip dobio ime

(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.

Dependency_inversion

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[уреди | уреди извор]

  1. ^ 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
  2. ^ Martin, Robert C. (13. 2. 2009). „Getting a SOLID start”. Uncle Bob Consulting LLC (Google Sites). Архивирано из оригинала 17. 9. 2013. г. Приступљено 2013-08-19. 
  3. ^ Metz, Sandi (мај 2009). „SOLID Object-Oriented Design”. YouTube. Архивирано из оригинала 2021-12-21. г. Приступљено 2019-08-13.  Govor održan 2009. godine na Gotam Ruby Konferenciji.
  4. ^ Martin, Robert (2018). Clean Architecture: A Craftsman's Guide to Software Structure and Design. стр. 58. ISBN 9780134494166. 
  5. ^ „Single Responsibility Principle” (PDF). objectmentor.com. Архивирано из оригинала 2. 2. 2015. г. 
  6. ^ https://www.cs.utexas.edu/users/downing/papers/OCP-1996.pdf
  7. ^ https://www.cs.utexas.edu/users/downing/papers/LSP-1996.pdf
  8. ^ https://condor.depaul.edu/dmumaugh/OOT/Design-Principles/isp.pdf
  9. ^ https://www.engr.mun.ca/~theo/Courses/sd/5895-downloads/sd-principles-4.ppt.pdf