You are on page 1of 34

Arhitektura i projektovanje

softvera
Studijski progam Računarstvo i informatika

- Principi objektno-orijentisanog
projektovanja -
Katedra za računarstvo
Elektronski fakultet u Nišu

Arhitektura i projektovanje softvera


Prof. dr Dragan Stojanović Računarstvo i informatika
Generalni principi projektovanja
Razdvajanje problema (Separation of concerns)
Podeliti aplikaciju u izdvojen skup funkcionalnosti sa što je moguće manjim
preklapanjem radi postizanja visoke kohezije i male sprege
Princip pojedinačne odgovornosti (Single responsibility
principle)
Svaka komponenta ili modul treba da bude odgovorna samo za određenu
funkcionalnost ili agregaciju srodnih funkcionalnosti
Princip najmanjeg poznavanja (Law of Demeter - LoD)
Komponenta ili objekat ne treba da znaju interne detalje drugih komponenti
ili objekata
Ne ponavljati se (Don’t repeat yourself - DRY)
Specifična funkcionalnost treba biti implementirana samo u jednoj
komponenti, a ne mulitplikovana u ostalim.
Minimizovati početni dizajn
U startu projektovati samo neophodne elemente softverskog sistema.
Ukoliko su aplikacioni zahtevi nejasni ili postoji mogućnost njihovog
menjanja sa vremenom izbeći obiman dizajn na samom početku.
Principi objektno-orijentisanog projektovanja
Prof. dr Dragan Stojanović Arhitektura i projektovanje softvera 2
Principi OO projektovanja
1. Minimizovati dostupnost klasama i njenim članovima
(private member variables/atributi - enkapsulacija)
2. Identifikovati aspekte aplikacije koji su promenljivi i
izdvojiti ih od onih koji su nepromenljivi (ili slabo
promenljivi)
3. Dati prednost kompoziciji objekata u odnosu na
nasleđivanje klasa
4. Programirati prema interfejsu (apstraktnoj klasi,
supertipu), a ne implementaciji
5. The Open-Closed Principle: Softverski entiteti treba da
budu otvoreni za proširenje, ali zatvoreni za modifikaciju
6. The Liskov Substitution Principle: Funkcije koje koriste
reference na osnovne (super) klase moraju biti sposobne
da koriste objekte izvedenih (pod) klasa bez potrebe da te
klase poznaju
Principi objektno-orijentisanog projektovanja
Prof. dr Dragan Stojanović Arhitektura i projektovanje softvera 3
1. Minimizovati dostupnost klasama
i njenim članovima
Apstrakcija i enkapsulacija
Koristiti privatne članove i odgovarajuće accessor-e i
mutator-e (set & get), a ne javne članove
Na primer:
Zameniti
public double speed;
sa
private double speed;
public double getSpeed() {
return(speed);
}
public void setSpeed(double newSpeed) {
speed = newSpeed;
}
Principi objektno-orijentisanog projektovanja
Prof. dr Dragan Stojanović Arhitektura i projektovanje softvera 4
1. Minimizovati dostupnost klasama
i njenim članovima
Koristiti set & get, a ne javne članove
Mogu se staviti ograničenja na vrednosti promenljivih
public void setSpeed(double newSpeed) {
if (newSpeed < 0) {
sendErrorMessage(...);
newSpeed = Math.abs(newSpeed);
}
speed = newSpeed;
}
Ukoliko bi korisnik vaše klase mogao da direktno pristupa
članovima – promenljivim instance, bio bi u obavezi da
implementira ova ograničenja

Principi objektno-orijentisanog projektovanja


Prof. dr Dragan Stojanović Arhitektura i projektovanje softvera 5
1. Minimizovati dostupnost klasama
i njenim članovima
Može se menjati interna reprezentacija bez menjanja
interfejsa

// Now using metric units (kph, not mph)


public void setSpeedInMPH(double newSpeed) {
speedInKPH = convert(newSpeed);
}

public void setSpeedInKPH(double newSpeed) {


speedInKPH = newSpeed;
}

Principi objektno-orijentisanog projektovanja


Prof. dr Dragan Stojanović Arhitektura i projektovanje softvera 6
1. Minimizovati dostupnost klasama
i njenim članovima
Mogu sa obaviti odgovarajući sporedni efekti

public double setSpeed(double newSpeed) {


speed = newSpeed;
notifyObservers();
}

Ukoliko bi korisnik vaše klase mogao da direktno pristupa


članovima – promenljivim instance, bio bi u obavezi da
implementira ove sporedne efekte

Principi objektno-orijentisanog projektovanja


Prof. dr Dragan Stojanović Arhitektura i projektovanje softvera 7
2. Izdvojiti aspekte koji su
promenljivi od onih koji nisu
Korišćenje nasleđivanja u
cilju reupotrebljivosti nije
pogodno za održavanje
koda (menjanje,
proširenje)
Ponašanje objekata klase
(fly, quack) nije dobro
implementirati u okviru
superklase, niti u
podklasama
Interfejsi Flyable i Quackable
takođe ne daju
zadovoljavajuće rešenje
Principi objektno-orijentisanog projektovanja
Prof. dr Dragan Stojanović Arhitektura i projektovanje softvera 8
2. Izdvojiti aspekte koji su
promenljivi od onih koji nisu
Metode fly() i quack() u klasi Duck zamenjeni su sličnim
metodama, nazvanim performFly() and performQuack();

Principi objektno-orijentisanog projektovanja


Prof. dr Dragan Stojanović Arhitektura i projektovanje softvera 9
2. Izdvojiti aspekte koji su
promenljivi od onih koji nisu

Principi objektno-orijentisanog projektovanja


Prof. dr Dragan Stojanović Arhitektura i projektovanje softvera 10
3. Dati prednost kompoziciji u
odnosu na nasleđivanje
Reupotreblivost korišćenjem nasleđivanja naziva se “reupotrebljivost
bele kutije” (“white-box reuse”)
Unutrašnji detalji super klase vidljivi su podklasama
Neophodno je razumevanje funkcionalnosti super klase
Promene u funkcionalnosti (atributima i metodama) roditeljske klase
može uticati na podklase
Prekomerno korišćenje nasleđivanja čini objektno-orijentisani softverski
sistem teškim za razumevanje, održavanje i proširenje
Alternativa u dobijanju nove funkcionalnosti je kompozicijom
objekata. Naziva se “reupotrebljivost crne kutije” (“black-box reuse”)
zato što interni detalji jednog objekta nisu vidljivi od strane objekta koji
ga sadrži/agregira
Omogućava dinamičku kompoziciju objekata u vreme izvršenja
(fleksibilnost), dok je hijerarhija dobijena nasleđivanjem statička i ne
može se rekonfigurisati u vreme izvršenja
Principi objektno-orijentisanog projektovanja
Prof. dr Dragan Stojanović Arhitektura i projektovanje softvera 11
Primer: Nasleđivanje vs kompozicija
Pretpostavite da treba projektovati varijantu HashSet koja
vodi evidenciju o broju izvedenih umetanja. Definišemo
podklasu klase HashSet kao:

public class InstrumentedHashSet extends HashSet {

// The number of attempted element insertions


private int addCount = 0;

public InstrumentedHashSet(Collection c) {super(c);}


public InstrumentedHashSet(int initCap, float loadFactor) {
super(initCap, loadFactor);
}

Principi objektno-orijentisanog projektovanja


Prof. dr Dragan Stojanović Arhitektura i projektovanje softvera 12
Primer: Nasleđivanje vs kompozicija
public boolean add(Object o) {
addCount++;
return super.add(o);
}

public boolean addAll(Collection c) {


addCount += c.size();
return super.addAll(c);
}

public int getAddCount() {


return addCount;
}
}

Principi objektno-orijentisanog projektovanja


Prof. dr Dragan Stojanović Arhitektura i projektovanje softvera 13
Primer: Nasleđivanje vs kompozicija
Izgleda dobro, a može i da se testira
public static void main(String[] args) {
InstrumentedHashSet s = new InstrumentedHashSet();
s.addAll(Arrays.asList(new String[]{"Snap","Crackle","Pop"}));
System.out.println(s.getAddCount());
}

Dobili smo rezultat 6, a ne 3 kako je očekivano. Zašto?


Zato što interna implementacija metoda addAll() u HashSet
superklasi takođe poziva add() metod. Tako smo prvo dodali 3 na
addCount u addAll() metodi InstrumentedHashSet klase.
Nakon toga je pozvan addAll() klase HashSet. Za svaki element,
metod addAll() poziva add() metod, koji kao preklopljen (overridden)
u klasi InstrumentedHashSet dodaje 1 za svaki element.
Rezultat: svaki element je brojan dvaput

Principi objektno-orijentisanog projektovanja


Prof. dr Dragan Stojanović Arhitektura i projektovanje softvera 14
Primer: Nasleđivanje vs kompozicija
Postoji nekoliko načina da se ovaj problem reši, ali dobro
demonstrira mogućnost greške prilikom nasleđivanja.
Implementacioni detalji superklase utiču na operacije podklase
Najbolji način za rešavanje ovog problema je korišćenje
kompozicije. Projektovaćemo InstrumentedSet klasu kao
kompoziciju Set objekata. Naša klasa InstrumentedSet će
duplirati Set interfejs, ali će sve operacije nad Set-om biti u
stvari prosleđene sadržanim Set objektima.
Klasa InstrumentedSet je omotač (wrapper) klasa, pošto
ona obmotava instancu Set objekta
Ovo je primer delegiranja kroz kompoziciju

Principi objektno-orijentisanog projektovanja


Prof. dr Dragan Stojanović Arhitektura i projektovanje softvera 15
Primer: Nasleđivanje vs kompozicija
public class InstrumentedSet implements Set {
private final Set s;
private int addCount = 0;

public InstrumentedSet(Set s) {this.s = s;}

public boolean add(Object o) {


addCount++;
return s.add(o);
}
public boolean addAll(Collection c) {
addCount += c.size();
return s.addAll(c);
}
public int getAddCount() {return addCount;}

Principi objektno-orijentisanog projektovanja


Prof. dr Dragan Stojanović Arhitektura i projektovanje softvera 16
Primer: Nasleđivanje vs kompozicija
// Metode za prosleđivanje (ostatak metoda Set interfejsa)
public void clear() { s.clear(); }
public boolean contains(Object o) { return s.contains(o); }
public boolean isEmpty() { return s.isEmpty(); }
public int size() { return s.size(); }
public Iterator iterator() { return s.iterator(); }
public boolean remove(Object o) { return s.remove(o); }
public boolean containsAll(Collection c)
{ return s.containsAll(c); }
public boolean removeAll(Collection c)
{ return s.removeAll(c); }
public boolean retainAll(Collection c)
{ return s.retainAll(c); }
public Object[] toArray() { return s.toArray(); }
public Object[] toArray(Object[] a) { return s.toArray(a); }
public boolean equals(Object o) { return s.equals(o); }
public int hashCode() { return s.hashCode(); }
public String toString() { return s.toString(); }
}

Principi objektno-orijentisanog projektovanja


Prof. dr Dragan Stojanović Arhitektura i projektovanje softvera 17
Primer: Nasleđivanje vs kompozicija
Zapazite sledeće:
Ova klasa je Set
Ima jedan konstruktor čiji je argument Set
Sadržani objekat klase Set može biti objekat bilo koje klase koja
implementira Set interfejs (a ne samo HashSet)
Ova klasa je veoma fleksibilna i može obmotati bilo koji postojeći Set
objekat
Primer:
List list = new ArrayList();
Set s1 = new InstrumentedSet(new TreeSet(list));
int capacity = 7;
float loadFactor = .66f;
Set s2 = new InstrumentedSet(new HashSet(capacity, loadFactor));

Principi objektno-orijentisanog projektovanja


Prof. dr Dragan Stojanović Arhitektura i projektovanje softvera 18
4. Programirati prema interfejsu, a
ne implementaciji
Nasleđivanje implementacije metoda i definicija atributa kao
objekata konkretnih klasa ...
... je jednostavan mehanizam za reupotrebljivost, ali...
uvodi visok nivo sprege
• Promene u super klasi mogu da poremete sve podklase
Nasleđivanje i implementacija interfejsa - nasleđivanje
apstraktnih klasa u C++ i interfejsa u Java-i
Definisanje podtipova i nasleđivanje interfejsa je značajnije od
nasleđivanja implementacije kroz nasleđivanje klasa
Uvek razmotriti mogućnost da tip promenljive bude interfejs
(apstraktna klasa/supertip), a ne konkretna klasa, tako da objekat
dodeljen toj promenljivoj može biti instanca bilo koje klase koja
implementira interfejs / nasleđuje apstraktnu klasu
Person pc = new Person(); // Ne preporučuje se
PersonInterface pi = new Person(); // Preporučuje se, kao i
PersonInterface pi = getPerson(); // Dodela u vreme izvršenja
Principi objektno-orijentisanog projektovanja
Prof. dr Dragan Stojanović Arhitektura i projektovanje softvera 19
4. Programirati prema interfejsu, a
ne implementaciji
Primer 1 - (Head first design patterns)
Programiranje prema implementaciji
Dog d = new Dog();
d.bark();

Programiranje prema
interfejsu/supetipu
Animal animal = new Dog();
animal.makeSound();

Dodela konkretnog objekta u vreme


izvršenja
a = getAnimal();
a.makeSound();

Principi objektno-orijentisanog projektovanja


Prof. dr Dragan Stojanović Arhitektura i projektovanje softvera 20
4. Programirati prema interfejsu, a
ne implementaciji
Primer 2 – Interfejs i implementacione klase
/**
* Interface IManeuverable provides the specification
* for a maneuverable vehicle.
*/
public interface IManeuverable {
public void left();
public void right();
public void forward();
public void reverse();
public void climb();
public void dive();
public void setSpeed(double speed);
public double getSpeed();
}

Principi objektno-orijentisanog projektovanja


Prof. dr Dragan Stojanović Arhitektura i projektovanje softvera 21
4. Programirati prema interfejsu, a
ne implementaciji
Klase koje implementiraju interfejs
public class Car
implements IManeuverable { // Code here. }
public class Boat
implements IManeuverable { // Code here. }
public class Submarine
implements IManeuverable { // Code here. }

Ovaj metod definisan u drugoj klasi može “manevrisati”


vozilom, bez poznavanja njegovog tipa - klase (car, boat,
submarine) ili u kojoj hijerarhiji nasleđivanja se nalazi
public void travel(IManeuverable vehicle) {
vehicle.setSpeed(35.0);
vehicle.forward();
vehicle.left();
vehicle.climb();
}
Principi objektno-orijentisanog projektovanja
Prof. dr Dragan Stojanović Arhitektura i projektovanje softvera 22
5. Open-Closed Principle
Open-Closed Principle (OCP) (Bertrand Meyer) - Softverski
entiteti (klase, moduli, funkcije, itd.) treba da budu otvoreni
za proširenje, a zatvoreni za modifikaciju
Ključ je u apstrakciji
Da bi proširili ponašanje i funkcionalnost sistema treba da
dodajemo novi kod, a ne modifikujemo postojeći kod
Moduli koji su u skladu sa OCP ispunjavaju dva kriterijuma:
Otvoreni za proširenje (Open For Extension) – Ponašanje i
funkcionalnost modula treba da budu proširljivi da bi se ispunili novi
zahtevi
Zatvoreni za modifikaciju (Closed For Modification) – izvorni kod
modula nije dozvoljeno menjati
Nije moguće postići da svi moduli softverskog sistema
zadovoljavaju OCP, ali treba se truditi da se minimizuje broj
modula koji ga ne zadovoljavaju
Principi objektno-orijentisanog projektovanja
Prof. dr Dragan Stojanović Arhitektura i projektovanje softvera 23
OCP primer (1)
Razmotrite sledeći metod neke klase:

public double totalPrice(Part[] parts) {


double total = 0.0;
for (int i=0; i<parts.length; i++) {
total += parts[i].getPrice();
}
return total;
}
Ako je Part superklasa ili interfejs i ako se koristi
polimorfizam tada ovaj metod može koristiti nove tipove
Part bez potrebe modifikacije
Ovaj metod je u skladu sa OCP

Principi objektno-orijentisanog projektovanja


Prof. dr Dragan Stojanović Arhitektura i projektovanje softvera 24
OCP primer (2)
Međutim ako se izvrši promena tako da za motherboard
parts i memory parts treba da se uračuna dodatna premija
kada se obračunava totalna cena.
Kako vam se čini sledeći kod? Nije u skladu sa OCP!
public double totalPrice(Part[] parts) {
double total = 0.0;
for (int i=0; i<parts.length; i++) {
if (parts[i] instanceof Motherboard)
total += (1.45 * parts[i].getPrice());
else if (parts[i] instanceof Memory)
total += (1.27 * parts[i].getPrice());
else
total += parts[i].getPrice();
}
return total;
}
Principi objektno-orijentisanog projektovanja
Prof. dr Dragan Stojanović Arhitektura i projektovanje softvera 25
OCP primer (3)
Primer klasa Part i ConcretePart - moramo menjati svaku
podklasu klase Part kad god se promeni cenovna politika
// Class Part is the superclass for all parts.
public class Part {
private double price;
public Part(double price) (this.price = price;}
public void setPrice(double price) {this.price = price;}
public double getPrice() {return price;}
}

// Class ConcretePart implements a part for sale.


// Pricing policy explicit here!
public class ConcretePart extends Part {
public double getPrice() {
// return (1.45 * price); //Premium
return (0.90 * price); //Labor Day Sale
}
} Principi objektno-orijentisanog projektovanja
Prof. dr Dragan Stojanović Arhitektura i projektovanje softvera 26
OCP primer (4)
Bolje rešenje koje zadovoljava OCP je projektovati klasu PricePolicy koja
će biti korišćena za definisanje različitih cenovnih politika
// The Part class now has a contained PricePolicy object.
public class Part {
private double price;
private PricePolicy pricePolicy;
public void setPricePolicy(PricePolicy pricePolicy) {
this.pricePolicy = pricePolicy;}
public void setPrice(double price) {this.price = price;}
public double getPrice() {return pricePolicy.getPrice(price);}
}

/** * Class PricePolicy implements a given price policy. */


public class PricePolicy {
private double factor;
public PricePolicy (double factor) {
this.factor = factor;
}
public double getPrice(double price) {return price * factor;}
}
Principi objektno-orijentisanog projektovanja
Prof. dr Dragan Stojanović Arhitektura i projektovanje softvera 27
6. Liskov Substitution Principle
Liskov Substitution Principle (LSP) – Barbara Liskov
Functions that use pointers or references to base classes must be
able to use objects of derived classes without knowing it.
LSP izgleda očigledan ukoliko poštujemo sve što znamo o
polimorfizmu
Na primer: public void drawShape(Shape s) {// Code here. }
Metod drawShape treba da radi sa bilo kojom podklasom klase
Shape (ili, ako je Shape Java interfejs, treba da radi sa svakom
klasom koja implementira Shape interfejs)
Ali moramo biti pažljivi kada implementiramo podklase da bi
obezbedili da bez namere narušimo LSP
Ako funkcija ne zadovoljava LSP, tada je moguće da
eksplicitno referencira neke ili sve podklase svoje
superklase. Takva funkcija takođe narušava OCP, pošto
mora biti modifikovana kad god se kreira nova podklasa.
Principi objektno-orijentisanog projektovanja
Prof. dr Dragan Stojanović Arhitektura i projektovanje softvera 28
6. Liskov Substitution Principle
Liskov Substitution Principle (LSP) nam govori da se IS-A
relacija odnosi samo na ponašanje (behavior)
Da bi LSP bio zadovoljen (i sa njim i OCP) sve podklase
moraju da budu u skladu sa ponašanjem koje klijenti
očekuju od osnovne klase koju koriste
Podklase ne smeju imati veća ograničenja nego njihove
osnovne klase, pošto podklase moraju biti upotrebljive gde
god se koriste osnovne klase
Ako podklase imaju veća ograničenja nego osnovne klase,
tada će postojati primene ovih klasa koje će biti validne za
osnovne klase, ali ne i za podklase, pošto će narušiti neko
od njegovih dodatnih ograničenja – time narušavajući LSP
Garancija LSP je da se podklase mogu koristiti gde god se
koristi i osnovna klasa
Principi objektno-orijentisanog projektovanja
Prof. dr Dragan Stojanović Arhitektura i projektovanje softvera 29
LSP primer (1)
Razmotrite sledeću klasu Rectangle
// A very nice Rectangle class.
public class Rectangle {
private double width;
private double height;
public Rectangle(double w, double h) {
width = w;
height = h;
}
public double getWidth() {return width;}
public double getHeight() {return height;}
public void setWidth(double w) {width = w;}
public void setHeight(double h) {height = h;}
public double area() {return (width * height);
}
Principi objektno-orijentisanog projektovanja
Prof. dr Dragan Stojanović Arhitektura i projektovanje softvera 30
LSP primer (2)
Kvadrat je četvorougao, pa klasa Square nasleđuje Rectangle
Za kvadrat nisu potrebni širina i visina, ali ih nasleđuje od klase
Rectangle
// A Square class.
public class Square extends Rectangle {
public Square(double s) {super(s, s);}
public void setWidth(double w) {
super.setWidth(w);
super.setHeight(w);
}
public void setHeight(double h) {
super.setHeight(h);
super.setWidth(h);
}
}
Principi objektno-orijentisanog projektovanja
Prof. dr Dragan Stojanović Arhitektura i projektovanje softvera 31
LSP primer (3)
Sve deluje korektno, ali razmotrite sledeću situaciju:
public class TestRectangle {
// Define a method that takes a Rectangle reference.
public static void testLSP(Rectangle r) {
r.setWidth(4.0);
r.setHeight(5.0);
System.out.println("Width is 4.0 and Height is 5.0" + ", so
Area is " + r.area());
if (r.area() == 20.0)
System.out.println("Looking good!\n");
else
System.out.println("Huh?? What kind of rectangle is
this??\n");
}
...

Principi objektno-orijentisanog projektovanja


Prof. dr Dragan Stojanović Arhitektura i projektovanje softvera 32
LSP primer (4)
Da li je narušen LSP? Odgovor: Da!
public static void main(String args[]) {
//Create a Rectangle and a Square
Rectangle r = new Rectangle(1.0, 1.0);
Square s = new Square(1.0);
// Now call the method above. According to the
// LSP, it should work for either Rectangles or
// Squares. Does it??
testLSP(r);
testLSP(s);
}
}
Izlaz iz test programa:
Width is 4.0 and Height is 5.0, so Area is 20.0
Looking good!
Width is 4.0 and Height is 5.0, so Area is 25.0
Huh?? What kind of rectangle is this??
Principi objektno-orijentisanog projektovanja
Prof. dr Dragan Stojanović Arhitektura i projektovanje softvera 33
Pročitati za sledeći čas
“Why extends is evil” - Improve your code by replacing
concrete base classes with interfaces
Allen Holub, JavaWorld.com, 08/01/2003
http://www.javaworld.com/javaworld/jw-08-2003/jw-0801-
toolbox.html?page=1

Principi objektno-orijentisanog projektovanja


Prof. dr Dragan Stojanović Arhitektura i projektovanje softvera 34

You might also like