OO projektovanje

Sada ćemo detaljnije razmotriti neke od karakteristika objektnog modela. Ovo razmatranje ima za cilj da nam pruži osnovu za objektno orjentisanu analizu i dizajn (projektovanje) objektno orjentisanog softvera. Analiza i projektovanje OO sistema je velika i ozbiljna tema koja se obično izučava nezavisno od programskoh jezika. Cilj našeg izučavanja je zapravo OO programiranje, tako da ovu temu dotičemo samo da upotpunimo opštu sliku o OO metodologiji.

UML

Kao što smo kod proceduralnih metoda koristili grafičke simbole za prikazivanje algoritama, tako se u OO metodologiji nametnuo jedan standardni grafički način za prikazivanje klasa.

UML (Unified Modeling Language) je grafički jezik za modeliranje koji se koristi za prikaz različitih OO komponenti pri analizi, projektovanju i implementaciji OO sistema. UML je postao međunarodni standard. Njegovi autori su Grady Booch, Jim Rumbaugh and Ivar Jacobson.

Detaljan opis UML-a i tutorijal možete naći na adresi: http://www.uml.org

Predstavljanje klasa pomoću UML-a

Klase se mogu predstaviti na jedan od sledećih načina u zavisnosti od nivoa detalja koji se želi prikazati.

Posmatrajmo ponovo naš primer bankovnog računa. Radi skraćenog pisanja malo smo promenili nazive podataka i metoda.

Klasa Racun

@startuml
scale 800 width

class Racun-1 {
}
note top: Samo naziv klase
class Racun-2 {
  {field} naziv_racuna
  {field} broj_racuna
  {field} stanje_racuna
}
note top: Naziv i atributi
class Racun-3 {
  {method} podizanjeGotovine()
  {method} ulaganjeGotovine()
  {method} proveraStanja()
}
note top: Naziv i metode
class Racun-4 {
  {field} naziv_racuna
  {field} broj_racuna
  {field} stanje_racuna
  {method} podizanjeGotovine
  {method} ulaganjeGotovine
  {method} proveraStanja
}
note top: Naziv,atributi,metode


@enduml

Klasa krug

@startuml
scale 800 width

class Krug-1 {
}
note top: Samo naziv klase
class Krug-2 {
  {field} centar
  {field} poluprecnik
}
note top: Naziv i atributi
class Krug-3 {
  {method} obimKruga()
  {method} povrsinaKruga()
  {method} pomeriKrug()
}
note top: Naziv i metode
class Krug-4 {
  {field} centar
  {field} poluprecnik
  {method} obimKruga()
  {method} povrsinaKruga()
  {method} pomeriKrug()
}
note top: Naziv,atributi,metode

@enduml

Sada kada smo se “naoružali” sa UML prikazom klase možemo da se detaljnije upoznamo sa procesom analize i dizajna (projektovanja) objektno orjentisanih sistema.

Učaurivanje (Encapsulation)

Sve informacije (atributi i metode) u OO sistemu su smeštene (sakrivene) unutar same klase (objekta).

Ovim informacijama se može pristupati samo preko metoda iz klase koje su stavljene na korišćenje spoljnjem svetu kao interfejs. Sama implementacija je sakrivena od korisnika.

Mogućnost da se kod klasa (objekata) od korisnika mogu sakriti detalji naziva se učaurivanjem (enkapsulacijom). Kao da je objekat (klasa) zatvoren u kapsulu, ne može se prići njegovim unutrašnjim delovima, već se objekat koristi samo pomoću javnih metoda koje jedino imaju “pravo” da pristupaju i menjaju podatke (atribute) objekta.

Ovo je ilustrovano na primeru klase Račun prikayom na sledećoj slici:

_images/image3344.png

Samo metode uloziGotovinu(), podigniGotovinu() i proveriStanje() mogu pristupati i modifikovati podatke naziv_racuna, broj_racuna i stanje_racuna.

Pogledajmo sada kako pseudokod i UML dijagram međusobno korespondiraju.

@startuml
scale 1.5
class Racun {
  {field} naziv_racuna
  {field} broj_racuna
  {field} stanje_racuna
  {method} podizanjeGotovine
  {method} ulaganjeGotovine
  {method} proveraStanja
}
note bottom
class Racun {
  private String naziv_racuna;
  private String broj_racuna;
  private float stanje_racuna;
  public podigniGotovinu();
  public uloziGotovinu();
  public proveriStanje();
} // Klasa Racun
end note
@enduml

Nasleđivanje (Inheritance)

Nove klase mogu biti kreirane korišćenjem postojećih klasa. Tako se stvara odnos Roditelj-Dete (Parent-Child), gde roditelj predstavlja postojeću klasu (super klasu) a dete novu (izvedenu) klasu (podklasu).

Podklasa (dete) nasleđuje svojstva od superklase (roditelj), što ilustruje sledeća slika:

_images/parent-child.png

Ponovo ćemo iskoristiti primer bankovnog računa da ilustrujemo nasleđivanje.

Klasa Racun – je klasa roditelj (superklasa) koja ima sledeće atribute:

  • naziv_racuna, broj_racuna i stanje_racuna

Ali, u bankama je moguće imati različite vrste računa: tekuće, žiro, itd.

Tako recimo, klasa TekuciRacun je nova klasa – dete (podklasa) koja pored gore navedenih atributa iz klase Racun (superklase) može imati i dodatne atribute:

  • broj_izdatih_cekova i broj_realizovanih_cekova

I sada, umesto da kreiramo novu klasu TekuciRacun koja bi imala svih pet atributa, možemo da klasu TekuciRacun definišemo kao podklasu klase Racun koja nasleđuje atribute naziv_racuna, broj_racuna i stanje_racuna, kao i sve metode klase Racun.

Prikaz nasleđivanja UML dijagramima

TekuciRacun je podklasa superklase Racun. To se UML dijagramom iskazuje na sledeći način:

@startuml
scale 1.5
class Racun
class TekuciRacun
Racun <|-- TekuciRacun
@enduml

Drugim rečima, klasa TekuciRacun je izvedena iz klase Racun.

Nasleđivanje – još jedan primer

Kasa krug u našem ranijem primeru imala je atribute centar i poluprecnik, kako kako prikazuje slika.

@startuml
scale 1.5
class Krug {
  {field} centar
  {field} poluprecnik
  {method} obimKrug
  {method} povrsinaKruga
  {method} pomeriKrug
}

@enduml

Posmatrajmo sada klasu Pravougaonik. Ta klasa može imati atribut centar, ali poluprečnik nije prikladan atribut za ovu klasu.

Pravougaonik treba da ima atribute dužina i širina, ali oni nisu prikladni za krug.

Metode površina, obim i pomeranje mogu biti primenjene i na krug i na pravougaonik (doduše sa različitim načinom izračunavanja površine i obima, ali o tomće biti reči kasnije).

Ali umesto da definišemo novu klasu Pravougaonik nezavisnu od klase Krug, mi ćemo definisati novu klasu Figura, tako što ćemo sve zajedničke atribute i metode kruga i pravougaonika smestiti u novu klasu. Onda ćemo Krug i Pravougaonik definisati kao podklase klase Figura.

Prikažimo to UML dijagramom:

@startuml
scale 1.5
class Figura
class Krug
class Pravougaonik
Figura <|-- Krug
Figura <|-- Pravougaonik
@enduml

Ponovljeno korišćenje nasleđivanja - Reuse

Ako više klasa imaju zajedničke atribute i metode onda takve klase možemo sakupitu u jednu zajedničku klasu – superklasu (klasu roditelj). Time se postiže brži razvoj aplikacija jer se klase ne moraju nanovo programirati već se koriste postojeće koje mogu biti sakupljene iz različitih izvora.

Primer: Pravougaonik i Krug imaju zajednički metod pomeranje jer je tada potrebno samo promeniti koordinate centra (to je atribut koji imaju i krug i pravougaonik).

@startuml
scale 1.5
class Krug {
  {field} centar
  {field} poluprecnik
  {method} obim()
  {method} povrsina()
  {method} pomeri(novi_centar)
}
note bottom
pomeri(novi_centar){
  centar = novi_centar
}
end note
class Pravougaonik {
  {field} centar
  {field} duzina
  {field} sirina
  {method} obim()
  {method} povrsina()
  {method} pomeri(novi_centar)
}
note bottom
pomeri(novi_centar){
  centar = novi_centar
}
end note

@enduml

Sada bi mogli da definišemo novu klasu Figura koja će sadržati zajedničke atribute i zajedničke metode za klase Krug i Pravougaoonik, što je ilustrovano na sledećoj slici:

@startuml
scale 1.5
class Figura {
  {field} centar
  {method} pomeri(novi_centar)
  {method} obim()
  {method} povrsina()
}
note right
pomeri(novi_centar){
  centar = novi_centar
}
end note
class Krug {
  {field} poluprecnik
  {method} obim()
  {method} povrsina()
}
class Pravougaonik {
  {field} duzina
  {field} sirina
  {method} obim()
  {method} povrsina()
}
Figura <|-- Krug
Figura <|-- Pravougaonik
@enduml

Korišćenje nasleđivanje za specijalizaciju

Subklasi (detetu) tokom nasleđivanja mogu biti pridodata nova svojstva. Taj postupak se naziva specijalizacijom klase.

Na primer, metod povrsina je različit kod kruga i pravougaonika. Isto važi i za obim. Zato se površina i obim ponovo definišu (specijalizuju) u izvedenim klasama - Krug i Pravougaonik, kako je ilustrovano na sledećoj slici:

@startuml
scale 1.5

class Krug {
  {field} poluprecnik
  {method} obim()
  {method} povrsina()
}
note bottom
povrsina(){
  return r**2*pi
}
end note
class Pravougaonik {
  {field} duzina
  {field} sirina
  {method} obim()
  {method} povrsina()
}
note bottom
povrsina(){
  return duzina*sirina
}
end note

@enduml

Korišćenje klase za proširenje funkcionalnosti postojeće klase

Klasa dete može imati i posebne metode koje nema klasa roditelj. Na primer pravougaoniku možemo dodati novi metod koji kod kruga može biti besmislen – rotaciju za 90 stepeni, kako je to ilustrovano na sledećoj slici:

@startuml
scale 1.5
class Figura {
  {field} centar
  {method} pomeri(novi_centar)
  {method} obim()
  {method} povrsina()
}
class Krug {
  {field} poluprecnik
  {method} obim()
  {method} povrsina()
}
class Pravougaonik {
  {field} duzina
  {field} sirina
  {method} obim()
  {method} povrsina()
  {method} rotacija90()
}
Figura <|-- Krug
Figura <|-- Pravougaonik
@enduml

Dakle u ovom slučaju samo je dodata još jedna metoda klasi Pravougaonik, dok se ništa drugo nije promenilo.

Polimorfizam (Polymorphism)

Polimorfizam znači pojavljivanje u “više oblika”. U OOP polimorfizam i sam ima više značenja.

Polimorfizam omogućava da objekti, metode, operatori imaju različita značenja zavisno od načina kako im se prenose parametri.

Na primer u sledećem pseudokodu biće korišćene dve različite metode za izračunavanje površine u zavisnosti od toga kojoj podklasi klase Figura pripada objekat čija se površina izračunava:

krugA = new Krug();                 // Kreiranje novog objekta iz klase Krug
Figura figura = krugA;              // kreiranje objekta figura iz klase figura, podklase Krug
Figura.povrsina();                  // povrsina ce biti izracunata prema metodi za krug
pravougaonikA = new Pravougaonik(); // Kreiranje novog objekta pravougaonika
figura= pravougaonikA;
Figura.povrsina();                  // povrsina za metod pravougaonik ce se koristiti

Overloding metoda(Method Overloading)

Jedan drugi slučaj korišćenja polimorfizma je overloding metoda, u kojem se istim imenom definiše više različitih metoda. Metode se međusobno razlikuju samo po parametrima koji im se pri pozivu prenose.

Na primer neka imamo metodu inicijalizacija sa dve varijante prenosa parametara:

  • Metod 1 - inicijalizacija(int a)
  • Metod 2 - inicijalizacija(int a, int b)

Kada u programu pozivamo metodu, od toga koje parametre stavimo u pozivu zavisiće koja od metoda (1 ili 2) će biti zapravo pozvana:

inicializacija(2) // Biće pozvana metoda 1
inicializacija(2,4) // Biće pozvana metoda 2

Overloding operatora (Operator Overloading)

Overloding-om operatora omogućava se davanje novog značenja operatorima kao što su +, -, *, /.

Na primer operator. + operator za Krug može biti definisan, pa izraz tipa:

Krug c = c + 2;

postaje legitiman (ovo nije podržano u Java i C++ jezicima).

Asocijacija (Association)

Neka klasa može biti povezana sa drugim klasama pa je potrebno omogućiti komunikaciju među objektima iz različitih klasa.

U svhu povezivanja ( asocijacije) klasa, koriste se takozvani dijagrami klasa koji pokazuju međusobnu povezanost klasa i tip veze među njima.

Na primer, Krug i Pravougaonik mogu biti deo klase Dijagram. Ovim se uspostavlja veza između klase Dijagram i klasa Krug i Pravougaonik kao što prikazuje sledeća slika:

@startuml
scale 1.5
class Dijagram
class Krug
class Pravougaonik
Dijagram --> Krug
Dijagram --> Pravougaonik
@enduml

Primer složenijeg dijagrama klase

Nacrtati dijagram klase prema sledećem opisu:

  • Student može biti dodiplomac ili postdiplomac.
  • Dodiplomac može biti demonstrator.
  • Demonstrator vrši demonstarcije studentima.
  • Predavač i profesor su dva tipa nastavnika.
  • Asistent je postdiplomac koji pomaže profesoru.

@startuml
scale 1.5
class Demonstrator
class Student
class Dodiplomac
class Postdiplomac
class Asistent
class Nastavnik
class Profesor
class Predavac
Demonstrator --> Student : demonstrira
Student <|-- Dodiplomac
Student <|-- Postdiplomac
Nastavnik <|-- Profesor
Nastavnik <|-- Predavac
Asistent <|-- Postdiplomac
Asistent --> Profesor : asistira
@enduml

U ovom poglavlju smo samo ovlaš dotakli temu objektno orjentisanog projektovanja. U dateljem tekstu čemo se koncentrisati na programskoj implementaciji ideja sa kojima smo se do sada upoznali. Kao radni programski jezik koristićemo Python.

Pitanja

  1. Šta je UML? Primer?
  2. Šta su dijagrami klasa? Primer?
  3. Draw a class inheritance diagram for the following set of classes:
  • Class Goat extends object and adds an instance variable tail and methods milk( ) and jump( ).
  • Class Pig extends object and adds an instance variable nose and methods eat(food) and wallow( ).
  • Class Horse extends object and adds instance variables height and color, and methods run( ) and jump( ).
  • Class Racer extends Horse and adds a method race( ).
  • Class Equestrian extends Horse, adding an instance variable weight and methods trot( ) and is trained( ).

4. Develop an inheritance hierarchy based upon a Polygon class that has abstract methods area( ) and perimeter( ). Implement classes Triangle, Quadrilateral, Pentagon, Hexagon, and Octagon that extend this base class, with the obvious meanings for the area( ) and perimeter( ) methods. Also implement classes, IsoscelesTriangle, EquilateralTriangle, Rectan- gle, and Square, that have the appropriate inheritance relationships. Fi- nally, write a simple program that allows users to create polygons of the various types and input their geometric dimensions, and the program then outputs their area and perimeter. For extra effort, allow users to input polygons by specifying their vertex coordinates and be able to test if two such polygons are similar.

5. Now, think about an upcoming project. It doesn’t matter what the project is; it might be a fun free-time project or a multimillion dollar contract. It doesn’t have to be a complete application; it could just be one subsystem. Perform a basic object-oriented analysis. Identify the requirements and the interacting objects. Sketch out a class diagram featuring the highest level of abstraction on that system. Identify the major interacting objects. Identify minor supporting objects. Go into detail for the attributes and methods of some of the most interesting ones. Take different objects to different levels of abstraction. Look for places you can use inheritance or composition. Look for places you should avoid inheritance.

The goal is not to design a system (although you’re certainly welcome to do so if inclination meets both ambition and available time). The goal is to think about object- oriented designs. Focusing on projects that you have worked on, or are expecting to work on in the future, simply makes it real.

Now, visit your favorite search engine and look up some tutorials on UML. There are dozens, so find the one that suits your preferred method of study. Sketch some class diagrams or a sequence diagram for the objects you identified earlier. Don’t get too hung up on memorizing the syntax (after all, if it is important, you can always look it up again), just get a feel for the language. Something will stay lodged in your brain, and it can make communicating a bit easier if you can quickly sketch a diagram for your next OOP discussion.