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

Корисник:ЛулеИзОрбите/песак

С Википедије, слободне енциклопедије

° СОЛИД је акроним коју означава пет основних принципа дизајна софтвера, осмишљених како би објектно-оријентисани дизајн био разумљивији, флексибилнији и лакши за одржавање. Ови принципи су подскуп многих принципа промовисаних од стране америчког софтверског инжењера и инструктора Роберта С. Мартина, познатијег као "Ујка Боб" (енгл. "Унцле Боб") [1][2][3], први пут представљене у његовом раду из 2000. године под називом Принципи дизајна и дизајнерски обрасци који се баве софтверском буђи.

Акроним СОЛИД је касније уведен око 2004. године од стране Мајкла Федерса[4].

Иако се СОЛИД принципи односе на било који објектно-оријентисани дизајн, такође могу да представљају основну филозофију за методологије попут агилног развоја или адаптивног софтверског развоја.

Идеје које СОЛИД обухвата су:

Принцип јединствене одговорности[уреди | уреди извор]

(енгл. Сингле Респонсибилитy Принципле): "Никада не би требало да постоји више од једног разлога за промену класе."[5] Другим речима, свака класа треба да има само једну одговорност.

У овом примеру, написаном у јави програмерском језику, класа Kalkulator у крши принципе јединствене одговорности јер осим тога што се бави логиком сабирања два броја, такође се брине о уносу и испису података.

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));

    }
}

Да би се класа више придржавала принципу, може се рефакторисати у нешто налик овоме

public class Kalkulator {

    public int saberi(int a, int b) {
       return a+b;
    }
}

Сада класа Kalkulator је одговорна само за дату логику сабирања два броја, док би се за: унос, испис итд... бринула нека друга класа.

Отворено/Затворен Принцип[уреди | уреди извор]

(енгл. Опен/Цлосед Принципле): "Софтверски ентитети би требало да буду затворени за модификацију, али отворени за проширивање."[6]

У овом примеру, у случају да постоји потреба за додавањем новог начина плаћања готовином, код би морао да се дода директно у класу ProcesorPlacanja чиме се ризикује промена већ дефинисаног понашања, тј. увођење бага.

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
    }
}

Извлачењем интерфејса ProcesorPlacanja и класа које га наслеђују PlacanjeKreditnomKarticomPlacanjePayPalom PlacanjeBitcoinom омогућава се лакше проширење функционалности плаћања, без икаквог утицаја на остале имплементације.

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
    }
}

Лисков принцип замене[уреди | уреди извор]

Барбара Лисков
Барбара Лисков 2010. године, по којој је принцип добио име

(енгл. Лисков Субститутион Принципле): "Ако је класа Б поткласа класе А, прослеђивање класе Б програму који очекује класу А не сме изазвати неочекивано или недефинисано понашање"[7]. Дакле у програму који руководи класама типа Б које наслеђују класу А, не сме се задесити да се надја класа која наслеђује класу А, а не испуњава све њене фунцкионалности.

У наредном примеру постоји абстрактну класа Vozilo коју наслеђују класе Auto и Bicikl и абстрактне функције zaustaviMotor() и pokreniMotor(). Пошто Bicikl нема мотор, то доводи до неочекиваног понашања са програмом који би користио функције zaustaviMotor() и pokreniMotor() од класе 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;
    }
}

Једно од могућих решења је да се од класе Vozilo издвоји друга абстрактна класаVoziloSaMotorom која ће наслеђивати класу Vozilo и да се ту додају фунцкије pokreniMotor() и zaustaviMotor(), док би се у Vozilo налазиле функције idiNapred() и brojTockova(). Класу Vozilo би наслеђивао Bicikl, док би класу VoziloSaMotorom наслеђивао 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;
    }
}

Принцип сегрегације интерфејса[уреди | уреди извор]

(енг.л Интерфаце Сегрегатион Принципле): "Клијенти не би требало да буду присиљени да зависе од интерфејса које не користе."[8]

У овом примеру KorisnickiServis је приморан да имплементира и функције izvrsiUplatu()izvrsiIsplatu()promeniPin(int noviPIN) чак иако клијента занима само тренутно стање рачуна.

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
    }
}

Да би овај пример био прилагоðен принципу сегрегације интерфејса, може се издвојити још један интерфејс од BankomatServis, тако да клијенти користе само оно што им је потребно.

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
    }
}

Принцип инверзије зависности[уреди | уреди извор]

(енгл. Депенденцy Инверсион Принципле): "Клијенти треба да зависе од апстракција, не од конкретних имплементација." [9] Ако класа А у себи садржи класу Б, класа А зависи од класе Б.

Депенденцy_инверсион

Овај принцип говори о томе да класа Б не би требало да буде конкретно нека имплементација, већ нека врсте апстракције. Што омогућава већу модуларност програма и лакшу замену имплементација у случају да дође до потребе за тим. Овај принцип је познат по томе што се често користи у потреби тестирања софтвера, јер омогућава лакшу замену правих имплементација са лажним, за потребе тестирања.

У овом примеру ако би програмер желео да замени извор корисника, из базе података у удаљени извор, то би му било отежано чињеницом да класа KorisnikServis у себи садрзи конкретну имплементацију 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
    }
}

Да би се то избегло може да се уведе интерфејс KorisnikIzvorPodataka који садржи функцију dobijKorisnika(int id) које ће класе KorisnikUdaljeniIzvoraPodataka и KorisnikBazaPodataka да имплементирају, а класа KorisnikServis да зависи од њега.

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
    }
}

Овим поступком се може једноставно заменити извор података у класи KorisnikServis, и додати други ако има потребе за тим.

Види такође[уреди | уреди извор]

 

Референце[уреди | уреди извор]

  1. ^ Мартин, Роберт C. „Принциплес Оф ООД”. БутУнцлеБоб.цом. Архивирано из оригинала 10. 9. 2014. г. Приступљено 2014-07-17. . (Референца се односи на "тхе фирст фиве принциплес", акроним СОЛИД није коришћен у овом извору.) Датира јос
  2. ^ Мартин, Роберт C. (13. 2. 2009). „Геттинг а СОЛИД старт”. Унцле Боб Цонсултинг ЛЛЦ (Гоогле Ситес). Архивирано из оригинала 17. 9. 2013. г. Приступљено 2013-08-19. 
  3. ^ Метз, Санди (мај 2009). „СОЛИД Објецт-Ориентед Десигн”. YоуТубе. Архивирано из оригинала 2021-12-21. г. Приступљено 2019-08-13.  Говор одржан 2009. године на Готам Рубy Конференцији.
  4. ^ Мартин, Роберт (2018). Цлеан Арцхитецтуре: А Црафтсман'с Гуиде то Софтwаре Струцтуре анд Десигн. стр. 58. ИСБН 9780134494166. 
  5. ^ „Сингле Респонсибилитy Принципле” (ПДФ). објецтментор.цом. Архивирано из оригинала 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