Professional Documents
Culture Documents
OOP Predavanje 08 White
OOP Predavanje 08 White
programiranje
Objektno orijentirani koncepti:
nastavak
2
ror handling
Er
Manipulacija pogreškama
error handling
• rijetki su slučajevi kada se klasa napiše savršeno u prvoj iteraciji
• Vrlo je vjerojatno da će doći do neke vrste pogreške
• 4 načina nošenja s pogreškama:
– Ignorirati problem – nije najbolja ideja!
– Provjeriti moguće probleme i u tom slučaju izaći iz programa
– Provjeriti moguće probleme, uhvatiti pogrešku i riješiti
problem
– Generirati iznimku – često najbolji način
Ignoriranje problema
• Najgori scenarij
• Iako možda utjecaj problema i ne izgleda značajno u
odnosu na cjelokupnu funkcionalnost problema – dobra
praksa je ispraviti ga
• Efekt problema se može dalje propagirati u programu što
vodi do ozbiljnijih pogrešaka
• Osnovno pravilo je da se aplikacija nikada ne bi smjela
srušiti
• Možemo i dobivati krive rezultate bez da smo svjesni
problema
Generiranje iznimki
• Većina OO jezika omogućava generiranje iznimki
• Iznimke nam pružaju način detektiranja problema i
manipulaciju
• Ključne riječi try i catch
try{
//Potencijalno nesiguran kod
}
catch (Exception e){
//Kod za hvatanje iznimke
}
Try-catch
• U slučaju da se generira iznimka u try bloku – onda je catch blok
„uhvati”
– Izvršavanje try bloka se zaustavlja
– catch blokovi se pregledavaju kako bi se ustanovilo koji blok
hvata tu vrstu iznimke (može biti više catch blokova za isti try)
– U slučaju da na jednoj razini nema prikladnih catch blokova
iznimka se šalje na višu razinu. U slučaju da nijedan kod ne
hvata tu vrstu iznimke onda se ona šalje sustavu
– U slučaju da postoji prikladni catch blok – njegov kod se
izvršava
– Slijed programa se onda nastavlja iza try-catch bloka
Try-catch
• Različite razine pogrešaka mogu se hvatati u try-catch
bloku
• Možemo hvatati sve pogreške ili samo one specifične, npr.
aritmetičke pogreške
class Program{
static void Main(string[] args){
int brojac;
try{ Dijeljenje nulom!
//Potencijalno nesiguran kod
brojac = 0;
float value = 5 / brojac;
}
catch (ArithmeticException e){
//Kod za hvatanje iznimke
Console.WriteLine(e.ToString());
brojac = 1;
}
Console.WriteLine("Uspjesno rukovanje iznimkom");
Console.ReadLine();
}
}
Try-catch
public class Program{
public static void Main(string[] args){
int brojac;
try{
//Potencijalno nesiguran kod
brojac = 0;
float value = 5 / brojac;
}
catch (DivideByZeroException e){
Console.WriteLine(e.ToString() );
}
catch (ArithmeticException e){
Console.WriteLine(e.ToString());
}
}
}
Try-catch
using namespace std;
int main(){
int brojac;
try {
//Potencijalno nesiguran kod
brojac = 0;
float value = 5 / brojac;
}
catch (std::overflow_error e) {
//Kod za hvatanje iznimke
cout << e.what();
brojac = 1;
}
cout << "Uspjesno rukovanje iznimkom";
}
Iz dokumentacije za C++: „If the second operand of / or % is zero, the
behavior is undefined.”
Objektno orijentirano programiranje
13
std:bad_alloc std:domain_error
std:bad_cast std:invalid_argument
std:bad_typeid std:length_error
std:bad_exception std:out_of_range
std:logic_failure
std:runtime_error std:overflow_error
std:range_error
std:underflow_error
Try-catch
divide by zero
0
x=5
y=0
z=0
try:
z = x/y
except ZeroDivisionError:
print("divide by zero")
print(z)
s design u C++
Error clas
double result;
...
return 0;
}
Error
+printMessage:void
+printMessage:void
Arithmetic
+printMessage:void
DivByZero BadOperator
+printMessage:void +printMessage:void
try{
result = math(oper, data1, data2);
cout << "rezultat: " << result << endl;
} catch(Error& error){
error.printMessage();
return 100;
}
return 0;
}
Dizajn klasa
• Osnovni cilj OO programiranja je modeliranje stvarnog
svijeta na način kako ljudi prirodno razmišljaju – u
okvirima objekata
• Kada dizajniramo klasu potrebno je ponašanje predstaviti
na način kako se stvarno u svijetu i odvija
• Jedna od najčešćih pogrešaka je dizajn klasa koje imaju
ponašanje, ali nemaju podatke
• Dizajn interfejsa
– Minimalni mogući interfejs čini klasu konciznom i
„čistom”
– Uključiti korisnika u definiranje dizajna od samog
početka
Objektno orijentirano programiranje
24
Dizajn klasa
• Robusni konstruktori i destruktori
– Destruktor prilikom završetka života objekta treba
osloboditi memoriju koju je objekt alocirao
– U protivnom dolazi do memory leak-a
• Uključiti error-handling u samu klasu
• Dokumentiranje klase i korištenje komentara
– Dobra praksa
– Potrebno je uložiti vrijeme
– Izrazito bitno kod većih projekata i za rad u timovima
• Stvaranje objekata imajući na umu njihovu suradnju
Komentari
• Važno je kvalitetno dokumentirati klasu i kroz korištenje
komentara
• Ponekad je potrebno detaljnije opisati funkcionalnost
pojedinih metoda ako iz imena nije lako zaključiti kako se
ona koristi
• Većina OO jezika ima dvije standardne vrste komentara
/* komentar u više
linija (moramo paziti da zatvorimo komentar */
null
• Null predstavlja vrijednost ništa
• Može biti korisno u programiranju
• Provjerom je li neka varijabla ili objekt null doznajemo je
li postavljena vrijednost
• Varijable/objekta možemo postaviti na null prije nego
imamo neku smislenu vrijednost (npr. prije nego korisnik
upiše neki traženi unos)
• null je validno stanje objekta
null
null
function getVowels(str) {
let m = str.match(/[aeiou]/gi);
if (m === null) {
return 0;
}
return m.length;
}
Dizajn klasa
• Dizajn s naglaskom na ponovno korištenje
• Dizajn s naglaskom na proširivost
• Deskriptivna imena
– Ako koristimo neku konvenciju ili standard imenovanja
bitno je da ga slijedimo do kraja
– Kada netko pročita ime trebao bi znati što taj objekt
predstavlja
Konvencija imenovanja 1
• Imena klasa
– Spojene riječi, svaka riječ počinje velikim slovom
Account, BankAccount, CashDispenser, SortedIntegerQueue
Konvencija imenovanja 2
• Imena klasa
– Spojene riječi, svaka riječ počinje velikim slovom
Account, BankAccount, CashDispenser, SortedIntegerQueue
• Objekti
– Malim slovima, odvojene donjom crtom
list, node_list, account, new_acct
• Metode
– Spojene riječi, prvo slovo malo, sljedeće riječi počinju
velikim slovom
deposit, balance, objectAt, dispenseMoney
Mađarska notacija
• Sve sadrži oznaku koja identificira o čemu je riječ
– npr. svaka klasa počinje s velikim slovom C
Dizajn klasa
• Dizajnirati kod s malim
dosegom class Matematika{
int temp;
– Lokaliziranje atributa i public int zamijeni(int a, int b){
ponašanja temp = a;
a = b;
– Pojednostavljuje
b = temp;
održavanje, testiranje i return temp;
proširivanje klasa }
};
• Desno: primjer
nepotrebnog povećanja
dosega
Dizajn klasa
• Klasa bi trebala biti odgovorna za svoje funkcionalnosti
• Primjer ne-OO pristupa
IspisiKrug(krug);
IspisiPravokutnik(pravokutnik);
switch (oblik){
case 1: IspisiKrug(krug); break;
case 2: IspisiPravokutnik(pravokutnik); break;
case 3: IspisiTrokut(trokut); break;
default: System.out.println("Nedefinirani oblik"); break;
}
Dizajn klasa
Dizajn klasa
• Primjer OO pristupa - polimorfizam
Povezane klase
• Situacija kada dvije (ili više) klasa u velikoj mjeri ovise jedna o
drugoj naziva se jako povezane klase
• Promjena na jednoj klasi vrlo vjerojatno znači da će biti
potrebno mijenjati i drugu klasu
• Ponekad nam priroda problema iz domene nameće jaku
povezanost klasa
Primjer testiranja
class CitacBazePodataka{
private string baza[] = { "Zapis1","Zapis2", "Zapis3","Zapis4",
"Zapis5" };
private bool bazaOtvorena = false;
private int pozicija;
public void Otvori(string ime){
bazaOtvorena = true;
}
public void Zatvori(){
bazaOtvorena = false;
}
public void IdiNaPrviZapis(){
pozicija = 0;
}
Primjer testiranja
public void IdiNaZadnjiZapis(){
pozicija = 4;
}
public int KolikoImaZapisa(){
int brojZapisa = 5;
return brojZapisa;
}
public string DohvatiZapis(int redniBroj){
/* implementacija specifična za konkretnu bazu*/
return baza[redniBroj];
}
public string DohvatiSljedećiZapis(){
/* implementacija specifična za konkretnu bazu*/
pozicija++;
return baza[pozicija];
}
};
Testiranje
• Tri varijacije „test double”-a:
– Fakes
– Stubs
– Mocks
Testiranje
• Fake
– Objekti koji imaju funkcionalnu implementaciju (ali
drugačiju od produkcijske funkcionalnosti)
– Često koriste prečac i imaju pojednostavljenu verziju
produkcijskog koda
Testiranje
Testiranje
public class FakeAccountRepository implements AccountRepository {
Map<User, Account> accounts = new HashMap<>();
public FakeAccountRepository() {
this.accounts.put(new User("john@bmail.com"), new UserAccount());
this.accounts.put(new User("boby@bmail.com"), new AdminAccount());
}
String getPasswordHash(User user) {
return accounts.get(user).getPasswordHash();
}
}
Testiranje
• Stub
– Objekt koji sadrži predefinirane podatke koje koristi
kao odgovor.
– Koristimo kad ne možemo ili ne želimo koristiti
objekte koji bi odgovorili s pravim podacima ili koji bi
imali side effects
Testiranje
Testiranje
public class GradesService {
private final Gradebook gradebook;
public GradesService(Gradebook gradebook) {
this.gradebook = gradebook;
}
Double averageGrades(Student student) {
return average(gradebook.gradesFor(student));
}
}
Testiranje
public class GradesServiceTest {
private Student student;
private Gradebook gradebook;
Korištenje alata (JUnit).
@Before Kreira se mock objekt
public void setUp() throws Exception {
gradebook = mock(Gradebook.class);
student = new Student();
}
@Test
public void calculates_grades_average_for_student() {
when(gradebook.gradesFor(student)).thenReturn(grades(8, 6, 10));
double averageGrades = new
GradesService(gradebook).averageGrades(student);
assertThat(averageGrades).isEqualTo(8.0);
}
}
Testiranje
• Mock
– Objekti koji registriraju pozive koje prime
– Omogućuju nam da potvrdimo da su se obavile sve
potrebne akcije prilikom testiranja
Testiranje
Testiranje
public class SecurityCentral {
private final Window window;
private final Door door;
Testiranje mocks
@Test
public void enabling_security_locks_windows_and_doors() {
SecurityCentral securityCentral = new
SecurityCentral(windowMock, doorMock);
securityCentral.securityOn();
verify(doorMock).close();
verify(windowMock).close();
}
}
Dizajn s objektima
• Pričali smo o načinu kako na dobar način dizajnirati klase
• Sada je fokus na dobrom dizajnu sustava
• Sustav se može definirati kao klase koje međudjeluju jedne s
drugima
• Potrebno je dobar dio vremena rada na projektu uložiti u
početni dizajn sustava jer su naknadne promjene jako skupe
• Ne postoji samo jedna „dobra” metoda dizajna
• Potrebno je odabrati metodu koja najviše paše našim
potrebama i sredstvima/resursima na raspolaganju
Dizajn s objektima
• Za razvoj OO sustava posebno je važan high-level
model
• Objektni model se sastoji od class-dijagrama i
definiranja interakcija među klasama
• Model bi trebao vjerno predstavljati sustav i trebalo bi
ga biti lako razumjeti ili mijenjati
• Za notaciju se koristi UML
• Postoji više metodologija npr. waterfall model, rapid
prototyping, Kanban, Agile, Scrum …
Waterfall metoda
• Definiranje zahtjeva u ranoj fazi i minimizirati naknadne
promjene
– Cijena promjene u fazi zahtjeva i dizajna je mala
– Cijena promjena u fazi implementacije je značajno
veća
– Cijena promjena u fazi deploymenta je vrlo velika u
odnosu na prvu fazu
Dizajn
Implementacija
Visoka cijena
Korisnik želi
mijenjati Deployment
aplikaciju
Dizajn s objektima
• Dobar OO dizajn proces uključuje sljedeće:
1. Kvalitetna analiza i istraživanje
2. Izrada SOW (Statement of work) dokumenta
3. Prikupljanje zahtjeva
4. Izrada prototipa korisničkog sučelja (eng. user interface)
5. Definiranje klasa
6. Određivanje odgovornosti svake klase
7. Određivanje kako različite klase obavljaju interakciju
8. Stvaranje high-level modela koji opisuje sustav koji će se
izraditi
dokumenta
dokumenta
if (z <= 10){
if (z == 3) System.out.println("z iznosi:" + z);
z = z+1;
}
}
};
}
}; class Matematika{
public int zbroji(int a, int b){
return a + b;
}
};
Metoda
Proceduralni kod
na primjer s
Vratimo se
matematikom
class TestiranjeKlaseMatematika{
public static void main(string args[])
{
int z = 0;
Matematika m = new Matematika();
z = m.zbroji(3, 4);
System.out.println("Vrijednost od z je " + z);
}
}; class Matematika{
public int zbroji(int a, int b){
return a + b;
}
};
class TestiranjeKlaseMatematika{
public static void main(string args[])
{
int z = 0;
z = Matematika.zbroji(3, 4);
System.out.println("Vrijednost od z je " + z);
}
};
class Matematika{
public static int zbroji(int a, int b){
return a + b;
}
};
ko OOP pojmova
Još nekoli
C++
operator overloading
§ Operator overloading u C++ omogućuje programeru davanje
novog značenja predefiniranim C++ operatorima
– Ovo se radi tako da kreiramo metode klase čije „ime”
odgovara imenu predefiniranih operatora u C++
– Na primjer, C++ string klasa ima definiran „+” operator za
konkatenaciju stringova
§ Programerima je dopušteno „overload-anje” većine operatora
– aritmetičke operacije (+, -, *, /, %)
– operacije usporedbe(<, <=, >, >=, ==, !=)
– input / output operacije (>>, <<)
C++
operator overloading
§ Sintaksa je relativno kompleksna
§ prilikom definiranja metode koristimo ključnu riječ
„operator” nakon čega slijedi operator kojeg želimo koristiti
§ Na primjer, možemo zamijeniti „add” metodu sa „operator +”
i „subtract” sa „operator –”
§ Kako bismo izgradili izraze koji izgledaju poput aritmetičkih,
moramo se držati ovih pravila:
§ Moramo poslati samo JEDAN value parametar koji je tipa
class_type
§ Vratiti vrijednost tipa class_type nakon obavljene operacije
class base {
Runtime public:
virtual void print ()
polimorfizam: { cout<< "print base class" <<endl; }
void show ()
• postiže se { cout<< "show base class" <<endl; }
};
korištenjem class derived:public base {
function public: Virtual po default-u
void print ()
overridinga { cout<< "print derived class" <<endl; }
void show ()
• Kažemo da je { cout<< "show derived class" <<endl; }
bazna klasa };
int main() { Pointer
overrideana base *bptr;
derived d;
bptr = &d; virtualna funkcija, binded at
bptr->print(); runtime (runtime polymorphism)
print derived class bptr->show();
show base class return 0; Ne-virtualna funkcija, binded at
} compile time
Polymorphism
• 1. Static binding/Compile-Time binding/Early
binding/Method overloading. (u istoj klasi)
• 2. Dynamic binding/Run-Time binding/Late
binding/Method overriding. (u različitim klasama)
KRAJ