Transcript
Page 1: Objektno usmjereno programiranje na slovenskom

Programski jezik C#

Osnove objektnega

programiranja

Gregor Jerše, Matija Lokar in Srečo Uranič V 0.9 marec 2009

Page 2: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

2

Page 3: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

3

Page 4: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

4

Predgovor

Omenjeno gradivo pokriva osnove objektnega programiranja v C# . Gradivo vsekakor ni dokončano in predstavlja delovno različico. V njem so zagotovo napake (upava, da čimmanj), za katere se vnaprej opravičujeva. Da bo lažje spremljati spremembe, obstaja razdelek Zgodovina sprememb, kamor bova vpisovala spremembe med eno in drugo različico. Tako bo nekomu, ki si je prenesel starejšo različico, lažje ugotoviti, kaj je bilo v novi različici spremenjeno. Gregor Jerše, Matija Lokar in Srečo Uranič Kranj, november 2008

Page 5: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

5

Zgodovina sprememb

14. 11. 2008: Različica V0.8 – le prenos (še neurejeno) Matijevega, Gregorjevega (str. 1 – 120) in Sašovega gradiva. Premisliti je še potrebno kaj in kako. 5. 3. 2009: Različica V0.9 – še vedno povsem neurejeno. Dodano nekaj gradiva, ki ga je smiselno vkomponirati. Gradivo dodano na konec in sicer:

Vsebina prosojnic, ki naj bi bile spredavane letos

Zgled za Avto

Gradivo, ki pokriva uvod v OOP iz C++. Tam je nekaj smiselnih zgledov, ki pa jih je potrebno prepisati v C#.

Page 6: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

6

KAZALO

Objektno programiranje ...................................................................................................................................... 9

Motivacija ....................................................................................................................................................... 9 Objektno usmerjeno programiranje – kaj je to .............................................................................................. 10 Objekti........................................................................................................................................................... 11 Objekti........................................................................................................................................................... 13 Programiranje v C# ....................................................................................................................................... 14 Objekti........................................................................................................................................................... 14 Klasično/objektno programiranje .................................................................................................................. 14 Od kje razredi? .............................................................................................................................................. 15 Objekt in ime spremenljivke ......................................................................................................................... 15 Ustvarjanje objektov ..................................................................................................................................... 15 C# in objekti .................................................................................................................................................. 15 Ustvarjanje objektov ..................................................................................................................................... 16 Zgled ............................................................................................................................................................. 16 Objektno programiranje ................................................................................................................................ 18

Objekti, razredi, ... ............................................................................................................................................ 19 Združevanje podatkov ................................................................................................................................... 19 Uporaba razreda Zajec .................................................................................................................................. 21 Dostop do podatkov v objektu ...................................................................................................................... 22 Še en primer - Ulomek .................................................................................................................................. 25 Povzetek ........................................................................................................................................................ 26 Konstruktorji ................................................................................................................................................. 26 this ................................................................................................................................................................. 27 Uporaba razreda: ........................................................................................................................................... 28 Privzeti konstruktor ....................................................................................................................................... 28 Več konstruktorjev ........................................................................................................................................ 29 Preobtežene metode ...................................................................................................................................... 29 Konstruktorji razreda Zajec – navzkrižno sklicevanje .................................................................................. 30 Še malo o this ................................................................................................................................................ 30 Zgled ............................................................................................................................................................. 31 Objektne metode ........................................................................................................................................... 34

Zgledi ................................................................................................................................................................ 37 Datum ............................................................................................................................................................ 37 Fibonaccijevo zaporedje................................................................................................................................ 38

Vaje ................................................................................................................................................................... 40 Kochova črta ................................................................................................................................................. 40 Razred Točka in Kochova črta ...................................................................................................................... 40 Ulomek .......................................................................................................................................................... 40 Razred Majica ............................................................................................................................................... 40 Razred Zajec ................................................................................................................................................. 41 Razred Kolega ............................................................................................................................................... 41 Uporaba razreda Kolega ................................................................................................................................ 41 Uporaba razreda Zajec .................................................................................................................................. 42 Datum ............................................................................................................................................................ 42 Zajci z datumom............................................................................................................................................ 42 Uporaba razreda Zajec z datumom ................................................................................................................ 42 Nadgradimo datum ........................................................................................................................................ 42

Dostop do stanj objekta ..................................................................................................................................... 42 Dostopi do stanj ............................................................................................................................................ 43 Zgledi ............................................................................................................................................................ 48

Page 7: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

7

Dostop do stanj/lastnosti ............................................................................................................................... 48 Nastavitve podatkov (stanj) ........................................................................................................................... 49 Dostop do stanj.............................................................................................................................................. 51 Ostale metode ................................................................................................................................................ 52 Metoda ToString ........................................................................................................................................... 52 Celotni razred Zajec ...................................................................................................................................... 54

Gradnja razredov - zgled .................................................................................................................................. 58 Ustvarjanje objektov ..................................................................................................................................... 58 Problem Banke Butale .................................................................................................................................. 58 Okostje razreda ............................................................................................................................................. 59 Razred Kovanec ............................................................................................................................................ 59 Uporaba razreda Kovanec ............................................................................................................................. 61 Zgled - razred Kvader ................................................................................................................................... 62 Ustvarjanje razredov in objektov: povzetek .................................................................................................. 66

Zgledi ................................................................................................................................................................ 67

Vaje ................................................................................................................................................................... 72 Razred Kolega ............................................................................................................................................... 74 Datum ............................................................................................................................................................ 76 Oseba............................................................................................................................................................. 76 Majica ........................................................................................................................................................... 77 Kandidat za direktorja banke ........................................................................................................................ 77 Denarnica ...................................................................................................................................................... 79 Podatkovni nosilec ........................................................................................................................................ 80 Razred Zajec ................................................................................................................................................. 80

Statično in objektno ........................................................................................................................................... 87 Statične komponente ..................................................................................................................................... 87 Statične spremenljivke .................................................................................................................................. 87 Uporaba statičnih spremenljivk ..................................................................................................................... 88 Statične komponente – dostop ...................................................................................................................... 88 Statične komponente – naslavljanje .............................................................................................................. 88 Zajec .............................................................................................................................................................. 88 Statične metode ............................................................................................................................................. 95 Statične metode ............................................................................................................................................. 96 Privatne metode............................................................................................................................................. 97

Dedovanje ......................................................................................................................................................... 97 Pogosto uporabljani postopki I ...................................................................................................................... 97 Dedovanje ..................................................................................................................................................... 98 Razred BoljsiUlomek .................................................................................................................................... 98 Uporaba Boljšega ulomka ............................................................................................................................. 98 Pogosto uporabljani postopki ........................................................................................................................ 99 Dedovanje ..................................................................................................................................................... 99 Dedovanje ................................................................................................................................................... 100 Iz muhe slon ................................................................................................................................................ 106 Osnova: razred ZajecNov ............................................................................................................................ 106 Prekrite metode ........................................................................................................................................... 106 "Umazane" podrobnosti .............................................................................................................................. 106 Uporaba ....................................................................................................................................................... 106

Zgledi .............................................................................................................................................................. 107

Vaje ................................................................................................................................................................. 107 Zival ............................................................................................................................................................ 107 Razred Pes ................................................................................................................................................... 109 Razred Dalmatinec ...................................................................................................................................... 109 Naloge brez računalnika .............................................................................................................................. 110 Razred Pes II ............................................................................................................................................... 111 Zajec iz živali .............................................................................................................................................. 113

Page 8: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

8

Embi d.o.o ................................................................................................................................................... 113 Vaje "peš" ................................................................................................................................................... 114 Halo Kitajc .................................................................................................................................................. 118 Trojiško ....................................................................................................................................................... 119 Redki vektorji .............................................................................................................................................. 119 Plavalec ....................................................................................................................................................... 120 Plavalec II ................................................................................................................................................... 121

Razredi in objekti (osnove) ............................................................................................................................... 121

Razred - class .................................................................................................................................................. 122 Enkapsulacija .............................................................................................................................................. 122 Konstruktor ................................................................................................................................................. 127 Preobloženi (overloaded) konstruktorji ....................................................................................................... 129 Kopirni (copy) konstruktorji ....................................................................................................................... 130 Statični (static) konstruktorji ....................................................................................................................... 130 Polja tipa readonly ...................................................................................................................................... 131 Destruktorji ................................................................................................................................................. 131 Lastnost (Property) ...................................................................................................................................... 134 Statične metode ........................................................................................................................................... 137 Statična polja ............................................................................................................................................... 137

Dedovanje (Inheritance) – izpeljani razredi ................................................................................................... 139 Kaj je to dedovanje ..................................................................................................................................... 139 Bazični razredi in izpeljani razredi.............................................................................................................. 139 Klic konstruktorja bazičnega razreda .......................................................................................................... 140 Določanje oz. prirejanje razredov ............................................................................................................... 140 Nove metode ............................................................................................................................................... 141 Virtualne metode ......................................................................................................................................... 143 Prekrivne (Override) metode ...................................................................................................................... 143

Polimorfizem - mnogoličnost .......................................................................................................................... 146

Uporaba označevalca protected ..................................................................................................................... 148

Prosojnice M. Lokar 2009 ................................................................................................................................ 149

Razred Avto ....................................................................................................................................................... 149

Testiranje razreda ........................................................................................................................................... 221

OOP v C++ (avtor dr. M. Krašna)................................................................................................................... 221

Page 9: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

9

Objektno programiranje

Motivacija Pri programiranju se je izkazalo, da je zelo koristno, če podatke in postopke nad njimi združujemo v celote, ki jih imenujemo objekte. Programiranje je potem sestavljeno iz tega, da objekte ustvarimo in jim naročamo, da izvedejo določene postopke. Večina sodobnih programskih jezikov je objektno ali kot tudi rečemo predmetno usmerjenih. Pri uporabi tovrstnih jezikov hitro naletimo naletimo na pojme kot so združevanje (enkapsulacija), večličnost (polimorfizem), dedovanje (inheritenca), ki so vsaj na prvi pogled zelo zapleteni, Nas ne bodo zanimali, kot tudi ne številne druge podrobnosti. Objektno usmerjeno programiranje nam omogoča, da na problem gledamo kot na množico objektov, ki med seboj sodelujejo in vplivajo drug na drugega. Na ta način lažje pišemo sodobne programe, kjer se nenehno odvija veliko število dogodkov kot so premikanje miške, zapiranje oken, klikanje na gumbe, potrditev določenih izbir, … ki se med sabo prepletajo, vplivajo drug na drugega in kar bi le s težavo sprogramirali na klasičen način z nizanjem zaporedja stavkov in ukazov. Številni jeziki (JavaScript, Visual Basic for Applications, Java, C#, Python, …) nudijo okolja v katerih so že določeni objekti, nad katerimi lahko izvajamo določene metode, funkcije. Tako "iz nič" dobimo grafični objekt g, na katerim potem z določeno metodo narišemo krog. Ko program zaženemo, ta samodejno ustvari okno z določenim imenom in gumbom za zapiranje … Večina jezikov omogoča, da program zlahka dopolnimo z gumbi, potrditvenimi stikali in podobnimi elementi. Vse to so objekti, ki imajo lastnosti in metode, s katerimi na te objekte vplivamo. Na primer v jeziku JavaScript. Zanima nas le označeni del

var i = 0;

function animacija() {

i = i + 1;

if (i == 6) i = 1;

document.menjajocaSlika.src = "album" + i + ".jpg";

setTimeout("animacija()", 1000)

}

JavaScript spletno stran, na kateri se nahaja, imenuje kot objekt z imenom document. Na tem objektu imamo tudi druge objekte, v našem konkretnem primeru sliko (vstavljeno v jeziku HTML z značko IMG), poimenovano kot menjajocaSlika:

<img name=" menjajocaSlika " src="album1.jpg">

S pomočjo stavkov v jeziku JavaScript vplivamo na ta objekt (document.menjajocaSlika) tako, da mu spremenimo lastnost src, kjer je zapisana datoteka, ki naj jo ta objekt prikazuje

document.menjajocaSlika.src = "album" + i + ".jpg";

Podobno z metodo writeln vstavimo v samo dototeko ustrezno kodo v jeziku html. Če v kodi spletne strani piše

<script type="text/javascript">

document.writeln("<p>To naj se pojavi na spletni strani!</p>");

</script>

pomeni, da naj se objekt document (kar pomeni kar ta spletna stran) spremeni tako, da vsebuje tekst, zapisan med narekovaji. Dejansko je torej učinek isti, kot če bi na spletni strani napisali kar <p>To naj se pojavi na spletni strani!</p>

Seveda pa smo se doslej naučili že dovolj, da vemo, kakšne vse možnosti s tem imamo – verjetno bomo JavaScript uporabili, da bomo napisali na spletno stran kaj bolj zapletenega, na primer besedilo, ki ga bomo prej s pomočjo ustreznega postopka obdelali. Tako smo na primer pri šesti nalogi hitrega testa v uvodu uporabili možnost pisanja na spletno stran zato, da smo na enostaven način poenotili vse naslove na spletni strani.

Page 10: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

10

Ko torej pišemo programe, imamo s samim izvajalnim okoljem ponavadi na voljo bogato zbirko različnih objektov (natančneje načrtov za to, kakšni so objekti določene vrste). Ni nam potrebno pisati na stotine vrstic kode le za to, da bi dosegli, da naš program uporabniku omogoča vnos datuma preko posebnega okna z že izpisanim koledarjem. Vedeti je potrebno le, da imamo v izvajalnem okolju že na voljo (načrt za) tovrstni objekt. Ostane nam le še, da tak objekt ustvarimo in s pripravljenimi metodami nanj vplivamo. Tako s spodnjim zgledom v programskem jeziku Java ustvarimo okno z vnosno površino, po kateri lahko pišemo.

Če ne bi imeli na voljo že ustreznih objektov, bi naš program sestavljalo na desetine ali celo stotine ustreznih ukazov. Tako pa le uporabimo dana načrta za objekta JFrame in JTextArea, z ukazom new ustvarimo objekta ustrezne vrste in z metodami, kot so setSize, show in drugimi vplivamo nanju.

import javax.swing.*;

import java.awt.*;

public class JFrameTest {

public static void main(String[] args) {

JFrame okno = new JFrame("Moje okno");

okno.setSize(250, 250);

okno.setLocation(300,200);

okno.getContentPane().add(BorderLayout.CENTER,

new JTextArea(10, 40));

okno.show();

}

}

In seveda imamo na voljo take objekte tudi v standardnih knjižnicah jezika C#.

Objektno usmerjeno programiranje nam omogoča, da na problem gledamo kot na množico objektov, ki med seboj sodelujejo in vplivajo drug na drugega. Za nas je pomembno to, da večina sodobnih jezikov prinaša v svojem izvajalnem okolje celo vrsto že pripravljenih objektov in metod za delo z njimi. Ti objekti so na primer spletna stran, slika na spletni strani, izbire v menuju programov v okolju Microsoft Office, grafično okno, tiskalnik, …. S pripravljenimi metodami potem vplivamo na te objekte – na primer zamenjamo sliko na spletni strani, spremenimo privzeto delovanje ukaza Print v izbiri File, na grafično površino narišemo krog, …

Objektno usmerjeno programiranje – kaj je to Pri objektno (ali kot tudi rečemo predmetno) usmerjenem programiranju se ukvarjamo z … objekti seveda. Objekt je nekakšna črna škatla, ki dobiva in pošilja sporočila. V tej črni škatli (objektu) se skriva tako koda (torej zaporedje programskih stavkov), kot tudi podatki (informacije nad katerimi se izvaja koda). Pri klasičnem programiranju imamo kodo in podatke ločene. Tudi mi smo do sedaj programirali več ali manj na klasičen način. Pisali smo metode, ki smo jim podatke posredovali preko parametrov. Parametri in metode niso bile prav nič tesno povezane. Če smo npr. napisali metodo, ki je na primer poiskala največji element v tabeli števil, tabela in metoda nista bili združeni – tabela nič ni vedela o tem, da naj bi kdo npr. po njej iskal največja števila. V objektno usmerjenem (včasih boste srečali tudi kratico OO ali O-O, kar izvira iz object oriented) programiranju pa sta koda in podatki zduženi v nedeljivo celoto – objekt. To prinaša določene prednosti. Ko uporabljamo objekte nam nikoli ni potrebno pogledati v sam objekt. To je dejansko prvo pravilo OO programiranja – uporabniku ni nikoli potrebno vedeti, kaj se dogaja znotraj objekta.

Page 11: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

11

Objekt

Zakaj ni potrebno vedeti tega? Vsi objekti med samo komunicirajo preko sporočil. Za vsak objekt se točno ve, kakšna sporočila lahko sprejme in kakšna lahko pošlje. Vse, kar objekt zna narediti, je določeno z vmesnikom objekta. Se vam zdi to zapleteno? No, pa povejmo malo bolj "po domače". Gre dejansko zato, da nad objektom izvajamo različne metode. In za vsak objekt se točno ve, katere metode zanj obstajajo in kakšne rezultate vračajo.

Recimo, da imamo določen objekt z imenom obj, ki predstavlja določeno osebo in vemo, da ta objekt lahko

povprašamo (mu pošljemo sporočilo) o tem, koliko je njegov IQ. To storimo tako, da nad njem izvedemo metodo

obj.KolikoJeIQ()

Objekt odgovori s sporočilom (metoda vrne rezultat), ki je neko celo število.

Tisto, kar je pomembno, je to, da nam, kot uporabnikom objekta, ni potrebno vedeti, kako objekt izračuna IQ (je to podatek, ki je zapisan nekje v objektu, je to rezultat nekega preračunavanja …). Kot uporabnikom nam je važno le, da se z objektom lahko pogovarjamo o njegovem IQ. Vse "umazane podrobnosti" o tem, kaj je znotraj škatle, je prepuščeno tistim, ki napišejo kodo s pomočjo katere se ustvari objekt in (če je zadeva seveda sprogramirana prav) uporabnik (programer, ki piše drug program, ki te objekte uporablja) se vanje ne more vtikati. Seveda se lahko zgodi, da se kasneje ugotovi, da je potrebno "preurediti notranjost objekta". In tu se izkaže prednost OO koncepta. Ker v programih, kjer objekte uporabljamo, nič ne vemo o tem, kaj je znotraj škatle, kaj se na primer dogaja, ko objekt povprašamo po IQ, bodo programi navkljub spremembam znotraj škatle še vedno delovali. Če torej na objekte gledamo kot na črne škatle (zakaj črne – zato, da se ne vidi, kaj je znotraj!) in z njimi ravnamo le preko sporočil (preko klicev metod), naši programi delujejo ne glede na morebitne spremembe v samih objektih. Omogočanje dostopa do objekta samo preko sporočil in onemogočanje uporabnikom, da vidijo (in brskajo) po podrobnostih, se imenuje skrivanje informacij (information hidding) ali še bolj učeno enkapsulacija (encapuslation). In zakaj je to pomembno? Velja namreč, da se programi ves čas spreminjajo. Velika večina programov se dandanes ne napiše na novo, ampak se spremeni obstoječ program. In večina napak izhaja iz tega, da nekdo spremeni določen delček kode, ta pa potem povzroči, da drug del programa ne deluje več. Če pa so tisti delčki varno zapakirani v kapsule, se spremembe znotraj kapsule ostalega sistema prav nič ne tičejo.

Objekti Objekt je skupek podatkov, s katerim želimo upravljati kot s celoto. Ima:

Podatke:

kaj o objektu vemo, kakšne podatke o objektu hranimo Metode:

Kaj objekt zna

Kakšne metode lahko izvajamo nad njem Vsak objekt pripada nekemu razredu. Če pripada objekt x razredu R, potem pravimo tudi da je x objekt tipa R.

Koliko je tvoj IQ ?

92

Page 12: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

12

Pravzaprav smo se že ves čas ukvarjali z objekti, saj smo v naših programih uporabljali različne objekte, ki so že pripravljeni v standardnih knjižnicah jezika C#. Oglejmo si nekaj primerov objektov iz standardne knjižnice jezika:

Objekt tipa StreamReader predstavlja vhodni kanal. Kadar želimo brati s tipkovnice ali z datoteke, naredimo tak objekt in kličemo njegovo metodo readLine za branje ene vrstice.

Objekt System.Console predstavlja standardni izhod. Kadar želimo kaj izpisati na zaslon, pokličemo metodo Write na objektu System.Console.

Objekt tipa Random predstavlja generator naključnih števil. Metoda Next(a, b), ki jo izvedemo nad objektom tega tipa pa nam vrne neko naključno celo število med a in b.

► StreamWriter pisanje = new StreamWriter(datoteka);

//kreiranje objekta StreamWriter

► pisanje.Flush(); //uporaba metode objekta

► new Random().Next(1,100);

//uporaba metode na objektu iz razreda Random

► Console.WriteLine();

//uporaba metode WriteLine na objektu Console

Naučili smo se torej uporabnosti objektnega programiranja, nismo pa se še naučili izdelave svojih razredov. In če se vrnemo na razlago v uvodu – kot uporabniki čisto nič ne vemo (in nas pravzaprav ne zanima), kako metoda Next določi naključno število. In tudi, če se bo kasneje "škatla" Random spremenila in bo metoda Next delovala na drug način, to ne bo "prizadelo" programov, kjer smo objekte razreda Random uporabljali. Seveda, če bo sporočilni sistem ostal enak. V omenjenem primeru to pomeni, da se bo metoda še vedno imenovala Next, da bo imela dva parametra, ki bosta določala meje za naključna števila in da bo odgovor (povratno sporočilo) neko naključno število z želenega intervala. Seveda pa nismo omenjeni le na to, da bi le uporabljali te "črne škatle", kot jih pripravi kdo drug (npr. jih dobimo v standardni knjižnici jezika). Objekti so uporabni predvsem zato, ker lahko programer definira nove razrede in objekte, torej sam ustvarja te nove črne škatle, ki jih potem on sam in drugi uporablja. Oglejmo si primer programa v C#, ki uporablja objekte, ki jih napišemo sami.

PRVI OBJEKTNI PROGRAM

1: using System;

2:

3: public class MojR {

4: private string mojNiz;

5:

6: public MojR(string nekNiz) {

7: mojNiz = nekNiz;

8: }

9:

10: public void Izpisi() {

11: Console.WriteLine(mojNiz);

12: }

13: }

14:

15: public class Pozdrav {

16: public static void Main(string[] arg) {

17: MojR prvi = new MojR("Pozdravljen, moj prvi objekt v C#!");

18: prvi.Izpisi();

19: }

20: }

Page 13: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

13

Program napišimo kar na običajni način v okolju SharpDevelop – torej New Solution/Windows Applications/Console Application. Vnesene vrstice kar vse pobrišimo in napišimo zgornjo kodo. Ko program prevedemo in poženemo, dobimo

Na kratko razložimo, "kaj smo se šli". V vrsticah 3 – 13 smo definirali nov razred z imenom MojR. Ta je tak, da o vsakem objektu te vrste poznamo nek niz, ki ga hranimo v njem (vrstica 4). Vse znanje, ki ga objekti tega razreda imajo, je, da se znajo odzvati ne metodo Izpis(), ko izpišejo svojo vsebino (torej niz, ki ga hranimo v njem). V vrsicah 15 – 20 je naš "pravi program", torej tisti del rešitve, ki opravi dejansko neko delo. V vrstici 17 naredimo primerek objekta iz razreda MojR in vanj shranimo niz "Pozdravljen, moj prvi objekt v C#!". V vrstici 18 pa temu objektu naročimo, naj izpiše svojo vsebino.

Objekti o stanja:

Page 14: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

14

Lastnosti, podatki, komponente o “znanje”

Odzivanje na dogodke o Združeno v celoto

Podatki in metode, ki opisujejo neko stvar/objekt Žoga:

Podatki: velikost, barva, položaj v prostoru, …

Metode: sprememba velikosti, sprememba položaja, … Ulomek:

Podatki: števec, imenovalec

Metode: spremeni števec, spremeni imenovalec, seštej, obratna vrednost, lepo izpiši, ... o Razlika med žogo kot “idejo žoge” in “mojo žogo” (konkretni primerek – realizacija ideje žoge) oziroma

med ulomkom “kar tako” in ulomkom ¾.

Programiranje v C# Sestavljanje razredov:

Opis lastnosti objektov Opis metod (“znanja” objektov):

Ustvarjanje objektov in njihova uporaba

"Ukazovanje" objektom, kaj naj počnejo

Objekt za izpolnitev določene naloge potrebuje druge objekte in njihove metode Začetek:

Glavni razred (ki ima metodo Main) Izvajanje metode Main – ustvarjanje objektov, proženje dogodkov, odzivanje na dogodke, …

Razred je abstraktna definicija objekta in če želimo nek razred uporabiti, mora običajno obstajati vsaj en

primerek razreda. Primerek nekega razreda imenujemo objekt. Ustvarimo ga s ključno besedo new. ImeRazreda primerek = new ImeRazreda();

Objekt je računalniški model predmeta ali stvari iz vsakdanjega življenja (avto, oseba, datum…). Za rešitev problema uvedemo množico objektov, ki si medsebojno pošiljajo sporočila. Lastnosti objektov so zapisane v

razredih (class), ki vsebujejo:

► podatke in ,

► metode oz. odzive objektov na sporočila.

Stanje objekta opišemo s spremenljivkami, njihovo obnašanje oz. odzive pa z metodami.

Objekti Objekt je kakršenkoli skupek podatkov, s katerimi želimo upravljati. Osnovni pristop objektnega programiranja:

objekt = podatki + metode za delo s podatki. Ko želimo kak podatek (objekt) obdelati, objektu (podatku) signaliziramo, kaj naj se zgodi:

Pokličemo ustrezno metodo v objektu. Objekt je "črna škatla“, ki sprejema in pošilja sporočila. Jedro objekta sestavljajo njegove spremenljivke, okrog katerih se nahajajo njegove metode.

Klasično/objektno programiranje Klasično programiranje:

program = metode Ko želimo kak podatek obdelati v klasičnem programiranju:

pokličemo ustrezno metodo z argumenti argumenti: podatki, ki naj jih obdela.

Če želimo izvesti metodo f na podatku x:

o Klasično:

Page 15: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

15

izvedi statično metodo f na podatku x f(x);

o Objektno: podatku/objektu x signaliziraj, da naj izvede metodo f x.f();

Od kje razredi? Veliko razredov je že vgrajenih (so v standardnih knjižnicah) v C# in njegovo oklolje. Taki razredi so na primer Math, System, StreamReader, …

o .NET Framework

C# je .NET jezik – ima dostop do vseh razredov, definiranih v knjižnicah (zbirkah razredov) okolja .NET

Glej npr. http://msdn2.microsoft.com/sl-si/library/ms229335(en-us).aspx

o Razporejeni v imenske prostore

System, System.IO …

Razrede lahko dobimo tudi iz drugih virov: o Naši “stari” razredi

o Drugi programerji

Potrebujemo dostop do ustrezne datoteke (dll, exe)

Objekt in ime spremenljivke NekiObjekt a;

a je naslov, kjer bo objekt (referenca na objekt) new NekiObjekt();

V pomnilniku se je naredil objekt tipa NekiObjekt() / po pravilih, ki so določena z opisom razreda NekiObjekt

a = new NekiObjekt();

V pomnilniku se je naredil objekt tipa NekiObjekt() in a kaže na ta novo ustvarjeni objekt

Ustvarjanje objektov Razred je šablona, ki definira spremenljivke in metode skupne vsem objektom iste vrste. class:

Načrt objekta, šablona Primerek razreda (instanca) – konkretni objekt:

Ustvarimo ga z new Brez tega objekta NE MOREMO uporabiti NekiObjekt a;

Objekt NE obstaja

a je IME objekta

natančneje

a je ime spremenljivke, kjer hranimo NASLOV objekta vrste NekiObjekt

C# in objekti Razred (class) je opis vrste objekta (načrt, kako naj bo objekt videti) – opis ideje objekta. Primerek razreda (instanca) – konkretni objekt.

Page 16: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

16

Ustvarjanje objektov Ulomek ime = new Ulomek(3, 4);

Rečemo: V objektu ime je shranjen ulomek ¾; Točneje: v spremenljivki ime je naslov nekega objekta tipa Ulomek, ki predstavlja ulomek ¾. Spremenljivka ime kaže na objekt tipa Ulomek, ki … Prvi ukaz je dejansko spoj dveh stavkov: deklaracija in prireditev Ulomek ime;

ime = new Ulomek(3, 4);

ime: _2h11b11__

2h11b11: ¾

o Brez new objekta še NI o včasih “skrito”

Nizi

string a = "bla";

string a = new string("bla");

Tabele

int[] tabela = {1, 2, 3};

int[] tabela = new int[3];

tabela[0] = 1; …

Zgled Poglejmo si še en zgled. Denimo, da bi radi napisali program, ki bo prebral nek ulomek in mu prištel 1/2. "Klasično" se bomo tega lotili takole (seveda pisec ni pozabil, kako se seštevajo ulomki, a takojle, po Janezkovo, si je lažje zapomniti ;-) )

POVEČAJMO ULOMEK KLASIČNO

1: using System;

2: namespace UlomkiKlasika {

3: class Program

4: {

5: public static void Main(string[] args)

6: {

Page 17: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

17

7: // vnos podatkov

8: Console.Write("Števec ulomka: ");

9: string beri = Console.ReadLine();

10: int stevec = int.Parse(beri);

11: Console.Write("Imenovalec ulomka: ");

12: beri = Console.ReadLine();

13: int imenovalec = int.Parse(beri);

14: // "delo"

15: stevec = stevec + 1;

16: imenovalec = imenovalec + 2;

17: // izpis

18: Console.WriteLine("Nov ulomek je: " + stevec + " / " +

imenovalec);

19: Console.Write("Press any key to continue . . . ");

20: Console.ReadKey(true);

21: }

22: }

23: }

Sedaj pa to naredimo še "objektno". Očitno bomo potrebovali razred Ulomek. Objekt tipa Ulomek hrani podatke o svojem števcu in imenovalcu. Njegovo znanje je, da "se zna" povečati za drug ulomek. To pomeni, da vsebuje metodo, ki ta ulomek poveča za drug ulomek. Shematsko bo program (kjer so določeni deli skriti) videti kot

Cel program pa bo:

POVEČAJMO ULOMEK - OBJEKTNO

24: using System;

25: namespace UlomkiObjektno

26: {

27: class Ulomek {

28: public int stevec;

29: public int imenovalec;

30:

Page 18: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

18

31: public Ulomek(int st, int im) {

32: this.stevec = st;

33: this.imenovalec = im;

34: }

35:

36: public void Pristej(Ulomek a) {

37: this.stevec = this.stevec + a.stevec;

38: this.imenovalec = this.imenovalec + a.imenovalec;

39: }

40: }

41: class Program

42: {

43: public static void Main(string[] args)

44: {

45: // vnos podatkov

46: Console.Write("Števec ulomka: "); 47: string beri = Console.ReadLine(); 48: int stevec = int.Parse(beri); 49: Console.Write("Imenovalec ulomka: "); 50: beri = Console.ReadLine(); 51: int imenovalec = int.Parse(beri); 52: Ulomek moj = new Ulomek(stevec, imenovalec); 53: // "delo" 54: Ulomek polovica = new Ulomek(1, 2); 55: moj.pristej(polovica); 56: // izpis 57: Console.WriteLine("Nov ulomek je: " + moj.stevec + " / " + 58: moj.imenovalec); 59: } 60: } 61: }

Če posebej označimo ključne točke (poleg definicije razreda Ulomek v vrsticah 4 – 17):

Objektno programiranje Imamo nek problem. Kako se torej lotiti načrtovanja rešitve s pomočjo objektnega programiranja?

Page 19: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

19

Z analizo ugotovimo, kakšne objekte potrebujemo za reševanje Pregledamo, ali že imamo na voljo ustrezne razrede

Standardna knjižnica

Druge knjižnice

Naši stari razredi Sestavimo ustrezne manjkajoče razrede Sestavimo “glavni” program, kjer s pomočjo objektov rešimo problem

Objekti, razredi, ...

Združevanje podatkov Denimo, da pišemo program, ki bo pomagal upravljati farmo zajcev. Za vsakega zajca poznamo:

serijsko številko spol težo

Kako doseči, da se podatki “držijo” skupaj: Tabela ni ustrezna

Tabela je zbirka istovrstnih podatkov

Pri tabelah smo omenili, da so sestavljen podatkovni tip. Vsebujejo elemente istega tipa. Velikokrat pa želimo ustvariti tip, ki je prav tako sestavljen, a iz različnih podatkovnih tipov. V ta namen imamo v jeziku C# tako imenovane strukture. A ker gre v bistvu le za posebni primer objektov, jih bomo kar preskočili in si rajši kar neposredno ogledali objekte. Da pa boste vedeli zakaj gre, če boste v kakšnem

programu naleteli na ključno besedo struct, si zapomnimo le, da gre za nekakšne "poenostavljene" objekte. Sestavimo razred Zajec Opis podatkov, ki sestavljajo poljubnega zajca

V zgledih do sem smo razrede pisali le za "lokalno uporabo", znotraj določenega programa. Sedaj se naučimo še, kako na najlažji način gradimo svojo knjižnico razredov, ki jih bomo uporabljali v različnih programih. Na ta način bomo tudi bolj ločili tisti del programiranja, ko gradimo razrede in tisti del, ko uporabljamo objekte določenega razreda. V okolju SharpDevelop tokrat ne izberemo Windows Applications/Console Application, ampak C# / Class Library

Page 20: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

20

Kot ime bomo napisali MojiRazredi. V ta projekt bomo dodajali vse tiste razrede, ki jih bomo ustvarjali,

vključno z našim razredom Zajec.

Vidimo, da je SharpDevelop pripravil okolje in naredil tako imenovam imenski prostor MojiRazredi. Znotraj

tega imenskega prostora bomo zlagali naše razrede. Ker nam ime MyClass ni všeč, bomo to spremenili v

Zajec. Omenimo še komentarje, ki se začno z ///. To so tako imenovani dokumentacijski komentarji. Ti služijo za to, da pripravimo dokumentacijo o razredu. Ta je zelo pomembna, saj nosi tisto informacijo, ki jo kasneje potrebujemo, če želimo razred uporabljati. Zanekrat si zapomnimo le, da na ustrezno mesto napišemo kratek povzetek o tem, čemu je razred namenjen. Več o tem si lahko preberete:

http://www.softsteel.co.uk/tutorials/cSharp/lesson19.html

http://www.winnershtriangle.com/w/Articles.XMLCommentsInCSharp.asp

http://www.csharphelp.com/archives3/archive598.html

...

RAZRED ZAJEC

21: public class Zajec {

22: public string serijska;

23: public bool spol;

24: public double masa;

25: }

Page 21: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

21

S tem imamo napisan opis, kako je določen poljuben zajec:

Načrt, kakšni so zajci Ni to konkreten zajec

Ni metode Main, dejansko se NIČ ne izvede, ... Ni namenjeno poganjanju kot program. Zato bo stvar potrebno prevesti drugače. V Build izberemo Build Moji Razredi (oziroma pritisnemo na F9). S tem smo ustvarili ustrezno prevedeno obliko in sicer MojiRazredi.dll. TA vsebuje med drugim tudi definicijo razreda Zajec.

Uporaba razreda Zajec Denimo, da sedaj pišemo nek program, kjer bomo potrebovali Zajce. V ta namen sedaj sestavimo program v C#. Obnašamo se tako kot smo pisali programe do sedaj.

v SharpDevelop: File/New Solution/Winows applications/Console Application

Sedaj moramo dodati še t.i. referenco, da bo naš program prepoznal naš razred.

Project / Add reference

Page 22: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

22

V oknu, ki se odpre, izberemo .Net Assembly Browser. Poiščemo imenik MojiRazredi/bin/Debug in v njem datoteko MojiRazredi.dll.

Da bomo krajše pisali, dodamo sedaj imenski prostor MojiRazredi (ker vsebuje definicijo razreda Zajec)

using MojiRazredi;

Če sedaj v programu potrebujemo konkretnega zajca, ga “ustvarimo” z new: new Zajec()

Ustvaril se je konkreten zajec po navodilih za razred Zajec (ta zajec ima torej tri podatke / lastnosti / komponente)

Metoda je vrnila naslov, kje ta konkretni zajec je Zajec rjavko = new Zajec();

V spremenljivki rjavko je naslov, kje je novo ustvarjeni zajec (objekt)

Dostop do podatkov v objektu rjavko.spol = true;

rjavko.serijska = “BRGH_17_A”;

rjavko.masa = 3.2;

UPORABA RAZREDA ZAJEC

26: using System;

27: using MojiRazredi;

62:

Page 23: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

23

63: namespace TestZajec

64: {

65: class Program

66: {

67: public static void Main(string[] args)

68: {

69: Zajec z1 = new Zajec();

70: z1.serijska = "1238-12-0";

71: z1.spol = false;

72: z1.masa = 0.12;

73: z1.masa = z1.masa + 0.3;

74: Console.WriteLine("Zajec ima ser. št.:" + z1.serijska);

75:

76: Console.Write("Press any key to continue . . . ");

77: Console.ReadKey(true);

78: }

79: }

80: }

FARMAZAJCEV

28: public class FarmaZajcev {

29: public static void Main(atring[] ar) {

30: Zajec[] zajci = new Zajec[10]; // na farmi imamo do 10 zajcev

31: int i = 0;

32: while (i < 10) {

33: zajci[i] = new Zajec(); // “rodil” se je nov zajec

34: zajci[i].serijska = "1238-12-“ + i;

35: zajci[i].spol = false;

36: zajci[i].masa = 0.12;

37: i++;

38: }

39: ...

40: }

41: }

Page 24: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

24

Page 25: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

25

Še en primer - Ulomek Denimo, da bi potrebovali še podatkovni tip Ulomek. Naš projekt MojiRazredi (seveda bi lahko naredili novo knjižnico razredov) dopolnimo z

RAZRED ULOMEK

81: public class Ulomek {

82: public int stevec;

83: public int imenovalec;

84: }

Potem, ko z Build/Build MojiRazredi prevedemo stvar na novo, je knjižnica z novim razredom Ulomek pripravljena za uporabo. To pomeni, da novi razred (če želite – novi podatkovni tip) lahko uporabljamo v drugih programih, kot tudi pri sestavljanju drugih razredov. Ne pozabimo – če želimo narediti objekt vrste (tipa) Ulomek, napišemo Ulomek x = new Ulomek();

Kako “napolniti” stevec in imenovalec?

x.stevec : spremenljivka tipa int!

x.imenovalec : enako

x.stevec = 1;

x.imenovalec = x.stevec + 1;

Page 26: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

26

No, ne pozabimo, da če določen razred potrebujemo le v sklopu rešitev enega primera, ga lahko ustvarimo tudi "lokalno", znotraj tistega programa.

PRIMER IZDELAVE IN UPORABE RAZREDA

42: using System;

43: public class Vozilo

44: {

45: public int letnik;

46: public string proizvajalec;

47: public int prevozeniKilometri;

48: }

49: class Program

50: {

51: public static void Main()

52: {

53: Vozilo mojAvto = new Vozilo();//kreiramo primerek

54: mojAvto.proizvajalec = "Citroën";

55: mojAvto.letnik = 2000;

56: mojAvto.prevozeniKilometri = 90000;

57: Console.WriteLine("Proizvajalec: " + mojAvto.proizvajalec);

58: Console.WriteLine("Letnik: " + mojAvto.letnik);

59: Console.WriteLine("Kilometri: " + mojAvto.prevozeniKilometri);

60: }

61: }

Razlaga. Najprej ustvarimo razred z imenom vozilo (vrstice 2-7). V glavnem programu kreiramo nov primerek

(objekt) z imenom mojAvto razreda Vozilo (vrstica 12). Objektu določimo vrednosti (vrstice 13-15) in izpišemo vrednosti (vrstice 16-18).

Povzetek Najenostavnejši razred kreiramo tako, da določimo spremenljivke, ki določajo objekte tega razreda. Te so lahko različnih podatkovnih tipov. Sintaksa najenostavnejše definicije razreda je naslednja: public class ImeRazreda {

public podatkovni tip element1;

public podatkovni tip element2;

public podatkovni tip elementn;

}

Če želimo tak razred uporabljati v nekem programu, moramo v programu dodati referenco na ustrezno prevedeno obliko (dll) tega razreda.

Objekt tega razreda ustvarimo z new ImeRazreda(); S tem dobimo naslov, kje ta objekt je in ga shranimo v spremenljivko tipa ImeRazreda:

ImeRazreda imeObjekta = new ImeRazreda();

Do posameznih spremenljivk (rečemo jim tudi komponente ali pa lastnosti), dostopamo z imeObjekta.imeKomponente

Konstruktorji Ob tvorbi objekta bi radi nastavili začetno stanje spremenljivk (in morda opravili še kaj – a o tem kasneje).

Page 27: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

27

V ta namen imamo v C# posebne metode, ki jih imenujemo konstruktorji.

Konstruktor je metoda, ki jo pokličemo ob tvorbi objekta z new. Je brez tipa rezultata. To ne smemo zamenjati

z metodami, ki rezuultata ne vračajo – so tipa void). Konstruktor tipa rezultata sploh nima, tudi void ne. Prav tako smo omenjeni pri izbirri imena te metode. Konstruktor mora imenti ime nujno tako. kot je ime razreda. Največja omejitev, ki loči konstruktorje od drugih metod pa je ta, da konstruktor ne moremo klicati na

poljubnem mestu (kot lahko ostale metode). Kličemo ga izključno skupaj z new:

Klic: new Zajec();

Razred Zajec

RAZRED ZAJEC S KONSTRUKTORJEM

85: public class Zajec {

86: public string serijska;

87: public bool spol;

88: public double masa;

89:

90: // konstruktor

91: public Zajec() {

92: this.spol = true; // vsem zajcem na začetku določimo m. spol

93: this.masa = 1.0; // in tehtajo 1kg

94: this.serijska = “NEDOLOČENO”;

95: }

96: }

this V zgornjem primeru smo v konstruktorju uporabili prijem, ki ga doselj še nismo srečali. Napisali smo this. Kaj

to pomeni?

this označuje objekt, ki ga "obdelujemo". V konstruktorju je to objekt, ki ga ustvarjamo. this.spol se torej

nanaša na lastnost/komponento spol objekta, ki se ustvarja (ki ga je ravno naredil new). Denimo, da v nekem programu napišemo

Zajec rjavko = new Zajec();

Zajec belko= new Zajec();

Pri prvem klicu se ob uporabi konstruktorja Zajec() this nanašal na rjavko, pri drugem na belko.

Na ta način (z this) se lahko sklicujemo na objekt vedno, kadar pišemo metodo, ki jo izvajamo nad nekim

objektom. Denimo, da smo napisali Random ng = new Random();

Random g2 = new Random();

Console.WriteLine(ng.Next(1,10));

Kako so programerji, ki so sestavljali knjižnico in v njej razred Random lahko napisali kodo metode, da se je

vedelo, da pri metodi Next mislimo na uporabo generatorja ng in ne na g2?

Denimo, da imamo razred MojRazred (v njem pa komponento starost) in v njem metodo MetodaNeka. V

programu, kjer razred MojRazred uporabljamo, ustvarimo dva objekta tipa MojRazred. Naj bosta to objA

in objC. Kako se znotraj metode MetodaNeka sklicati na ta objekt (objekt, nad katerim je izvajana metoda)?

Če torej metodo MetodaNeka kličemo nad obema objektoma z objA.MetodaNeka() oziroma z

objC.MetodaNeka(), kako v postopku za metodaNeka povedati, da gre:

Prvič za objekt z imenom objA

drugič za objekt z imenom objC

Če se moramo v kodi metode metodaNeka sklicati recimo na komponento starost (jo recimo izpisati na zaslon),

kako povedati, da naj se ob klicu objA.MetodaNeka() uporabi starost objekta objA, ob klicu

objC.MetodaNeka() pa starost objekta objC?

Console.WriteLine("Starost je: " + ?????.starost);

Ob prvem klicu so ???? objA, ob drugem pa objC. To "zamenjavo" dosežemo z this. Napišemo

Console.WriteLine("Starost je: " + this.starost);

Page 28: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

28

Ob prvem klicu this pomeni objA, ob drugem pa objC.

Torej v metodi na tistem mestu, kjer potrebujemo konkretni objekt, nad katerim metodo izvajamo, napišemo this.

Uporaba razreda: Kot smo povedali že večkrat, je razred le načrt, kako so videti objkekti določenega razreda. Če potrebujemo konkreten primer, v našem primeru Zajca, ga “ustvarimo” z new:

new Zajec()

S tem se je ustvaril konkreten zajec (torej tak, ki ima tri spremenjlivke za hranjenje podatkov), nad katerim smo

takoj potem izvdeli določeno akcijo po navodilih iz konstruktorja Zajec(). Kombinacija new Zajec()je

torej poskrbela, da ima objekt tipa Zajec tri podatke z vrednostmi, kot je predpisano v konstruktorju.

Privzeti konstruktor Če konstruktorja ne napišemo (kot ga nismo prej), ga “naredi” prevajalnik sam (a metoda ne naredi nič). Dokler je bil naš razred zajec

RAZRED ZAJEC

1: public class Zajec {

2: public string serijska;

3: public bool spol;

4: public double masa;

5: }

je prevajalnik sam dodal privzeti kostruklor.

PRIVZETI KONSTRUKTOR RAZREDA ZAJEC

97: public Zajec() {

98: }

Kakor hitro pa smo konstruktor Zajec() napisali sami, se seveda prevajalnik ni več "vmešaval":

Ko smo napisali

RAZRED ULOMEK

99: public class Ulomek {

100: public int stevec; 101: public int imenovalec; 102: } je torej to enako, kot če bi napisali

RAZRED ULOMEK S PRIVZETIM KONSTRUKTORJEM

103: public class Ulomek { 104: public int stevec; 105: public int imenovalec; 106: 107: public Ulomek() { 108: } 109: }

Konstruktorjev ne moremo klicati posebej (kot ostale metode), ampak le, ko tvorimo objekt z new.

Konstruktorje uporabljamo za vzpostavitev začetnega stanja objekta. Ne pozabimo, da imajo enako

ime kot razred. Nimajo tipa rezultata (tudi void ne!). Prat tako ne vsebujejo stavka return.

Page 29: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

29

Več konstruktorjev Uporabniki bi poleg privzetega zajca, radi še možnost, da bi takoj, ko zajca naredijo, temu določili še serijsko številko. Radi bi torej konstruktor

public Zajec(string serijskaSt)

Poleg tega pa včasih na farmo dobijo tudi pošiljko samic. Torej potrebujejo še konstruktor public Zajec(bool spol)

Včasih pa so zajci "nestandardni" public Zajec(string serijskaSt, bool spol, double teza)

Potrebovali bi torej, da ima razred več konstruktorjev. A ker morajo imeti vsi konstruktorji ime točno tako, kot je ime razreda, to pomeni, da mora biti metoda

public Zajec(string serijskaStev, bool spol, double masa)

Ker pa imamo že od prej metodo

public Zajec()

to pomeni, da bi imeli več metod z enakim imenom. Je to sploh možno? Jezik C# podpria tako imenovano preobteževanje (overloading) metod. Tako imamo lahko znotraj istega programa več metod z enakim imenom. To ne velja le za konstruktorje, ampak tudi v splošnem. Metode se morajo razlikovati ne v imenu, ampak podpisu. Podpis metode je sestavljen iz imena metode in tipov vseh parametrov. Konstruktor

Zajec()

ima torej podpis enostavno Zajec, konstruktor public Zajec(string serijskaStev, bool spol, double masa)

pa podpis Zajec_string_bool_double

Preobtežene metode Kot smo omenili, so lahko preobtežene tudi “navadne” metode (ne le konstruktorji). Prav tako enako velja Še enkrat – preobteženost označuje možnost, da imamo več enako poimenovanih metod, ki pa sprejemajo parametre različnega tipa. Zakaj je to uporabno? Denimo, da imamo metodo Ploscina, ki kot parameter dobi objekt iz razreda Kvadrat, Krog ali Pravokotnik. Kako jo začeti? public static double Ploscina(X lik)

Ker ne vemo, kaj bi napisali za tip parametra, smo napisali kar X. Če preobteževanje ne bi bilo možno, bi morali napisati metodo, ki bi sprejela parameter tipa X, kjer je X bodisi Kvadrat, Krog ali Pravokotnik. Seveda to ne gre, saj prevajalnik nima načina, da bi zagotovil, da je X oznaka bodisi za tip Kvadrat, tip Krog ali pa tip Pravokotnik. Pa tudi če bi šlo – kako bi nadaljevali? V kodi bi morali reči ... če je lik tipa kvadrat, uporabi to formulo, če je lik tipa Krog spet drugo ... S preobteževanjem pa problem rešimo enostavno. Napišemo tri metode public static double Ploscina(Kvadrat lik) { …

public static double Ploscina(Krog lik) { …

public static double Ploscina(Pravokotnik lik) { …

Ker za vsak lik znotraj ustrezne točno vemo, kakšen je, tudi ni težav z uporabo pravilne forumule. Imamo torej tri metode, z enakim imenom, a različnimi podpisi. Seveda bi lahko problem rešili brez preobteževanja, recimo takole: public static double PloscinaKvadrata(Kvadrat lik) { …

public static double PloscinaKroga(Krog lik) { …

public static double PloscinaPravokotnika(Pravokotnik lik) { …

A s stališča uportabnika metod je uporaba preobteženih metod enostavnejša. Če ima nik lik bla, ki je bodisi tipa

Kvadrat, tipa Krog ali pa tipa Pravokotnik, metodo pokliče z Ploscina(bla), ne da bi mu bilo potrebno razmišljati, kakšno je pravilno ime ustrezne metode ravno za ta lik. Prav tako je enostavneje, če dobimo še

Page 30: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

30

četrti tip objekta – v “glavno” kodo ni potrebno posegati – naredimo le novo metodo z istim imenom

(Ploscina) in s parametrom katerega tip je novi objekt. Klic pa je še vedno Ploscina(bla)!

Podpisi metod Podpis metode:

Podpis: ime + število in tipi parametrov! Tip rezultata (return tip) NI del podpisa!

public static int NekaMetoda(double x)

Podpis metode:

public static int NekaMetoda(double x)

Podpis: NekaMetoda_double “interno” ime

Kaj je lahko sočasno: public static int NekaMetoda()

public static int NekaMetoda(double y)

public static int NekaMetoda(double x)

public static double NekaMetoda(double x)

public static int nekaDrugaMetoda(double y)

Konstruktorji razreda Zajec – navzkrižno sklicevanje Poglejmo, kako bi kostruktorje razreda Zajec napisali "zares".

KONSTRUKTORJI RAZREDA ZAJEC

62: public Zajec() {

63: this.spol = true; // vsem zajcem na začetku določimo m. spol

64: this.masa = 1.0; // in tehtajo 1kg

65: this.serijska = “NEDOLOČENO”;

66: }

67:

68: public Zajec(string serijskaStev) : this() {

69: this.serijska = serijskaStev;

70: }

71: public Zajec(string serijskaStev,

72: bool spol, double masa) : this(serijskaStev) {

73: this.masa = masa; // popravimo maso

74: this.spol = spol; // popravimo spol

75: }

Ključna beseda this v zgornjem zgledu nastopa v dveh vlogah. Enkrat je uporabljena za ime objekta, kar že

poznamo (this.spol, this.serijska …), novo pa je uporaba this( takoj za seznamom parametrov

nekega konstruktorja.

Še malo o this o this(

Za klic drugega konstruktorja pred vsemi ostalimi stavki drugega konstruktorja

this()

this(<parametri>)

o this.

Za dostop do lastnosti

this.serijska

Če ni možnosti zamenjave, lahko izpustimo

Page 31: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

31

serijska this.spol = spol;

Obstajalo je že DRUGO ime spol

Ločiti med spremenljivko spol, ki je parameter in lastnostjo objekta z imenom spol

"Prednost" ima bolj "lokalna" stvar – torej parameter

spol = spol;

Zgled Oglejmo si še en primer sestavljanja razredov. Denimo, da bi radi napisali program, ki vodi evidenco o članih športnega kluba. Podatki o članu obsegajo ime, priimek, letnico vpisa v klub in vpisno številke (seveda je to poenostavljen primer). Torej objekt, ki predstavlja člana kluba, vsebuje štiri podatke:

RAZRED CLAN

76: public class Clan {

77: public string ime;

78: public string priimek;

79: public int leto_vpisa;

80: public string vpisna_st;

81: }

S tem smo povedali, da je vsak objekt tipa Clan sestavljen iz štirih komponente: ime, priimek, leto_vpisa, vpisna_st. Če je a objekt tipa Clan, potem lahko dostopamo do njegove komponente ime tako, da napišemo a.ime. Nov objekt naredimo z ukazom new, kot to kaže naslednji primer:

TESTKLUB

82: public class TestKlub {

83: public static void Main(string[] args) {

84:

85: Clan a = new Clan();

86: a.ime = "Janez";

87: a.priimek = "Starina";

88: a.leto_vpisa = 2000;

89: a.vpisna_st = "2304";

90:

91: Clan b = new Clan();

92: b.ime = "Mojca";

93: b.priimek = "Mavko";

94: b.leto_vpisa = 2001;

95: b.vpisna_st = "4377";

96:

97: Clan c = b;

98: c.ime = "Andreja";

99:

100: Console.WriteLine("Clan a:\n" + a.ime + " " + a.priimek + 101: " " + a.leto_vpisa + " (" + a.vpisna_st + ")\n"); 102: 103: Console.WriteLine("Clan b:\n" + b.ime + " " + b.priimek + 104: " " + b.leto_vpisa + " (" + b.vpisna_st + ")\n"); 105: 106: Console.WriteLine("Clan c:\n" + c.ime + " " + c.priimek + 107: " " + c.leto_vpisa + " (" + c.vpisna_st + ")\n"); 108: Console.ReadLine(); 109: } 110: }

Page 32: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

32

Hm, kako to, da sta dva zadnja izpisa enaka? V programu smo naredili dva nova objekta razreda Clan, ki sta shranjena v spremenljivkah a in b. Kot vedno, se nove objekte naredi z ukazom new. Spremenljivka c pa je isti objekt kot b, se pravi, da se v 16. vrstici ni naredila nova kopija objekta b, ampak se spremenljivki b in c sklicujeta na isti objekt. In zato smo z vrstico 23 vplivali tudi na ime objekta b! V vrsticah 4—8 smo naredili objekt a in nastavili vrednosti njegovih komponent. Takole nastavljanje je v praksi dokaj neprimerno, ker se zlahka zgodi, da kako komponento pozabimo nastaviti. Zato C# omogoča, da delamo nove objekte na bolj praktičen način s pomočjo konstruktorjev. Konstruktor je posebna metoda, ki ima enako ime kot je ime razreda. Konstruktor uporabimo v ukazu new. C# najprej naredi nov objekt, nato pa pokliče konstruktor. Znotraj konstruktorja se pravkar narejeni objekt imenuje this. Primer:

111: public class Clan { 112: public string ime; 113: public string priimek; 114: public int leto_vpisa; 115: public string vpisna_st; 116: 117: public Clan(string i, string p, int l, string v) { 118: this.ime = i; 119: this.priimek = p; 120: this.leto_vpisa = l; 121: this.vpisna_st = v; 122: } 123: }

Poskusimo – ali naše "pacanje" po razredu kaj vpliva na testni program. Ko poskusimo prevesti program, prevajalnik "protestira". Kako, kaj pa tisto govorjenje o tem, da spremembe razreda ne vplivajo na programe, ki razred le uporabljajo. Se je kje spremenilo kaj na nivoju sporočil? Saj jih v prvotnem programu sploh nimamo. Torej se ne bi smelo nič pokvariti!

Razlog je v tem, da uporabimo privzeti konstruktor (Clan()), ki ga pa v razredu Clan nismo napisali. Ampak,

saj ga prej tudi ni bilo? Kako je pa prej delovalo? Kot smo rekli že prej, če konstruktorja ne napišemo, C# sam

doda konstruktor Clan(). Zato je prej zadeva delovala.

Kakor hitro pa napišemo poljubni konstruktor, se C# ne vmešava več. Zato sedaj ni sam dodal konstruktor

Clan(), in testni program ne dela več prav. V ta namen si je dobro zapomniti, da v razredih vedno napišemo

tudi konstruktor Clan().

RAZRED CLAN

110: public class Clan { 111: public string ime; 112: public string priimek; 113: public int leto_vpisa; 114: public string vpisna_st; 115: 116: public Clan() {

Page 33: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

33

117: this.ime = "Ne vem"; 118: this.priimek = "Ne vem"; 119: this.leto_vpisa = 0; 120: this.vpisna_st = "Ne vem"; 121: } 122: 123: public Clan(string i, string p, int l, string v) { 124: this.ime = i; 125: this.priimek = p; 126: this.leto_vpisa = l; 127: this.vpisna_st = v; 128: } 129: }

Ko sedaj poskusimo izvesti naš testni program, težav ni več in dobimo isti rezultat. A čemu smo potem pisali še drugi konstruktor in zakaj smo se sploh ubadali z njimi, če pa je stvar enaka kot prej. Poglejmo ta testni program

TESTCLAN

130: public class TestClan{ 131: public static void Main(string[] args) { 132: Clan a = new Clan("Janez", "Starina", 2000, "2304"); 133: Clan b = new Clan("Mojca", "Mavko", 2001, "4377"); 134: Clan c = b; 135: c.ime = "Katarina"; 136: 137: Console.WriteLine("Clan a:\n" + a.ime + " " + a.priimek + 138: " " + a.leto_vpisa + " (" + a.vpisna_st +

")\n");

139: 140: Console.WriteLine("Clan b:\n" + b.ime + " " + b.priimek + 141: " " + b.leto_vpisa + " (" + b.vpisna_st +

")\n");

142: 143: Console.WriteLine("Clan c:\n" + c.ime + " " + c.priimek + 144: " " + c.leto_vpisa + " (" + c.vpisna_st + ")\n"); 145: } 146: } Zgornji program deluje povsem enako kot prejšnji testni program, je pa vsekakor bolj pregleden. Namen konstruktorja je, da nastavi vrednosti komponent objekta. Ker konstruktorji (pa tudi druge metode znotraj

razreda) zelo pogosto dostopajo do komponent, nam C# omogoča, da namesto this.komponenta pišemo

kar komponenta:

RAZRED CLAN (BREZ THIS)

124: public class Clan { 125: public string ime; 126: public string priimek; 127: public int leto_vpisa; 128: public string vpisna_st; 129: 130: public Clan() { 131: ime = "Ne vem"; 132: priimek = "Ne vem"; 133: leto_vpisa = 0;

Page 34: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

34

134: vpisna_st = "Ne vem"; 135: } 147: 136: public Clan(string i, string p, int l, string v) { 137: ime = i; 138: priimek = p; 139: leto_vpisa = l; 140: vpisna_st = v; 141: } 142: }

Objektne metode Svoj pravi pomen dobi sestavljanje razredov takrat, ko objektu pridružimo še metode, torej znanje nekega objekta. Kot že vemo iz uporabe vgrajenih razredov, metode nad nekim objektom kličemo tako, da navedemo ime objekta, piko in ime metode. Tako če želimo izvesti metodo WriteLine nad objektom System.Console napišemo

System.Console.WriteLine("To naj se izpiše");

Če želimo izvesti metodo Equals nad nizom besedilo, napišemo besedilo.Equals(primerjava)

in tako dalje. Denimo, da potrebujemo metodo, ki vrne inicialke nekega člana. To bomo napisali takole:

METODA V RAZREDU CLAN

148: public string Inicialke() { 149: return this.ime[0] + "." + this.priimek[1] + "."; 150: } Oglejmo si še uporabo te metode v nekem testnem programu:

TESTCLAN

151: public class TestClan{ 152: public static void Main(string[] args) { 153: Clan a = new Clan("Janez", "Starina", 2000, "2304"); 154: string inicialnkeClanaA = a.Inicialke(); 155: Console.Write("Clan a:\n" + a.ime + " " + a.priimek + 156: " " + a.leto_vpisa + " (" + a.vpisna_st + ")

");

157: Console.WriteLine("ima inicialke: " + inicialnkeClanaA); 158: } 159: }

Denimo, da smo kasneje ugotovili, da bi bili uporabniki razreda Clan veliko bolj zadovoljni, če bi metoda Inicialke vrnila niz brez pik, le s presledkom med začetnico imena in priimka. Kaj storiti? Potrebno je popraviti le razred Clan, kjer spremnimo metodo

METODA V RAZREDU CLAN

160: public string Inicialke() { 161: return this.ime[0] + " " + this.priimek[0]; 162: } Ko razred ponovno prevedemo, imamo že vse pripravljeno za "pravilno" izvajanje vseh tistih programov, ki uporabljajo ta razred. Brez kakršnekoli spremembe v uporabniškem programu (in tudi brez ponovnega prevajanja tega programa), metoda sedaj deluje v skladu z v razredu narejenimi spremembami. Če torej opazimo, da je potrebno v razredu narediti kakšen popravek, le vsem našim uporabnikom pošljemo novo različico prevedenega razreda (ali knjižnice, ki vsebuje ta razred). Brez kakršnihkoli sprememb na strani uporabnika njihovi programi delujejo "popravljeno".

Page 35: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

35

Vemo, da z a = b;

kjer sta a in b obe spremenljivki tipa Clan, v a ne shranimo kopije objekta b, ampak sedaj a in b označujeta isti objekt. Zato napišimo metodo, ki naredi kopijo objekta.

a = b.Kopija(); V a je nov objekt, ki pa ima iste podatke kot b.

KOPIJA OBJEKTA

163: public Clan Kopija() { 164: Clan nov = new Clan(); 165: nov.ime = this.ime; 166: nov.priimek = this.priimek; 167: nov.leto_vpisa = this.leto_vpisa; 168: nov.vpisna_stevilka = 169: this.vpisna_stevilka; 170: } Uporabna je tudi metoda, ki izpiše objekt

IZPIS OBJEKTA

171: public void Izpis() { 172: Console.WriteLine("Clan:\n" + this.ime + " " + 173: this.priimek + " " + this.leto_vpisa + 174: " (" + this.vpisna_st + ")\n"); 175: } ali pa še

OPIS OBJEKTA

176: public string Opis() { 177: return this.ime + " " + this.priimek + " " + 178: this.leto_vpisa + 179: " (" + this.vpisna_st + "); 180: }

Če sedaj navedemo celotni razred

RAZRED CLAN

181: public class Clan { 182: public string ime; 183: public string priimek; 184: public int leto_vpisa; 185: public string vpisna_st; 186: 187: public Clan() { 188: ime = "Ne vem"; 189: priimek = "Ne vem"; 190: leto_vpisa = 0; 191: vpisna_st = "Ne vem"; 192: } 193: 194: public Clan(string i, string p, int l, string v) : this() { 195: ime = i; 196: priimek = p; 197: leto_vpisa = l;

Page 36: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

36

198: vpisna_st = v; 199: } 200: 201: public string Inicialke() { 202: return this.ime[0] + "." + this.priimek[0] + "."; 203: } 204: 205: public Clan Kopija() { 206: Clan nov = new Clan(); 207: nov.ime = this.ime; 208: nov.priimek = this.priimek; 209: nov.leto_vpisa = this.leto_vpisa; 210: nov.vpisna_stevilka = this.vpisna_stevilka; 211: return nov; 212: } 213: 214: public void Izpis() { 215: Console.WriteLine("Clan:\n" + this.ime + " " + 216: this.priimek + " " + this.leto_vpisa +

217: " (" + this.vpisna_st + ")\n");

218: } 219: 220: public string Opis() { 221: return this.ime + " " + this.priimek + " " +

222: this.leto_vpisa + " (" + this.vpisna_st + ");

223: } 224: }

In še zgled uporabe tega razreda

UPORABA RAZREDA CLAN

225: public class TestKlub { 226: public static void Main(string[] args) { 227: 228: Clan a = new Clan("Janez", "Starina", 2000, "2304"); 229: Clan b = new Clan("Mojca", "Mavko", 2001, "4377"); 230: Clan c = b; 231: c.ime = "Andreja"; 232: Clan d = b.Kopija(); 233: 234: Console.WriteLine("Clan a"); a.Izpis(); 235: Console.WriteLine("Clan b:\n" + b.Opis()); 236: Console.WriteLine("Clan c:\n" + c.Opis()); 237: Console.WriteLine("Clan d"); d.Izpis(); 238: Console.ReadLine(); 239: } 240: }

Page 37: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

37

Zgledi

Datum Denimo, da v naših programih pogosto delamo z datumi. Zato bomo sestavili ustrezni razred. Najprej moramo pripraviti načrt razreda. Odločiti se moramo torej, kako bi hranili podatke o datumu. Podatki, ki sestavljajo datum, so:

o dan (število) o mesec (izbira: število ali pa niz) o Leto (število)

Denimo, da se odločimo, da bomo dan in leto hranili kot število, mesec pa kot niz (torej kot januar, februar, ...). Sedaj se moramo odločiti, katere metode bo poleg konstruktorjev poznal naš razred. Nekaj idej:

o Lepo izpiši o Povečaj za 1 dan o Je datum smiselen o Je leto prestopno o Nov datum za toliko in toliko dni pred/za danim datumom o Dan v tednu o ...

Potem, ko smo si pripravili načrt, se lotimo sestavljanja razreda

RAZRED DATUM

241: public class Datum { 242: public int dan; 243: public string mesec; 244: public int leto; 245: public Datum() { 246: dan = 1; 247: mesec = "januar" 248: leto = 2000; 249: } // privzeti datum je torej 1.1.2000 Dodajmo še dva konstruktorja 250: public Datum(int leto) : this() { 251: this.leto = leto; // this je nujen 252: } // datum je torej 1.1.leto 253: public Datum(int d, string m, int l) : this(l) 254: { // leto smo ţe nastavili 255: this.mesec = m; // this ni nujen 256: this.dan = d; 257: } // datum je torej d.m.l

Sedaj se lahko lotimo metod. Kot prvno, nas zanima, ali je leto prestopno 258: public bool JePrestopno() { 259: int leto = this.leto; 260: if (leto % 4 != 0) return false; 261: if (leto % 400 == 0) return true; 262: if (leto % 100 == 0) return false; 263: return true; 264: }

Večjih težav ni bilo. No, verjetno bolj uporabna pa bop metoda, ki bo dani datum povečala za en dan. To pomeni, da bomo objektu, nad katerim bomo metodo izvedli, povečali dan za 1. Potem pa se lahko zaplete, saj s tem lahko naredimo neobstoječ datum, recimo 32. Januar. Sveda ne smemo pozabiti na prestopna leta. Lotimo se metode

Page 38: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

38

265: public void PovecajZaEnDan() { 266: dan = dan + 1; 267: if (dan < 29) return; 268: if (dan == 29 && mesec != "februar") return; 269: if (dan == 29 && mesec == "februar" && this.JePrestopno()) return; 270: // lahko nehamo, mesec in leto sta ok 271: string[] meseciPo30 = {"april","junij","september", "november"}; 272: if (dan == 31) { 273: if (meseciPo30.IndexOf(mesec) > 0){ 274: mesec = mesec + 1; 275: if (mesec == 13) { 276: mesec = 1; 277: leto++; 278: } 279: return; 280: } 281: // če je 32 dni, je zagotovo

Ko privandramo do sem, pa ugotovimo da imamo težave. Vrstica 34 je pač napačna (sintaktično sicer pravilna, a pomensko povsem zgrešena). Ker v spremenljivki mesec hranimo niz, bi morali namesto povečanja za 1 vzeti naslednje ime meseca. Tudi vrstica 35 je narobe .... Ko še enkrat premislimo, vidimo, da bi šlo z manj truda, če bi si tudi za mesec namesto imena meseca raje zapomnili kar številko. Če želimo datum videti z imeni mesecev pa za to lahko poskrbimo denimo ob izpisu. Zato se spodobi lotiti stestavljanja razreda datum znova. Vendar to, kot tudi preostale metode z našega spiska, prepustimo bralcu v izziv. Denimo, da smo razred Datum uspešno naredili. Sedaj pa ga še koristno uporabimo. Denimo, da želimo ugotoviti, če je letošnje leto prestopno. Dovolj bo, če le naredimo objekt, v katerega shranimo katerikoli letošnji datum in pokličemo pripravljeno metodo JePrestopno().

JE LETOŠNJE LETO PRESTOPNO

282: using MojiRazredi; 283: public class JeLetosPrestopnoLeto { 284: Datum danes = new Datum(22, 1, 2008); 285: if (danes.jePrestopno()) { 286: Console.WriteLine("Je prestopno leto"); 287: } else { 288: Console.WriteLine("Ni prestopno leto"); 289: } 290: }

Fibonaccijevo zaporedje V naravi večkrat opazimo zanimive vzorce. Tako če gremo šteti koliko je zrn v posamezni spirali sončničnega cveta pa tudi npr v številu lusk ananasa, brovega storža, semen na jagodi ... dobimo zanimivo zaporedje, ki ga imenujemo Fibonnaccijevo zaporedje. Fibonaccijeva števila definiramo z začetnima pogojema (prvo in drugo število sta enaka 1) ter z rekurzivno formulo (n-to Fibonaccijevo število je vsota prejšnjih dveh) . S temi podatki lahko izračunamo vsa ostala števila. Fibonaccijevo zaporedje je torej 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ... Če začetna pogoja spremenimo (rekurzivno pravilo pa ostane isto), dobimo posplošena Fibonaccijeva števila. Če si npr. za prvo število izberemo 3 in za drugo -1 dobimo zaporedje števil 3, -1, 2, 1, 3, 4, 7, 11...

Sestavimo razred PosploseniFibonacci, ki bo vseboval metodo public int VrniNto(int n), ki bo

izračunal n-to posplošeno Fibonaccijevo število. Razred naj vsebuje dva konstruktorja:

publicPosploseniFibonacci() - ta naj za začetni pogoj postavi pogoj za običajna

Fibonaccijeva števila.

Page 39: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

39

public PosploseniFibonacci(int prvo, int drugo) - temu podamo vrednosti

prvega in drugega posplošenega Fibonaccijevega števila.

Če torej ustvarimo objekta PosploseniFibonacci fibo = new PosploseniFibonacci() in

PosploseniFibonacci splosni = new PosploseniFibonacci(3, -1) nam klica metod

fibo.VrniNto(4) in splosni.VrniNto(4) vrneta 3 oz. 1.

Hraniti bo potrebno začetna člena

RAZRED POSPLOŠENIFIBONACCI

291: public class PosploseniFibonacci { 292: public int prvo; 293: public int drugo; 294: 295: public PosploseniFibonacci() { 296: this.prvo = 1; 297: this.drugo = 1; 298: } 299: 300: public PosploseniFibonacci(int p, int d) { 301: this.prvo = p; 302: this.drugo = d; 303: } Metoda bo enostaven primer uporabe rekurzije 304: public int VrniNto(int n) { 305: if (n == 1) return this.prvo; 306: if (n == 2) return this.drugo; 307: return this.VrniNto(n – 2) + 308: this.VrniNto(n – 1); 309: }

Tako, končali smo. A ni nam preveč ušeč hitrost tega našega razreda. Bi šlo hitreje? Zelo pogosto potrebujemo prvih 20 členov Fibonnacijevega zaporedja, ostala pa bolj redko. Kaj storiti, da izboljšamo odzivnost? Naj objekt hrani tabelo prvih 20 členov, Če potem uporabimo VrniNto(i) in je i <= 20, le pogledamo v tabelo. Le v tistih izjemni primerih, ko potrebujemo kasnejše člene pa uporabimo rekurzijo. Kje narediti tabelo? Seveda v konstruktorju, saj mora ta poskrbeti za začetne nastavitve!

IZBOLJŠANI RAZRED POSPLOŠENIFIBONACCI

310: public class PosploseniFibonacci { 311: public int[] tabela = new int[20] 312: public PosploseniFibonacci() { 313: tabela[0] = tabela[1] = 1; 314: for (int i = 2; i < 20; i++) { 315: tabela[i] = tabela[i – 2] + tabela[i - 1]; 316: } 317: } 318: public PosploseniFibonacci(int p, int d) { 319: tabela[0] = p; 320: tabela[1] = d; 321: for (int i = 2; i < 20; i++) { 322: tabela[i] = tabela[i – 2] + tabela[i - 1]; 323: } 324: } 325: public int VrniNto(int n) { 326: if (n < 20) return this.tabela[n – 1]; // če imamo ţe naračunanao 327: return this.VrniNto(n – 2) + // sicer uporabi rekurzijo 328: this.VrniNto(n – 1);

Page 40: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

40

329: } 330: }

Vaje

Kochova črta

Sestavi metodo, ki s pomočjo rekurzije izračuna dolžino vodoravne Kochove črte stopnje N, ki jo narišemo od koordinate x1 do koordinate x2. Na osnovi prejšnje metode sestavi metodo, ki izračuna dolžino Kochove črte, ki gre od točke T1 s koordinatami (x1,y1) do točke T2 s koordinatami (x2, y2). Namig: sestavi metodo, ki ima 5 parametrov: n, x1, x2, y1 in y2.

Razred Točka in Kochova črta

Pri prvi podtočki naloge 1 se koordinati x in y nista "držali" skupaj. Zato sestavi razred, ki vsebuje koordinato x in y in metodo iz naloge 1 prepiši tako, da bo imela tri parametre: n (stopnjo črte), T1 in T2 (ki sta tipa Tocka, ki si ga ravno sestavil).

Ulomek

Sestavite razred Ulomek in napišite metodo, ki ustvari tabelo naključnih ulomkov s števci med 1 in 10 ter imenovalci med 1 in 10. Velikost tabele naj bo parameter metode. Nato napišite še eno metodo, ki v tabeli ulomkov poišče največji ulomek. Še tretja metoda pa naj zna izpisati ulomek. Vse skupaj pa zložite v program, ki v tabeli naključnih ulomkov velikosti 20 izpiše vse ulomke, ki so (matematično) enaki največjemu ulomku. Če je npr. največji ulomek 2/4, naj program izpiše (seveda, če so v tabeli) tudi vse pojavitve ulomkov 1/2, 3/6, 5/10 in 4/8.

Razred Majica

Sestavite razred Majica, ki hrani osnovne podatke o majici:

o velikost: število med 1 in 5, o barvo: poljuben niz in o ali ima majica kratke ali dolge rokave.

Razred mora tudi poznati metode za nastavljanje in branje teh vrednosti. Podpisi teh metod naj bodo:

o public int VrniVelikost() ter public void SpremeniVelikost(int velikost)

o public string VrniBarvo() ter public void SpremeniBarvo(string barva)

o public boolean ImaKratkeRokave() ter public void NastaviKratkeRokave(boolean imaKratkeRokave)

Napiši testni program, ki naredi tabelo 100 različnih majic. Potem prepiši to tabelo v datoteko tako, da podatke o vsaki majici zapišeš v svojo vrstico.

Page 41: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

41

Nato razred Majica dopolni s konstruktorjem, ki kot parameter dobi niz v takšni obliki, kot si podatke zapisal na datoteko in v skladu s podatki v tem nizu nastavi ustrezne podatke o majici.

Testni program potem dopolni tako, da potem, ko si podatke o majicah zapisal na datoteko, narediš novo tabelo 100 majic, ki pa podatke o teh majicah prebere s pravkar ustvarjene datoteke.

Celotni program zaključi s tem, da napišeš še metodo, ki preveri, če sta obe tabeli enaki!

Razred Zajec

Denimo, da pišemo program, ki bo pomagal upravljati farmo zajcev. Za vsakega zajca poznamo serijsko številko, spol in maso.

Sestavi razred Zajec, ki vsebuje komponente:

spol //vrednost true, če je zajec moškega spola in false, če ženskega (kaj hočete, moški šovinisti ...)

masa //masa zajca v kilogramih (tip double)

serijska //serijska številka zajca (tip String)

Sestavi program, ki naredi tri zajce s težami, ki so naključna števila med 1.2 in 5.5. Izpiši vse tri teže zajcev. Enemu povečaj njegovo težo za 2kg. Izpiši serijsko številko najlažjega zajca.

Razred Kolega

Sestavi razred Kolega, ki ima tri komponente:

ime priimek telefonska številka

Vse tri komponente naj bodo tipa String in javno dostopne. Napiši konstruktor:

ki vse tri komponente nastavi na "NI PODATKOV".

Sestavi testni program, v katerem ustvariš dva objekta tipa Kolega. Oba objekta tudi izpiši na zaslon.

Uporaba razreda Kolega

Napiši program, ki sestavi tabelo 10ih objektov tipa Kolega, prebere telefonsko številko in izpiše tiste objekte iz tabele, ki imajo tako telefonsko številko. Če takega objekta v tabeli ni, naj se izpiše: "Noben moj kolega nima take telefonske številke."

Page 42: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

42

Uporaba razreda Zajec

Sestavi program, ki ustvari farmo zajcev (tabelo n zajcev) in med njimi izpiše:

1. najtežjega 2. najcenejšega 3. vse zajklje 4. najtežjega samca

Datum

Denimo, da v naših programih pogosto delamo z datumi. Zato bomo sestavili ustrezni razred. Vsebuje naj le dva konstruktorja.

Zajci z datumom

Razred Zajec nadgradi tako, da boš za vsakega zajca tudi hranil datum, klo se je skotil in datum, ko je bil zadnjič pregledan.

Uporaba razreda Zajec z datumom Ustvari farmo zajcev in na njej poišči vse tiste zajce, ki v tem koledarskem letu še niso bili pregledani. Nato pa izpiši še tiste zajklje, ki so bile se skotile v obdobju enega leta (torej med 23. 1. 2007 in 22. 1. 2008.

Nadgradimo datum

Razred Datum nadgradimo tako, da naj poleg kostruktorjev pozna naslednje metode

Lepo izpiši

Povečaj za 1 dan

Je datum smiselen

Je leto prestopno

Vrni nov datum za toliko in toliko dni pred danim datumom

Vrni nov datum za toliko in toliko dni za danim datumom

Vrni dan v tednu

...

Dostop do stanj objekta Možnost, da neposredno dostopamo do stanj/lastnosti objekta ni najboljši! Glavni problem je v tem, da na ta način ne moremo izvajati nobene kontrole nad pravilnostjo podatkov o objektu! Tako lahko za našega zajca

rjavka v programu mirno napišemo rjavko.masa = -3.2;

Ker ne moremo vedeti, ali so podatki pravilni, so vsi postopki (metode) po nepotrebnem bolj zapleteni, oziroma so naši programi bolj podvrženi napakam. Ideja je v tem, da naj objekt sam poskrbi, da bo v pravilnem stanju. Seveda potem moramo tak neposreden dostop do spremenljivk, ki opisujejo objekt, preprečiti. Dodatna težava pri neposrednem dostopu do spremenljivk, ki opisujejo lastnosti objekta se pojavi , če moramo kasneje spremeniti način predstavitve podatkov o objektu. S tem bi "podrli" pravilno delovanje vseh starih programov, ki bi temeljili na prejšnji predstavitvi.

Page 43: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

43

Torej bomo, kot smo omenjali že v uvodu, objekt res imeli za "črno škatlo", torej njegove notranje zgradbe ne bomo "pokazali javnosti". Zakaj je to dobro? Če malo pomilsimo, uporabnika razreda pravzaprav ne zanima, kako so podatki predstavljeni. Če podamo analogijo z realnim svetom:

Ko med vožnjo avtomobila prestavimo iz tretje v četrto prestavo, nas ne zanima, kako so prestave realizirane. Zanima nas le to, da se stanje avtomobila (prestava) spremeni. Ko kupimo nov avto nam je načeloma vseeno, če ima ta drugačno vrsto menjalnika, z drugačno tehnologijo. Pomembno nam je le, da se način prestavljanja ni spremenil, da še vedno v takih in takoh okoliščinah prestavimo iz tretje v četrto prestavo.

S "skrivanjem podrobnosti" omogočimo, da če pride do spremembe tehnologije se za uporabnika se stvar ne spremeni. V našem primeru programiranja v C# bo to pomenilo, da če spremenimo razred (popravimo knjižnico), se za uporabnike razreda ne bo nič spremenilo. Denimo, da so v C# 3.0 spremenili način hranjenja podatkov za določanje naključnih števi v objektih razreda Random. Ker dejansko nimamo vpogleda (neposrednega dostopa) v te spremenljivke, nas kot uporabnike razreda Random ta sprememba nič ne prizadane. Programe še vedno pišemo na enak način, kličemo iste metode. Skratka - ni potrebno spreminjati programov, ki razred Random uporabljajo.Še posebej je pomembno, da programi, ki so delovali prej, še vedno delujejo (morda malo bolje, hitreje, a bistveno je, da delujejo enako). In še enkrat .- s tem ko "ne dovolimo" vpogleda v zgradbo objekta, lahko poskrbimo za zaščito pred nedovoljenimi stanji. Do sedaj smo spremenljivke, ki opisujejo objekt, kazali navzven (imele so določilo public). To pomeni, da uporabnik lahko neposredno dostopa do teh spremenljivk in morebiti objekt spravi v nemogoče stanje:

mojAvto.prestava = -10;

Dostopi do stanj Do sedaj smo pisali programe tako, da smo naredili objekt. Ko smo želeli v objekt zapisati kak podatek, smo uporabili zapis

ime_objekta.stanje = <izraz>;

DOSTOP DO PODATKOV

331: public class TestClan{ 332: public static void Main(string[] args) { 333: Clan novClan = new Clan(); 334: novClan.ime = "Katarina"; 335: novClan.leto_vpisa = 208; 336: } 337: }

Problem s takim pristopom nam kaže ravno vrstica 5. Ker smo se zatipkali in vnesli nemogoč podatek, objekt novClan sedaj hrani napačen podatek. Radi pa bi, da take tipkarske napake "polovimo". Seveda bi lahko rekli – v redu, napisali bomo metodo, s katero bomo nastavljali leto vpisa in v metodi preverili, če je podatek smiselen. Takega pristopa smo se do sedaj že večkrat poslužili v zgledih in vajah. Če sedaj ponovno napišemo razred Clan, le da dodamo metodo za nastavljanjen leta vpisa

RAZRED CLAN Z METODOZA DOLOČANJE LETA VPISA

338: public class Clan { 339: public string ime; 340: public string priimek; 341: public int leto_vpisa; 342: public string vpisna_st; 343: 344: … // konstruktorji in metode kot prej! 345: public bool SpremeniLetoVpisa(int leto) { 346: if ((2000 <= leto) && (leto <= 2020)) { 347: this.leto_vpisa = leto; 348: return true; //leto je smiselno, popravimo stanje objekta in vrnemo

true

Page 44: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

44

349: } 350: return false; // leto ni smiselno, ne spremenimo nič in vrnemo false 351: }

352: }

In še zgled uporabe tega razreda

SPREMINJANJE Z METODO

353: public class TestKlub { 354: public static void Main(string[] args) { 355: 356: Clan novClan = new Clan(); 357: novClan.ime = "Katarina"; 358: novClan.leto_vpisa = 2007; 359: novClan.SpremeniLetoVpisa(208); 360: novClan.SpremeniLetoVpisa(2008);

Sedaj v 7. vrstici ne bomo podatka popravili na napačnega, ker letnica ni ustrezna, v 8. vrstici pa je bila sprmememba uspešna. Če bi želeli, bi to, ali je sprememba uspela, lahko tudi preverili tako, da bi pogledali, kakšno vrednost je vrnila metoda. Po drugi strani pa že iz vrstice 6 vidimo, da je naša "zaščita" zelo pomanjkliva. Če bi napisali še 361: novClan.leto_vpisa = 20008; bi se vrstica veselo izvedla in spet bi imeli v objektu napačen podatek. Če bi torej preprečili uporabo

ime_objekta.stanje = <izraz>;

in bi se stanje spremenljivk, ki opisujejo objekt lahko spreminjalo le preko metod, bi lahko poskrbeli, da bi pred spremembo podatka izvedli ustrezne kontrole in v primeru poskusa nastavljanja napačnih podatkov ustrezno reagirali. Po drugi strani pa tudi ni dobro, da se ime_objekta.stanje pojavi kjerkoli. Denimo, da imamo v nekem

programu napisano if (enClan.leto_vpisa > drugClan.leto_vpisa) { …

Kasneje pa ugotovimo da moramo razred Clan spremeniti in namesto leta_vpisa hraniti kompleten datum vpisa. Ni problema, sprememba par vrstic kode knjižnice MojiRazredi v razredu Clan in že je tukaj nov razred Clan

RAZRED CLAN Z DATUMOM

362: public class Clan { 363: public string ime; 364: public string priimek; 365: public Datum datum_vpisa; 366: public string vpisna_st; 367: 368: public Clan() { 369: ime = "Ne vem";

370: priimek = "Ne vem";

371: datum_vpisa = new Datum();

372: vpisna_st = "Ne vem";

373: } 374: 375: public Clan(string i, string p, Datum d, string v) : this() { 376: ime = i;

377: priimek = p;

Page 45: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

45

378: datum_vpisa = d;

379: vpisna_st = v;

380: } 381: 382: public string Inicialke() { 383: return this.ime[0] + "." + this.priimek[0] + "."; 384: } 385: 386: public Clan Kopija() { 387: Clan nov = new Clan();

388: nov.ime = this.ime;

389: nov.priimek = this.priimek;

390: nov.datum_vpisa = this.datum_vpisa.Kopija();

391: nov.vpisna_stevilka = this.vpisna_stevilka;

392: Return nov; 393: }

394: 395: public void Izpis() {

396: Console.WriteLine("Clan:\n" + this.ime + " " +

397: this.priimek + " " +

this.Datum_vpisa.OpisDat() +

398: " (" + this.vpisna_st + ")\n");

399: }

400: 401: public string Opis() {

402: return this.ime + " " + this.priimek + " " +

403: this.datum_vpisa.OpisDat() + " (" + this.vpisna_st + ");

404: }

405: public bool SpremeniLetoVpisa(int l) { 406: if ((2000 <= leto) && (leto <= 2020)) { 407: this.datum_vpisa.leto = l; 408: return true; //leto je smsielno, popravimo stanje onbjekta in vrnemo

true

409: } 410: return false; // leto ni smsielno, ne spremnimo inč in vrnemo false 411: }

412: } Preden nadaljujemo, opozorimo na vrstico 29. Če bi tam napisali

nov.datum_vpisa = this.datum_vpisa;

bi bilo to narobe. V novem objektu bio namreč ne hranili novega datuma, ampak bi le kazali na isti datum, kot ga vsebuje obstoječi objekt. In če bi potem kasneje spremnili datum v kateremoli od teh objektov, bi se spremil za oba objekta, saj gre za isti datum. Zato smo v 29. vrstici naredili tudi kopijo datuma! Dodatno razlago morda zahtevata še zapisa

this.datum_vpisa.Kopija();

in this.Datum_vpisa.OpisDat()

Pa ni nič čudnega. this pač označuje objekt, nad katerim bomo izvajali metodo. Ta objekt ima polje datum_vpisa. This.datum_vpisa je torej objekt tipa Datum. V tem razredu imamo metodi Kopija() in OpisDat(), ki ju izvedemo nad tem objektom. Pri vrstici 45 this.datum_vpisa.leto = l;

je podobno. this.datum_vpisa je objekt tipa Datum, torej ima polje leto.

Page 46: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

46

Vrnimo se k razlagi, zakaj ni dobro, da je uporabnik razreda Clan v svojem programu napisal npr. if (enClan.leto_vpisa > drugClan.leto_vpisa) { …

Če bi ta uporabnikov program sedaj poskusili uporabiti z našim razredom, pa seveda ne bi šlo, saj polja leto_vpisa sploh ni več! Zaradi spremembe tehnologije (spremembe razreda Clan) torej stari programi (ki uporabljajo razred Clan) ne delujejo več. Kako pa bi morali potem pisati? Že prej bi morali v razredu Clan ponuditi metodo, ki vrne leto vpisa.

RAZRED CLAN Z METODAMA ZA DOLOČANJE/VRAČANJE LETA VPISA

413: public class Clan { 414: public string ime; 415: public string priimek; 416: public int leto_vpisa; 417: public string vpisna_st; 418: 419: public bool SpremeniLetoVpisa(int leto) { 420: if ((2000 <= leto) && (leto <= 2020)) { 421: this.leto_vpisa = leto; 422: return true; //leto je smsielno, popravimo stanje onbjekta in vrnemo

true

423: } 424: return false; // leto ni smsielno, ne spremnimo inč in vrnemo false 425: }

426: public int VrniLetoVpisa() { 427: return this.leto_vpisa; 428: }

429: } "Pametni" uporabniki našega razreda bi potem seveda programirali z if (enClan.VrniLetoVpisa() > drugClan.VrniLetoVpisa()) { …

Če bi potem prišlo do omenjene zamenjave tehnologije (leto nadomeščeno z datumom), bi seveda lahko poskrbeli, da bi prav delovali tudi pametno napisani programi. V razredu Clan bi seveda imeli nekaj dela, da bi ustrezno popravili vse metode, a temu se ob "sprmemebi tehnologije" seveda ne da izogniti – saj ravno to hočemo!

METODA VRNILETOVPISA V RAZREDU CLAN Z DATUMOM

430: public int VrniLetoVpisa() { 431: return this.datum_vpisa.leto; 432: }

Ker je metoda VrniLetoVpisa "na zunaj" (torej za uporabnike) enaka kot prej, programi, kli so jo uporabljali, delujejo pravilno. Problem pa je v besedi "pametni uporabnik". Namreč pri tako sestavljenem razredu nihče ne preprečuje uporabniku, da ne bi napisal kar if (enClan.datum_vpisa.letoa > drugClan.datum_vpisa.leto) { …

in morebiti kasneje spret povzročil, da ob še eni spremebi razreda Clan spet stvari ne bi delovale. Če torej povzamemo. Uporabniki razredov konstrukta

ime_objekta.stanje

naj sploh ne bi nikoli uporabljali. Zakaj je to lahko problem? “zunanji” uporabnik nastavi napačno vrednost:

z1.masa = -100.10;

Uporabnik pozna predstavitev:

Kasneje je ni mogoče spremeniti

No na srečo pa imamo možnost, da dostop do spremenljivk lahko nadziramo. V C# poznamo 4 načine dostopa:

public

private

Page 47: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

47

protected

internal

Zadnjih dveh mi ne bomo uporabljali, zato si njihov pomen in uporabo oglejte kar sami. Dostop public smo že ves čas uporabljali, saj smo pisali na primer:

o public string serijska;

o public double masa;

Besedi public pomenita, da do teh dveh spremenljivk dostop ni omejen (je javen – public). Če tega ne želimo , public zamenjamo s private

o private string serijska;

o private int[] tab;

o private Datum datumRojstva;

Včasih besedo, ki označuje način dostopa (javen ali privaten) spustimo in napišemo le

o bool spol;

Kakšen dostop velja takrat, je odvisno od okoliščin. Običajno bo to kar public, ne pa vedno. Zato se opuščanju navedbe dostopa izogibajmo in ga pišimo vedno. Zakaj bi zgubljali čas s premišljevanjem o tem, v kakšnih okoliščinah je ta spremenjlivka in kakšen bo potem dostop do nje. Oglejmo si sedaj razliko med public in private. Zavedati pa se moramo, da znotraj razreda (torej ko sestavljamo razred in pišemo njegove metode) ni omejitev, vedno (ne glede na način dostopa) je možen dostop do komponent. Omejitve pri načinu dostopa veljajo le, ko objekte določenega razreda uporabljamo – torej v drugih programih in razredih!

public Če je način dostopa nastavljen na public, to pomeni, da do lastnosti (komponent, spremnljivk, polj ...) lahko dostopajo vsi, od kjerkoli (iz katerihkoli datotek (razredov)) in sicer z

ime_objekta.lastnost

Denimo, da smo v razredu MojObjekt napisali

public int javnaLastnost;

Kdorkoli naredi objekt vrste MojObjekt:

MojObjekt x = new MojObjekt();

lahko dostopa do javnaLastnost: x.javnaLastnost

Ta način smo uporabljali do sedaj.

private Dostop private pomeni, da do lastnosti ne more dostopati nihče, razen metod znotraj razreda. Ko pišemo načrt razreda, sevedda lahko napišemo

this.lastnost

lastnost

Denimo, da smo v razredu MojObjekt napisali

private int privatnaLastnost;

Če kdo naredi objekt vrste MojObjekt:

MojObjekt x = new MojObjekt();

Page 48: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

48

bo ob poskusu dostopa do privatnaLastnost: x.privatnaLastnost

prevajalnik javil napako.

Zgledi

RAZRED ZAJEC

143: public class Zajec { 144: public string serijska; // serijska stevilka zajca 145: public bool spol; // true = moski, false = zenska 146: private double masa; // masa zajca ob zadnjem pregledu 147: } 148: 149: public class Zajčnik { 150: public static void Main(String[] ar) { 151: Zajec z1 = new Zajec(); 152: z1.serijska = "1238-12-0“; 153: z1.spol = false; 154: z1.masa = 0.12; 155: z1.masa = z1.masa + 0.3; 156: System.Console.WriteLine(“Zajec ima ser. št.:” + z1.serijska); 157: } 158: }

RAZRED ZAJEC2

159: public class Zajec2 { 160: public string serijska; // serijska stevilka zajca 161: public bool spol; // true = moski, false = zenska 162: private double masa; // masa zajca ob zadnjem pregledu 163: 164: public SpremeniTezo(double x) { 165: this.masa = x; 166: } 167: 168: }

UPORABA RAZRED ZAJEC2

169: public class Zajčnik { 170: public static void Main(string[] ar) { 171: Zajec z1 = new Zajec2(); 172: z1.serijska = "1238-12-0“; 173: z1.spol = false; 174: z1.SpremeniTezo(0.11); 175: z1.masa = 0.12; 176: z1.masa = z1.masa + 0.3; 177: System.Console.WriteLine(“Zajec ima ser. št.:” + z1.serijska); 178: } 179: }

Dostop do stanj/lastnosti Odločili smo se torej, da bomo iz objektov naredili črne škatle. Uporabnikom bomo s private preprečili, da bodo "kukali" v zgradbo objektov določenih razredov. Če bodo želeli dostopati do lastnosti (podatkov) objekta (zvedeti, kakšne podatke v objektu hranimo) ali pa te podatke spremeniti, bodo morali uporabljati metode. In v teh metodah bo sestavljalec razreda lahko poskrbel, da se s podatki "ne bo kaj čudnega počelo".

Page 49: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

49

Potrebujemo torej: Metode za dostop do stanj (za dostop do podatkov v objektu) Metode za nastavljanje stanj (za spreminjanje podatkov o objektu)

Prvim pogosto rečemo tudi "get" metode (ker se v razredih, ki jih sestavljajo angleško usmerjeni pisci, te metode pogosto prično z Get (Daj) ). Druge pa so t.i. "set" metode (set / nastavi)

1.

Ponovimo še enkrat, zakaj je pristop z get in set metodami boljši od uporabe javnih spremenljivk. Spotoma pa bomo navedli še kak razlog in (upamo) prepričali še zadnje dvomljvce, ki se tako težko poslovijo od javnih spremenljivk. Razlogi, zakaj je dostop do podatkov v objektu preko metod boljši kot neposredni dostop (preko javnih spremenljivk) so med drugim:

Možnost kontrole pravilnosti! Možnost kasnejše spremembe načina predstavitve (hranjenja podatkov). Možnost oblikovanja pogleda na podatke:

Podatke uporabniku posredujemo drugače, kot jih hranimo. Tako denimo v razredu Datum mesec hranimo kot število, proti uporabniku pa je zadeva videti, kot bi uporabljali besedni opis meseca.

Dostop do določenih lastnosti lahko omejimo:

Npr. spol lahko nastavimo le, ko naredimo objekt (kasneje pa uporabnik spola sploh ne more spremeniti, saj se ne spreminja ... če odmislimo kakšne operacije, določene vrste živali ... seveda)

Hranimo lahko tudi določene podatke, ki jih uporabnik sploh ne potrebuje ..

Nastavitve podatkov (stanj) Potrebujemo torej metode, ki bodo nadomestile prireditveni stavek. Za tiste podatke, za katere želimo, da jih uporabnik spreminja, napišemo ustrezno set metodo. Na primer:

zajcek.SpremeniTezo(2.5);

V bistvu smo s tem dosegli isto kot prej s prireditvenim stavkom

zajcek.masa = 2.5; A metoda spremeniTezo lahko PREVERI, če je taka sprememba teže smiselna! Tako je zapis

zajcek.SpremeniTezo(-12.5);

načeloma nadomestek stavka zajcek.masa = -12.5;

Vendar gre tu za bistveno razliko. Prireditveni stavek se bo zagotovo izvedel (in bo s tem zajec nevarno shujšal). Pri spreminjanju teže zajca z metodo, pa lahko preprečimo postavitev lastnosti objekta v napačno stanje! V metodi SpremeniTezo lahko izvedemo ustrezne kontrole in če sprememba ni smislena, je sploh ne izvedemo.

RAZRED ZAJEC – SPREMENITEZO

433: public void SpremeniTezo(double novaTeza) { 434: // smiselna nova teza je le med 0 in 10 kg 435: if ((0 < novaTeza) && (novaTeza <= 10)) 436: this.masa = novaTeza; 437: // v nasprotnem primeru NE spremenimo teţe 438: } 439: 440: public bool SpremeniTezo(double novaTeza) { 441: // smislena nova teza je le med 0 in 10 kg 442: if ((0 < novaTeza) && (novaTeza <= 10)){ 443: this.masa = novaTeza; 444: return true; // sprememba uspela 445: } 446: // v nasprotnem primeru NE spremenimo teţe

1 Mimogrede, če bi šli v podrobnosti jezika C#, bi videli, da lahko določene komponente proglasimo za lastnosti

(Property), ki zahtevajo, da jim napišemo metodi z imenom get in set in potem lahko uporabljamo notacijo imeObjekta.imeLastnosti. Na zunaj je videti, kot bi imeli javne spremenljivke. V resnici pa je to le "zamaskiran" klic get oziroma set metode. Mi bomo lastnosti spustili in bomo pisali "svoje" get in set metode.

Page 50: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

50

447: // in javimo, da spremembe nismo naredili 448: return false; 449: } Ali imamo lahko obe metodi? Žal ne, saj imata obe enak podpis. Spomnimo se, da podpis sestavljajo ime metode in tipi parametrov. Tip rezultata pa ni del podpisa! Pri sestavljanju razreda se bomo torej odločili za eno teh dveh metod (odvisno od ocenjenih potreb). Metoda je seveda lahko bolj kompleksna. Denimo da vemo, da se teža zajca med dvema tehtanjima ne more spremeniti bolj kot za 15%. Zato lahko z metodo "polovimo" morda napačne poskuse sprememb.

RAZRED ZAJEC – SPREMENITEZOVMEJAH

450: public bool SpremeniTezoVmejah(double novaTeza) { 451: // smislena nova teza je le med 0 in 10 kg 452: // in če ni več kot 15% spremembe od zadnjič 453: int sprememba = (int)(0.5 + (100 * 454: Math.Abs(this.masa – novaTeza) / this.masa); 455: 456: if ((0 < novaTeza) && (novaTeza <= 10) && (sprememba <= 15) ){ 457: masa = novaTeza; // this.masa ... Lahko pa this spustimo! 458: return true; // sprememba uspela 459: } 460: // v nasprotnem primeru NE spremenimo teţe 461: // in javimo, da spremembe nismo naredili 462: return false; 463: } Kaj pa metodi za spreminjanje serijske številke in spola? Na zadnjo lahko mirno pozabimo. Zajec naj ima ves čas tak spol, kot mu ga določimo pri "stvarjenju" (torej v konstruktorju). Zato set metode za spremninjanje spola sploh ne bomo napisali. In ker je spremenljivka spol zaščitena s private, je uporabnik ne bo mogel spremeniti. Seveda, če bomo naš razred Zajec potrebovali za kakšen genetski laboratorij, kjer se gredo čudne stvari, pa bo morda metoda SpremeniSpol potrebna. Tudi serijske številke verjetno ne bomo spreminjali. No, če malo bolje premislimo, pa nam konstruktor brez parametrov ustvari zajca, ki ima za vrednost serijske številke niz "NEDOLOČENO". Takim zajcem bomo verjetno želeli spremniti serijsko številko. Zato naj metoda SpremeniSerijsko pusti spreminjati le nedoločene serijske številke!

RAZRED ZAJEC – SPREMENISERIJSKO

public bool SpremeniSerijsko(string seStev) {

// sprememba dopustna le, če serijske štev. ni

if (this.serijska.Equals("NEDLOČENO")) {

this.serijska = seStev;

return true;

}

return false; // ne smemo spremniti ţe obstoječe!

}

Tudi tu lahko npr. poskrbimo, da je metoda SpremeniSerijsko bolj kompleksna. Na primer lahko zahtevamo, da se vse serijske številke začno s predpono SN, ki ji potem zraven pripnemo poljuben niz. In ker so uporabniki pozabljivi in bodo nekateri že sami začeli niz s SN, mi pa nočemo imeti serijskih številk oblike SNSN..., bo metoda poskrbekla tudi za to. Lahko bi tudi vzdrževali seznam vseh podeljenih serijskih številk in bi metoda poskrbela, da imajo vsi zajci različne serisske številke. Lahko bi ...

RAZRED ZAJEC – SPREMENISERIJSKO S PREDPONO

public bool SpremeniSerijsko(string seStev) {

// sprememba dopustna le, če serijske štev. ni

if (this.serijska.Equals("NEDLOČENO")) {

Page 51: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

51

if ((seStev.Length > 1) && (seStev[0] == 'S') && (seStev[1] == 'N'))

this.serijska = seStev; // ţe ima predpono

else

this.serijska = "SN" + seStev; // nima predpone

return true;

return false; // ne smemo spremniti ţe obstoječe!

}

Opozorimo še na stvar, ki jo pri sestavljanju razreda pogosto pozabimo. Glavni razlog, da smo napisali te set metode, je, da bi zagotovili, da so podatki pravilni. Zato je smiselno, da zagotovimo, da je teža ustrezna že ves čas! A zaenkrat smo na nekaj pozabili! Kaj pa začetno stanje, ki ga nastavimo v konstruktorju! Tudi v konstruktorju preverimo, če se uporabnik "obnaša lepo". Pogosto na to pozabimo in uporabnik lahko napiše npr.

Zajec neki = new Zajec("X532", true, 105);

105 kilogramskega zajca verjetno ni, a uporabnik je pozabil na decimalno piko v 1.05. Zato je kontrola smiselnosti podatkov potrebna tudi v konstruktorjih!

Dostop do stanj Pogosto nem je vseeno, če uporabnik "vidi", kako hranimo podatke v objektu. A zardi zagortavljanja pravilnosti in možnosti kontrole sprememb podatkov, smo dostop nastavili na private. S tem smo seveda onemogočili tudi enosatven način poizvedbe po vrednosti podatka v obliki rjavk.masaČe bomo torej potrebovali težo zajca, bo

potrebno pripraviti ustrezno metodo. Če bomo torej potrebovali težo zajca zajcek, bomo napisali zajcek.PovejTezo()

Večina tovrstnih get metdo je enostavnih in v telesu metode vsebujejo le ukaz return.

RAZRED ZAJEC – POVEJTEZO

180: public double PovejTezo() { 181: return this.masa; // ali return masa 182: } A ni nujno, da so metode get tako enostavne. Lahko pogled na podatke "oblikujemo". Recimo, da bomo uporabniku vedno povedali le težo na pol kilograma, mi pa bomo interno težo zajca vodili na gram (ali še bolj) natančno. Če torej težo povemo na 0.5 kg natančno

2.721 2.5, 2.905 3, 2.502 2.5, ... Ustrezen podstopek bo:

Vzamemo težo v celih kg in pogledamo prvo decimalko decimalnega dela

Če je med 3 in 7, prištejemo k celim kg 0.5

Če je več kot 7, prištejemo k celim kg 1.0

RAZRED ZAJEC – POVEJTEZO NA 0.5KG NATANČNO

464: public double PovejTezo() { 465: // teţo bomo povedali le na 0.5 kg natančno 466: int tezaKg = (int)this.masa; 467: int decim = (int)((this.masa – tezaKg) * 10); 468: if (decim < 3) return tezaKg + 0.0; 469: if (decim < 8) return tezaKg + 0.5; 470: return tezaKg + 1.0; 471: }

Page 52: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

52

Zgled – razred Datum Kot primer oblikovanja pogleda na podatke si oglejmo še razred Datum. Prej (glej stran 37) smo ugotovili, da ni smiselno, da podatke o mesecu hranimo kot niz. A uporabniku bi jih še vedno raje "servirali" kot februar in ne kot 2. Zato bi metoda KateriMesec() bila lahko taka

RAZRED DATUM – KATERIMESEC

1: public string KateriMesec() {

2: string[] imenaMesecev = {"januar", "februar", "marec", "april",

"maj",

3: "junij", "julij", "avgust", "september", "oktober",

4: "november", "december"};

5: return imenaMesecev[this.mesec];

6: }

Seveda so potrebne še spremembe v drugim metodah. Npr. uporabnik bo verjetno želel imeti metodi NastaviMesec, ki bosta sprejeli bodisi številčni ali pa opisni podatek.

Ostale metode Poleg get/set metod in konstruktorjev v razredu napišemo tudi druge metode, saj objektov ne potrebujemo samo kot "hranilnikov" podatkov. S temi metodami določimo

odzive objektov

"znanje objektov" Če se na primer spomnimo na objekte tipa string:

Znajo povedati, kje se v njih začne nek podniz: o "niz".IndexOf("i")

Znajo vrniti spremiti črke v male in vrniti nov niz: o nekNiz.toLower()

Znajo povedati, če so enaki nekemu drugemu nizu: o mojPriimek.Equals("Lokar")

Zato bomo tudi v "naših" razredih sprogramirali znanje objektov določenega razreda s tem, da bomo napisali ustrezne metode. Katere bodo te metode je pač odvisno od načrtovane uporabe naših razredov. Kot enostaven zgled recimo, da bo zajec znal povedati svojo vrednost, če mu povemo ceno za kg žive teže. "Komercialno" zanimiva bi bila še metoda, ki bi vrnila true, če je smiselno zajca tik pred tehtanjem (in prodajo) pošteno napojiti.Spomnimo se namreč, da težo zajcev uporabnikom povemo na pol kg natančno, čeoprav jo interno hranimo točno.

RAZRED ZAJEC – DODATNE METODE

183: public double Vrednost(double cenaZaKg) { 184: // pove vrednost zajca 185: // zajce tehtamo na dogovorjeno natančnost 186: return cenaZaKg * this.PovejTezo(); 187: } 188: 189: public bool MeNapojiti() { 190: // ugotovimo, če ga je smiselno 191: // napojiti, da bomo "ujeli" naslednjih pol kg 192: // dejanska teţa je večja od "izmerjene" 193: return (this.masa – this.povejTezo() > 0); 194: }

Metoda ToString Poglejmo si naslednji program:

Page 53: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

53

IZPIS OBJEKTA

using System;

using MojaKniznica;

namespace Izpis

{

class Program

{

public static void Main(string[] args)

{

Zajec zeko = new Zajec();

Console.WriteLine("To je zajec: " + zeko);

Console.Write("Press any key to continue . . . ");

Console.ReadKey(true);

}

}

}

Če ga izvedemo, na zaslon dobimo

Od kod, zakaj? Očitno objekte lahko tudi izpišemo. In če napišemo

string niz = "Zajec " + zeko + "!"; prevajalnik tudi nič ne protestira. Torej se tudi objekti "obnašajo" tako kot int, double ... in se torej po potrebi pretvorijo v niz.

Obnašanje, ki smo ga opazili (da se števila, objekti ...) pretvorijo v niz, omogoča metoda ToString. Metoda je

nekaj posebnega, saj se lahko "pokliče kar sama". Namreč, če na določenem mestu potrebujemo niz, a naletimo na objekt, se metoda pokliče avtomatično. Torej

string niz = "Zajec " + zeko + "!";

dejansko pomeni string niz = "Zajec " + zeko.ToString() + "!";

Od kje pride ta metoda, saj je v razredu Zajec nismo napisali. Gre spet za eno od tistih "čarovnij", ki jo počne prevajalnik, kot na primer pri konstruktorjih? Zadeva je malček drugačna in jo bomo pojasnili v nadaljevanju. Zaenkrat bodi dovolj kle to, da ta metoda vedno obstaja, tudi če je ne napišemo in omogoča pretvorbo poljubnega tipa v niz. A s pretvorbo nismo ravno zadovoljni. Radi bi kakšne bolj smiselne informacije. To storimo tako, da v razredu, ki

ga definiramo (recimo Zajec), sami napišemo metodo ToString. Če to storimo, in zaženemo isti program:

V razred Zajec smo napisali

METODA TOSTRING

public override string ToString()

{

return "Zajec: " + this.PovejSerijsko();

}

Page 54: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

54

Opazimo novo neznano besedo – override. Uporabiti jo moramo zaradi "dedovanja". Več o tem, kaj

dedovanje je, kasneje, a povejmo, da smo z dedovanjem avtomatično pridobili metodo ToString. To je

razlog, da je ta metoda vedno na voljo v vsakem razredu, tudi če je ne napišemo. Če želimo napisati svojo metodo, ki se imenuje enako kot podedovana metoda, moramo napisati besedico override. S tem "povozimo " podedovano metodo.

Seveda bi lahko napisali tudi neko drugo metodo, na primer Opis, ki bi prav tako vrnila niz s smiselnim opisom objekta. Razlika med

METODA TOSTRING

public override string ToString()

{

return "Zajec: " + this.PovejSerijsko() ;

}

in

METODA OPIS

public string Opis()

{

return "Zajec: " + this.PovejSerijsko() ;

}

je v tem, da moramo metodo Opis poklicati sami, metoda ToString pa se kliče avtomatsko (če je potrebno). Če torej napišemo

string niz1 = "Zajec " + zeko.ToString() + "!";

string niz2 = "Zajec " + zeko + "!";

string niz3 = "Zajec " + zeko.Opis() + "!";

so vsi trije dobljeni niz enaki! Zapomnimo si torej, da metoda ToString obstaja tudi, če jo ne napišemo. A verjetno z vrnjenim nizom nismo najbolj zadovoljni, zato jo praktično vedno napišemo sami.

Celotni razred Zajec Oglejmo si sedaj celotno kodo razreda Zajec s spremembami, ki smo jih naredili. Spomnimo se, kaj vse smo opravili:

Pripravili smo nekaj konstruktorjev

Zaščitili smo dostop do podatkov (private)

Napisali smo ustrezne metode za spreminjanje podatkov o objektu

Napisali smo metode za poizvedovanje po vrednosti podatkov

Napisali smo metodo ToString, ki vrne opis objekta

Napisali smo dve metodi, ki pomenita "znanje" objektov

CELOTNI RAZRED ZAJEC

472: public class Zajec { 473: private string serijska; 474: private bool spol; 475: private double masa; 476: // konstruktor 477: public Zajec() { 478: this.spol = true; // vsem zajcem na začetku določimo m. spol 479: this.masa = 1.0; // in tehtajo 1kg

Page 55: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

55

480: this.serijska = "NEDOLOČENO"; 481: } 482: public Zajec (string serijskaStev) { 483: this(); // poklicali smo konstruktor Zajec() 484: this.serijska = serijskaStev; 485: } 486: public Zajec(string serijskaStev, bool spol, double masa) { 487: this(); // poklicali smo konstruktor Zajec() 488: this.serijska = serijskaStev; 489: this.SpremeniTezo(masa); // uporabimo metodo za sprem. 490: this.spol = spol; 491: } 492: 493: public double PovejTezo() { 494: // teţo bomo povedali le na 0.5 kg natančno 495: int tezaKg = (int)this.masa; 496: int decim = (int)((this.masa – tezaKg) * 10); 497: if (decim < 3) return tezaKg + 0.0; 498: if (decim < 8) return tezaKg + 0.5; 499: return tezaKg + 1.0; 500: } 501: 502: public bool SpremeniTezo(double novaTeza) { 503: // smislena nova teza je le med 0 in 10 kg 504: if ((0 < novaTeza) && (novaTeza <= 10)){ 505: masa = novaTeza; 506: return true; // sprememba uspela 507: } 508: // v nasprotnem primeru NE spremenimo teţe 509: // in javimo, da spremembe nismo naredili 510: return false; 511: } 512: 513: public string PovejSerijsko() { 514: return this.serijska; 515: } 516: 517: public bool SpremeniSerijsko(string seStev) { 518: // sprememba dopustna le, če serijske štev. ni 519: if (this.serijska.Equals("NEDLOČENO")) { 520: this.serijska = seStev; 521: return true; 522: } 523: return false; // ne smemo spremniti ţe obstoječe! 524: } 525: 526: public bool JeSamec() { 527: return this.spol; 528: } 529: 530: // ker se spol naknadno NE spremeni, metode za 531: // spreminjanje spola sploh ne ponudimo uporabniku! 532: 533: public double Vrednost(double cenaZaKg) {

Page 56: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

56

534: // pove vrednost zajca 535: // zajce tehtamo na dogovorjeno natančnost 536: return cenaZaKg * this.PovejTezo(); 537: } 538: 539: public bool MeNapojiti() { 540: // ugotovimo, če ga je smiselno 541: // napojiti, da bomo "ujeli" naslednjih pol kg 542: // dejanska teţa je večja od "izmerjene" 543: return (this.masa – this.povejTezo() > 0); 544: } 545: 546: public override string ToString() 547: { 548: return "Zajec: " + this.PovejSerijsko(); 549: } 550: 551: } // Zajec

Uporaba razreda Zajec Poglejmo si, kako opravljene spremembe vplivale na uporabo rezreda Zajec v uporabniških programih. Prej so bili ti takšni:

UPORABA STAREGA RAZREDA ZAJEC

195: public class Zajčnik { 196: public static void Main(string[] ar) { 197: Zajec z1 = new Zajec(); 198: z1.serijska = "1238-12-0“; 199: z1.spol = false; 200: z1.masa = 0.12; 201: z1.masa = z1.masa + 0.3; 202: System.Console.WriteLine("Zajec ima ser. št.:" + z1.serijska); 203: } 204: } Z novim razredom Zajec seveda se način uporabe povsem spremeni, saj ne moremo pričakovati, da bodo tako drastične spremembe, ki smo jih naredile, pustile uporabniške programe nespremenjene. Še enkrat povejmo, da razreda Zajec ne bi nikoli "zares" napisali tako, kot smo ga po starem (z javnimi spremenljivkami).

UPORABA NOVEGA RAZREDA ZAJEC

205: public class ZajčnikNov { 206: public static void Main(string[] ar) { 207: Zajec z1 = new Zajec("1238-12-0", false, 0.12); 208: if (z1.SpremeniTezo(z1.PovejTezo() + 0.3)) 209: System.Console.WriteLine("Teţa je spremenjena na " 210: + z1.PovejTezo()); 211: else System.Console.WriteLine("Teţa je nespremenjena!" 212: + " Prirastek je prevelik! Preveri!"); 213: System.Console.WriteLine("Zajec ima ser. št.:" +

z1.PovejSerijsko());

214: } 215: }

Ko sestavljamo razred, kot objektne spremenljivke lahko uporabimo tudi spremenljivke istega razreda

Page 57: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

57

Npr.: starsi Zajca

Razred Clan

public class Clan {

private string ime;

private Clan gaJePriporocil;

...

Ţelimo hraniti tudi starše zajca

public class Zajec {

private double masa;

private string serijska;

private bool spol;

private Zajec oce;

private zajec mati; Še konstruktor

Sestavimo privzeti konstruktor

public Zajec() {

this.spol = true;

this.masa = 1.0;

this.serijska = "NEDEOLOČENO";

this.oce = new Zajec();

this.mama = new Zajec();

}

Ko stvar prevedemo, je vse v redu. A ob poskusu

Zajec rjavko = new Zajec();

Rekurzija brez ustavitve

Tudi konstruktor je metoda

Ustavitveni pogoj

A pri konstruktorju brez parametrov ni parametrov, ki bi lahko določili ustavitveni pogoj

"Privzeti" zajec ne pozna staršev

oce = null;

mama = null;

Če malo pomislimo – ko zajca "naredimo", morata starša ţe obstajati

Ju nima smisla ustvarjati na novo

Page 58: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

58

Gradnja razredov - zgled

Ustvarjanje objektov Razred je šablona, ki definira spremenljivke in metode skupne vsem objektom iste vrste. Ko torej napišemo

class

gre za to, da začeno pisati načrt objekta, šablono po kateri bomo ustvarjali objekte. Z ustvarjanjem razredov si pripravljamo sestavne dele, s pomočjo katerih bomo reševali nek problem. Ko pa sestavljamo program, ki predstavlja algoritem, s katerim rešujemo problem, pa na podlagi teh pripravljenih načrtov (razredov) ustvarimo primerek razreda – konkretni objekt. Pogosto bomo prebrali, da je objekt instanca nekega razreda, a to je nepotrebno "pačenje" slovenskega jezika. Za besedo instanca obstaja čisto lep slovenski izraz – primerek. Primerek razreda (konkretni objekt) ustvarimo ga z new. Dokler new ne uporabimo, objekta ni in ga seveda ne moremo uporabiti. Če torej napišemo

NekiObjekt a;

Ne pozabimo, da objekt s tem še ne obstaja. Spremenljivka a je samo ime objekta, oziroma natančneje , a je ime spremenljivke, kjer bomo hranili naslov objekta vrste NekiObjekt. Šele new bo naredil objekt in vrnil naslov, kje ta objekt je. In ta naslov bomo shranili v a.

Problem Banke Butale Banka Butale se intenzivno pripravlja na prihod nove valute arve. Med drugim bo potrebno nakovati nove kovance. Za ustrezno informacijsko podporo kovačnici najamejo tebe. Po nekajmesečnih pogovorih ugotoviš, da bomo v programih potrebovali objekte vrste Kovanec. Potrebujemo torej razred Kovanec. Kaj pa sedaj?

Kako do svojega razreda Najlažje je, če ustrezen razred že obstaja v standardnih knjižnicah jezika C#. Če ga tam ni, se splača pogledati okoli. Morda je kdo drug pripravil knjižnico, v kateri je iskani razred. Kaj pa, če na tržišču ni niti približno ustreznega razreda. Takrat moramo narediti razred sami. Kako se tega lotimo? Skica postopka z opombami bi bila:

o Analiza: Pa ne matematični predmet Katera so stanja/lastnosti:

Kaj hočemo o objektih te vrste vedeti Kakšne lastnosti imajo

Katere so metode: Kakšno je “znanje” objektov Na katere “ukaze” se odzovejo

o Predstavitev razreda (priprava class) Način predstavitve lastnosti objekta (podatke o objektu) skrijemo:

private enkapsulacija

Odločitev o načinu predstavitve Pisanje metod

o Preverjanje delovanja posameznih metod Pisanje testnih programov, s katerimi preverimo, ali se posamezna metoda obnaša na

predpisan način o S pomočjo ustreznih orodij izdelamo dokumentacijo (na podlagi dokumentacijskih komentarjev)

Najprej torej pošteno proučimo naš problem in analiziramo zahteve, ki jih mora izpolnjevati razred Kovanec. Prvenstveno moramo ugotoviti, kaj hočemo o objektih vrste Kovanec vedeti – torej kakšne podatke bomo o posameznem kovancu hranili. Potem ugotovimo, kaj bi z objekti vrste Kovanec radi počeli – torej kakšne metode naj bi lahko izvajali nad objekti te vrste. Seveda premislek o možnih metodah lahko vpliva tudi na to, katere podatke bomo o kovancih hranili.

Page 59: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

59

Ko smo z analizo končali, se lotimo pisanja razreda. Pa tudi tu še ne pride takoj na vrsto kodiranje. Anjprej se moramo odločiti, kako bomo željene podatke hranili. Kakšne bodo spremenljivke (kakšnega tipa), koliko jih bo ... Praviloma vse spremenljivke, v akterih hranimo podatke o objektu, "skrijemo". Za zunanje uporabnike jih napravimo nevidne z določilom private. Ko smo se odločili o načinu predstavitve lastnosti, se lotimo pisanja ustreznih metod. Ko smo končali, dobro preverimo delovanje vseh metod. Na koncu, a kot zelo pomembno zadevo, pa pripravimo še dokumentacijo o razredu, kjer točno opišemo parametre vseh metod, tip rezultata ter namen posamezne metode. Pri pisanju komentrajev soi pomagamo z orodji, ki to dokumentacijo avtomatično ustvarijo na podlagi dokumentacijskih komentarjev.

Okostje razreda Oglejmo si, kako bo videti tipični razred, ki ga bomo sestavljali. Vrstni red sicer ni pomemben, a se največkrat držimo prav takega. Pohosto sicer vidimo, da je v razredu tudi metoda Main, ki jo uporabljamo za preverjanje delovanja razreda. A načeloma to ni najbolje, kar "zamegljuje" sliko. Razred naj bo pač načrt za objekte nekega tipa. V drugem programu (ki pa je tudi razred, a brez komponent, objektnih metod ...) pa pač objekte pripravljenih razredov uporabljamo. public class Razred {

// deklaracija lastnosti - komponent - polj - ...

// konstruktorji

// metode

// ToString

}

Razred Kovanec Sestavimo sedaj razred Kovanec, s katerim predstavimo kovance v obliki, kot jih bomo potrebovali za butalsko banko.. Naredimo analizo:

Ugotovimo, da so kovanci okrogli, z danim polmerom in višino. Poleg konstruktorjev in standardnih metod, ki nastavijo in vrnejo vrednosti lastnosti in za pretvorbo v niz, naj razred pozna še metodi, ki vrneta površino in volumen kovanca.

Sedaj se moramo odločiti o načinu predstavitve podatkov. Torej vedeti moramo kje in kako bomo podatke (polmer, višina) hranili. Denimo, da vemo, da so to vedno cele vrednosti, zato se odločimo kar za dve celoštevilski spremenljivki. To seveda ni edina možna odločitev. Ker se mere kovancev lahko spreminjajo in ker je smiselno, da uporabnik mere lahko zve, bomo pripravili tudi dve set in dve get metodi. Pripravimo skico razreda, kjer bomo vsebino metod izpustili.

KOVANEC - SKICA

216: public class Kovanec { 217: 218: private int polmer; 219: private int visina; 220: 221: // konstruktorji 222: public Kovanec() {} 223: public Kovanec(int r, int v) {} 224: 225: // "SET" metode 226: public void NastaviPolmer(int r) { } 227: public void NastaviVisino(int visina) { } 228: 229: // "GET" metode

Page 60: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

60

230: public int PovejPolmer() { } 231: public int PovejVisino() { } 232: 233: // tostring 234: overide public string ToString() { } // kako je objekt “videti” kot

niz

235: 236: // "znanje" 237: public double Volumen() { } 238: public double Povrsina() { } 239: 240: } Lotimo se pisanja metod. Denimo, da napišemo nekaj takega:

KOVANEC – DEL METOD

241: public class Kovanec { 242: private int polmer; 243: private int visina; 244: 245: // konstruktorji 246: public Kovanec() {} 247: public Kovanec(int r, int v) {} 248: 249: // "SET" metode 250: public void NastaviPolmer(int r) { 251: polmer = r; 252: } 253: 254: public void NastaviVisino(int visina) { 255: // smiselna visina je le med 1 in 100 256: // drugace jo pustimo pri miru 257: if ((1 <= visina) && (visina <= 100)) 258: this.visina = visina; 259: } 260: 261: // "GET" metode 262: public int PovejPolmer() { 263: return polmer; 264: } 265: 266: public int PovejVisino() { 267: return visina; 268: } 269: 270: override public string ToString() { 271: return “r = “ + this.PovejPolmer() + “\nvisina = “ 272: + this.PovejVisino(); 273: } 274: }

Primerjajmo metodi NastaviPolmer in NastaviVisino. Pri slednji smo preverili, ali je uporabnik te metode navedel smiselne parameter. Iz analize problema vemo, da je smiselna višina kovancev med 1 in 100. Zato metoda to preveri. Če parameter ni v teh mejah, potem metoda ne spremeni stanja objekta.

Page 61: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

61

Pri metodi NastaviPolmer pa zgolj vrednost parametra zapišemo v spremenljivko in s tem spremenimo lastnost objekta. Torej neprevidni programer lahko "naredi kovanec" z negativnim polmerom. Vsekakor pri "pravem programiranju" metode ne bomo napisali na ta način.

2

Pri metodi za opis objektov vrste kovanec smo se odločili, da je smielen opis objekta dvovrstični. V prvi vrstici piše r – xx, v drugi pa visina – xxx, kjer je namesto xx seveda ustrezna vrednost.Opazimo tudi, da do vrednosti polmera dostopamo preko metod. To sicer ni potrebno in bi metodo lahko napisali tudi kot

METODA TOSTRING Z NEPOSREDNIM DOSTOPOM

1: override public string ToString() {

2: return “r = “ + this.polmer + “\nvisina = “

3: + this.visina;

4: }

Torej z neposrednim dostopom do spremenljivk. To lahko naredimo, saj smo znotraj razreda. A je vseeno boljše, če metodo napišemo na prej predstavljeni način. Vemo, da se Butalci pogosto radi malo postavijo nasproti Veržejcem. In ker se Veržejci hvalijo, da so njihovi kovanci precej večji, jim Butalci ne bodo ostali dolžni. Po e-pošti dobiš zahtevo, da "popraviš" razred Kovanec tako, da bo javljal 2x večji polmer (podatek o polmeru naj bo v objektu za vsak primer zapisan kar pravi, le navzven naj se "pretvarja" da je dvakrat večji) . In če smo dostop do lastnosti polmer povsod izvajali preko metode PovejPolmer() ustrezno spremembo napravimo le na enem mestu.

Uporaba razreda Kovanec Prvi problem, ki ga moraš rešiti za butalsko banko, je problem njihove državne kovnice. Državna kovnica želi, da napišeš program, ki reši naslednji problem. Kovino za izdelovanje kovancev imajo v kovnici obliki enega samega velikega kvadra z merami 20 x 12 x 30 (za več ni prostora, saj je arhitekt, ko (ki je povsem slučajno isti kot arhitekt neke fakultete) je risal načrte za kovnico pozabil na skladišče. Ko Banka Butale potrebuje nekaj kovancev, jih zanima, če bodo iz tega kvadra res lahko izdelali željeno število kovancev. Zato bi radi imeli program,m ki pove, ali lahko naredimo n "običajnih" kovancev (torej takih, kot jih naredi konstruktor Kovanec()). Problem sploh ni lahek. A če ga "razumemo prav" in upoštevamo da lahko kvader potem tudi malo preoblikujemo, odpadke spet zlijemo skupaj in ponovno kujemo, ... Poleg tega moram,o odgovore programa (DA/NE) pravilno tolmačiti.

ne: nikakor to ne gre

da: mogoče, ni pa nujno Program bo enostaven. Z new Kovanec() ustvarimo nov, “običajni” kovanec. Povprašajmo ga, koliko je njegov volumen. Če n kratnik volumna presega volumen kvadra, vemo, da s kovanjem takšnega števila kovancev ne bo nič in dogovorimo NE. V nasprotnem pa ostaja upanje, da bodo vrli butalski kovači že nekako nakovali n kovancev in odgovorimo DA.

UPORABA RAZREDA KOVANEC

275: public static Drzava { 276: public static void Main(string[] a) { 277: int kvader_x = 20; 278: int kvader_y = 12; 279: int kvader_z = 30; 280: int volumenKvadra = kvader_x * kvader_y * kvader_z; 281: Console.Write("koliko kovancev potrebujemo: "); 282: int n = int.Parse(Console.ReadLine()); 283: Kovanec en = new Kovanec(); 284: int volKovanca = en.Volumen(); 285: if (volumenKvadra > n * volKovanca) 286: System.Console.WriteLine("DA"); 287: else 288: System.Console.WriteLine("NE");

2 Lahko pa in si s tem zagotovimo možnost nadaljnih "izboljšav" (in novih zaslužkov)

Page 62: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

62

289: } 290: } Program je napisan, v Butalah so nad njim navduzšeni. A ne mine leto, ko državni zakladnik spremeni dimenzije "običajnih" kovancev. Je potrebno spremeniti program? Kje so potrebne spremembe? Spremembe v programu niso potrebne. Popraviti je potrebno seveda razred Kovanec, kjer spremenimo delovanje konstruktorja brez parametrov. A to je vse. V uporabniške programe, torej tiste, ki razred Kovanec uporabljajo, se ni potrebno vtikati! Razrede sestavljamo tako, da uporabniki tega razreda niso prizadeti ob morebitnih spremembah razredov

Glave metod pustimo enake (podpis + tip rezultata + način dostopa) Uporabniški programi ostanejo enaki Potrebujejo le nove dll datoteke razreda Za uporabnika se s tem ne spremeni nič!

Zgled - razred Kvader Naša naslednja naloga je sestaviti razred, ki bo opisoval kvadre. Najprej nas spet čaka analiza problema. Odločiti se moramo, kaj za nas kvader sploh pomeni. Denimo, da ga potrebujemo zgolj kot geometrijsko telo. Zato bomo o njem hranili le njegove mere. Potem moramo pripraviti ustrezne metode za ravnanje s podatki ter predvideti "znanje " objektov. Skica našega razmišljanja bo torej:

o Geometrijsko telo o Podatki

Komponente opisujejo stanje objekta

o Metode Konstruktorji (posebne metode)

Kaj se zgodi ob new “znanje” objektov tega razreda

volumen, površina, ... Metode za ravnanje s podatki

Nastavi podatek

Vrni podatek ToString

Kvader – podatki Kvader kot geometrijsko telo je podan z dolžinami vseh treh stranci. Odločiti se moramo torej kako bo imel objekt predstavljene stranice. Ena od možnosti je, da uporabimo 3 spremenljivke tipa int. Druga možnost bi bila, da vzamemo tabelo s tremi polji. Spet tretja, da podatke predstavimo kot niz oblike "a x b x c". Spet četrta možnost ... V našem zgledu se bomo odločili kar za prvo možnost. Uporabili bomo torej private int a; // stranica a

private int b; // stranica b

private int c; // stranica c

Vendar bomo razred sestavljali tako, da uporabniški programi pri mrebitni kasnejši spremembi načina predstavitve podatko v ne bodo prizadeti. Se še spomnimo, zakaj se s private "gremo" kapsuliranje (enkapsulacijo).? Način predstavitve uporabnika NE zanima – s private mu preprečimo dostop. Kako pa potem pridemo do podatkov? S tem, da pripravimo ustrezne metode. V njih bomo lahko tudi preverjali smiselnost podatkov. Z enkapsulacijo tem smo si omogočili tudi morebitno naknadno spreminjanje razreda, ne da bi s tem "trpeli" obstoječi programi, ki ta razred uporavljajo. Če se pri spremembi razreda podpisi metod ne spreminjajo – ni težav s spreminjanjem razreda:

Vsi programi, ki uporabljajo objekte določenega razreda, si podatke izmenjujejo le preko metod Če se klic ne bo spremenil, je vseeno, kako je spremenjen razred!

Page 63: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

63

Kvader – metode za delo s podatki Za podatke, za katere je smiselno, da jih uporabnik vidi, napišemo metode, ki povejo vrednost podatka. Pri tem lahko prikaz podatkov oblikujemo (npr. vrnemo podatek le na eno decimalno mesto, kot niz, ...). Včasih določenih podatkov sploh ne bomo "pokazali" upotrabniku. V tem primeru pač ne napišemo nobene get metode, ki bi vrnila ta podatek. V našem razredu bomo napisali metode oblike

PovejA(), …

Za podatke, za katere je smiselno, da jih uporabnik spremeni, pa pripravimo metode, ki spremenijo vrednost podatka. Ob tem običajno preverimo, če je podatek smiselen. Kaj pa naredimo, če podatek ni smsielene, je odvisno od namena, za katerega sestavljamo razred. Tako se na primer lahko odločimo, da podatka enostavno ne spremenimo in morda to javimo, preko izhodnega rezultata tipa int ali bool. Lahko v primeru napačnih podatkov privzamemo, da potem podatek objekta spremenimo na neko vnaprej določeno vrdnost ... Vse te odločitve (kaj storimo ob napšačnih podatkih) pa dobro dokumentiramo v dokumentaciji. Tako bodo uporabniki tega razreda vedeli, kaj lahko pričakujejo. . V našem razredu bomo napisali metode oblike

StranicaA(), …

Kvader – konstruktorji K metodam, ki spreminjajo podatke, na določen način spadajo tudi konstruktorji. Z njimi najpogosteje nastavimo začetne vrednosti podatkov v objektu. Pri tem ne smemo pozabiti, da mora tudi konstruktor poskrbeti, da bodo shranjeni podatki smiselni. Še posebej pa moramo biti pazljivi na to, da objekt ob klicu konstruktorja še nima nastavljenih smiselnih podatkov

3. Zato si običajno ne privoščimo tega, da bi v primeru

napačnih poarametrov v konstruktorju ne naredili nič, amapak zamenjamo napačne vrednosti parametrov z nekeimi, vnaprej dogopvorjenimi (in v dokumentaciji dobro opisanimi) vrednostmi. Že pri razlagi uporabe konstruktorjev smo omenili, da skoraj vedno pripravimo konstruktor brez parametrov. Tak konstruktor naredi “privzeti” objekt. Denimo, da je naš privzeti objekt– kvader s stranicami: 1 x 1 x 1. Ustrezni konstruktor bo

KONSTRUKTOR BREZ PARAMETROV

public Kvader(){

a = 1;

b = 1;

c = 1;

}

Načeloma ta konstruktor zadostuje. Če želimo narediti drugačen kvader, pač naredimo osnovni kvader in mu z ustreznimi metodami spremenimo stranice. Kaj pa, če bi uporabniku radi ponudili več možnosti “začetnega” kvadra? Denimo

Konstruktor, ki naredi kocko

Konstruktor, kjer podamo stranice

Konstruktor, ki naredi kopijo objekta

Navedimo sedaj kar celoten razred. V njem smo napisali cel kup komentarjec, določene zadeve smo po nepotrebnem pisali drugače, le zato, da smo lahko pripraili ustrezne komentarje. Sem in tja bomo kodo prekinili z razlago. Opazili boste, da smo namenoma malo premešali vrstni red. Na ta način želimo opozoriti, da je vrstni red metod v razredu povsem poljuben.

RAZRED KVADER

291: public class Kvader { 292: // z /* ... */ so oznaceni komentarji za razlago zakaj doloceni konstrukcijski prijemi 293: // v "pravem" razredu jih ne bi bilo! 294: 3 Rsnici na ljubo imajo vse komponente, ki opisujejo objekt, takoj nastavljene privzete vrednosti. A do sedaj

smo se že naučili, da se zanašanje na to, da bo za nekaj poskrbel prevajalnik, običajno ne konča najbolje. Spomnimo se le težav, ki smo jih imeli, ker je prevajalnik sam namesto nas pripravil konstruktor brez parametrov.Tudi tu je vprašanje, lče privzete vrednosti opisujejo "legalno" stanje objekta.

Page 64: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

64

295: private int a; /* uporabnika ne zanima, kako imamo 296: shranjene podatke */ 297: private int b; 298: private int c; 299: 300: // minimalna dolzina stranice 301: public const int MIN_DOL_STR = 1; 302: // maksimalna dolzina stranice 303: public const int MAX_DOL_STR = 100;

Če pred spremenljivko ob deklaraciji napišemo const, s tem povemo, da gre za konstanto. To pomeni, da

vrednosti, ki jo priredimo ob deklaraciji, potem ne moremo več spreminjati. Tudi konstante so lahko javne ali privatne. Lahko jih pišemo lokalno (znotraj metod), lahko pa rečemo, da so to lastnost razreda (in jih napišemo izven vseh metod, tja kot objektne spremenljivke. Navada je, da jih poimenujemo z imenom, ki vsebuje same velike črke. Pogosto jim določimo javni dostop, saj jih uporabnik tako ali tako ne more spreminjati, saj so konstante. Seveda pa, če bi jih uporabniku radi predstavili drugače (prego get metod) ali jih pred njim skrili, lahko uporabimo private. Gre za tako imenovane razredne (statične) podatke . To pomeni, da obstajajo neodvisno od obstioja kakšnega primerka in da do njih dostopamo (če so public) z

ImeRazreda.IME_KONSTANTE

Več o tem v razdelku, ko bomo na dolgo govorili o statičnih (razrednih) podatkih. 304: /* Ker menimo, da je smiselno, da uporabnik neposredno vidi 305: ti dve kolicini - dostop 306: public. Spreminjati ju ne more zaradi const. 307: Dostop: Kvader.MIN_DOL_STR 308: 309: Gre za razredno spremenljivko - 1 310: kopija za vse objekte, ne gre 311: za lastnost posameznega razreda - več kasneje 312: */ 313: 314: // vrni podatek 315: 316: public int PovejA() { /* omogocimo uporabniku dostop 317: do vrednosti zanj zanimivih podatkov */ 318: return a; 319: } 320: public int PovejB() { 321: return b; 322: } 323: public int PovejC() { 324: return c; 325: } 326: 327: private bool Kontrola(int x){ /* pomozna metoda, zato private */ 328: // preverimo, ce velja MIN_DOL_STR <= x <= MAX_DOL_STR 329: return ((MIN_DOL_STR <= x) && (x <= MAX_DOL_STR)); 330: } Tu smo napisali pomožno metodo, ki preveri, če je podatek v mejah, kot jih določata konstanti. Ker smo ocenili,

da metoda za uporabnika ni uporabna, smo jo s private pred njim skrili. Tako je ne more uporavljati in je na

voljo izključno znotraj tega razreda. 331: // nastavi podatek 332:

Page 65: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

65

333: public void StranicaA(int a) { /* omogocimo uporabniku 334: spreminjanje podatkov */ 335: // Ce je podatek v mejah med MIN_DOL_STR in MAX_DOL_STR, 336: // ga spremenimo, drugace pustimo 337: // taksnega, kot je 338: if (Kontrola(a)) 339: this.a = a; /* this je potreben, da locimo med imenom 340: podatka objekta in parametrom a */ 341: /* verjetno bi bilo bolj smiselno namesto 342: a parameter poimenovati s ali kako drugače */ 343: } 344: public void StranicaB(int a) { 345: // Ce je podatek v mejah med MIN_DOL_STR in MAX_DOL_STR, 346: //ga spremenimo, drugace pustimo 347: // taksnega, kot je 348: if (Kontrola(a)) 349: this.b = a; /* this v nasprotju s prejsnjo metodo tu 350: ni potreben. 351: se vedno pa velja, da bi bilo boljse, 352: da bi parameter a poimenovali 353: drugace - npr. s */ 354: } 355: public void StranicaC(int s) { 356: // Ce je podatek v mejah med 1 in 100, ga spremenimo, 357: //drugace pustimo 358: // taksnega, kot je 359: if (Kontrola(s)) /* na ta nacin bi verjetno "zares" 360: sprogramirali tudi 361: zgornji metodi */ 362: c = s; 363: } 364: 365: //........................................... 366: // konstruktorji 367: 368: public Kvader() { 369: a = b = c = 1; 370: } Tu smo spet enkrat za spremembo uporabili kombinacijo prireditvenega stavka in prireditvenega izraza. Če se spomnimo iz poglavja o datotekah, kjer smo tudi naleteli na prireditveni izraz, je to izraz, ki ima poleg tega, da vrne vrednost, še stranski učinek. Ta je, da spremenjlivko nastavi na to vrednost, ki jo sicer vrne kot vrednost izraza. Konkretno vrstica 79 vse tri stranice nastavi na 1. 371: public Kvader(int s) { // kocka s stranico s. če podatek ni 372: // v redu - kocka z robom 1 373: if (!Kontrola(s)) s = 1; // ce podatek ni v mejah, 374: //ga postavimo na 1 375: a = b = c = s; 376: } Tu vidimo, da smo namesto napačne vrednosti kar sami določili ustrezni podatek (1). Neklaj takega moramo storiti, saj konstruktor mora nastaviti podatke o objektu na neke smiselne vrednosti (in jih ne moremo kar pustiti nenastavljen – oziroma upati, da so privzete vrednosti v jeziku C# ustrezne za naš razred). 377: public Kvader(int s1, int s2, int s3) { // kvader s1 x s2 x s3

Page 66: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

66

378: // Ce kak podatek ni v redu - ga postavimo na 1 379: if (!Kontrola(s1)) s1 = 1; // ce podatek ni v mejah, 380: // ga postavimo na 1 381: a = s1; 382: 383: if (!Kontrola(s2)) s2 = 1; // ce podatek ni v mejah, 384: // ga postavimo na 1 385: b = s2; 386: 387: if (!Kontrola(s3)) s3 = 1; // ce podatek ni v mejah, 388: // ga postavimo na 1 389: c = s3; 390: } 391: 392: public Kvader(Kvader sk) { // Novi objekt je kopija 393: // obstojecega objekta sk 394: this.a = sk.a; /* this ni potreben */ 395: StranicaB(sk.b); /* tudi znotraj razreda se splaca 396: uporabljati metode za delo s podatki */ 397: StranicaC(sk.c); /* ceprav imamo neposreden dostop 398: do spremenljivk, saj nam ob morebitni 399: spremembi predstavitve ni potrebno 400: toliko popravljati! */ 401: } Morda se sprašujete, kje je tukaj new. Ko smo v enem od prejšnjih zgledov sestavljali metodo, ki vrne kopijo objekta, smo še posebej poudarili, da ne smemo pozabiti na new znotraj metode. A ne pozabimo, da smo v konstruktorju. Torej se je objekt ravnokar naredi (prav z new) in mi le nastavljamo njegove podatke. 402: 403: // 404: // --------------- to string 405: // 406: override public String ToString(){ /* prekrivanje metode iz razreda 407: Object */ 408: // Metoda vrne podatek v obliki Kvader: a x b x c 409: return "Kvader: " + PovejA() + " x " + PovejB() + " x " 410: + PovejC(); 411: /* tudi znotraj razreda se splaca uporabljati metode za 412: delo s podatki 413: ceprav imamo neposreden dostop do spremenljivk, saj 414: nam ob morebitni 415: spremembi predstavitve ni potrebno toliko popravljati! */ 416: } 417: } // class Kvader

Ustvarjanje razredov in objektov: povzetek Nredimo sedaj povzetek vsega povedanega. Konkretni objekt je primerek nekega razreda . Nastane z new. Izjema so na prvi pogled objekti razreda string. A dejansko ne gre za izjemo, gre le drugačen zapis, ki "skrije" uporabo operatiorja new! Podobno je pri tabelah ko uporabimo inicializacijo začetnih vrednosti, napštetih v zavitih oklepajih. Posamičnim lastnostim objekta vedno določimo privatni način dostopa (da so torej dostopni le metodam znotraj razreda). To povemo tako, da ob deklaraciji napišemo private. Razlog za to je v tem, da uporabnika ne zanima način hranjenja lastnosti objekta, ampak le smiselna uporaba objektov. S tem tudi omogočimo tudi bolj kontrolirano delo s podatki v objektu. V ta namen pripravimo metode za delo s podatki/lastnosti kot so :

VrniPodatek, NastaviPodatek

Page 67: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

67

V razredu moramo obvezno imet tudi konstruktorje. To so metode, ki povedo, kaj se zgodi ob kreaciji novega objekta. Prvenstveno podskrbijo, da so vsi podatki o objektu takoj nastavljeni na smislene vrednosti. Ko pišemo metode, s katerimi nastavljamo vrednost (set), ne smemo pozabiti na preverjanje pravilnosti parametrov, Stanje objekta mora biti ves čas pravilno. Ne smemo pozabiti na ustreno preverjanje parametrov že pri pisanju konstruktorjev. Kot smo omenili, morajo tudi ti poskrbeti, da je novoustvarjeni objek v "smiselenem stanju") Pri metodah, s katerimi vračamo vrednost (t.i. get metode, večjih zapletov ne gre pričakovati. Seveda pa lahko metode izkoristimo, da podatke, ki jih v objektu hranimo na en način, vrnemo na drug način,. Vsak naš razred naj bi imel tudi metodo ToString. Ta poskrbi, da se bodisi ob klicu, bodisi avtomatično (če se na tistem mestu namesto objekta pričakuje niz) objekt pretvori v niz. Uporabljamo jo za “predstavitev” objekta. Ker zaradi mehanizma dedovanja ta metoda avtomatično obstaja v vsakem razredu, moramo na začetek dodati še besedo override, Poleg metod za nastavljanje/vračanje podatkov in metode ToString imamo v razredu običajno še več drugih metod. Tiste, katerih uporaba je namenjena uporabnikom, se začno z določilom public, “interne” (pomožne) metode pa s private. Predvsem ori konstruktorjih smo uporabili tehniko preobteževanja (overloading). Gre za to, da imamo lahko več metod, ki se imenujeo enako, razlikjejo pa se v seznamu parametrov.

Zgledi Ogledali si bomo nekaj postopkov pri katerih uporabimo objektno programiranje.

Avto Izdelujemo spletno anketo o priljubljenosti različnih avtomobilov. V ta namen bomo sestavili razred Avto, ki naj hrani tri osnovne podatke o velikosti (dolžini), barvi in o tipu (limuzina ali kupe):

• velikost (dolžino): decimalno število med 4.0 in 5.0, • barvo: poljuben niz in • ali je avto tip limuzine ali kupeja.

Vse spremenljivke razreda morajo biti privatne, razred pa mora tudi poznati metode za nastavljanje in branje teh vrednosti. Metoda za nastavljanje velikosti avta naj poskrbi tudi za to, da bo velikost vedno ostala znotraj sprejemljivih meja! Če uporabnik poskusi določiti napačno velikost, naj se obdrži prejšnja vrednost. Možna rešitev:

RAZRED AVTO

public class Avto

{

private double velikost;

private string barva;

private bool tip; // true pomeni, da gre za limuzino,

// false, da je kupe

// konstruktor

public Avto()

{

this.tip = true; // vsem privzetim avtom na začetku

// določim tip limuzine

this.velikost = 4.0; // in so 4 m veliki

this.barva = "nedoločena";

}

public Avto(string barvaKatera, bool tip, double velikost)

{

this.barva = barvaKatera;

this.tip = tip;

Page 68: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

68

if (!this.SpremeniVelikost(velikost))

//če metoda spremeni velikost ni uspela

//moramo "ročno" nastaviti velikost

this.velikost = 4.0;

}

public double PovejVelikost ()

{

// velikost bomo povedali le na 0.5 metra natančno

int velikostMetri = (int) this.velikost;

int decim = (int) ((this.velikost - velikostMetri)*10);

if (decim < 4) return velikostMetri + 0.0;

if (decim < 5) return velikostMetri + 0.5;

return velikostMetri + 4.0;

}

public bool SpremeniVelikost (double novaVelikost)

{

//smiselna nova velikost je le med 4 in 5 metri

if ((4 < novaVelikost) && (novaVelikost <= 5))

{

this.velikost = novaVelikost;

return true; // sprememba je uspela

}

// v nasprotnem primeru ne spremenimo velikosti in

// javimo, da spremembe nismo naredili

return false;

}

public string PovejBarvo ()

{

return this.barva;

}

public void SpremeniBarvo (string b)

{

this.barva = b;

}

public bool JeLimuzina ()

{

return this.tip;

}

public string KaksenTip ()

{

if (this.JeLimuzina()) return "limuzina";

return "kupe";

}

// ker se tip avta naknadno ne spremeni,

// metode za spreminjanje tipa sploh ne ponudimo uporabniku!

// metoda ToString

Page 69: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

69

override public string ToString ()

{

return ("Podatki o avtu so naslednji: Velikost je " +

this.PovejVelikost() + "m, barva: " +

this.PovejBarvo() + " in je tipa: "

this.JeLimuzina() + ".");

}

}

} //class Avto

Uporaba razreda:

Spodaj je naveden program, ki ustvari en avto in mu naknadno podaljša

dolţino za 30cm. Program tudi izpiše, kakšne barve je avtomobil.

public static void Main(string[] args)

{

Avto a1 = new Avto();

a1.barva = "rdeča";

a1.tip = false;

a1.velikost = a1.velikost + 0.3;

Console.WriteLine("Avto je barve: " + a1.barva);

}

Kaj je narobe s tem programom? V programu se obnašamo, kot bi bile spremenljivke, ki opisujejo objekte tipa Avto javne (public). Ker pa niso (so privatne (private)), je v vsaki od vrstic od 4 do 7 kakšna napaka. Do

ustreznih podatkov moramo dostopati preko metod. Popravimo program. S četrto vrstico ne bo težav a1.SpremeniBarvo("rdeča");

Kaj pa 5 vrstica? Ustrezne metode za spreminjanje tipa ni. Tip avtomobilu lahko nastavimo le, ko ustvarimo nov objekt. Zato bodo potrebne malo večje spremembe. Pobrisali bomo tudi 3. vrstico in 3. – 5. vrstico zamenjali z Avto a1 = new Avto("rdeča", false, 4.0);

S tem smo ustvarili enak avto, kot so jih prej vrstice 3. – 5. Sedaj lahko spremenimo velikost. Vrstica 6 postane

a1.SpremeniVelikost(a1.PovejVelikost() + 0.3);

vrstica 7 pa Console.WriteLine("Avto je barve: " + a1.PovejBarvo());

Dodajmo še vrstico, ki izpiše novo velikost avtomobila Console.WriteLine("Avto je dolg: " + a1.PovejVelikost());

Ko pa program poženemo, smo preseneči, saj se izpiše Avto je dolg: 4.5m

Od kje pa to – ali ne znamo več računati? 4m + 0.3m je vendar 4.3m in ne 4.5m. Z našim znanjem računstva ni nič narobe. Le pozabiti ne smemo, da smo rekli, da bomo uporabnikom velikost avtomobila sporočali na pol metra natančno. In ker je torej za poizvedovanje po velikosti na voljo le ta metoda, ima to lahko zanimive posledice. Tako če naš avto poskušamo povečati za 0.3m to sploh ni nujno možno. Naš avto je sedaj dejansko dolg (vsaj podatek je tak) 4.5m. In če sedaj avto podaljšamo še za 0.3m z

a1.SpremeniVelikost(a1.PovejVelikost() + 0.3);

bo njegova dejanska velikost 4.8m, čeprav bo metoda PovejVelikost() sporočala, da je dolg 5m.

Denarnica

Sestavi razred Denarnica, ki vsebuje tri komponente:

celoštevilsko spremenljivko, ki predstavlja količino evrov v njej

celoštevilsko spremenljivko, ki predstavlja količino centov v njej

spremenljivko tipa String, ki vsebuje ime lastnika denarnice.

Page 70: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

70

Napiši vsaj dva konstruktorja:

prazen konstruktor, ki količino evrov in centov v denarnici postavi na 0 in ime lastnika

na "NEZNANO"

konstruktor, ki sprejme koliko evrov in centov naj bo v denarnici in ime lastnika

Napiši metode get in set. V set metodi za evre in cente preveri smiselnost podatka - če je

količina denarja, ki naj bo v denarnici, manjša od 0, naj se vrednost komponente ne spremeni.

Napiši tudi metodo ToString, ki vrne niz V denarnici, katere lastnik je #ime_lastnika je

#st_evrov evrov in #st_centov centov.

Napiši program, ki ustvari tabelo desetih denarnic z naključno mnogo denarja. S pomočjo metode izpiši to tabelo.

Rešitev: public class Denarnica

{

//komponente

private int evri;

private int centi;

private string lastnik;

//konstruktorji

public Denarnica()

{//prazen konstruktor, ki ustvari prazno denarnico brez lastnika

this.evri = 0;

this.centi = 0;

this.lastnik = "NEZNANO";

}

public Denarnica(int e, int c, string l) {

//konstruktor, ki ustvari denarnico z e evri, c centi in lastnikom l

this(); // naredimo prazno denarnico in jo

// potem poskusimo napolniti

this.NastaviEvre(e);//pogledamo če so podani podatki smiselni

this.NastaviCente(c);

this.NastaviLastnika(l);

}

//set, get metode

public bool NastaviEvre(int e)

{//set metoda, ki sprejme količino evrov v denarnici

if (e >= 0)

{//količina evrov mora biti pozitivno število

this.evri = e;

return true;

}

return false;//če smo vnesli negativno vrednost,

//ostane število evrov enako kot prej, metoda nam vrne false

}

Page 71: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

71

public bool NastaviCente(int c)

{//set metoda, ki sprejme količino centov v denarnici

if (c >= 0)

{//količina centov mora biti pozitivno število

this.centi = c;

return true;

}

return false;

}

public bool NastaviLastnika(string l)

{//set metoda, ki sprejme ime lastnika

if(l.Equals(""))//če smo vnesli prazen niz,

// nam metoda ne nastavi lastnika, vrne nam false

return false;

else

{

this.lastnik = l;//če niz l ni prazen, shranimo niz l

// v ime lastnika denarnice ter vrnemo true

return true;

}

}

public int VrniEvre()

{//get metoda, ki vrne koliko evrov je v denarnici

return this.evri;

}

public int VrniCente()

{

return this.centi;

}

public string VrniLastnika()

{//get metoda, ki pove ime lastnika denarnice

return this.lastnik;

}

//metoda ToString

override public string ToString()

{//metoda, ki izpiše podatke o denarnici

return "V denarnici, katere lastnik je " +

this.VrniLastnika() + ", je " + this.VrniEvre() + " evrov in "

+ this.VrniCenti() + " centov.";

}

}

Še program public class TestDenarnica {

public static void Main(string[] args) {

Denarnica[] d = new Denarnica[10]; //ustvarimo tabelo desetih denarnic

string[] lastniki = {"Ana", "Ema", "Sara", "Andreja",

"Mojca", "Samo", "Anţe", "Bor", "Aleš", "Jure"};

//tabela lastnikov, iz katere bomo jemali lastnike denarnic

Page 72: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

72

Random random = new Random();

int i = 0;

while (i < 10){//ustvarimo i-to denarnico

z lastnikom iz tabele lastniki in s poljubno mnogo

denarja med 0 in 10 evri in 0 in 150 centi

d[i] = new Denarnica(

(int)(random.Next(0,10)),

(int)(random.Next(0,150)),

lastniki[i]);

i++;

}

// izpis

IzpiTabeleDenarnic(d);

}

public static void IzpisTabeleDenarnic(Denarnica[] t) {

int i = 0;

while (i < t.Length) {

Console.WriteLine(d[i].toString());//izpišemo lastnosti denarnice

i = i + 1;

}

}

}

Vaje

Zival

V ogrodju se nahaja razred Zival, ki vsebuje spremenljivke

private int steviloNog;

private string vrsta;

private string ime;

in metode za nastavljanje in spreminjanje le-teh. Vsebuje tudi konstruktor brez parametrov, ki

postavi spremenljivke na smiselno začetno vrednost.

OGRODJE RAZREDA ZIVAL:

public class Zival

{

/*Razred predstavlja objekte tipa Zival.

Vsebuje osnovne podatke o zivali, kot so vrsta, stevilo nog in

ime zivali.

*/

//objektne spremenljivke

private int steviloNog;

private string vrsta;

private string ime;

/* Edini razredni konstruktor.

Ustvari objekt Zival s stirimi nogami vrste pes

in imenom Pika.

Page 73: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

73

*/

public Zival() {

steviloNog = 4;

vrsta = "pes";

ime = "Pika";

}

// Vrne ime zivali.

public string VrniIme() {

return this.ime;

}

// Vrne vrsto zivali.

public string VrniVrsto() {

return this.vrsta;

}

/* Vrne stevilo nog zivali.

Zagotovljeno je, da bo stevilo nog vedno vecje od 0.

*/

public int VrniSteviloNog() {

return this.steviloNog;

}

/*Nastavi ime zivali.*/

public void NastaviIme(string vrednost) {

if(vrednost != null) {

this.ime = vrednost;

}

}

/*Nastavi vrsto zivali.*/

public void NastaviVrsto(string vrednost) {

if(vrednost != null) {

this.vrsta = vrednost;

}

}

/*Nastavi stevilo nog zivali.*/

public void NastaviSteviloNog(int vrednost) {

if(vrednost > 0) {

this.steviloNog = vrednost;

}

}

}

Naloga:

Dopolnite razred z metodo override public String ToString(), ki naj izpiše podatke o

objektih v obliki: #Ime: je vrste #vrsta in ima #steviloNog nog. Pri tem seveda

zamenjajte #vrsta in #steviloNog z vrednostmi v istoimenskih spremenljivkah.

Nato napišite testni program, kjer ustvarite 5 živali in jim nastavite smiselno vrsto in imena

ter število nog. Izpišite jih!

Page 74: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

74

Razred Kolega Sestavi razred Kolega, ki ima tri komponente:

ime

priimek

telefonska številka Vse tri komponente naj bodo tipa string in javno dostopne. Napiši vsaj dva konstruktorja:

prazen konstruktor, ki vse tri komponente nastavi na "NI PODATKOV".

konstruktor, ki sprejme vse tri podatke in ustrezno nastavi komponente

Napiši tudi metodo public string ToString(), ki vrne niz s smiselnim izpisom podatkov o objektu tipa Kolega (ime, priimek in telefonska številka).

Preizkus razreda Kolega Sestavi testni program, v katerem ustvariš dva objekta tipa Kolega. En objekt ustvari s praznim konstruktorjem, drugega s konstruktorjem, ki sprejme tri parametre. Oba objekta tudi izpiši na zaslon.

Uporaba razreda Kolega

Napiši program, ki sestavi tabelo 10ih objektov tipa Kolega, prebere telefonsko številko in izpiše tiste objekte iz tabele, ki imajo tako telefonsko številko. Če takega objekta v tabeli ni, naj se izpiše: "Noben moj kolega nima take telefonske številke." Naloge brez računalnika

Sledeče naloge rešuj brez uporabe računalnika!

1. V spodnji kodi je napaka. Poišči jo in jo odpravi.

KODA Z NAPAKO

552: public class X { 553: public int a = 0; 554: public int b = 2; 555: 556: override public ToString() { 557: return "a = " + a + " b = " + b; 558: } 559: }

2. Dana je koda: 560: public class Y { 561: private int x; 562: private int y; 563: 564: public Y(int row, int col) { 565: x = row; 566: y = col; 567: } 568: }

Kateri izmed spodnjih konstruktorjev je pravilen: a. y1 = new Y(2,3);

Page 75: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

75

b. y2 = new Y(2); c. y3 = new Y; d. y4 = new Y(); e. y5 = new Y(10.0, 3); f. y6 = new Y[5];

3. Razredu Y dodamo še konstruktor

public Y(int row) {

x = row;

y = 80;

}

Kateri izmed konstruktorjev pa so sedaj pravilni? a. y1 = new Y(2,3); b. y2 = new Y(2); c. y3 = new Y; d. y4 = new Y(); e. y5 = new Y(10.0, 3); f. y6 = new Y[5];

4. Dana je koda 569: public class Z { 570: private int a = 0; 571: private int b =0; 572: 573: public Z(int aa, int bb) { 574: a = aa; 575: b = bb; 576: } 577: }

Ustvarimo dva objekta tipa Z, in sicer z1 = new Z(5, 4) ter z2 = new Z(5, 4).

Kakšno vrednost nam vrne test z1 == z2 ? Trditev obrazloži.

Kako »pravilno« preveriti, če sta dva objekta »enaka« ? Namig: metoda equals.

Ali lahko metodo »equals« takoj uporabiš v zgornji kodi, ali moraš razred kaj spremeniti?

5. Dana je koda razreda Povecaj: 578: public class PovecajR { 579: public void Povecaj(int a) { 580: a++; 581: } 582: 583: public void Povecaj(int[] a) { 584: for(int i =0; i < a.Length; i++) 585: a[i] = a[i] + 2; 586: } 587: }

Ali je v kodi kakšna napaka? Če je, jo odpravi!

Kaj izpiše sledeča koda?

588: PovecajR inc = new PovecajR(); 589:

Page 76: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

76

590: int a = 5; 591: int[] b = {5}; 592: 593: inc.Povecaj (a); 594: inc.Povecaj (b); 595: Console.WriteLine("a = " + a); 596: Console.WriteLine("b = " + b[0]); 597:

Datum

Sestavi razred Datum, ki predstavlja datum (dan, mesec in leto). Definiraj ustrezne komponente in

konstruktorje.

Definiraj metodo override public string ToString(), ki vrne datum predstavljen z nizom

znakov.

Definiraj metodo public bool Equals(Datum d), ki vrne True, če je datum this enak

datumu d.

Datum 2

Razredu Datum dodaj metodo public int CompareTo(Datum d), ki primerja this in d

ter vrne celo število:

< 0, če je this pred d

0, če je this enak d

> 0, če je this za d

Oseba

Oseba 1

Sestavi razred Oseba, ki vsebuje podatke o osebi, in sicer:

ime

priimek

datum rojstva

Za predstavitev datuma uporabi razred Datum iz prejšnje naloge.

Definiraj ustrezne konstruktorje in metodo public string ToString(), ki predstavi osebo z

nizom znakov. Razred preizkusi tako, da narediš osebo, ki predstavlja tebe.

Oseba 2

Razredu Oseba dodaj metodo public String Polno_ime(), ki vrne polno ime osebe, se

pravi ime in priimek, ločena s presledkom.

Oseba in datoteke

Razredu Oseba dodaj metodi public void Shrani(StreamWriter ime) in

konstruktor public Oseba(StreamReader ime) za zapisovanje in branje osebe z datoteke.

Metoda shrani(w) zapiše na w v eno vrstico podatke o osebi v obliki, ki je primerna za branje, na primer

nekaj takega kot

Miha:Novak:23:7:1982

Konstruktor Oseba(r) prebere z r eno vrstico, ki je bila predhodno zapisana s Shrani, in zapiše

podatke v komponente objekta this.

Preveri, ali metoda in konstruktor pravilno delujeta.

Page 77: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

77

Oseba in datoteke 2

Razredu Oseba dodaj statični metodi public static void

ShraniSpisek(Oseba[] spisek, string dat) in public Oseba[]

static NaloziSpisek(string dat), ki shranita in naložita tabelo oseb na datoteko z

imenom dat.

Preveri, ali metodi pravilno delujeta. V pomoč naj ti bo spisek naslednjih oseb:

osebe.dat Miha:Novak:23:7:1982

Josip:Broz:7:5:1892

Josip:Plemelj:11:12:1873

Britney:Spears:12:2:1981

Kurt:Goedel:28:4:1906

Alan:Turing:23:6:1912

Leonhard:Euler:15:4:1707

Carl Friedrich:Gauss:30:4:1777

Oseba in datoteke 3

Napiši statično metodo public static void rojeni_pred(string dat,

Datum d), ki iz datoteke z imenom dat prebere spisek oseb in na zaslon izpiše tiste osebe, ki so rojene

pred datumom d.

Preveri, ali metoda pravilno deluje.

Majica Sestavite razred Majica, ki hrani osnovne podatke o majici:

o velikost: število med 1 in 5, o barvo: poljuben niz in o ali ima majica kratke ali dolge rokave.

Vse spremenljivke razreda morajo biti privatne, razred pa mora tudi poznati metode za nastavljanje in branje teh vrednosti. Podpisi teh metod naj bodo:

o public int VrniVelikost() ter public void SpremeniVelikost(int velikost)

o public string VrniBarvo() ter public void SpremeniBarvo(string barva)

o public boolean ImaKratkeRokave() ter public void NastaviKratkeRokave(boolean imaKratkeRokave)

Metoda za nastavljanje velikosti majice naj poskrbi tudi za to, da bo velikost vedno ostala znotraj sprejemljivih meja! Če uporabnik poskusi določiti napačno velikost, naj se obdrži prejšnja vrednost.

Kandidat za direktorja banke V banki je direktor uspel povzročiti politični škandal takšnih razsežnosti, da je moral odstopiti (seveda z odpravnino v višini 50 povprečnih plač) in zato sedaj isčejo novega direktorja. Za to službo se je prijavilo kar nekaj kandidatov, od katerih je vsak predložil sledeče podatke:

o ime o priimek o starost (v letih) o stopnjo izobrazbe - številka med 3 in 8 (vključno)

Banka želi izbrati direktorja, ki ne bi bil starejši od 50 let (da bo lepo izgledal v medijih) in ima vsaj 6. stopnjo izobrazbe. Tvoja naloga je:

sestaviti razred, ki bo hranil podatke o kandidatih za direktorja

dopolniti priložen testni program tako, da bo našel primerne kandidate za to mesto in jih izpisal

Razred Kandidat mora poleg ustreznih spremenljivk (ki naj bodo privatne!) vsebovati vsaj

javne metode:

Page 78: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

78

o public Kandidat () - konstruktor brez parametrov - ustvari kandidata s starostjo

20, stopnjo izobrazbe 6 in imenom in priimkom ko Neznani Neznanec.

o public void NastaviImePriimek(string ime, string priimek) -

metoda, ki nastavi ime in priimek kandidata. Če je slučajno ime oz. priimek enak null, naj starega imena in priimka ne spremeni.

o public string[] VrniImePriimek() - metoda, ki vrne ime in priimek kandidata.

Metoda naj vrne seznam nizov dolžine 2 - v prvem polju naj bo zapisano ime, v drugem pa priimek kandidata.

o public void NastaviStarost(int starost) - metoda, ki nastavi starost

kandidata. Starost mora biti v mejah med 20 in 80 let - če ni, naj se stara starost ne spremeni.

o public int VrniStarost() - metoda, ki vrne starost kandidata.

o public void NastaviIzobrazbo(int izobrazba) - metoda, ki nastavi

izobrazbo kandidata. Izobrazba mora biti v mejah med 3 in 8 - če ni, naj se stara izobrazba ne spremeni.

o public int VrniIzobrazbo() - metoda, ki vrne izobrazbo kandidata.

o public string ToString()

Dano je okostje programa, ki obravnava prijavljene kandidate. 598: public static Kandidat NajPrimernejši(Kandidat[] tabelaKandid) { 599: // tu poskrbi, da bodo v ime, priimek, ... 600: // prišli podatki o najprimern. kandidatu 601: string ime = "Moj"; 602: string priimek = "Prijatelj"; 603: int starost = 42; 604: int izobrazba = 6; 605: Kandidat nov = new Kandidat(); 606: nov.NastaviImePriimek(ime, priimek); 607: nov.NastaviIzobrazbo(izobrazba); 608: nov.NastaviStarost(starost); 609: return nov; 610: } 611: public static void Main(string[] args) 612: { 613: public string[] imena = {"Jana", "Jure", "Bernarda", 614: "Anţe", "Danijel", "Primoţ", "Anita", "Tina", "Matija"}; 615: public string[] priimki = {"Mali", "Ankimer", "Zupan", 616: "Zevnik", "Bogataj", "Koci", "Vilis", "Jerše", "Lokar"}; 617: int kolikoKandidatov = 30; 618: public Kandidat[] kandidati = new Kandidati[kolikoKandidatov]; 619: Random gen = new Random(); 620: 621: for(int i=0; i < kolikoKandidatov; i++) { 622: Kandidat tmp = new Kandidat(); 623: int randime = gen.Next(0, imena.Length - 1); 624: int randpriimek = gen.Next(0,priimki.Length - 1); 625: int randizobrazba = gen.Next(3,8); 626: int randstarost = gen.Next(20,90); 627: tmp.NastaviImePriimek(randime, randpriimek); 628: tmp.NastaviIzobrazbo(randizobrazba); 629: tmp.NastaviStarost(randstarost); 630: kandidati[i] = tmp; 631: }

Page 79: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

79

632: // tu poišči najprimernejšega kandidata in ga izpiši 633: Console.WriteLine("Najprimernejsi je: " +

NajPrimernejsi(kandidati));

634: 635: 636: Console.Write("Press any key to continue . . . "); 637: Console.ReadKey(true); 638: } 639: }

Spremeni metodo NajPrimernejsi, tako, da vrne kandidata, ki je najmlajši in ima zahtevano stopnjo

izobrazbe (ali višjo).

Kandidat - najstarejši Recimo, da so si v banki premislili in bi radi namesto mladega neizkušenega direktorja zaposlili najstarejšega (ne glede na izobrazbo). Poišči ga!

Denarnica

Sestavi razred Denarnica, ki vsebuje celoštevilsko spremenljivko, ki predstavlja količino denarja v njej kot javno spremenljivko (v centih) in metodo public string ToString(), ki vrne niz "V denarnici je # centov". Ustvari tudi tabelo desetih denarnic z naključno mnogo denarja in jih izpiši.

o Ali lahko na ta način zagotoviš, da v denarnici nikoli ne bo negativno mnogo denarja?

o Seveda ne. To bi se na primer zgodilo, če bi programer s firme Nekaj pacamo d.o.o. po pomoti vstavil v spremenljivko, ki predstavlja količino denarja, negativno vrednost. Zato moramo razred popraviti! Namesto javne spremenljivke vzemimo privatno, poleg tega pa dopišimo še metodi dvigni in placaj, ki v denarnico dodata oz. iz nje odvzameta denar. Če želimo plačati več, kot imamo denarja v denarnici, naj metoda placaj vrže napako! Opomba: namesto teh dveh metod bi lahko napisali samo t.i. "set" metodo: public void SetVrednost(int vrednost), ki količino denarja v denarnici nastavi na vrednost (in pri tem seveda pregleda, če je ta vrednost pozitivna). Ampak glede na naš razred je izbira dveh metod bolj naravna.

o Sestavi tudi testni program, kjer preizkusiš delovanje tojega razreda. V denarnico dodaj nekaj denarja, nekaj ga odvzemi in se prepričaj, da je v njej pravilna količina denarja. Preveri tudi plačilo prevelikega zneska in ustrezno odreagiraj!

o Ali prejšnjo nalogo sploh lahko v celoti izpolniš? Denar res lahko dodaš in ga odvzameš, ker pa je količina denarja privatna spremenljivka razreda, je od zunaj nikakor ne moreš prebrati! Ta problem se reši tako, da v razred dodaš javno metodo public int KolicinaDenarja(), ki nam vrne količino denarja v denarnici.

o Dopolni razred s konstruktorjem public Denarnica(int denar), ki ustvari denarnico z dano količino denarja. S pomočjo metode KolicinaDenarja

Page 80: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

80

preveri, če se je tvoja denarnica pravilno inicializirala. Ustvari tudi denarnico z negativno mnogo denarja in preveri, če metode razreda Denarnica v tem primeru vržejo napako. Ne pozabi je tudi ujeti v glavnem programu, sicer se ti bo program sesul!

o Denarnica ima tudi svojega lastnika. Dodaj privatno spremenljivko lastnik tipa string, ki vsebuje ime lastnika denarnice. Dopolni konstruktor tako, da poleg količine denarja sprejme tudi ime lastnika. Razredu dodaj tudi metodo VrniLastnik, ki vrne ime lastnika denarnice. Popravi tudi metodo ToString, tako da izpis vključuje tudi ime lastnika denarnice.

o Dopolni testni program tako, da ustvariš tabelo desetih denarnic z naključno količino denarja med 0 in 1000. Izpiši jih!

o Kdo izmed lastnikov si lahko privošči nakup banjice sladoleda, ki stane 800 centov?

Podatkovni nosilec Sestavite razred PodatkovniNosilec, ki predstavlja medij za shranjevanje podakov. Vsebuje naj informacije o kapaciteti medija (celo število, enota je bajt), izdelovalcu (niz ), osnovni ceni (celo število, enota je evroCent) in stopnji obdavčitve (celo število, enota je procent). Vsebovati mora vsaj javne metode:

public PodatkovniNosilec() - konstruktor, ki ustvari nosilec kapacitete 650Mb, proizvajalca "neznan", s ceno 100 centov in 20% davkom.

public PodatkovniNosilec(string ime, int kapaciteta, int cena, int davek) - konstruktor s parametri.

public void NastaviKapaciteto(int bajti) - nastavi kapaciteto medija v bajtih. Če je kapaciteta negativna, naj se ne spremeni.

public void NastaviIme(string ime) - nastavi ime izdelovalca medija. Če je ime enako null, naj se ne spremeni.

public void NastaviCeno(int cena) - nastavi ceno. Če je negativna, naj se ne spremeni.

public void NastaviObdavcitev(int pocent) - nastavi obdavčitev v procentih. Če so procenti izven meja 0-100, naj se ne stara vrednost ne spremeni.

public int VrniOsnovnoCeno() - vrne osnovno ceno medija (brez davka).

public double VrniDavek() - koliko davka je potrebno plačati za ta medij (v evrih).

public double VrniProdajnoCeno() - vrne osnovno ceno + davek (v evrih).

public string VrniIme() - vrne ime proizvajalca medija.

public double VrniKapaciteto(char enota) - vrni kapaciteto v enoti, ki jo določa znak enota: o 'b' - bajti o 'k' - kilobajti o 'M' - megabajti o 'G' - gigabajti

Pazi na to, da je en kilobajt 1024 (in ne 1000) bajtov!

public override string ToString() - vrne niz, ki smiselno opisuje razred. Sestavi tudi testni program, v katerem preveriš delovanje tvojega razreda.

Razred Zajec Denimo, da pišemo program, ki bo pomagal upravljati farmo zajcev. Za vsakega zajca poznamo serijsko številko, spol in maso

Sestavi razred Zajec, ki vsebuje komponente:

spol //vrednost true, če je zajec moškega spola in false, če ženskega (kaj hočete, moški šovinisti ...)

masa //masa zajca v kilogramih (tip double)

serijska //serijska številka zajca (tip string)

Page 81: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

81

Razred Zajec naj vsebuje vsaj dva konstruktorja: Prazen konstruktor, v katerem zajcu nastavimo težo na 1.0 kg, določimo moški spol in nastavimo serijsko številko na "NEDOLOČENO". Konstruktor, ki sprejme maso (spremenljivka tipa double), spol (spremenljivka tipa boolean) in serijsko številko (spremenljivka tipa string) zajca. Če želiš, lahko dodaš še kakšne konstruktorje, npr. takega, ki sprejme samo spol in serijsko številko zajca. Sestavi program, ki naredi tri zajce s težami, ki so naključna števila med 1.2 in 5.5. Izpiši vse tri teže zajcev. Enemu povečaj njegovo težo za 2kg. Izpiši serijsko številko najlažjega zajca. Metoda toString, razred Zajec V razredu Zajec napiši metodo public String ToString(), ki vrne niz s smiselnim izpisom spola, serijske številke in mase zajca. Prejšnji program spremeni tako, da vedno izpišeš kompletne podatke o zajcu.

Metoda VrednostZajca, razred Zajec Napiši metodo VrednostZajca, ki sprejme ceno za kg žive teže v evrih, in vrne vrednost zajca v €. Uporaba razreda Zajec Sestavi program, ki ustvari farmo zajcev (tabelo 10 zajcev) in med njimi poišče:

najtežjega

najcenejšega

vse zajklje najtežjega samca Spol je true za samico, razred Zajec Zaradi demostracij (žal tudi nasilnih), ki jih je povzročila odločitev glede načina hranjenja podatka o spolu, smo se odločili, da v spremenljivki spol ne bomo več hranili true za samca, ampak za samico. Kaj vse moramo

spremeniti v razredu Zajec? Kakšen vpliv ima to na program, ki si ga napisal pri nalogi Uporaba razreda Zajec? Spol ni ne true, ne false ampak m in f, razred Zajec Po spremembi opisani zgoraj je ponorel moški del populacije. Zato bomo spremenljivko spol spremenili v tip

char in tam hranili 'm' za samca in 'f' za samico. Kaj vse moramo spremeniti v razredu Zajec? Get in set metode v razredu Zajec Vsem trem komponentam v razredu Zajec spremeni način dostopa iz public v private. Ker sedaj ni več mogoče dostopati do komponent neposredno (npr. zajcek.serijska), dodaj še get in set metode za vračanje in nastavljanje vrednosti vseh treh komponent. Set metode napiši tako, da v njih preveriš, če je podana vrednost smiselna (npr. smislena nova masa je le med 0 in 10 kg). Če je podana vrednost smiselna, naj metoda nastavi komponento na to vrednost in vrne true. Sicer naj se vrednost komponente ne spremeni in metoda naj vrne false. Metode za nastavljanje vrednosti komponent uporabi tudi v tistih konstruktorjih, ki sprejmejo podatke od uporabnika. Točka Da bi se zajci bolje počutili, jim kmet vsak dan dovoli par ur skakljanja po travniku. Ker pa je travnik velik, je moral spremljati položaj vsakega zajca (da jih ne bi kdo ukradel in snedel). V tam namem napiši razred Tocka, ki naj vsebuje spremenljivki x in y tipa double. Napiši ustrezne konstruktorje - osnovni konstruktor (brez parametrov) naj točko postavi v koordinatno izhodišče. Dopiši tudi metodi za nastavljanje in spreminjanje lege točke (vsake kompomente posebej). Zajci na travniku Popravi razred Zajec tako, da mu dodaš parameter tipa Tocka, ki določa lego na travniku. Dodaj ustrezen konstruktor in upoštevaj, da moraš točko ustvariti tudi v konstruktorjih, kjer lege ne dobiš kot podatek. V takem primeru naj bo zajec v koordinatnem izhodišču.

Dopolni razred Zajec z metodama za branje in spreminjanje lege zajca. Zajci na travniku II Ker je kmet travnik ogradil z ograjo, se zajci lahko nahajajo samo znotraj v naprej znanega območja, recimo v kvadratu s spodnjim levim krajiščem v točki (0,0) in širino ter višino 100 metrov.

Page 82: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

82

Popravi konstruktor in metodo za nastavljanje pozicije zajca tako, da ne bo dovolil vnosa napačne lege zajca (izven dovoljenga območja)! Pri tem bodi pozoren na to, da moraš pri branju lege zajca vrniti duplikat točke, saj bo drugače uporabnik lahko v točko od zunaj vnesel napačne podatke! Mogoče se ti splača napisati metodo public static Tocka Kopija(Tocka t) v razredu

Tocka, ki vrne kopijo točke t - torej novo točko na istih koordinatah. Razdalja med dvema točkama Ker velikokrat pride prav, da izračunamo razdaljo med dvema točkama, dopolni razred Tocka z metodo

public double Razdalja(Tocka t), ki vrne razdaljo med trenutno točko in točko t, ki jo metoda dobi kot parameter. Razdalja med dvema zajcema Dopolni razred Zajec z metodo RazdaljaMedZajcema, ki kot parameter sprejme drugega zajca in vrne razdaljo med njima. Uporaba razreda Zajec II Oboroženi z novimi metodami dopolnimo naš program za gojenje zajcev tako, da generira zajce na naključnih položajih. Nazadnje poiščimo zajca, ki je:

1. najbolj oddaljen od prvega zajca 2. najbolj oddaljen od koordinatnega izhodišča 3. ima največji produkt med maso in oddaljenostjo od zhodišča

Razred Ulomek

Podana je koda razreda Ulomek. Dopolni manjkajoče metode ter v glavnem programu

(metoda Main) ustvari dva ulomka, katerih števec in imenovalec naj bo naključno število

med med 1 in 9 (vključno z mejama). Ulomka seštej in rezultat izpiši na zaslon.

RAZRED ULOMEK

public class Ulomek {

640: int stevec;

641: int imenovalec;

642:

643: // Privzeti konstruktor ustvari ulomek 1/1

644: // DOPOLNI!

645:

646:

647: // Ustvari podani ulomek. Pazi tudi na pravilnost podatkov.

648: // Ce so napačni, ustvari enak ulomek kot v privzetem

649: // konstruktorju.

650: public Ulomek(int stevec, int imenovalec) {

651: // DOPOLNI!

652: }

653:

654: // Metoda prišteje ulomek u k trenutnemu ulomku.

655: public void Pristej(Ulomek u) {

656: // DOPOLNI!

657: }

658:

659: // Metoda zmnoţi trenutni ulomek z ulomkom u ter

660: // zmnoţek vrne kot rezultat. Trenutni ulomek ter

661: // ulomek u se pri tem ne smeta spremeniti.

662: public DOPOLNI Zmnozi(Ulomek u) {

Page 83: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

83

663: // DOPOLNI!

664: }

665: // Metoda vrne vrednost ulomka kot realno število.

666: public DOPOLNI Vrednost() {

667: // DOPOLNI!

668: }

669:

670: // Vrne niz oblike »stevec/imenovalec«

671: DOPOLNI string ToString() {

672: // DOPOLNI!

673: }

674:

675: // Metoda okrajša dani ulomek

676: public void Okrajsaj() {

677: // DOPOLNI

678: }

679:

680: // Obratna vrednost.

681: public void Obrni() {

682: // DOPOLNI

683: }

684: }

TESTNI PROGRAM

public class TestUl

685: public static void Main(string[] argc) {

686: // DOPOLNI po navodilih naloge!

687: }

688: }

Razred Stranka Na osnovi kode spodaj podanega razreda Oseba sestavite razred Stranka, ki predstavlja bančno strankoega komitenta. Poleg lastnosti, so enake kot v razredu Oseba, mora hraniti tudi podatke o tekočem računu (niz), številki EMŠO (niz) ter povprečnem mesečnem prilivu na tekoči račun (realno število).

RAZRED OSEBA

public class Oseba {

private string ime;

private string priimek;

public Oseba(string ime, string priimek) {

this.ime = ime;

this.priimek = priimek;

}

public string VrniIme() {

return this.ime;

}

public string VrniPriimek() {

return this.priimek;

}

Page 84: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

84

}

Pri tem mora razred zadoščati naslednjim pogojem:

Vse spremenljivke v njem morajo biti privatne.

Poznati mora javni konstruktor, ki sprejme pet parametrov: ime, priimek, številko tekočega

računa, številko EMŠO ter podatek o povprečnem mesečnem prilivu na račun. Pri tem mora biti

zadnji podatek pozitivni+o realno število, prvi štirje pa neprazni nizi. Če to ni izpolnjeno,

ustrezno reagiraj.

Poznati mora metode za vračanje podatkov, ki jih hrani: imena, priimka, številke tekočega

računa, številke EMŠO ter povprečnega priliva na račun.

Metoto ToString, ki na smiselen način izpiše podatke o stranki.

Nazadnje sestavite testni program, kjer ustvarite tabelo desetih strank z izmišljenimi podatki, ter med njimi poščite stranko z najvišjim mesečnim prilivom na račun ter tistega z po leksikografski urejenosti prvim imenom (če jih je več z enakim največjim prilivom ali enakim prvim imenom poiščite kateregakoli) ter izpišite njune podatke na zaslon (z uporabo metode ToString).

Kubični polinom Na osnovi razreda Polinom, ki hrani polinome do stopnje dva in pozna operacijo seštevanja dveh polinomov (njegovo kodo glej spodaj) sestavi razred KubicniPolinom, ki bo lahko hranil kubične polinome ter bo poleg vsote podpiral tudi operacijo, ki obstoječi polinom pomnoži s skalarjem (spremeni torej obstoječi objekt in ne ustvari novega). Seveda naj tvoj razred ne pozabi na metodo ToString, ki v razredu Polinom manjka.

RAZRED POLINOM

//razred predstavlja polinom oblike ax^2 + bx + c

public class Polinom {

private double a, int b, int c;

//Ustvari ničelni polinom.

public Polinom() {

this.a = this.b = this.c = 0;

}

//Ustvari polinom z danimi koeficienti.

public Polinom (double a, double b, double c) {

this.a = a;

this.b = b;

this.c = c;

}

public Polinom Sestej(Polinom p) {

return new Polinom(this.vrniKoeficient(2) + p.vrniKoeficient(2),

this.vrniKoeficient(1) + p.vrniKoeficient(1),

this.vrniKoeficient(0) + p.vrniKoeficient(0));

}

//metoda nam vrne koeficient pred x^i

public int VrniKoeficient(int i) {

if (i == 2)

return this.a;

if (i == 1)

return this.b;

Page 85: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

85

if (i == 0)

return this.c;

return 0;

}

//Metoda nastavi koeficient pred x^i na k.

//Če je i napačen, naj ne stori nič

public void NastaviKoeficient (int i, double k) {

if (i == 2)

this.a = k;

else if (i == 1)

this.b = k;

else if (i == 0)

this.c = k;

}

}

Sestavi testni program, ki vsebuje metodo, ki zna odšteti dva kubična polinoma (vrne polinom, ki je razlika dveh polinomov). Namig. p1 – p2 lahko izračunaš kot p1 + ((-1)*p2). Pregledno izpiši nekaj računov s polinomi.

Polinom Sestavi razred s pomočjo katerega predstaviš poljubni polinom. Namig: koeficiente hrani v tabeli. Razred naj pozna enake metode kot razred KubicniPolinom.

Dalmatinci I (brez računalnika) Denimo, da smo želeli sestaviti razred Dalmatinec, ki ima lastnosti ime psa in število njegovih pik. Koda razreda je:

RAZRED DALMATINEC

public class Dalmatinec{

public string ime;

private int steviloPik;

public Dalmatinec() {

this.ime = "Reks";

this.steviloPik = 0;

}

public void ImePsa(string ime) {

ime = this.ime;

}

private void NastaviIme(string ime) {

this.ime = ime;

}

public void NastaviSteviloPik(int steviloPik) {

this.steviloPik = steviloPik;

}

}

V glavnem programu smo ustvarili objekt Dalmatinec z imenom d in mu želimo nastaviti število pik na 100 in ime na Pika. Kateri način je pravilen? Pri nepravilnih povej, kaj in zakaj ni pravilno.

a) d.NastaviIme("Pika"); d.NastaviSteviloPik(100);

Page 86: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

86

b) d.ime = "Pika"; d.steviloPik = 100;

c) d.ime = "Pika"; d.NastaviSteviloPik(100);

d) d.imePsa("Pika"); d.NastaviSteviloPik(100);

e) d.imePsa("Pika"); d.steviloPik = 100;

f) d.NastaviIme("Pika"); d.steviloPik = 100;

g) nobeden, ker tega sploh ne moremo storiti

Dalmatinci II Sedaj želimo našemo razredu Dalmatinec dodati tudi podatke o spolu psa. Ta podatek bomo hranili v spremenljivki spol. Interno (znotraj razreda) naj logična vrednost true pomeni ženski, false pa moški spol. Dopolnite razred tako, da bo zadoščal naslednjim trem pogojem:

Spremenljivka spol naj ne bo dostopna izven razreda Dalmatinec

obstaja naj metoda KaksenSpol, ki v primeru samca vrne 'm', v primeru samice pa 'f'.

spol se nastavi le ob ustvarjanju objekta. Morali boste torej napisati konstruktor razreda Dalmatinec, ki sprejme kot parameter znak za spol. Ta naj bo kot zgoraj 'm' za samca in 'f' za samico. Predpostavite, da bo parameter zagotovo znak 'm' ali 'f'.

Dalmatinci III Predpostavimo, da razred, ki zadošča kriterijem naloge Dalmatinci III, že imamo. Sestavi metodo public

static int SteviloSamcev(Dalmatinec[] dalmatinci), ki prešteje število samcev v tabeli dalmatincev.

Oseba

Podan imamo razred Oseba z objektnima metodama VrniIme() ter VrniPriimek() (ki nam vrneta niza, ki predstavljata ime oz. priimek osebe). Sestavite metodi, ki:

a. iz tabele tipa Oseba[] izpiše vse osebe z imeni dolgimi 5 znakov.

b. iz tabele tipa Oseba[], kjer so osebe urejene po priimkih (ter nato po imenih), učinkovito

poiščite osebo Janez Novak, ter kot rezultat metode vrnite referenco na to osebo. Če

takšna oseba ne obstaja, vrnite null.

Oseba II

Podan imamo razred Oseba z objektnima metodama VrniIme() ter VrniPriimek() (ki nam vrneta niza, ki predstavljata ime oz. priimek osebe). Sestavite statični metodi, ki:

a. Za dano tabelo tipa Oseba[] izpiše priimke vseh oseb, katerih skupna dolžina imena in

priimka presega k znakov, kjer je k parameter metode.

b. V tabeli tipa Oseba[], kjer so osebe urejene po priimkih, učinkovito poiščite poljubno osebo

s priimkom Novak (ime torej ni pomembno), ter kot rezultat metode vrnite nov objekt, ki

ima podatke enake kot najdena oseba. Če takšna oseba ne obstaja, vrnite null.

Zgoščenka Pesem na zgoščenki je predstavljena z objektom razreda Pesem:

public class Pesem

{

public string naslov;

public int minute;

public int sekunde;

Page 87: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

87

public Pesem(string nasl, int min, int sek)

{

naslov = nasl; minute = min; sekunde = sek;

}

}

Na primer, objekt new Pesem("Echoes",15,24) predstavlja pesem “Echoes”, ki traja 15 minut in 24 sekund.

(a) Sestavi razred Zgoscenka, ki vsebuje naslov zgoščenke, ime izvajalca in tabelo pesmi na zgoščenki. Predpostavi, da vse podatke (naslov, izvajalca in tabelo pesmi) obvezno dobiš s konstruktorjem in jih naknadno nikoli ne smeš (moreš) spremeniti.

Definiraj ustrezne komponente s pravilnim dostopom in konstruktor.

(b) Razredu Zgoscenka dodaj objektno metodo Dolzina(), ki vrne skupno dolžino vseh pesmi na zgoščenki, izraženo v sekundah.

(c) Razredu Zgoscenka dodaj objektno metodo PovprecnaDolzina(), ki vrne povprečno dolžino vseh pesmi na zgoščenki, izraženo v sekundah.

Statično in objektno

Statične komponente o V razredu Kvader

public static final int MIN_DOL_STR = 1; o final

gre za konstanto

Nič novega, že poznamo o static

Obstajajo neodvisno od obstoja objekta tega razreda Le en primerek za cel razred! Math.PI

Statične spremenljivke public class Foo {

public static int x = 13;

public int y;

public Foo(int z) {

this.y = z;

}

}

o x: statična komponenta, y: objektna (nestatična) komponenta o Statične komponente: vsebovane v razredu, objektne komponente: v objektih. o Vsak objekt vsebuje svojo kopijo objektnih komponent, vsaka statična komponenta pa vedno obstaja v eni

sami kopiji. Foo t1 = new Foo(42);

Foo t2 = new Foo(30);

o Dva objekta - dve kopiji komponente y, po eno v vsakem objektu. Ker je komponenta x statična, vedno

obstaja v eni sami kopiji, tudi če nimamo nobenih objektov razreda Foo:

Page 88: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

88

Uporaba statičnih spremenljivk o Različne konstante o Enolična identifikacija

Objekt naj ima svojo serijsko številko Denimo, da so te številke zaporedne Vedeti, katera je bila dodeljena zadnja Zadnja dodeljena številka: ni lastnost objekta, ampak cele skupine objektov

o Vedno, ko je določen podatek skupen za vse objekte tega razreda o Kako vemo, ali naj bo komponenta statična ali objektna?

Statične komponente so tiste, ki so skupne za celoten razred – niso posebna lastnost enega (ali nekaj) primerkov objektov tega razreda.

Statične komponente – dostop o public ali private (odvisno od željenega načina dostopa) o public praviloma le, če gre za konstante

zaradi final ni težav s pravilnostjo podatkov konstante imajo način dostopa private, če jih želimo "skriti" pred uporabniki

Statične komponente – naslavljanje Naslavljanje (če je dostop public):

ime_objekta.ime_komponente ali ime_razreda.ime_komponente ali (znotraj razreda) ime_komponente Praviloma: ime_razreda.ime_komponente Izogibamo se ime_objekta.ime_komponente, saj ta komponenta ni vezana na objekt!

Ni potrebno, da bi sploh obstajal kak objekt te vrste

Zajec o Radi bi vedeli, koliko zajcev imamo o Števec ustvarjenih objektov

NI lastnost posameznega zajca SKUPNA lastnost private static int kolikoZajcev = 0;

Na začetku 0 Spreminjamo takrat, ko ustvarimo novega zajca

new

V konstruktorju! o Serijska številka naj bo zagotovo enolična

Določimo jo le enkrat – ob rojstvu (konstruktor) ZAJEC_n (če je to n-ti zajec)

Page 89: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

89

RAZRED ZAJEC

418: public class Zajec { 419: // SKUPNE SPREMENLJIVKE 420: private static string OsnovaSerijska = "ZAJEC_"; 421: private static int kolikoZajcev = 0; 422: public static final int MIN_TEZA = 0; // minimalna teţa 423: public static final int MAX_TEZA = 10; // maksmialna teţa 424: 425: // OBJEKTNE SPREMENLJIVKE 426: private string serijska; 427: private bool spol; 428: private double masa; 429: 430: // konstruktorji 431: 432: public Zajec() { 433: this.spol = true; // vsem zajcem na začetku določimo m. spol 434: this.masa = 1.0; // in tehtajo 1kg 435: ZajecNov.kolikoZajcev++; // NAREDILI SMO NOVEGA ZAJCA 436: this.serijska = ZajecNov.OsnovaSerijska + ZajecNov.kolikoZajcev; 437: } 438: public Zajec(bool spol, double masa) { 439: this(); // poklicali smo konstruktor Zajec() - ta 440: // bo ţe povečal število zajcev + naredil 441: // nj. ser. številko 442: this.SpremeniTezo(masa); // uporabimo metodo za sprem. 443: this.spol = spol; 444: } 445: 446: // get/set metode 447: 448: public double PovejTezo() { 449: // teţo bomo povedali le na 0.5 kg natančno 450: int tezaKg = (int)this.masa; 451: int decim = (int)((this.masa - tezaKg) * 10); 452: if (decim < 3) return tezaKg + 0.0; 453: if (decim < 8) return tezaKg + 0.5; 454: return tezaKg + 1.0; 455: } 456: 457: public bool SpremeniTezo(double novaTeza) { 458: // smislena nova teza je le med MIN_TEZA in MAX_TEZA 459: if ((novaTeza > MIN_TEZA) && (novaTeza <= MAX_TEZA)){ 460: this.masa = novaTeza; 461: return true; // sprememba uspela 462: } 463: // v nasprotnem primeru NE spremenimo teţe 464: // in javimo, da spremembe nismo naredili 465: return false; 466: } 467: 468: public string PovejSerijsko() { 469: return this.serijska; 470: }

Page 90: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

90

471: 472: // public void SpremeniSerijsko(string s) { TO IZLOČIMO; 473: // KER SE SER_ŠT NE SPREMINJA! 474: 475: public bool JeSamec() { 476: return this.spol; 477: } 478: 479: // ker se spol naknadno NE spremeni, metode za 480: // spreminjanje spola sploh ne ponudimo uporabniku! 481: 482: // Ostale metode 483: 484: public double Vrednost(double cenaZaKg) { 485: // pove vrednost zajca 486: // zajce tehtamo na dogovorjeno natančnost 487: return cenaZaKg * this.povejTezo(); 488: } 489: 490: public bool MeNapojiti() { 491: // ugotovimo, če ga je smiselno 492: // napojiti, da bomo "ujeli" naslednjih pol kg 493: // dejanska teţa je večja od "izmerjene" 494: return (this.masa - this.povejTezo() > 0); 495: } 496: 497: } // Zajec

Štetje ustvarjenih objektov V konstruktorjih povečujemo števec

RAZRED KVADER

498: public class Kvader { 499: // z /* ... */ so oznaceni komentarji za razlago 500: // zakaj doloceni konstrukcijski prijemi 501: // v "pravem" programu jih ne bi bilo! 502: 503: private double a; /* uporabnika ne zanima, kako 504: imamo shranjene podatke */ 505: private double b; 506: private double c; 507: 508: private int serijskaStevilka; // serijska stevila objekta 509: 510: private static int kolikoNasJe = 0; // koliko je 511: // ustvarjenih objektov 512: 513: public static final int MIN_DOL_STR = 1; // minimalna 514: // dolzina stranice 515: public static final int MAX_DOL_STR = 100; // maksimalna 516: // dolzina stranice 517: /* Ker menimo, da je smiselno, da uporabnik neposredno 518: vidi ti dve kolicini - dostop 519: public. Spreminjati ju ne more zaradi final. 520: Dostop: Kvader.MIN_DOL_STR

Page 91: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

91

521: 522: Določilo static - gre za razredno spremenljivko - 1 523: kopija za vse objekte 524: */ 525: 526: // vrni podatek 527: 528: public int PovejA() { /* omogocimo uporabniku dostop do 529: vrednosti zanj zanimivih podatkov */ 530: return (int)this.a; 531: } 532: public int PovejB() { 533: return (int)this.b; 534: } 535: public int PovejC() { 536: return (int)c; 537: } 538: 539: private bool Kontrola(int x){ /* pomozna metoda, zato private */ 540: // preverimo, ce velja MIN_DOL_STR <= x <= MAX_DOL_STR 541: return ((MIN_DOL_STR <= x) && (x <= MAX_DOL_STR)); 542: } 543: 544: // nastavi podatek 545: 546: public void StranicaA(int a) { /* omogocimo uporabniku 547: spreminjanje podatkov */ 548: // Ce je podatek v mejah med 1 in 100, ga spremenimo, 549: // drugace pustimo 550: // taksnega, kot je 551: if (Kontrola(a)) 552: this.a = a; /* this je potreben, da locimo med 553: imenom podatka objekta in parametrom a */ 554: /* verjetno bi bilo bolj smiselno namesto 555: a parameter poimenovati s ali kako drugače */ 556: } 557: public void StranicaB(int a) { 558: // Ce je podatek v mejah med 1 in 100, ga spremenimo, 559: // drugace pustimo 560: // taksnega, kot je 561: if (Kontrola(a)) 562: 563: this.b = a; /* this v nasprotju s prejsnjo metodo 564: tu ni potreben. 565: se vedno pa velja, da bi bilo boljse, 566: da bi parameter a poimenovali 567: drugace - npr. s */ 568: } 569: public void StranicaC(int s) { 570: // Ce je podatek v mejah med 1 in 100, ga spremenimo, 571: // drugace pustimo 572: // taksnega, kot je 573: if (Kontrola(s)) /* na ta nacin bi verjetno "zares" 574: sprogramirali tudi

Page 92: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

92

575: zgornji metodi */ 576: c = s; 577: } 578: 579: //........................................... 580: // konstruktorji 581: 582: public Kvader() { 583: a = b = c = 1; 584: kolikoNasJe++; 585: serijskaStevilka = kolikoNasJe; 586: } 587: 588: public Kvader(int s) { 589: // kocka s stranico s. če podatek ni v redu - kocka z robom 1 590: if (!kontrola(s)) s = 1; // ce podatek ni v mejah, 591: // ga postavimo na 1 592: a = b = c = s; 593: kolikoNasJe++; 594: serijskaStevilka = kolikoNasJe; 595: } 596: 597: public Kvader(int s1, int s2, int s3) { // kvader s1 x s2 x s3 598: // Ce kak podatek ni v redu - ga postavimo na 1 599: if (!kontrola(s1)) s1 = 1; // ce podatek ni v mejah, 600: // ga postavimo na 1 601: a = s1; 602: 603: if (!Kontrola(s2)) s2 = 1; // ce podatek ni v mejah, 604: // ga postavimo na 1 605: b = s2; 606: 607: if (!Kontrola(s3)) s3 = 1; // ce podatek ni v mejah, 608: // ga postavimo na 1 609: c = s3; 610: kolikoNasJe++; 611: serijskaStevilka = kolikoNasJe; 612: 613: } 614: 615: public Kvader(Kvader sk) { 616: // Novi objekt je kopija obstojecega objekta sk 617: this.a = sk.a; /* this ni potreben */ 618: StranicaB(sk.PovejB()); /* tudi znotraj razreda se 619: splaca uporabljati metode 620: za delo s podatki */ 621: StranicaC(sk.PovejC()); /* ceprav imamo neposreden dostop 622: do spremenljivk, saj nam ob 623: morebitni spremembi predstavitve 624: ni potrebno toliko popravljati! */ 625: kolikoNasJe++; 626: serijskaStevilka = kolikoNasJe; 627: 628: }

Page 93: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

93

629: 630: public static int PovejKolikoNasJe() { 631: return kolikoNasJe; 632: } 633: 634: // 635: // --------------- to string 636: // 637: public string ToString() { /* prekrivanje metode iz razreda 638: Object */ 639: // Metoda vrne podatek v obliki Kvader: a x b x c 640: return "Kvader: " + PovejA() + " x " + PovejB() + " x " 641: + PovejC(); 642: /* tudi znotraj razreda se splaca uporabljati metode za 643: delo s podatki ceprav imamo neposreden dostop do 644: spremenljivk, saj nam ob morebitni spremembi predstavitve 645: ni potrebno toliko popravljati! */ 646: } 647: 648: } // class Kvader Zgled 2:

Uporaba konstant Meje pravilnosti podatkov so konstantne

689: public class Kovanec_nov { 690: 691: private int polmer; 692: private int visina; 693: public static final int OBI_POLMER = 3; // obicajni polmer 694: public static final int OBI_VISINA = 1; // obicajna visina 695: public static final int NAJ_POLMER = 3; // največji moţni polmer 696: public static final int NAJ_VISINA = 1; // največja moţna visina 697: 698: 699: // konstruktorji 700: public Kovanec() { 701: // "obicajni" kovanec 702: this.polmer = OBI_POLMER; 703: this.visina = OBI_VISINA; 704: } 705: 706: public Kovanec(int polmer, int visina) { 707: // "drugacni" kovanec 708: // ce ni ustreznih dimenzij, naredimo "obicajni" kovanec 709: if { (Vmes(polmer, 1, NAJ_POLMER) && 710: Vmes(visina, 1, NAJ_VISINA)) 711: // mere so v redu 712: this.visina = visina; 713: this.polmer = polmer; 714: } 715: else { // ce ni ustreznih dimenzij

716: this.polmer = OBI_POLMER;

Page 94: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

94

717: this.visina = OBI_VISINA; 718: } 719: } 720: 721: // "SET" metode 722: public bool NastaviPolmer(int r) { 723: // smiselni polmer je le med 1 in NAJ_POLMER 724: // drugace ga pustimo pri miru 725: // metoda vrne vrednost true, ce smo polmer spremenili 726: if (Vmes(r, 1, NAJ_POLMER)) { 727: this.polmer = r; 728: return true; 729: } 730: return false; // polmer je ostal nespremenjen 731: } 732: 733: public bool NastaviVisino(int visina) { 734: // smiselni polmer je le med 1 in NAJ_VISINA 735: // drugace jo pustimo pri miru 736: // metoda vrne vrednost true, ce smo visino spremenili 737: if (vmes(visina, 1, NAJ_VISINA)) { 738: this.visina = visina; 739: return true; 740: } 741: return false; // visina je ostala nespremenjena 742: } 743: 744: // "GET" metode 745: public int PovejPolmer() { 746: return polmer; 747: } 748: 749: public int PovejVisino() { 750: return visina; 751: } 752: 753: // tostring 754: override public string ToString() { 755: return "Polmer: " + this.polmer + " visina: " + this.visina; 756: } 757: 758: // volumen in povrsina 759: public double Volumen() { 760: // volumen kovanca PI * r^2 * v 761: return Math.PI * this.polmer * this.polmer * this.visina; 762: } 763: 764: public double Povrsina() { 765: // povrsina kovanca 2 * PI * r * v + 2 * PI * r^2 766: return (2 * Math.PI * this.polmer) * (this.polmer + this.visina); 767: } 768: 769: // pomozne metode 770: private static bool Vmes(int x, int a, int b) {

Page 95: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

95

771: // ali je x med a in b 772: return (a <= x) && (x <= b); 773: } 774: }

Statične metode Objekt:

Objektne komponente Objektne metode

Skupno vse objektom – na nivoju razreda:

statične metode statične komponente

Statične metode se v neobjektnih programskih jezikih imenujejo "funkcije" ali "procedure", statične komponente pa se imenujejo "globalne spremenljivke".

Statične/objektne metode Statično metodo bla() v razredu Foo lahko vedno izvedemo z ukazom Foo.bla(). Znotraj statične metode objekt this ni definiran, ker se statična metode ne kliče na objektu. Objektno metodo hej() v razredu Foo lahko izvedemo, če imamo neki objekt x razreda Foo, z ukazom x.hej(). Znotraj metode hej() označuje this objekt, na katerem je metoda poklicana.

Zgled 649: public class Foo { 650: static public int x = 12; 651: public int y; 652: 653: public Foo(int z) { 654: this.y = z; 655: } 656: 657: public static int F(int a) { 658: return x + a; 659: } 660: // ali return Foo.x + a; 661: 662: public int G(int a) { 663: return this.y + Foo.x + a; 664: } 665: // ali y + Foo.x + a ALI y + x + y ALI ... 666: } Statična metoda F ima dostop do (statične) komponente x. Dostopa do komponente y nima, saj znotraj statične metode ne moremo pisati this.y. Objektna metoda G ima dostop do komponente this.y, kjer je this objekt, na katerem je metoda g klicana. Prav tako ima dostop do (statične) komponente x. int p = Foo.G(7); // p == 12 + 7 == 19

Foo.x = -3;

int q = Foo.F(5); // q == -3 + 5 == 2

Foo t = new Foo(100);

Foo s = new Foo(200);

int r = t.G(50); // r == 100 + (-3) + 50 == 147

Page 96: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

96

Statične metode o Kadar za izvajanje ne potrebujemo objekta tega razreda. o Praviloma ne spreminjajo stanja nekega objekta. o Vsi podatki podani preko parametrov metode. o Main

667: public class Kovanec_nov { 668: 669: private int polmer; 670: private int visina; 671: public static final int OBI_POLMER = 3; // obicajni polmer 672: public static final int OBI_VISINA = 1; // obicajna visina 673: public static final int NAJ_POLMER = 3; // največji moţni polmer 674: public static final int NAJ_VISINA = 1; // največja moţna visina 675: 676: // konstruktorji 677: public Kovanec() { 678: // "obicajni" kovanec 679: this.polmer = OBI_POLMER; 680: this.visina = OBI_VISINA; 681: } 682: 683: public Kovanec(int polmer, int visina) { 684: // "drugacni" kovanec 685: // ce ni ustreznih dimenzij, naredimo "obicajni" kovanec 686: if (Vmes(polmer, 1, NAJ_POLMER) && Vmes(visina,1,NAJ_VISINA)){ 687: // mere so v redu 688: this.visina = visina; 689: this.polmer = polmer; 690: } 691: else { // ce ni ustreznih dimenzij 692: this.polmer = OBI_POLMER; 693: this.visina = OBI_VISINA; 694: } 695: } 696: 697: // "SET" metode 698: public bool NastaviPolmer(int r) { 699: // smiselni polmer je le med 1 in NAJ_POLMER 700: // drugace ga pustimo pri miru 701: // metoda vrne vrednost true, ce smo polmer spremenili 702: if (Vmes(r, 1, NAJ_POLMER)) { 703: this.polmer = r; 704: return true; 705: } 706: return false; // polmer je ostal nespremenjen 707: } 708: 709: public bool NastaviVisino(int visina) { 710: // smiselni polmer je le med 1 in NAJ_VISINA 711: // drugace jo pustimo pri miru 712: // metoda vrne vrednost true, ce smo visino spremenili 713: if (Vmes(visina, 1, NAJ_VISINA)) {

Page 97: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

97

714: this.visina = visina; 715: return true; 716: } 717: return false; // visina je ostala nespremenjena 718: } 719: 720: // "GET" metode 721: public int PovejPolmer() { 722: return polmer; 723: } 724: 725: public int PovejVisino() { 726: return visina; 727: } 728: 729: // ToString 730: override public string ToString() { 731: return "Polmer: " + this.polmer + " visina: " + this.visina; 732: } 733: 734: // volumen in povrsina 735: public double Volumen() { 736: // volumen kovanca PI * r^2 * v 737: return Math.PI * this.polmer * this.polmer * this.visina; 738: } 739: 740: public double Povrsina() { 741: // povrsina kovanca 2 * PI * r * v + 2 * PI * r^2 742: return (2 * Math.PI * this.polmer) * 743: (this.polmer + this.visina); 744: } 745: 746: // pomozne metode 747: private static bool Vmes(int x, int a, int b) { 748: // ali je x med a in b 749: return (a <= x) && (x <= b); 750: } 751: }

Privatne metode o Tudi metode so lahko privatne (glej prejšnji zgled)

objektne, statične o private void metodaA() {...}; o private static int metodaB{...}; o Kadar metodo potrebujemo interno – uporabnikom ni potrebna!

Dedovanje

Pogosto uporabljani postopki I Imamo razred Ulomek:

Page 98: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

98

Denimo, da z ulomki zelo pogosto izvajamo določen postopek:

npr. jim spremnimo predznak Napišemo ustrezno metodo. Vedno, ko želimo ulomkom spreminjati predznak, moramo napisati to metodo:

Naredimo iz nje knjižnico – "zaprimo v svoj razred" Ampak to ni "objektno":

Znanje spreminjanja predznaka je ulomku želvi – objektu Ulomek (objekt) naj bi znala spremniti predznak :

Odzvati se metodi spremeniPredznak:

nekUlomek.spremeniPredznak() Imamo Ulomek.java:

Ni problem, dopišemo metodo Popravljati dobro preizkušen razred?

... Do not fix, until you are completely desperate ... Kaj pa, če izvorne kode nimamo?

Dedovanje o "razširjanje" objektov o Naredimo načrt za razred boljših ulomkov

Boljši: Znajo spremniti predznak o public class BoljsiUlomek : Ulomek o Vsaka boljši ulomek "zna" vse, kar zna običpajni ulomek iz razreda Ulomek in morda še kaj o Ima ista stanja/lastnosti

Morebiti tudi dodatna

Razred BoljsiUlomek 752: public class BoljsiUlomek : Ulomek { 753: // naredimo nov razred "boljših ulomkov" 754: public void SpremeniPredznak() { 755: // Vsaka boljši ulomek si zna 756: // spremeniti predznak 757: Ulomek pom = new Ulomek(-1, 1); 758: this.Pomnozi(pom); // lahko tudi brez this 759: // metoda pomnozi je podedovana iz razreda Ulomek 760: } 761: }

Uporaba Boljšega ulomka 762: public class Test4 { 763: public static void Main(string[] args) { 764: BolsiUlomek boUl; 765: boUl = new BoljsiUlomek(); // "ustvarimo" boljši ulomek 766: Ulomek navadniUl = new Ulomek(); 767: // spremenimo predznak navadnega ulomka 768: navadniUl.Pomnozi(new Ulomek(-1,1)); // "čaramo" 769: boUl.SpremeniPredznak(); // pri "boljših" ulomkih je zadeva

Page 99: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

99

770: // elegantnejša 771: } 772: }

Pogosto uporabljani postopki Denimo, da imamo na voljo razred Turtle. S tem razredom znamo ustvariti objekt, želvo, ki zna reagirati na ukaze (pozna metode):

Fd(int n)

Rt(int st)

PenUp()

PenDown() Zgled: Denimo, da z želvo zelo pogosto izvajamo določen postopek. Npr. rišemo kvadrat. Napišemo ustrezno metodo. Vedno, ko z želvo rišemo kvadrate, moramo napisati to metodo: Ampak to ni "objektno":

Znanje risanja kvadratov je lastno želvi – objektu Želva (objekt) naj bi znala narisati kvadrat:

Odzvati se metodi kvadrat: nekaZelva.kvadrat(100) Imamo dostop do knjižnice :

Ni problem, dopišemo metodo Popravljati dobro preizkušen razred?

... Do not fix, until you are completely desperate ... Kaj pa, če izvorne kode nimamo?

Dedovanje o "razširjanje" objektov o Naredimo načrt za razred pametnih želv:

Znajo risati kvadrate o public class PametnaZelva : Turtle

o Vsaka pametna želva "zna" vse, kar zna razred Turtle in morda še kaj o Ima ista stanja/lastnosti:

Morebiti tudi dodatna

RAZRED PAMETNAZELVA

773: public class PametnaZelva : Turtle { 774: // naredimo nov razred "pametnih zelv" 775: public void Kvadrat(int velikost) { 776: // Vsaka pametna zelva ve, kako se nariše 777: // kvadrat s stranico velikost 778: int i = 1; 779: while (i <= 4) { 780: base.Fd(velikost); // lahko tudi brez base 781: // metoda fd je podedovana iz razreda Turtle 782: base.Rt(90); 783: // metoda rt je podedovana iz razreda Turtle

Page 100: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

100

784: } 785: } 786: } Naredimo pametno želvo 787: public class Zelva4 { 788: public static void main(string[] args) { 789: PametnaZelva zelvak; // zelvak bo ime moje pametne ţelve 790: zelvak = new PametnaZelva(); // "ustvarimo" "pametno" ţelvo 791: 792: // naj ţelva zelvak nariše kvadrat 793: // ker je pametna, ji ni potrebno posredovati 794: // podrobnih navodil, ampak le ukaz za risanje kvadrata 795: 796: zelvak.Kvadrat(40); 797: 798: } 799: }

Dedovanje o Izpeljava novih razredov iz obstoječih o Uvajanje dodatnih metod o Lahko tudi dodatne lastnosti (podatki) o Primer:

matrike

Seštevanje, odštevanje, množenje Kvadratne matrike / class KvMatrike : Matrike

Ker je razred izpeljan – ni potrebno na novo pisati metod za seštevanje, odštevanje, množenje

Možne dodatne operacije

Inverz, deljenje, … o Hierarična zgradba razredov o Vrhnji objekt:

Object Iz njega izpeljani vsi drugi razredi » : Object« (deluje iz razreda Object) Ima metodo ToString() Zato jo "imajo" vsi razredi

o “specializacija” objektov Sestavi razred Zlatnik, ki deduje iz razreda Kovanec. Zlatnik ima poleg osnovnih lastnosti kovanca podano še gostoto materiala, iz katerega je izdelan, čistočo zlata (podano z realnim številom med 0 in 1) in vrednost čistega zlata na masno enoto. Razred naj pozna tudi metodo double Vrednost(), ki vrne vrednost zlatnika. o "razširjanje" objektov o Naredimo načrt za razred Zlatnik

Kovanec s posebnimi lastnostmi o public class Zlatnik : Kovanec o Vsaka zlatnik "je in zna" vse, kar zna kovanec in morda še kaj o Vrednost čistega zlata je enaka za VSE zlatnike

razredna spremenljivka (static)

Page 101: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

101

ZLATNIK -SKICA

800: public class Zlatnik : Kovanec { 801: 802: private static double vrednostCistegaZlata = 125.4; 803: // za vse zlatnike je to enako! 804: 805: private int polmer; 806: private int visina; 807: private double gostota; 808: private double cistost; 809: // "SET" metode 810: public bool NastaviPolmer(int r) { } 811: public bool NastaviVisino(int visina) { } 812: public void NastaviGostoto(double x) { } 813: public void NastaviCistost(double x) { } 814: public static void NastaviVrednostCistegaZlata(int v) { } 815: // vrednost lahko spreminajmo tudi, ce se ni nobenega zlatnika! 816: 817: // "GET" metode 818: public int PovejPolmer() { } 819: public int PovejVisino() { } 820: public double PovejGostoto() { } 821: public double PovejCistost() { } 822: public static double PovejVrednostCistegaZlata() { } 823: // vrednost lahko zvemo tudi, ce se ni nobenega zlatnika! 824: 825: // tostring 826: override public string ToString() { } 827: // podedovana tostring metoda nam ne ustreza! 828: 829: // "znanje" 830: public double Volumen() { } 831: public double Povrsina() { } 832: public double Vrednost() {} 833: } S krepko, ležečo pisavo smo označili tistio, kar smo podedovali. Tega ne pišemo v razredu!!

RAZRED ZLATNIK

834: public class Zlatnik : Kovanec { 835: 836: private static double vrednostCistegaZlata = 125.4; 837: // za vse zlatnike je to enako! 838: 839: private double gostota; 840: private double cistost; 841: public static double OBI_GOSTOTA = 19.3; // obicajna gostota g/cm^3 842: public static double OBI_CISTOST = 0.81; // obicajna cistost 843: 844: /* TA del NE spada v razred, je podedovan iz razreda Kovanec – 845: tukaj ga napisemo v komentar le zaradi preglednosti pri 846: učenju - pravi program ga NE BI vseboval !! 847:

Page 102: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

102

848: private int polmer; 849: private int visina; 850: public static final int OBI_POLMER = 3; // obicajni polmer 851: public static final int OBI_VISINA = 1; // obicajna visina 852: public static final int NAJ_POLMER = 3; // največji moţni 853: polmer 854: public static final int NAJ_VISINA = 1; // največja moţna 855: visina 856: */ 857: 858: // konstruktorji 859: public Zlatnik() : base() { // Ustvarimo običajni kovanec s klicem 860: // konstruktorja razreda Kovanec ( : base() ) 861: this.gostota = OBI_GOSTOTA; 862: this.cistost = OBI_CISTOST; 863: } 864: 865: public Zlatnik(int polmer, int visina, double gostota, 866: double cistost) : base(polmer, visina) { 867: // "drugacni" zlatnik 868: // ce ni ustreznih dimenzij, naredimo "obicajni" kovanec, 869: // gostota in cistost sta posebej! 870: // konstruktor v kovancu je poskrbel 871: // za kontrolo mer! 872: // Poklicali smo ga s kodo “: base(polmer, visina)” 873: this.gostota = SmiselnaGostota(gostota); // pravilnost podatkov! 874: this.cistost = SmiselnaCistost(cistost); // pravilnost podatkov! 875: } 876: 877: // "SET" metode 878: /* TA del NE spada v razred, je podedovan iz razreda Kovanec – 879: tukaj ga napisemo v komentar 880: le zaradi preglednosti pri učenju - pravi program ga 881: NE BI vseboval !! 882: 883: public bool NastaviPolmer(int r) 884: public bool NastaviVisino(int visina) 885: 886: */ 887: 888: public void NastaviGostoto(double x) { 889: // ce uporabnik poskusi gostoto nastaviti na napačno vrednost 890: // uporabimo privzeto gostoto 891: this.gostota = SmiselnaGostota(x); 892: } 893: 894: public void NastaviCistost(double x) { 895: // ce uporabnik poskusi cistost nastaviti na napačno vrednost 896: // uporabimo privzeto cistost 897: this.cistost = SmiselnaCistost(cistost); // pravilnost podatkov! 898: } 899: 900: public static void NastaviVrednostCistegaZlata(int v) { 901: // vrednost lahko spreminajmo tudi, ce se ni nobenega zlatnika!

Page 103: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

103

902: if (v > 0) 903: vrednostCistegaZlata = v; // vsaka pozitivna vrednost je OK 904: } 905: 906: // "GET" metode 907: /* Ta del NE spada v razred, je podedovan iz razreda Kovanec – 908: tukaj ga napisemo v komentar 909: le zaradi preglednosti pri učenju - pravi program ga 910: NE BI vseboval !! 911: 912: public int PovejPolmer() 913: public int PovejVisino() 914: */ 915: 916: public double PovejGostoto() { 917: return this.gostota; 918: } 919: public double PovejCistost() { 920: return this.cistost; 921: } 922: 923: public static double PovejVrednostCistegaZlata() { 924: return vrednostCistegaZlata; 925: } 926: 927: // tostring 928: public override string ToString() { 929: return "Polmer: " + base.PovejPolmer() + // zakaj ne gre 930: // this.polmer! 931: " visina: " + base.PovejVisino() + 932: " gostota: " + this.gostota + 933: " cistost: " + this.cistost; 934: } 935: 936: /* Ta del NE spada v razred, je podedovan iz razreda Kovanec – 937: tukaj ga napisemo v komentar 938: le zaradi preglednosti pri učenju - pravi program ga 939: NE BI vseboval !! 940: 941: // volumen in povrsina 942: public double Volumen() 943: public double Povrsina() 944: */ 945: 946: public double Vrednost() { 947: // vrednost je volumen * gostota * cistost * vrednost enote 948: // metodo volumen dobimo iz Kovanca! 949: return base.Volumen()* gostota * cistost * vrednostCistegaZlata; 950: } 951: 952: // pomozne metode 953: private static double SmiselnaGostota(double gostota) { 954: // vrne smiselno gostoto 955: final double DOPUSTNO_ODSTOPANJE = 0.1;

Page 104: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

104

956: double rel_razlika = (Math.Abs(gostota - OBI_GOSTOTA) / 957: OBI_GOSTOTA); 958: if (rel_razlika > DOPUSTNO_ODSTOPANJE) 959: gostota = OBI_GOSTOTA; // ce so podatki "neumni" 960: return gostota; 961: 962: } 963: private static double SmiselnaCistost(double cistost) { 964: // vrne smiselno cistost 965: if ((cistost <= 0) || (cistost >= 1)) // cistost naj bo med 0 966: // in 1, brez robnih vrednosti 967: 968: cistost = OBI_CISTOST; // ce so podatki "neumni" 969: return cistost; 970: } 971: }

RAZRED ZLATNIK - ZARES

972: public class Zlatnik : Kovanec { 973: 974: private static double vrednostCistegaZlata = 125.4; 975: // za vse zlatnike je to enako! 976: 977: private double gostota; 978: private double cistost; 979: public static double OBI_GOSTOTA = 19.3; // obicajna gostota g/cm^3 980: public static double OBI_CISTOST = 0.81; // obicajna cistost 981: 982: // konstruktorji 983: public Zlatnik() : base() { 984: // "obicajni" zlatnik 985: this.gostota = OBI_GOSTOTA; 986: this.cistost = OBI_CISTOST; 987: } 988: 989: public Zlatnik(int polmer, int visina, double gostota, 990: double cistost) : base(polmer, visina) { 991: // "drugacni" zlatnik 992: // ce ni ustreznih dimenzij, naredimo "obicajni" kovanec, 993: // gostota in cistost sta posebej! 994: // konstruktor v kovancu je poskrbel za kontrolo mer! 995: this.gostota = SmiselnaGostota(gostota); // pravilnost podatkov! 996: this.cistost = SmiselnaCistost(cistost); // pravilnost podatkov! 997: } 998: 999: // "SET" metode 1000: public void NastaviGostoto(double x) { 1001: // ce uporabnik poskusi gostoto nastaviti na napačno vrednost 1002: // uporabimo privzeto gostoto 1003: this.gostota = SmiselnaGostota(x); 1004: } 1005: 1006: public void NastaviCistost(double x) { 1007: // ce uporabnik poskusi cistost nastaviti na napačno vrednost

Page 105: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

105

1008: // uporabimo privzeto cistost 1009: this.cistost = SmiselnaCistost(cistost); // pravilnost podatkov! 1010: } 1011: 1012: public static void NastaviVrednostCistegaZlata(int v) { 1013: // vrednost lahko spreminajmo tudi, ce se ni nobenega zlatnika! 1014: if (v > 0) 1015: vrednostCistegaZlata = v; // vsaka pozitivna vrednost je OK 1016: } 1017: 1018: // "GET" metode 1019: public double PovejGostoto() { 1020: return this.gostota; 1021: } 1022: public double PovejCistost() { 1023: return this.cistost; 1024: } 1025: 1026: public static double PovejVrednostCistegaZlata() { 1027: return vrednostCistegaZlata; 1028: } 1029: 1030: // tostring 1031: public override string ToString() { 1032: return "Polmer: " + base.PovejPolmer() + 1033: " visina: " + base.PovejVisino() + 1034: " gostota: " + this.gostota + 1035: " cistost: " + this.cistost; 1036: } 1037: 1038: public double Vrednost() { 1039: // vrednost je volumen * gostota * cistost * vrednost enote 1040: return this.Volumen() * gostota * cistost * vrednostCistegaZlata; 1041: 1042: // pomozne metode 1043: private static double SmiselnaGostota(double gostota) { 1044: // vrne smiselno gostoto 1045: final double DOPUSTNO_ODSTOPANJE = 0.1; // lokalna konstanta 1046: double rel_razlika = (Math.Abs(gostota - OBI_GOSTOTA) / 1047: OBI_GOSTOTA); 1048: if (rel_razlika > DOPUSTNO_ODSTOPANJE) 1049: gostota = OBI_GOSTOTA; // ce so podatki "neumni" 1050: return gostota; 1051: } 1052: private static double SmiselnaCistost(double cistost) { 1053: // vrne smiselno cistost 1054: if ((cistost <= 0) || (cistost >= 1)) // cistost naj bo med 1055: // 0 in 1, brez robnih vrednosti 1056: cistost = OBI_CISTOST; // ce so podatki "neumni" 1057: return cistost; 1058: } 1059: }

Page 106: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

106

Iz muhe slon Ne, iz muhe ne bomo naredili slona, ampak pri nas zajci postanejo ovce. Pri nas se je oglasil bogati Rus, ki je bil tako navdušen nad našim programom za vodenje farme zajcev, da hoče, da mu zanj pripravimo podoben program.

Posebno nad metodo MeNapojiti() je bil navdušen! A žal on ne vodi farme zajcev, ampak farmo ovac. Poleg tega, da ovce redi za zakol, prodaja tudi njihove kožuhe, zato bi rad, da vodimo še barvo njihovega kožuha.

Osnova: razred ZajecNov Kakšne spremembe so potrebne? Napačne lastnosti:

OsnovaSerijska ni OK Prav tako MAX_TEZA

Dodati lastnosti:

Barva kožuha Get/set metodi:

denimo, da si tudi ovce barvajo kožuhe Popraviti konstruktor in dodati metodo ToString:

V razredu ZajecNov smo nanjo pozabili!

Prekrite metode o V predniku (neposrednem ali posrednem) definirana metoda nam ne ustreza o Predefiniranje: prekrite metode o Overriding o Enak podpis metode kot pri predniku – velja naša definicija o ToString() o določilo override

"Umazane" podrobnosti o base() o serijska ponovno! o ker zaradi private ne moremo do serijska iz ZajecNov o PovejSerijska() na novo o enaka že v ZajecNov o Razlog: nova spremenljivka serijska o če ne navedemo - metoda bi vračala serijsko iz zajcev!!

Uporaba 1060: public class Ovce { 1061: public static void Main(string[] ar) { 1062: Ovca z1 = new Ovca(); 1063: System.Console.WriteLine(z1); 1064: System.Console.WriteLine(z1.PovejSerijsko()); 1065: z1.nastaviBarvo("rdeča"); 1066: System.Console.WriteLine(z1); 1067: System.Console.WriteLine(z1.PovejSerijsko()); 1068: z1.spremeniTezo(12);

Page 107: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

107

1069: System.Console.WriteLine(z1 + " Vredna sem " +

z1.Vrednost(12.8));

1070: } 1071: }

Zgledi

Vaje Razred Kovanec

Sestavi razred Kovanec, s katerim predstavimo kovance.

Denimo, da so kovanci okrogli, z danim polmerom in višino. Poleg konstruktorjev in standardnih metod, ki nastavijo in vrnejo vrednosti lastnosti in za pretvorbo v niz, naj razred pozna še metodi, ki vrneta površino in volumen kovanca. Razred Zlatnik

Sestavi razred Zlatnik, ki deduje iz razreda Kovanec. Zlatnik ima poleg osnovnih lastnosti kovanca

podano še gostoto materiala, iz katerega je izdelan, čistočo zlata (podano z realnim številom med 0 in 1) in vrednost čistega zlata na masno enoto. Napiši get in set metode za nastavljanje in branje dodanih komponent.

Razred naj pozna tudi metodo double Vrednost(), ki vrne vrednost zlatnika.

Namig:

Vrednost čistega zlata je enaka za VSE zlatnike, torej je razredna spremenljivka (static)

Razred BoljsiZlatnik V deželi Fiksniji so se odločili prevzeti finančni sistem dežele Spremenije, kjer je bila osnovna enota zlatnik,

opisan v razredu Zlatnik.

Vladar Fiksnije se je temu dolgo upiral, a na koncu le popustil. Zahteval pa je: V deželi bodo le ene vrste zlatniki in njihove lastnosti se ne smejo spreminjati. Svetovalci so ga opozarjali, da bo vsa stvar videti preveč diktatorska, če ne bo na voljo vsaj navideznih možnosti

sprememb. Zato so mu svetovali, naj obdrži vse možnosti razreda Zlatnik, a le navidezno, torej tako, da bo

vse ostalo nespremenjeno. Tvoja naloga je, da pripraviš razred BoljsiZlatnik, ki bo tak, kot Zlatnik iz Spremenije, le da

upoštevaš zahteve vladarja Fiksnije, ohraniš vse metode razreda Zlatnik na zunaj enake, a jih predrugačiš

tako, da ne bodo omogočale sprememb. Razred Ovca Pri tebi se je oglasil bogati Rus, ki je bil tako navdušen nad tvojim programom za vodenje farme zajcev, da hoče, da mu zanj pripraviš podoben program. Vendar on ne vodi farme zajcev, ampak farmo ovac. Poleg tega, da ovce redi za zakol, prodaja tudi njihove kožuhe, zato bi rad, da vodimo še barvo njihovega kožuha.

Iz razreda Zajec izpelji razred Ovca, razredu Ovca dodaj lastnost barvaKoţuha in napiši get in set

metodo za to lastnost (recimo, da si tudi ovce barvajo kožuhe ). Ustrezno popravi konstruktorje.

Napiši tudi metodo toString, ki bo prekrila metodo toString iz razreda Zajec in bo vrnila smiselen

izpis podatkov o ovci.

Zival Sestavi razred Zival, ki vsebuje spremenljivke

o private int steviloNog;

o private String vrsta;

o private String ime;

in metode za nastavljanje in spreminjanje le-teh. Vsebuje tudi konstruktor brez parametrov, ki postavi spremenljivke na smiselno začetno vrednost.

Page 108: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

108

Dopolnite razred z metodo public string toString(), ki naj izpiše podatke o razredu v obliki:

#Ime: je vrste #vrsta in ima #steviloNog nog.

Pri tem seveda zamenjajte #vrsta in #steviloNog z vrednostmi v istoimenskih spremenljivkah. Nato napišite testni program, kjer ustvarite 5 živali in jim nastavite smiselno vrsto in imena ter število nog. Izpišite jih!

Vzemi razred Kvader in iz njega izpelji razred Kocka. Dodaj mu konstruktor, ki sprejme le en parameter in pravilno nastavi širino, višino in globino (objektne spremenljivke v razredu Kvader). Seveda obstaja problem, da lahko uporabnik od zunaj preko metod NastaviSirino, NastaviGlobino in NastaviVisino spremeni našo kocko v kvader - zaenkrat to pustimo ob strani. Sestavi tudi testni program, kjer ustvariš nekaj kock in izpišeš njihovo površino in volumen. 775: public class Kvader { 776: private int sirina, visina, globina; 777: 778: //ustvarimo kvader visine, sirine in globine ena 779: public Kvader() { 780: sirina = visina = globina = 1; 781: } 782: 783: //ustvarimo kvader dane sirine, globine in visine 784: //pri tem pazimo na to, da so podatki pravilni 785: public Kvader(int sirina, int globina, int visina) { 786: NastaviSirino(sirina); 787: NastaviGlobino(globina); 788: NastaviVisino(visina); 789: } 790: 791: //metoda vrne visino kvadra 792: pub lic int VrniVisinO() { 793: return visina; 794: } 795: 796: 797: //metoda vrne globino kvadra 798: public int VrniGlobinO() { 799: return globina; 800: } 801: 802: //metoda vrne sirino kvadra 803: public int VrniSirino() { 804: return sirina; 805: } 806: 807: //nastavi globino kvadra - pazi na pravilnost podatkov! 808: public void NastaviGlobino(int globina) { 809: if(globina >= 0) { 810: this.globina = globina; 811: } 812: } 813: 814: //nastavi sirino kvadra - pazi na pravilnost podatkov!

Page 109: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

109

815: public void NastaviSirino(int sirina) { 816: if(sirina >= 0) { 817: this.sirina = sirina; 818: } 819: } 820: 821: 822: //nastavi visino kvadra - pazi na pravilnost podatkov! 823: public void NastaviVisino(int visina) { 824: if(visina >= 0) { 825: this.visina = visina; 826: } 827: } 828: 829: public int Volumen() { 830: return this.VrniSirino() * this.VrniVisino() * this.VrniGlobino(); 831: } 832: 833: public int Povrsina() { 834: return 2 * this.VrniSirino() * this.VrniVisino() + 835: 2 * this.VrniGlobino() * this.VrniVisino() + 836: 2 * this.VrniSirino() * this.VrniGlobino(); 837: } 838:

839: }

Razred Pes Iz razreda Zival izpelji razred Pes. Razred naj vsebuje dodatne metode:

o public void NastaviPasmo(String pasma): če uporabnik "podtakne" prazen niz, naj vrže napako!

o public String VrniPasmo() o public void NastaviStarost(int starost): poskrbite, da bo starost vedno med 1 in 25 let. o public int VrniStarost()

Seveda boste za hrambo dodatnih podatkov rabili nekaj dodatnih objektnih spremenljivk. Poskrbi tudi, da bo osnovni konstruktor razreda Pes nastavil vrsto na pes, število nog na štiri ter pasmo in starost postavil na smiselno vrednost (naredite tabelo vsaj desetih pasem in izbirajte med njimi). Nato vsakemu objektu nekajkrat (naključno mnogokrat) spremenite pasmo in število nog. Ko boste ustvarili razred, dopolnite testni program tako, da vam namesto petih živali ustvari pet objektov tipa Pes. Denimo, da nas zanima statistika ustvarjanja objektov, in sicer:

o koliko objektov smo tekom delovanja programa ustvarili? o kolikokrat smo prebrali število nog? o kolikokrat smo spremenili pasmo?

Dopolni program tako, da bo na koncu delovanja te podatke izpisal. Namig: razredne spremenljivke (ključna beseda: static).

Razred Dalmatinec Iz razreda Pes izpelji razred Dalmatinec. Razred naj vsebuje dodatne metode:

o public void NastaviSteviloPik(int pike): poskrbi za to, da bo število pik med 1 in 1000. o public int VrniSteviloPik()

Page 110: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

110

o public int VrniVrednost(): vrne vrednost dalmatinca glede na njegovo starost in število pik. Več ko ima pik, več naj bo vreden, in sicer naj bo vsaka pika vredna 1000 - #steviloLet tolarjev. Poleg tega je še omejitev na zgornjo in spodnjo ceno: dalmatinec ne more stati več manj kot 10000SIT in ne več kot 1000000 SIT.

Dopolni testni program tako, da ustvariš 100 dalmatincev z naključnim številom pik. Poišči med njimi najdražjega in ga izpiši! Če je taki dalmatincev več, izpiši vse.

Naloge brez računalnika 1. Kakšna je razlika med statičnimi in dinamičnimi spremenljivkami oz. metodami v razredu? Katere izmed

spodnjih trditev o dinamičnih/statičnih spremenljivkah v razredu so pravilne?

Za dostop do statične spremenljivke oz. metode ne potrebujemo primerka razreda.

Za dostop do dinamične spremenljivke oz. metode ne potrebujemo primerka razreda.

Če posebej ne poudarimo, je metoda statična.

Objekt razreda lahko kliče statično metodo oz. dostopa do statične spremenljivke. 2. Ali se sledeča koda prevede? Kaj izpiše? 840: public class CharZ { 841: private static char c = 'a'; 842: public CharZ(char c) { 843: this.c = c; 844: } 845: public char GetChar() { 846: return c; 847: } 848: } 849: 850: public static void Main(string[] argc) { 851: CharZ a, b; 852: a = new CharZ('a'); 853: b = new CharZ('b'); 854: Console.WriteLine("a: " + a.GetChar() + ", " + " b: " + b.GetChar()); 855: }

3. Sledeča koda predstavlja razred atleta na olimpijskih igrah. 856: public public class Athlete { 857: private string name; 858: private string country; 859: private int age; 860: private static int numCanadians, numAmericans, numMexicans, numOther

= 0;

861: 862: public Athlete(string name, string country, int age) { 863: this.name = name; 864: this.country = country; 865: this.age = age; 866: if(country.equals("Canada")) 867: numCanadians++; 868: else if(country.equals("USA")) 869: numAmericans++; 870: else if(country.equals("Mexico")) 871: numMexicans++;

Page 111: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

111

872: else 873: numOther++; 874: } 875: 876: public string GetName() { 877: return name; 878: } 879: 880: public String GetCountry() { 881: return country; 882: } 883: 884: public int GetAge() { 885: return age; 886: } 887: 888: public void IncreaseAge() { 889: age++; 890: } 891: }

Ustvarimo dva nova objekta Athlete a1 = new Athlete("Lewis","USA",30);

Athlete a2 = new Athlete("Johnson","Canada",28);

Kateri izmed spodnjih stavkov niso v pravilni obliki? Zakaj?

a1.GetName();

a2.age = a2.age + 1;

a2.GetAge();

a2.IncreaseAge();

a1.country = "Mexico";

a2.IncreaseAge();

a2.GetAge();

Athlete a3 = new Athlete("Hamm","USA",31);

a4.GetAge();

Athlete a1 = new Athlete("Gretzky","Canada",39);

a1 = a3;

a1.GetAge();

a1 == a2;

a1 == a3;

a3.IncreaseAge();

a1.GetAge();

Kaj pravilni stavki pomenijo in kakšen učinek imajo?

Kaj bi šlo lahko narobe, če bi bila spremenljivka country v zgornji kodi javna? Napiši funkcijo public

void setCountry(String country), ki bo spremenila vrednost države. Ali je dovolj popraviti le

spremenljivko country, ali je potrebno paziti na še kaj drugega?

Razred Pes II

Razred Pes ima nekaj pomankljivosti, in sicer:

o uporabnik lahko spremeni število nog o uporabnik lahko spremeni vrsto živali

Page 112: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

112

Problema se lotimo na sledeč način:

o spremenite razred Zival tako, da mu dodate konstruktor, ki sprejme vrsto, število nog in ime živali in jih nastavi na smiselne vrednosti. Za nastavljanje uporabite metode NastaviXyz, saj le te preverijo tudi pravilnost podatkov!

o uporabniku onemogočite spreminjanje števila nog in vrste živali tako, da spremenite tip metode v protected. Metodo sedaj lahko uporabljamo interno v razredu in v vseh iz njega izpeljanih razredih, le uporabnik je ne more klicati "od zunaj". Popravi tudi testni program za razred Zival!

o denimo, da nas pri razredu Zival moti, ker moramo za vse vrste živali podajati število nog, čeprav je to pravzaprav že v naprej znan podatek. Zato razred dopolnite z dvema razrednima tabelama (torej tipa static, saj bosta za vse objekte tipa Zival vedno enaki) string[] pasme in int[] steviloNog, kjer v prvo vpišemo nekaj pasem, v drugo pa na isto mesto vpišemo število nog, ki pripada tej vrsti.

o dopolni metodo nastaviVrsto tako, da v primeru poznane vrste (takšne, ki je v naši tabeli), pravilno nastavi tudi število nog živali.

napiši tudi konstruktor, ki sprejme le ime in pasmo živali in pravilno nastavi število nog. Če mu pasma ni poznana, naj uporabnika vpraša še za število nog in pokliče star konstruktor, ki sprejme vse parametre. Ustrezno popravite tudi testni program,

o napišite nov konstruktor v razredu Pes, ki sprejme pasmo in ime psa, število nog in vrsto živali pa nastavi na 4 oz. na "pes".

V nei prejšnjih vaj smo iz razreda Kvader izpeljali razred Kocka, ki je preko konstruktorja nastavil višino, širino in globino kvadra na enake vrednosti (dolžino stranice kocke). Obstajal pa je problem, da je uporabnik lahko od zunaj preko metod nastaviSirino, nastaviGlobino oz. nastaviVišino našo kocko spremenil v kvader. Ker pa poznamo prekrivanje metod, znamo to storiti bolje: prekrij metode za nastavljanje globine, širine in višine v razredu Kvader tako, da bodo spreminjale edino mero za kocko. Razredu Kocka dodaj tudi dodatni metodi public void NastaviStranico(double a) in public double PreberiStranico(), ki nastavi oz. vrne dolžino stranice kocke. Pri tem bodi pozoren na veljavnost podatkov!

Prekrij tudi metodo public string ToString().

Razred Kocka je nastavil višino, širino in globino kocke v konstruktorju z uporabo metod NastaviSirino, NastaviGlobino in NastaviVisino baznega razreda. Spremeni kodo tako, da bo konstruktor razreda Kocka, ki kot parameter dobi dolžino stranice kocke, klical konstruktor razreda Kvader in mu podal širino, višino in globino kocke, ki jo ustvarjamo (torej tri enaka števila). Vprašanje: kako lahko sedaj iz razreda Kocka kličemo metode NastaviSirina, NastaviGlobina in NastaviVisina iz razreda Kvader? Novi razred Kocka tudi preizkusi: ustvari testni program, kjer narediš tabelo vsaj 100 kock z naključno dolgimi stranicami med 10 in 100 enot. Poizkusi jim nastaviti širino, višino in globino. Med njimi poišči kocki z največjo površino in prostornino ter ju izpiši. Podoben problem kot pri razredu Kocka smo imeli tudi pri razredu Pes, kjer je uporabnik lahko spreminjal vrsto živali in število njenih nog preko metod v razredu Zival. Problem smo rešili tako, da smo uporabniku popolnoma onemogočili dostop do metod za spreminjanje števila nog in vrste živali, kar pa ni najlepša rešitev. Zato spremeni razred Pes tako, da prekriješ metodi za nastavljanje števila nog in vrste živali - naj enostavno ne naredita nič! Če metode res delujejo kot je potrebno preverite s testnim programom, kjer poskusite spremeniti vrsto in število nog objekta tipa Pes. Razred Dalmatinec iz prejšnjih vaj dopolni z lastnostma maksimalna hitrost (celo število med 10 in 50 kilometrov na uro) in položaj (položaj dalmatinca v ravnini - je spremenljivka tipa Tocka, ki smo jo naredili v eni prejšnjih nalog). Razred mora seveda imeti tudi metode za branje oz. spreminjanje hitrosti oz. položaja dalmatinca. Pazi na to, da preveriš podatke preden jih nastaviš! Ustvari tudi testni program, kjer ustvariš 100 naključnih dalmatincev, ki so razporeni na naključnih lokacijah na ravnini (naključni dalmatinec pomeni, da je naključno star, hiter in ima naključno mnogo pik). Nato med njih na

Page 113: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

113

naključno točko vrži klobaso. Če predpostavimo, da se v trenutku ko klobasa prileti med njih vsi dalmatinci naenkrat zapodijo ponjo, kateri jo bo dobil prvi? Izpiši ga. Poišči tudi najbližja in najbolj oddaljena dalmatinca in ju izpiši.

Zajec iz živali Iz razreda Zival izpelji razred Zajec. Poleg osnovnih lastnosti iz razreda Zival naj pozna še:

o težo zajca v kilogramih (med 0 in 50 kilogramov (Garfield)) o spol zajca (spremenljivka tipa bool) o starost zajca (med 0 in 20 let)

Sestavi tudi metode za spreminjanje in branje teh lastnosti. Tako kot v razredu Pes prekrij metodi razreda Zival za nastavljanje števila nog in vrste živali, ki naj vrže napako. Sestavi tudi primerne konstruktorje (vsaj dva) in prekrij metodo ToString. Sestavi testni program, kjer ustvariš tabelo desetih zajcev nakjučne teže, spola in starosti. Nato med njimi poišči najtežjega samca in samico ter ju izpiši. Smiselno bi bilo, da lastnosti kot so teža, starost in spol premakeš v osnivni razred žival (ker so lastnosti vsake živali). Tja premakni tudi metode za branje in spreminjanje teh lastnosti in dodaj smiselne konstruktorje. Poleg tega ne pozabi, da moraš v obstoječih konstruktorjih razreda žival postaviti te lastnosti na smiselno vrednost. Ko to končaš, vzemi razreda Pes in Zajec tej jima odstrani lastnosti, ki so sedaj na voljo že v razredu žival. Če si stvari zastavil pravilno, bi morali sedaj vsi psi in zajci imeti spol, težo in starost. Preveri to s testnim programom!

Embi d.o.o Podjetje Embi d.o.o. izdeluje embalaže vseh vrst (kartonske, plastične, kovinske...) in oblik (kocka, piramida...). Sestavi jim osnovni razred za vse embalaže Embalaza, če veš, da vsaka embalaža vsebuje podatke o:

tipu embalaže: ker izdelujejo le končno mnogo tipov embalaže, se sme tip izbrati le izmed nekaj v naprej definiranih. V razredu Embalaza zato definirajte tabelo nizov, ki predstavljajo dovoljene tipe embalaže. Tip embalaže naj bo možno spremeniti tako, da direktno povemo tip embalaže kot niz ali pa tako, da podamo indeks tipa embalaže v tabeli tipov. Če tak tip embalaže ne obstaja, naj se vrže napaka. Poznamo vsaj plastično in kartonsko embalažo.

obliki embalaže: za obliko veljajo enaka pravila kot za tip, le da zaenkrat poznamo le oblike: kocka, tetraeder in valj.

barvo embalaže: barva je lahko poljuben neprazen niz. maso embalaže: pozitivno število. ceno izdelave: ker cene izdelave za splošno embalažo ne poznamo, naj bo

nastavljena na 0.

Seveda mora razred vsebovati metode za nastavljanje in branje teh lastnosti ter vsaj konstruktor brez parametrov, ki ustvari embalažo s smiselnimi lastnostmi. Poleg tega podjetje zanima tudi, koliko embalaže so izdelali. V ta namen bo potrebno v razred dodati razredno spremenljivko, ki se poveča vsakič ko se ustvari nov objekt tipa Embalaza in metodo, ki vrača njeno vrednost. Nikakor pa ne smete dodati metode za spreminjanje te vrednosti! Zakaj? Seveda je takšna embalaža še povsem neuporabna (ne vemo npr. na kakšni temperaturi jo lahko skladiščimo...), dodatne lastnosti embalaže pa so močno odvisne od materiala, iz katerega je embalaža izdelana. Denimo, da nas trenutno najbolj zanima plastična embalaža. Zato iz razreda Embalaza izpelji razred PlasticnaEmbalaza, ki vsebuje lastnosti:

vrsta plastičnega materiala, iz katerega je narejena ta embalaža (izbira iz nekaj vnaprej definiranih možnosti)

temperaturno območje, v katerem je embalaža primerna za skladiščenje. To je lastnost razreda in ne posameznega objekta!

maksimalna nosilnost embalaže - odvisna od vrste plastičnega materiala in je enaka za vse embalaže, ki so narejene iz določene vrste plastike!

Page 114: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

114

ali je primerna za skladiščenje hrane: nekatere vrste plastike niso primene za skladiščenje hrane. Ker pa je stvar odvisna tudi od načina izdelave, je potrebno to posebej povedati za vsak objekt posebej.

cena za kg embalaže: koliko stane 1kg plastične embalaže. Predpostavi, da vsaka plastična embalaža stane enako za kg.

Razred naj vsebuje tudi nekaj smiselnih konstruktorjev.

Vsebuje naj metodo, ki pove koliko kilogramov tovora še lahko spravimo v embalažo. Je linearno odvisna od mase emblaže(v embalaži, ki tehta 1kg, lahko shranimo 5kg tovora), a ne večja od neke v naprej določene vrednosti, ki je shranjena v maksimalni nosilnosti embalaže, in jo dobimo na podlagi testiranja našega plastičnega materiala. Ker se metodologija testiranja lahko spremeni, se lahko tudi največja nosilnost velikokrat spremeni.

Vsebuje naj tudi metodo, ki pove, koliko stane določena plastična embalaža. Cena je odvisna od teže te embalaže, ter od oblike. Izdelava v obliki tetraedra podraži izdelavo za 20%, izdelava v obliki valja pa za 35%. Razred tudi preizkusi: ustvari tabelo 100 plastičnih embalaž z naključnimi začetnimi podatki. Nato med njimi poišči tako, v kateri bi lahko prepeljali hrano težko 10kg, ki stane najmanj (oblika ni pomembna, pomembno pa je, da je material ustrezen - torej tak, da dovoljuje pakiranje hrane). Ali nas stane kaj manj, če namesto hrane želimo prepeljati računalniško opremo iste teže? Kaj pa, če je pomembna tudi oblika embalaže? Denimo, da letalski prevoznik sprejme le embalažo v obliki kocke. Katera embalaža je najbolj ugodna v tem primeru (torej v prej ustvarjeni tabeli embalaž upoštevaj le tiste, ki so v obliki kocke)? Ustvari tudi razred KartonEmbalaza, ki naj ima iste lastnosti kor razred PlasticnaEmbalaza. Vemo, da je ta embalaža vedno primerna za hrano, a ima nižjo nosilnost kot plastična embalaža (embalaža teže 1kg zdrži le okrog 2kg tovora) in nekoliko nižje stroške izdelave (izdelava embalaže težke 1kg nas stane 900 SIT). Razred preveri na enak način kot razred PlasticnaEmbalaza. Sestavi testni program, ki bo ustvaril naključno mnogo plastičnih in kartonastih embalaž (a vsaj 100 vsake vrste) in jih shrani v tabelo. Med njimi nato poišči takšno embalažo, ki bo:

najprimernejša za prevoz hrane teže 1 kg v obliki valja. najprimernejša za prevoz hrane teže 100 kg v obliki kocke. najprimernejša za prevoz strojenih kož mase 2 kg katerekoli oblike. najprimernejša za prevoz strojenih kož mase 20 kg v obliki tetraedra.

Opomba: z "najprimernejša" seveda mislimo najcenejša. Na koncu še izpiši, koliko embalaže si ustvaril tekom programa. Pri tem si lahko pomagaš le z lastnostmi razreda Embalaza.

Vaje "peš"

1. Podana imaš razreda 892: public class Vozilo { 893: public int steviloKoles; 894: private string barva; 895: private int maxHitrost; 896: 897: public Vozilo() { 898: barva = "bela"; 899: maxHitrost = 100; 900: steviloKoles = 4; 901: } 902: 903: public Vozilo(int steviloKoles, string barva) {

Page 115: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

115

904: this.barva = barva; 905: this.maxHitrost = 100; 906: this.steviloKoles = steviloKoles; 907: } 908: 909: private Vozilo(int steviloKoles, string barva, int maxHitrost) { 910: this.barva = barva; 911: this.maxHitrost = maxHitrost; 912: this.steviloKoles = steviloKoles; 913: } 914: 915: public Tovornjak Klonirajv0() { 916: return new Tovornjak(this.steviloKoles, this.barva, 917: this.maxHitrost); 918: } 919: 920: 921: public Tovornjak Klonirajv1() { 922: return new Tovornjak(this.steviloKoles, this.barva, 923: this.maxHitrost, nosilnost); 924: } 925: 926: public Tovornjak Klonirajv2() { 927: return new Tovornjak(this.steviloKoles, this.barva, 928: this.maxHitrost, 1000); 929: } 930: 931: public void Hitrost(int mh) { 932: this.maxHitrost = mh; 933: } 934: 935: public int JeHitrost() { 936: return this.maxHitrost; 937: } 938: public string Barva() { 939: return this.barva; 940: } 941: 942: override public string ToString() { 943: return "Avtomobil barve " + barva + " s " + 944: steviloKoles + " kolesi razvije najvecjo hitrost " 945: + maxHitrost; 946: } 947: 948: }

in

949: public class Tovornjak : Vozilo { 950: private int nosilnost; 951: 952: public Tovornjak() : base() { 953: nosilnost = 1000; 954: this.steviloKoles = 8;

Page 116: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

116

955: } 956: 957: public Tovornjak(string barva) : base(8, barva) { 958: } 959: 960: public Tovornjak(int maxHitrost) : this() { 961: this.maxHitrost = maxHitrost; 962: } 963: 964: public Tovornjak(int steviloKoles, string barva, int maxHitrost) : 965: base(steviloKoles, barva, maxHitrost) { 966: this.nosilnost = 10000; 967: } 968: 969: public Tovornjak(int steviloKoles, string barva, int maxHitrost, 970: int nosilnost) : base(steviloKoles, barva) { 971: this.Hitrost(maxHitrost); 972: this.nosilnost = nosilnost; 973: } 974: 975: public Vozilo Klonirajt0() { 976: return new Vozilo(this.steviloKoles, this.barva, this.maxHitrost); 977: } 978: 979: public Vozilo Klonirajt1() { 980: return new Vozilo(this.steviloKoles, this.barva()); 981: } 982: 983: public Vozilo Klonirajt2() { 984: Vozilo a = new Vozilo(this.steviloKoles, this.barva()); 985: a.Hitrost(this.JeHitrost()); 986: return a; 987: } 988: 989: public override string ToString() { 990: return "Tovornjak barve " + this.Barva() + " s " 991: + steviloKoles + " kolesi ima nosilnost " +

this.nosilnost;

992: } 993: }

o kateri konstruktorji v razredu Tovornjak so sintaktično pravilni?

o Razloži katere od metdo Klonirajv0, Klonirajv1, Klonirajt0 in Klonirajt1 so nepravilne in zakaj!

o kaj izpiše stori spodnja koda

994: public static void Main(string[] argc) { 995: Tovornjak t = new Tovornjak(8, "zelena", 70, 500); 996: Vozilo a = t.Klonirajt2(); 997: Tovornjak tt = t.Klonirajt2().Klonirajv2(); 998: System.Console.WriteLine(t); 999: System.Console.WriteLine (a); 1000: System.out.println(tt); 1001: }

Page 117: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

117

6. Denimo, da smo želeli sestaviti razred Dalmatinec, ki ima lastnosti ime in število pik. Koda razreda je: 1002: public class Dalmatinec{ 1003: public string ime; 1004: private int steviloPik; 1005: 1006: public Dalmatinec() { 1007: this.ime = "Reks"; 1008: this.steviloPik = 0; 1009: } 1010: 1011: public void ImePsa(string ime) { 1012: ime = this.ime; 1013: } 1014: 1015: private void NastaviIme(string ime) { 1016: this.ime = ime; 1017: } 1018: public void NastaviSteviloPik(int steviloPik) { 1019: this.steviloPik = steviloPik; 1020: } 1021: }

V glavnem programu smo ustvarili objekt Dalmatinec z imenom d in mu želimo nastaviti število

pik na 100 in ime na Pika. Kateri način je pravilen? Pri nepravilnih povej, kaj in zakaj ni

pravilno.

h) d.NastaviIme("Pika"); d.NastaviSteviloPik(100);

i) d.ime = "Pika"; d.steviloPik = 100;

j) d.ime = "Pika"; d.NastaviSteviloPik(100);

k) d.ImePsa("Pika"); d.NastaviSteviloPik(100);

l) d.ImePsa("Pika"); d.steviloPik = 100;

m) d.NastaviIme("Pika"); d.steviloPik = 100;

n) nobeden, ker tega sploh ne moremo storiti

Sedaj želimo našemo razredu Dalmatinec dodati tudi podatke o spolu psa. Ta podatek bomo hranili v spremenljivki spol. Interno (znotraj razreda) naj logična vrednost true pomeni ženski, false pa moški spol.

Dopolnite razred tako, da bo zadoščal naslednjim trem pogojem:

a) Spremenljivka spol naj ne bo dostopna izven razreda Dalmatinec

b) obstaja naj metoda kaksenSpol, ki v primeru samca vrne 'm', v primeru samice pa 'f'.

c) spol se nastavi le ob ustvarjanju objekta. Morali boste torej napisati konstruktor razreda Dalmatinec, ki sprejme kot parameter znak za spol. Ta naj bo kot zgoraj 'm' za samca in 'f' za samico. Predpostavite, da bo parameter zagotovo znak 'm' ali 'f'.

1022: public class Dalmatinec { 1023: public string ime; 1024: private int steviloPik;

Page 118: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

118

1025: // PO POTREBI DOPOLNI 1026: 1027: 1028: // DO SEM 1029: // konstruktorji 1030: 1031: public Dalmatinec() { 1032: this.ime = "Reks"; 1033: this.steviloPik = 0; 1034: } 1035: // PO POTREBI DOPOLNI 1036: 1037: // DO SEM 1038: 1039: public void ImePsa(string ime) { 1040: ime = this.ime; 1041: } 1042: 1043: private void NastaviIme(string ime) { 1044: this.ime = ime; 1045: } 1046: 1047: public void NastaviSteviloPik(int steviloPik) { 1048: this.steviloPik = steviloPik; 1049: } 1050: 1051: // kakšen spol ima (metoda kaksenSpol) 1052: 1053: // PO POTREBI DOPOLNI 1054: 1055: // DO SEM 1056: }

Predpostavimo, da razred, ki zadošča zgornjim kriterijem, že imamo. Sestavi metodo public static int

steviloSamcev(Dalmatinec[] dalmatinci), ki prešteje število samcev v tabeli dalmatincev.

Halo Kitajc Po koncu prejšnje naloge morate imeti tri razrede: Embalaza, PlasticnaEmbalaza in KartonEmbalaza. Uporabite jih za rešitev sledečih problemov:

o Podjetu "Halo, Kitajc!" se je nabralo kar nekaj kosov plastične in kartonske embalaže (1000 kosov vsake). Upoštevajte, da so embalaže različnih tež, oblik, varv, tipov plastike... zato jih naključno generirajte. Ker so se sklenili porabiti zalogo, za vsako naročilo posebej vzamejo najprimernejšo (beri najcenejšo) embalažo, ki je ustrezna. Pomagaj jim izbati najprimernejšo embalažo, če za vsako naročilo poznaš težo blaga, ki ga je potrebno dostaviti in ali je blago hrana. Podatke o naročilu naključno generiraj - naročil naj bo vsaj 100 in ko enkrat neko embalažo porabiš, je ne smeš več uporabiti.

o Prevozniki so podjetje opozorili, da vsaka oblika embalaže ni primerna za vse vrste transporta. Tako na letala sprejemajo le embalažo v obliki kocke, za transport mleka hočejo le tetraedre... Zato poleg vsakega naročila izveš še, kakšne oblike naj bo embalaža. Poišči najcenejšo!

o Za dostavo nekega zdravila zdravniška zbornica zahteva, da je zapakirano v embalaži iz plastike tipa HDPE ali PETE, ki mora biti valjaste oblike in mora imeti nosilnost vsaj 200% višjo, kot je dejanska masa zdravila. Poišči najprimernejšo embalažo, če ti povedo maso zdravila.

Page 119: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

119

o Na koncu leta so v podjetju delali inventuro in zanimalo jih je, koliko denarja lahko dobijo s prodajo vse embalaže, ki jim je ostala. Pomagaj jim in izračunaj skopno vrednost vseh vrst embalaže če veš, da je v tem času embalaža v obliki kocke izgubila 10%, emblaža v obliki valja 20% in embalaža v obliki tetraedra 30% svoje vrednosti.

Trojiško Sestavi razred Trojisko, v katerem hranimo števila v trojiškem zapisu kot nize z obveznim predznakom. Tako število 13 zapišemo kot '+111', število -5 pa kot '-12'. Razred naj vsebuje:

konstruktor public Trojisko(), ki ustvari nov objekt, v katerem hranimo število z vrednostjo 3.

konstruktor public Trojisko (int vrednost), ki ustvari nov objekt, ki predstavlja

število vrednost.

konstruktor public Trojisko (Trojisko a), ki ustvari kopijo objekta a.

metodo override public String ToString(), ki vrne število v trojiški obliki, a brez predznaka, če je le-ta pozitiven.

metodo public int Vrednost(), ki vrne vrednost tega objekta.

metodo public Trojisko Zmnozi(Trojisko a), ki zmnoži trenutni objekt z

objektom a in vrne rezultat.

Pri tem si lahko pomagaš z metodama :

public static int ParseInt(String parse, int radix) , ki pretvori niz

parse, ki vsebuje število v številskem sistemu z bazo radix v število tipa int.

public static string ConvertToString(int i, int radix), ki vzame

število i in vrne niz, v katerem je to število zapisano v bazi radix. Če je število negativno, na začetek niza zapiše minus, za pozitivno število pa plusa pred niz ne doda! Torej klic

ConvertToString (3,3) vrne niz “10”, klic ConvertToString (-3,3) pa niz “-10”.

Redki vektorji Redek vektor je vektor, ki vsebuje veliko ničel. Takšen vektor namesto z običajno tabelo predstavimo s seznamom neničelnih elementov, kjer poleg vsakega elementa hranimo še indeks tega elementa v tabeli. Seznam je naraščajoče urejen po indeksih. Na osnovi sheme razreda RedekVektor podane spodaj, sestavi

ustrezni razred, potreben za predstavitev redkega vektorja. Dopolni razred Naloga1 s statično metodo, ki sešteje dva redka vektorja. Rezultat naj bo nov redek vektor (sumanda ostaneta nespremenjena). Primer (neformalen zapis!): {(2,1),(5,3),(-1,6)} + {(3,1),(8,2),(1,6),(3,15)} = {(5,1),(8,2),(5,3),(3,15)}

1057: public class RedekVektor 1058: { 1059: ???????? 1060: 1061: ??? RedekVektor(int[][] vektor) 1062: { 1063: // vektor[i][0] - koeficient 1064: // vektor[i][1] - indeks 1065: // vektor je UREJEN po drugi komponenti! 1066: // morebitne nicelne elemente izpustimo! 1067: 1068: ???? 1069: } 1070: 1071: ??? int SteviloClenov()

Page 120: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

120

1072: { // stevilo clenov redkega vektorja 1073: ???? 1074: } 1075: 1076: ???? Koeficienti() 1077: { // tabela koeficientov 1078: ????? 1079: } 1080: 1081: ???? int[] Indeksi() 1082: { // tabela indeksov 1083: ???? 1084: } 1085: 1086: ??????? 1087: } 1088: 1089: public class Naloga1 1090: { 1091: public static void Main(string[] param) 1092: { 1093: int[][] podatki1={{2,1},{5,3},{0, 5},{-1, 6}}; 1094: int[][] podatki2={{3,1}, {8,2}, {1, 6}, {3, 15}}; 1095: 1096: RedekVektor v1 = new RedekVektor(podatki1); 1097: RedekVektor v2 = new RedekVektor(podatki2); 1098: RedekVektor v3 = Vsota(v1, v2); 1099: 1100: System.Console.WriteLine("V1 = " + v1); 1101: System.Console.WriteLine("V2 = " + v2); 1102: System.Console.WriteLine("Vsota = " + v3); 1103: } 1104: 1105: // staticna metoda vsota !! 1106: 1107: ???????? 1108: }

Plavalec

Podan imamo razred Oseba z objektnima metodama VrniIme() ter VrniPriimek() (ki nam vrneta niza, ki predstavljata ime oz. priimek osebe).

Iz zgoraj opisanega razreda Oseba, ki poleg zgoraj omenjenih metod pozna še metode za nastavljanje imena

(NastaviIme) in priimka (NastaviPriimek) ter konstruktor Oseba(), ki naredi osebo z imenom Janez in priimkom Novak, izpeljite razred Plavalec, ki poleg imena in priimka vsebuje še podatke o:

starosti plavalca (celo število med 10 in 80 let) ter

stilu plavanja plavalca (niz).

Page 121: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

121

Vsebuje naj dva konstruktorja:

privzetega, ki ustvari plavalca Janeza Novaka, ki je star 20 let in plava kravl

konstruktor s štirimi parametri (ime, priimek, starost in stil plavanja), ki ustvari podanega plavalca. Če parametri niso pravilni, naj se ustvari privzeti plavalec.

ter metode za branje in spreminjanje lastnosti (pri tem pazite na pravilnost podatkov) plavalca. Vsebuje naj tudi objektno metodo public void Zapisi (string imeDatoteke), ki podatke o plavalcu zapiše v

datoteko z imenom imeDatoteke. Vanjo naj se zapiše vrstica

Plavalec #ime #priimek plava #slog.

kjer namesto #ime, #priimek ter #slog vstavite prave vrednosti.

Sestavite tudi testni program, kjer ustvarite plavalca Danijel Ančimer, starega 20 let, ki plava prsno, ter podatke o njem zapišite v datoteko c:\plavalec.txt.

Plavalec II

Podan imamo razred Oseba z objektnima metodama VrniIme() ter VrniPriimek() (ki nam vrneta niza, ki predstavljata ime oz. priimek osebe).

Iz zgoraj opisanega razreda Oseba, ki poleg zgoraj omenjenih metod pozna še metode za nastavljanje imena

(NastaviIme) in priimka (NastaviPriimek) ter konstruktor Oseba(), ki naredi osebo z imenom Janez in priimkom Novak, izpeljite razred Plavalec, ki poleg imena in priimka vsebuje še podatke o:

najljubšem stilu plavanja plavalca (indeks ustreznega stila, ki ustrezna vnosu iz spodaj omenjene tabele – če je npr. v tabeli stilov plavanja niz "prsno" na 2. tem mestu v tabeli, in želimo za tega plavalca povedati, da najraje plava prsno, imamo tu shranjeno 2, če je "vseeno" in je niz "vseeno" v tabeli na začetku, pa imamo shranjeno 0)

možnih stilih plavanja (tabela nizov) – tabela vsebuje vsaj nize "vseeno", "kravl" in "prsno". Tabela se nikoli ne spreminja!

Vsebuje naj dva konstruktorja:

privzetega, ki ustvari plavalca Janeza Novaka, ki plava kravl

konstruktor s tremi parametri (ime, priimek in stil plavanja), ki ustvari podanega plavalca. Če parametri niso pravilni, naj se ustvari privzeti plavalec.

ter potrebni metodi za branje (vrne naj opisno ime stila iz tabele stilov in ne indeks) in spreminjanje najljubšega stila posameznega plavalca (parameter je ime stila). Če parameter pri tej metodi, ki označuje stil, v tabeli stilov ne obstaja, nastavimo najljubši stil plavanja na "vseeno" (torej shranimo podatek 0). Vsebuje naj tudi objektno

metodo public void Zapisi (string imeDatoteke), ki podatke o plavalcu zapiše v datoteko z

imenom imeDatoteke. Vanjo naj se zapiše vrstica

Plavalec #ime #priimek plava #slog.

kjer namesto #ime, #priimek ter #slog vstavite prave vrednosti (v datoteki naj bo izključno ta vrstica).

Sestavite tudi testni program, kjer ustvarite plavalca Danijel Ančimer, ki najraje plava prsno, ter podatke o njem

zapišite v datoteko c:\plavalec.txt.

Razredi in objekti (osnove)

Page 122: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

122

Razred - class Pojem razred (class) je temeljni pojem objektno (predmetno) orientiranega programiranja. Kreirati nov razred pomeni sistematično urediti podatke in informacije, ter manipulacije nad njimi v neko pomensko celoto. Takšno urejanje podatkov in informacij je nekaj, kar je običajen pojav tudi v vsakdanjem življenju in ne velja le za programiranje. Kot primer iz vsakdanjega življenja vzemimo pojem avto: vsi avtomobili imajo nekaj skupnih zmožnosti (lahko jih usmerjamo oz. vodimo, lahko jih zaustavimo, pospešujemo, itd. ) in nekaj skupnih lastnosti oz. atributov ( imajo volanski obroč, pogonski motor, kolesa itd.). Ko torej pomislimo na avto, takoj vemo, da gre za pojem oz. objekte ki si delijo prej omenjene skupne lastnosti in zmožnosti. Klasificiranje oz. razvrščanje pojmov v neko celoto je torej temeljna spretnost, ki je lastna vsem ljudem in brez katere si težko zamišljamo, kako bi ljudje razmišljali in komunicirali med seboj. Prav zaradi tega so tudi programerji prišli na idejo, da bi posamezne pojme, njihove lastnosti (atribute) in operacije nad njimi, združili v neko celoto, ki so jo poimenovali razred – class. Prav to pa je natanko tisto, kar nam ponujajo moderno zasnovani objektno orientirani programski jeziki, kot je npr. Microsoft Visual C#. Omogočajo definicijo novih razredov, ki v sebi združujejo lastnosti (atribute oz. podatke) in operacije nad njimi oz obnašanje (metode). Sveta trojica predmetno usmerjenega programiranja so pojmi enkapsulacija, dedovanje in polimorfizem

Enkapsulacija Glavni del pojma enkapsulacija predstavlja besedica kapsula: enkapsulacija pomeni postaviti nekaj v kapsulo. Za kapsulo je značilno, da ima vidno (javno) površino in nevidno (zasebno) notranjost, v kateri se nekaj nahaja. Vsak predmet ali pojem si lahko ponazorimo s kapsulo. Površino predstavljajo podatki in operacije, ki jih lahko od zunaj spreminjamo. Notranjost pa so zasebni deli predmeta, ki od zunaj niso dostopni. Tudi v svetu programiranja poznamo dve pomembni uporabi oz razlagi pojma enkapsulacija:

Princip, kako v enoto združimo podatke in metode, ki operirajo nad temi podatki. Gre torej za kombinacijo metod in podatkov znotraj razreda, z drugimi besedami razvrščanju oz. klasifikaciji;

Notranje podatke in procese navzven skrijemo – gre torej za kontrolo dostopa do metod in podatkov v kapsuli, z drugimi besedami za kontrolo uporabe razreda.

Razred – class je programska struktura, ki zagotavlja sintakso in semantiko za podporo teh dveh dveh temeljnih načel enkapsulacije. Kot primer kreirajmo razred krog, ki vsebuje eno metodo (za izračun ploščine kroga) in eno lastnost oz. podatek (polmer kroga). Deklaracijo novega razreda začnemo z rezervirano besedico class, ki ji sledi ime razreda, nato pa še telo razreda, zapisano med dvema zavitima oklepajema. V telesu razreda so metode (npr. metoda za ploščino), spremenljivke, ki jim pravimo tudi polja razreda ( npr. polmer kroga) in pa lastnosti (properties) – o lastnostih bo več zapisanega kasneje. class krog

{

double Ploscina() //metoda razreda

{

return Math.PI * polmer * polmer;

}

double polmer; //polje razreda

}

Tako deklariran razred krog pa nima praktične uporabe, saj nismo upoštevali drugega načela enkapsulacije: dostopnost. Potem, ko smo enkapsulirali metode in polje znotraj razreda, smo pred temeljno odločitvijo, kaj naj bo javno, kaj pa zasebno. Vse kar smo zapisali med zavita oklepaja v razredu, spada v notranjost razreda, vse kar pa je pred prvim oklepajem in za zadnjim oklepajem pa je zunaj razreda. Z besedicama public, private in protected pa lahko kontroliramo, katere metode in polja bodo dostopna tudi od zunaj:

Page 123: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

123

Metoda ali polje je mišljeno kot privatno (private), kadar je dostopno le znotraj razreda. Tako polje oz. metodo deklariramo tako, da pred tipom metode oz. polja postavimo besedico private.

Metoda ali polje je mišljeno kot javno (public), kadar je dostopno tako znotraj, kot tudi izven razreda. Tako polje oz. metodo deklariramo tako, da pred tipom metode oz. polja postavimo besedico public..

Metoda ali polje je mišljeno kot zaščiteno (protected), kadar je vidno le znotraj razreda, ali pa v podedovanih (izpeljanih) razredih.

Deklaracijo našega razreda krog sedaj napišimo še enkrat. Tokrat bomo metodo Ploscina deklarirali kot javno metodo, polje polmer pa kot privatno polje. Metodo Ploscina bomo v tem primeru lahko poklicali oz. uporabili tudi izven razreda, neposrednega dostopa do polja polmer pa ne bomo imeli. class Krog

{

public double Ploscina() //javna metoda razreda

{

return Math.PI * polmer * polmer;

}

private double polmer; //zasebno polje razreda

}

Če metode ali polja ne deklariramo kot public ali pa private, bo privzeto, da je metoda oz. polje private. Seveda pa velja, da če so vse metode in polja znotraj razreda privatne, razred nima praktične uporabe. Če hočemo razred, ki smo ga definirali tudi uporabiti, moramo kreirati novo spremenljivko tega tipa, oz. kot temu pravimo v svetu objektnega programiranje, kreirati moramo novo instanco (primerek) razreda, ki ji pravimo tudi objekt. Novo spremenljivko tipa Krog lahko deklariramo tako kot vsako drugo spremenljivko. Naredimo primerjavo med kreiranjem in inicializacijo spremenljivk osnovnih (primitivnih) podatkovnih tipov in spremenljivk izpeljanih iz razredov (objektov). Osnovna sintaksa je enaka: int i; //deklaracija spremenljike i, ki je celoštevilčnega tipa

Krog K //deklaracija spremenljivke K ki je tipa Krog (razred) Če hočemo uporabiti vrednost katerekoli spremenljivke, moramo biti prepričani, da ta spremenljivka že ima vrednost. int i; //deklaracija spremenljike i, celoštevilčnega tipa

Console.WriteLine(i); //NAPAKA – uporaba neinicilaizirane spremenljivke

To pravilo velja seveda za vse spremenljivke, ne glede na njihov tip. Krog K //deklaracija spremenljivke k, ki je tipa Krog (razred)

Console.WriteLine(K); //NAPAKA – uporaba neinicilaizirane spremenljivke

Spremenljivke osnovnih podatkovnih tipov seveda lahko inicializiramo enostavno takole: int i=10; //deklaracija in inicilaizacija spremenljike i, celoštevilčnega tipa

Console.WriteLine(i); //OK!!

//ali pa takole

int j;

j = 10;

Console.WriteLine(j); //OK!! Spremenljivkam, ki so izpeljane (ustvarjene) iz razreda pa vrednosti ne moremo prirejati tako kot spremenljivkam osnovnih (vrednostih) tipov. Uporabiti moramo rezervirano besedo new in za naš razred poklicati ustrezen konstruktor. Novo spremenljivko (nov objekt oz. novo instanco) razreda Krog torej ustvarimo s pomočjo rezervirane besede new takole:

Page 124: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

124

Krog K = new Krog();/*ustvarili smo nov objekt razreda Krog: pri tem smo uporabili privzeti

konstruktor – to je metodo Krog(), ki ima enako ime kot razred. */

//Ali pa takole

Krog K;

K = new Krog();

Primer: class Zgradba//deklaracija razreda Zgradba z dvema javnima poljema. Razred nima nobene metode.

{

public int kvadratura;

public int stanovalcev;

}

static void Main(string[] args)

{

Zgradba hiša = new Zgradba(); //nov objekt razreda Zgradba

Zgradba pisarna = new Zgradba(); //nov objekt razreda Zgradba

int kvadraturaPP; // kvadratura na osebo

hiša.stanovalcev = 4;

hiša.kvadratura = 2500;

pisarna.stanovalcev = 25;

pisarna.kvadratura = 4200;

kvadraturaPP = hiša.kvadratura / hiša.stanovalcev;

Console.WriteLine("Hiša ima:\n " +

hiša.stanovalcev + " stanovalcev\n " +

hiša.kvadratura + " skupna kvadratura\n " +

kvadraturaPP + " kvadratura na osebo");

Console.WriteLine();

kvadraturaPP = pisarna.kvadratura / pisarna.stanovalcev;

Console.WriteLine("Pisarna ima:\n " +

pisarna.stanovalcev + " stanovalcev\n " +

pisarna.kvadratura + " skupna kvadratura\n " +

kvadraturaPP + " kvadratura na osebo");

}

Vaja:

/*Kreirajmo razred oseba s štirimi polji (ime, priimek, letnik in višina), ter dvema metodama:

metodo za prirejanje podatkov posameznim poljem in metodo za izpis podatkov*/

class Oseba

{

//razred ima štiri zasebna polja

private string ime;

private string priimek;

private int letnik;

private int visina;

//razred ima tudi dve javni metodi

public void VpisiOsebo(string ime,string priimek,int letnik,int visina)

{

//ker imajo parametri metode enako ime kot polja razreda, smo za dostop do polj razreda

//uporabili rezervirano besedo this - ta pomeni referenco na konkreten primerek razreda

//(objekt), oz. referenco na polje konkretnega objekta

this.ime = ime;

this.priimek = priimek;

this. letnik = letnik;

this.visina = visina;

}

public void IzpisiOsebo()

{

Page 125: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

125

Console.Write("Ime: " + ime + "\nPriimek: " + priimek + "\nStarost: " + letnik +

"\nVišina: " + visina+"\n");

}

}

//glavni program

static void Main(string[] args)

{

Oseba nogometas = new Oseba();//nova instanca (primerek) razreda Oseba

nogometas.VpisiOsebo("David", "Becham", 1980, 181);

nogometas.IzpisiOsebo();

Oseba predsednik = new Oseba();//nova instanca (primerek) razreda Oseba

predsednik.VpisiOsebo("George", "Bush", 1951, 173);

predsednik.IzpisiOsebo();

}

Vaja:

/*Kreirajmo razred avto s štirimi zasebnimi polji (znamka, model, najvecjahitrost in teza),

ter dvema javnima metodama: metodo za inicializacijo (vnos) polj tega razreda, ter javno

metodo za izpis polj tega razreda. Nato kreirajmo dva objekta, ju inicializirajmo ter izpišimo

njune podatke*/

class avto

{

//zasebna polja razreda avto

private string znamka;

private string model;

private int najvecjahitrost;

private double teza;

public void Inicializacija()//javna metoda za vnos podatkov o konkretnem avtomobilu

{

Console.Write("\nZnamka: ");

znamka = Console.ReadLine();

Console.Write("\nModel: ");

model = Console.ReadLine();

Console.Write("\nNajvečja hitrost: ");

najvecjahitrost = Convert.ToInt32(Console.ReadLine());

Console.Write("\nTeţa vozila: ");

teza = Convert.ToDouble(Console.ReadLine());

}

public void IzpisPodatkov()//javna metoda za izpis podatkov o konkretnem avtomobilu

{

Console.WriteLine("\nIzpis podatkov o vozilu:\n");

Console.WriteLine("Znamka: "+znamka

+"\nModel: "+model

+"\nNajvečja hitrost: "+najvecjahitrost

+"\nteţa vozila: "+teza);

}

}

static void Main(string[] args)

{

avto mojavto=new avto(); //nov objekt tipa avto (nova instanca razreda)

mojavto.Inicializacija(); //klic metode za inicializac.(vnos) podatkov za konkreten avto

mojavto.IzpisPodatkov(); //klic metode za izpis podatkov o avtomobilu

//kreiramo še drugi objekt, ga inicializiramo in izpišimo še njegove podatke

avto prijateljevavto = new avto();

prijateljevavto.Inicializacija();

prijateljevavto.IzpisPodatkov();

}

Vaja:

/*Kreirajmo razred Prodajalec, ki bo imel zasebno polje zneski (tabela 12 števil tipa double),

ter javne metode za inicializacijo te tabele: metodo za določanje prodaje za posamezen mesec,

metodo za izračun celoletne prodaje in metodo za izpis celoletne prodaje*/

class Prodajalec

{

private double[] zneski; //zasebna tabela ki hrani mesečne zneske prodaje

public void inicializacija() //metoda ki inicializira tabelo prodaje

{

Page 126: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

126

zneski = new double[12];

}

public void doloci_prodajo(int mesec, double znesek) //Vnos zneska prodaje za posam. mesec

{

if (mesec >= 1 && mesec <= 12 && znesek > 0)

zneski[mesec - 1] = znesek; //[mesec -1] zato, ker v gredo tabeli indeksi od 0 d0 11

else

Console.WriteLine("Napačen mesec ali znesek prodaje");

}

public void izpisi_letno_prodajo()

{

Console.WriteLine("Skupna letna prodaja je : " + skupna_letna_prodaja() + " EUR");

}

public double skupna_letna_prodaja() // funkcija ki vrne skupno letno prodajo

{

double vsota = 0.0;

for (int i = 0; i < 12; i++)

vsota += zneski[i];

return vsota;

}

};

static void Main(string[] args)

{

Prodajalec p = new Prodajalec(); // tvorba objekta p tipa Prodajalec

p.inicializacija(); //inicializacija tabele zneskov prodaje

double znesek_prodaje;

for (int i = 1; i <= 12; i++)

{

Console.Write("Vnesi znesek prodaje za mesec "+i+": ");

znesek_prodaje = Convert.ToDouble(Console.ReadLine());

p.doloci_prodajo(i, znesek_prodaje);

}

p.izpisi_letno_prodajo();

}

Vaja:

/*Napišimo razred mojstring, v katerem bomo napisali nekaj javnih metod za delo s stringi, ki

naj pomenijo alternativo obstoječim metodam – npr. Length, Replace, ToUpper, ...*/

class mojstring

{

private string stavek; //zasebno polje razreda mojstring

public void DolociStavek(string st) //metoda za inicializacijo polja stavek

{

stavek = st;

}

public int dolzina()//metoda ki vrne število znakov v stringu

{

return stavek.Length;

}

//metoda za zamenjavo določenih znakov v stringu z novimi znaki

public void ZamenjajZnak(string stari, string novi)

{

stavek = stavek.Replace(stari, novi);

}

public string IzpisStavka()

{

return stavek;

}

public void VelikeCrke()

{

stavek = stavek.ToUpper();

}

}

static void Main(string[] args)

{

mojstring st=new mojstring();

st.DolociStavek("Razred mojstring: metodam za delo s stringi smo priredili slovenska

imena!");

Console.WriteLine("\nV stavku:\n\n"+st.IzpisStavka()+"\n\nje "+st.dolzina()+" znakov!");

st.ZamenjajZnak(" ", "X");//presledke nadomestimo z znakom X

Page 127: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

127

Console.WriteLine(st.IzpisStavka());

st.ZamenjajZnak("X"," ");//vse znake "X" nadomestimo s presledki

st.VelikeCrke(); //vse črke v stringu spremenimo v velike črke

Console.WriteLine(st.IzpisStavka());

}

Naloge:

Napiši razred Kosarka, za spremljanje košarkaške tekme. Voditi moraš število prekrškov za vsakega

tekmovalca (10 igralcev), število doseženih točk (posebej 1 točka, 2 točki in 3 točke), ter metodo za izpis statistike tekme. Doseganje košev in prekrškov realiziraj preko metode zadelProstiMet(), zadelZa2Tocki, zadelZa3Tocke in prekrsek.

Sestavi razred, ki bo v svoja polja lahko shranil ulico, številko nepremičnine ter vrsto nepremičnine.

Ustvari poljuben objekt, ga inicializiraj in ustvari izpis, ki naj zgleda približno takole: Na naslovu Cankarjeva ulica 32, Kranj je blok.

Konstruktor Konstruktor je metoda razreda, ki se uporablja za kreiranje novega primerka (instance) razreda. Njegovo ime je vedno enako kot je ime njegovega razreda. Namen konstruktorja pa je, da poskrbi za inicializacijo polj novo kreiranega objekta. Če konstruktorja ne napišemo sami, ga avtomatično za nas skreira prevajalnik. Z drugimi besedami, originalni razred Krog, za katerega smo zgoraj napisali eno samo metodi in eno polje, v resnici vsebuje še eno metodo: to je nevidni konstruktor, ki poskrbi za inicializacijo polja polmer. Ta metoda je povsem enaka, kot če bi napisali celoten razred takole: class Krog

{

public Krog()

{

polmer = 0.0;

}

public double Ploscina()

{

return Math.PI * polmer * polmer;

}

private double polmer;

}

Konstruktor, ki ga avtomatično generira prevajalnik, je vedno public, nima nobenega tipa (niti void), nima argumentov, vrednosti numeričnih polj postavi na 0, polja tipa bool postavi na false, vsem referenčnim poljem (spremenljivkam) pa priredi vrednost null. Ko je nov objekt inicializiran, lahko dostopamo do njegovih polj in uporabljamo njegove metode. Do javnih (public) polj (lastnosti razreda) in javnih metod dostopamo s pomočjo operatorja pika, tako kot pri strukturah. Krog K = new Krog();

Console.WriteLine(K.Ploscina()); /*Izpis bo seveda enak 0, ker je privzeti konstruktor polju

polmer avtomatično dodelil vrednost 0!*/

Primer: /*Kreiraj razred Pravokotnik z dvema poljema (dolţina in višina pravokotnika) in dvema

metodama (metoda za izračun ploščine in metoda za izračun obsega pravokotnika. Nato ustvari

nov objekt tipa Pravokotnik, določi stranice pravokotnika in s pomočjo metod razreda

Pravokotnik izračunaj njegovo ploščino in obseg*/

public class Pravokotnik

{

Page 128: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

128

//deklariramo polja; polja bodo javna (public), da bomo imeli do njih dostop izven razreda

public int dolzina,visina; //polji (lastnosti) razreda sta stranici pravokotnika

public Pravokotnik() //konstruktor (naš konstruktor je enak privzetemu konstruktorju!)

{

dolzina=0;

visina=0;

}

public double ploscina() //metoda, ki izračuna in vrne ploščino pravokotnika

{

return (dolzina*visina);

}

public double obseg() //metoda, ki izračuna in vrne obseg pravokotnika

{

return (2*dolzina+2*visina);

}

};

static void Main(string[] args)

{

Pravokotnik P = new Pravokotnik(); //na kopici ustvarimo nov objekt razreda pravokotnik

P.dolzina = 10; //določimo dolţino pravokotnika

P.visina = 8; //določimo višino pravokotnika

Console.WriteLine("Ploščina pravokotnika: " + P.ploscina()); //klic metode za izračun

//ploščine

Console.WriteLine("Obseg pravokotnika: " + P.obseg()); //klic metode za izračun obsega

}

Vaja:

/*Kreiraj razred Kocka z enim privatnim poljem (rob kocke) in s tremi javnimi metodami (metoda

za določanje robu kocke, metoda za izpis robu kocke in metoda za izračun prostornine kocke.

Nato ustvari nov objekt tipa Kocka, določi rob kocke in s pomočjo metode razreda Kocka

izračunaj njegovo prostornino */

public class Kocka

{

private double rob;

public Kocka() //konstruktor (enak je privzetemu konstruktorju)

{

rob = 0;

}

public void Nastavi_Rob(double x) //metoda, ki omogoča nastavitev roba kocke

{

rob = x;

}

public double izpis_Roba()

{

return rob;

}

public double prostornina() //metoda, ki izracuna in vrne prostornino kocke

{

return (rob*rob*rob);

}

};

static void Main(string[] args)

{

Kocka K = new Kocka(); //na kopici ustvarimo novo instanco oz. nov objekt razreda Kocka

Random naklj=new Random();

K.Nastavi_Rob(Math.Round(naklj.NextDouble()*10,1)+1); //Rob kocke bo naključno realno

//število med 1 in 10

Console.WriteLine("Rob kocke: " + K.izpis_Roba());

Console.WriteLine("Prostornina kocke: " + K. prostornina ());//klic metode za izračun

//prostornine

}

Vaja:

/*Napiši razred Kompleksno z dvema poljema (realna in imaginarna), ki naj predstavljata realno

in imaginarno komponento nekega kompleksnega števila. Napiši konstruktor, ki komponentama

priredi določeni vrednosti. Napiši še metodo, ki v primerni obliki izpiše poljuben objekt tega

razreda!*/

Page 129: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

129

class Kompleksno

{

private int realna, imaginarna; //zasebni polji razreda

public Kompleksno (int real, int imag) // konstruktor, ki poljema priredi celošt.vrednosti

{

realna = real;

imaginarna = imag;

}

public void izpisi(string ime) //metoda za izpis

{

Console.WriteLine(ime+" = "+realna + " + " + imaginarna + " * i");

}

}

static void Main(string[] args)

{

Kompleksno alfa=new Kompleksno(1,1); //kreiranje novega objekta tipa Kompleksno

alfa.izpisi("alfa");

Kompleksno beta=new Kompleksno(6,8); //kreiranje novega objekta tipa Kompleksno

alfa.izpisi("beta");

}

Preobloženi (overloaded) konstruktorji Poglejmo si še enkrat prvotno deklaracijo razreda Krog, ki ima deklarirano eno privatno polje (polmer) in javno metodo Ploscina(). class Krog

{

public double Ploscina()

{

return Math.PI * polmer * polmer;

}

private double polmer;

}

Deklarirajmo novo spremenljivko tipa Krog, ji priredimo vrednost novega objekta Krog, nato pa pokličimo metodo Ploscina: Krog K = new Krog();

Console.WriteLine(K.Ploscina());

Privzeti konstruktor v vsakem primeru postavi polmer na 0 (ker je polmer private, ga tudi ne moremo spremeniti), zaradi česar bo tudi ploščina objekta Krog vsakič enaka 0. Rešitev tega problema je v dejstvu, da je konstruktor prav tako metoda (sicer metoda posebne vrste), za metode pa velja, da so lahko preobložene (preobložene metode so metode, ki imajo enako ime, razlikujejo pa se v številu ali pa tipu parametrov). Z drugimi besedami, napišemo lahko svoj lasten konstruktor z vrednostjo polmera kot parametrom. Novi konstruktor ima seveda enako ime kot razred, ne vrača pa ničesar. Razred Krog bo sedaj izgledal takole: class Krog

{

public Krog(double inicPolmer) //naš lasten konstruktor {

polmer = inicPolmer;

}

public double Ploscina()

{

return Math.PI * polmer * polmer;

}

private double polmer;

}

V primeru, da za nek razred napišemo lasten konstruktor velja, da prevajalnik ne generira privzeti konstruktor. Če pa smo napisali lasten konstruktor, ki sprejme en parameter in bi kljub temu želeli poklicati

Page 130: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

130

tudi privzeti konstruktor, ga moramo napisati sami. Pri tem pa moramo biti pozorni na dejstvo, da bodo vrednosti polj, ki jih v konstruktorju ne bomo inicializirali, še vedno ostala implicitno inicializirana na 0, false ali pa null.

Kopirni (copy) konstruktorji Kopirni konstruktor je konstruktor, ki kreira nov objekt tako, da kopira polja nekega že obstoječega objekta istega tipa. Primer: public class Cas

{

// konstruktor

public Cas(DateTime dt)

{

Leto = dt.Year;

Mesec = dt.Month;

Dan = dt.Day;

}

// kopirni konstruktor public Cas(Cas obstojeciObjekt)

{

Leto = obstojeciObjekt.Leto;

Mesec = obstojeciObjekt.Mesec;

Dan = obstojeciObjekt.Dan;

}

// zasebna polja razreda Cas

int Leto;

int Mesec;

int Dan;

}

// kreiranje objektov DateTime trenutniCas = DateTime.Now;

Cas t = new Cas(trenutniCas); // kreiranje objekta t s pomočjo konstruktorja

Cas t3 = new Cas(t); // kreiranje objekta t3 s pomočjo kopirnega konstruktorja

Statični (static) konstruktorji Statični konstruktor je konstruktor, ki se izvede še preden je iz tega razreda izpeljan katerikoli objekt. Kdaj se bo statični konstruktor izvedel ne moremo vnaprej natančno vedeti, vemo pa, da se bo zagotovo izvedel nekje med zagonom našega programa in pred kreiranjem prve instance razreda, v katerem je ta konstruktor napisan. Pred statičnim konstruktorjem nikoli ne napišemo nivoja dostopnosti (besedic private, public, ..), ampak le besedico static. Ker gre za statično metodo (tudi konstruktor je metoda) v njem ne moremo dostopati do nestatičnih članov, ampak le do statičnih članov. Primer: public class Sporocilo

{

public static string Naslov = "Knjiga";

static Sporocilo()

{

Naslov= "C#";

}

...

}

Page 131: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

131

Za zagon statičnega konstruktorja ne potrebujemo nobenega objekta, saj lahko do njega dostopamo kar preko imena razreda, npr.: Console.WriteLine (Sporocilo.Naslov); //Izpis: C#

Polja tipa readonly Včasih želimo narediti javno polje, ki pa naj ga uporabnik ne bo mogel spremeniti. V takem priemru označimo da je polje readonly. Primer: public class DanasnjiDatum

{

static DanasnjiDatum() // statični konstruktor {

DateTime dt = DateTime.Now;

Leto = dt.Year;

Mesec = dt.Month;

Dan = dt.Day;

}

// zasebna readonly polja razreda

public static readonly int Leto;

public static readonly int Mesec;

public static readonly int Dan;

}

Ker so polja readonly njihove vrednosti ne moremo spreminjati_

Console.WriteLine("Leto: {0}", DanasnjiDatum.Leto); //izpis trenutne letnice

DanasnjiDatum.Leto = 2009; //NAPAKA: polje Leto je readonly

Destruktorji Destruktor je metoda, ki počisti za objektom. Destruktor se torej izvede, ko objekt odmre ( npr. ko se zaključi

nek blok, ali pa se zaključi neka metoda, v kateri je bil objekt ustvarjen) – pokliče ga torej smetar (Garbage

Collector). Destruktor torej sprosti pomnilniški prostor objekta (prostor, ki ga zasedajo njegovi podatki).

Destruktor ima enako kot konstruktor, enako ime kot razred sam in nima tipa. Ne sprejema argumentov, za

razliko od konstruktorja pa pred destruktorjem stoji še znak '~ '. Med konstruktorjem in destruktorjem pa obstaja

še ena velika razlika. Medtem ko je konstruktorjev določenega objekta lahko več, je destruktor vedno samo eden.

Pomembno je tudi to, da destruktorji za razliko od konstruktorjev ne obstajajo v strukturah – obstajajo le v

razredih.

class Krog

{

... //deklaracija polj

public Krog() //prvi konstruktor {

...

}

public Krog(double inicPolmer) //drugi konstruktor {

...

}

~ Krog() //destruktor {

...

}

}

Page 132: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

132

Glede destruktorjev je torej pomembno sledeče :

Destruktor je metoda razreda, ki ima isto ime kot razred in dodan prvi znak '~ '.

Destruktor je metoda razreda, ki nič ne vrača ( pred njo ne sme stati niti void!) in nič ne sprejme.

Razred ima lahko samo en destruktor.

Destruktor se izvede, ko se objekt uniči.

Dogovor o poimenovanju Pri poimenovanju javnih polj in metod se skušajmo držati pravila, ki ga imenujemo PascalCase (ker je bilo prvič uporabljeno v programskem jeziku Pascal). Imena javnih polj in metod naj se začenjajo z veliko črko (v zgornjem primeru Ploscina). Pri poimenovanju zasebnih polj in metod pa se skušajmo držati pravila, ki ga imenujemo camelCase. Imena zasebnih polj in metod naj se začenjajo z malo črko (v zgornjem primeru polmer). Pri tako dogovorjenem poimenovanju je ena sama izjema. Imena razredov naj bi se začenjala z veliko črko. Ker pa se mora ime konstruktorja natančno ujemati z imenom razreda, se mora torej tudi ime konstruktorja v vsakem primeru začeti z veliko črko, ne glede na to ali je javen ali zaseben. Primer: Kot primer napišimo razred Tocka, ki naj ima dve privatni polji x in y, ter dva lastna konstruktorja. Prvi naj bo brez parametrov in v njegovem telesu le zapišimo stavek, ki bo v oknu izpisal, da je bil ta konstruktor poklican. Drugi konstruktor pa naj ima dva parametra, s katerima inicializiramo obe polji razreda. V telesu drugega konstruktorja napišimo stavek, ki izpiše vrednosti obeh polj. class Tocka

{

public Tocka()//Konstruktor brez parametrov

{

Console.WriteLine("Klican je bil privzeti konstruktor!");

}

public Tocka(int x, int y) //Preobloţeni konstruktor z dvema parametroma

{

Console.WriteLine("x:{0} , y:{1}", x, y);

}

private int x, y;

}

static void Main(string[] args)

{

Tocka A = new Tocka(); //Klic konstruktorja brez parametrov

Tocka B = new Tocka(600, 800); //KLic konstruktorja z dvema parametroma

}

Vaja:

/*Napišimo razred Tocka, ki nja ima dve zasebni polji (koordinati točke), privzeti

konstruktor, ki obe koordinati postavi na 0, ter konstruktor z dvema parametroma, s katerima

inicializiramo koordinati nove točke. Napišimo še metodo, ki izračuna in vrne razdaljo točke

od neke druge točke*/

class Tocka

{

public Tocka()//lasten privzeti konstruktor

{

x = 0;

y = 0;

Page 133: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

133

}

public Tocka(int initX, int initY) //Preobloţeni konstruktor z dvema parametroma

{

x = initX;

y = initY;

}

public double RazdaljaOd(Tocka druga) //metoda za izračun razrdalje od poljubne točke

{

int xRazd = x - druga.x;

int yRazd = y - druga.y;

return Math.Sqrt(Math.Pow(xRazd,2) + Math.Pow(yRazd,2));

}

private int x, y;

}

static void Main(string[] args)

{

Tocka A = new Tocka(); //Klic konstruktorja brez parametrov

Tocka B = new Tocka(600, 800); //Klic konstruktorja z dvema parametroma

double razdalja = A.RazdaljaOd(B); //Izračun razdalje obeh točk

Console.WriteLine("Razdalja točke A od točke B je enaka " + razdalja+" enot!");

}

Vaja:

/*Napišimo razred Zaposleni z enim samim zasebnim poljem (dohodek) in dvema konstruktorjema.

Prvi konstruktor naj ima kot parameter letni dodek zaposlenega, drugi pa naj imam dva

paramera: tedenski dohodek in število tednov. Napišimo še metodo za izpis skupnega dohodka*/

public class Zaposleni

{

private double dohodek;

public Zaposleni(int letniDohodek) //konstruktur

{

dohodek = letniDohodek;

}

public Zaposleni(int tedenskiDohodek, int številoTednov)//preobloţeni konstruktur

{

dohodek = tedenskiDohodek * številoTednov;

}

public double vrniDohodek() //metoda ki vrne vrednost zasebnega polja dohodek

{

return dohodek;

}

}

static void Main(string[] args)

{

Zaposleni Janez = new Zaposleni(20000); //nov objekt, izvede se prvi konstruktor

Zaposleni Tina = new Zaposleni(550, 54); //nov objekt, izvede se drugi (preobloţeni)

//konstruktor

Console.WriteLine("Janez ima letni dohodek : " + Janez.vrniDohodek()+" EUR" );

Console.WriteLine("Tina ima letni dohodek : " + Tina.vrniDohodek()+ " EUR");

}

Naloge:

Sestavi razred denarnica, ki bo omogočal naslednje operacije: dvig, vlogo in ugotavljanje stanja.

Začetna vrednost se naj postavi s konstruktorjem. Ustvari tabelo desetih denarnic z naključno mnogo denarja in jih izpiši.

Potrebujemo razred, ki bo hranil podatke o objektih tipa Instrukcije z naslednjimi komponentami: vrsta

inštrukcije, število opravljenih ur in ali je inštrukcija možna. Razred naj ima tudi metode in sicer: inštrukcija se opravlja, inštrukcija se ne opravlja. Z osnovnim konstruktorjem določi vrednost (poljubno) vsem komponentam razreda. Z dodatnim konstruktorjem poskrbi za možnost nastavitve

Page 134: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

134

začetne vrednosti za vrsto inštrukcije, opravljene ure ter ali je inštrukcija možna. Na osnovi razreda Instrukcije napiši še testni program, ki bo kreiral in izpisal dva objekta razreda Instrukcije in sicer tako, da bodo začetne vrednosti pri prvem objektu nastavljene z osnovnim konstruktorjem, pri drugem objektu pa z dodatnim konstruktorjem.

Sestavi razred, ki predstavlja osnovo za izdelavo programa, s pomočjo katerega bomo pregledovali

rezultate nekega športnega tekmovanja. Sestavi razred Tekmovalec, ki ima naslednje komponente: startno številko, ime, priimek in klub. Vsa polja so tipa string. Napiši vsaj dva konstruktorja in pripravi ustrezne get/set metode za vse te podatke. Z metodo public string tostring() naj se izpišejo podatki o tekmovalcih (startna številka, ime, priimek, klub).

Sestavi razred Pacient, ki ima tri komponente: ime, priimek, krvna_skupina. Komponenti ime in priimek

naj bosta public, krvna_skupina private, vse tri pa tipa string. Napiši vsaj dva konstruktorja:

prazen konstruktor, ki vse tri komponente nastavi na "NI PODATKOV", konstruktor, ki sprejme vse tri podatke in ustrezno nastavi komponente.

Z metodo public string tostring() naj se izpišejo podatki o pacientu (ime, priimek, krvna skupina).

Lastnost (Property) Polja so znotraj razreda običajno deklarirana kot zasebna (private). Vrednosti jim priredimo tako, da zapišemo ustrezen konstruktor (konstruktorje), ali pa da napišemo posebno javno metodo (metode) za prirejanje vrednosti polj. Ostaja pa še tretji način, ki je najbolj razširjen – za vsako polje lahko definiramo ustrezno lastnost (property), s pomočjo katere dostopamo do posameznega polja, ali pa z njeno pomočjo prirejamo (nastavljamo) vrednosti polja. Lastnosti (properties) v razredih torej uporabljamo za inicializacijo oziroma dostop do polj razreda (objektov). Lastnost (property) je nekakšen križanec med spremenljivko in metodo. Pomen lastnosti je v tem, da ko jo beremo, ali pa vanjo pišemo, se izvede koda, ki jo zapišemo pri tej lastnosti. Branje in izpis (dostop) vrednosti je znotraj lastnosti realizirana s pomočjo rezerviranih besed get in set – imenujemo ju pristopnika (accessors). Accessor get mora vrniti vrednost, ki mora biti istega tipa kot lastnost (seveda pa mora biti lastnost istega tipa kot polje, kateremu je naemnjena), v accessor-ju set pa s pomočjo implicitnega parametra value lastnosti priredimo (nastavimo) vrednost. Navzven pa so lastnosti vidne kot spremenljivke, zato lastnosti v izrazih in prirejanjih uporabljamo kot običajne spremenljivke. Primer: class LastnostDemo {

int polje; //zasebno polje

public LastnostDemo() //konstruktor

{

polje = 0;

}

public int MojaLastnost //deklaracija lastnosti (property) razreda LastnostDemo

{

get //metoda get za pridobivaje vrednosti lastnosti polje

{

return polje ;

}

set //metoda set za prirejanje (nastavljanje) vrednosti lastnosti polje

{

polje = value;

}

}

}

Page 135: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

135

static void Main(string[] args)

{

LastnostDemo ob = new LastnostDemo();//nov objekt razreda LastnostDemo

Console.WriteLine("Originalna vrednost ob.MojaLastnost: " + ob.MojaLastnost);

ob.MojaLastnost = 100;

Console.WriteLine("Vrednost ob.MojaLastnost: " + ob.MojaLastnost);

Console.WriteLine("Prirejanje nove vrednosti -10 za ob.MojaLastnost");

ob.MojaLastnost = -10;

Console.WriteLine("Vrednost ob.MojaLastnost: " + ob.MojaLastnost);

}

Vaja:

/*Deklarirajmo razred Oseba z dvema poljema (_ime in _priimek) ter javno metodo za

nastavljanje vrednosti obeh polj. Razred naj ima tudi lastnost PolnoIme, za prirejanje in

vračanje polnega imena osebe (imena in priimka). Ustvarimo nov objekt in demonstrirajmo

uporabo lastnosti PolnoIme*/

class Oseba

{

private string _priimek; //zasebno polje razreda Oseba

private string _ime; //zasebno polje razreda Oseba

public void NastaviIme(string ime, string priimek) //javna metoda razreda

{

_priimek = priimek;

_ime = ime;

}

public string PolnoIme //javna lastnost razreda

{

get

{

return _ime + " " + _priimek;

}

set

{

string zacasna = value;

string[] imena = zacasna.Split(' '); //Celotno ime razdelimo na posamezne besede

//in jih spravimo tabelo

_ime = imena[0]; //za ime vzamemo prvi string v tabeli

_priimek = imena[imena.Length - 1]; //za priimek vzamemo drugi string v tabeli

}

}

}

static void Main(string[] args)

{

Oseba politik = new Oseba();

politik.NastaviIme("Nelson", "Mandela");

Console.WriteLine("Polno ime osebe je " + politik.PolnoIme); //Izpis: Polno ime osebe je

// Neslon Mandela

politik.PolnoIme = "George Walker Bush";

Console.WriteLine("Polno ime osebe je " + politik.PolnoIme); //Izpis: Polno ime osebe je

// George Bush

politik.PolnoIme = "France Prešeren";

Console.WriteLine("Polno ime osebe je " + politik.PolnoIme); //Izpis: Polno ime osebe je

// France Prešeren

}

Za posamezno polje lahko napišemo le eno lastnost, v kateri s pomočjo stavkov get ali set dostopamo oz. nastavljamo vrednosti posameznega polja. Besedici get ali set lahko izpustimo in dobimo write-only ali read-only lastnosti. Primer: class MojRazred

{

double A = 3; //zasebno polje

Page 136: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

136

double B = 4; //zasebno polje

public double MojaVrednost //MojaVrednost je ReadOnly: vsebuje le get, ne pa tudi set

{

get { return A * B; } //lastnost vrne vrednost produkta obeh polj

}

}

static void Main(string[] args)

{

MojRazred c = new MojRazred(); //nov objekt tipa MojRazred

Console.WriteLine("MojaVrednost: "+c.MojaVrednost); //prikaz vrednosti lastnosti

//MojaVrednost

}

Naloge:

Sestavi razred Piramida, ki predstavlja pokončno piramido, ki ima za osnovno ploskev kvadrat. V

razredu hrani podatke o dolžini stranice osnovne ploskve in višino piramide. Obe komponenti naj imata tip dostopa private. Višina in stranica nista nujno celi števili. Napiši vsaj dva konstruktorja:

prazen konstruktor, ki ustvari piramido višine 1 in s stranico osnovne ploskve dolžine 1 konstruktor, ki sprejme podatka o višini in dolžini stranice osnovne ploskve Napiši program, ki ustvari tabelo 50 naključnih piramid in med njimi poišče piramido z

največjo višino.

Napiši get in set metode. V set metodah pazi na smiselnost podatkov (višina in dolžina stranice ne smeta biti negativni,...). Napiši tudi metodo tostring, ki vrne niz s smiselnim izpisom podatkov o piramidi.

Sestavi razred, kjer boš v objektih te vrste hranil ime države, njeno glavno mesto, površino (v km

2) in

število prebivalcev. Pripravi ustrezne get/set metode za vse te podatke in vsaj dva konstruktorja. Ne pozabi tudi na smiselno metodo tostring.

Sestavi razred Igrača, ki ima tri privatne spremenljivke: tip igrače(tip string), število igrač na zalogi(tip int)

ter ceno igrače(tip double).

sestavi prazen konstruktor;

sestavi konstruktor s tremi parametri;

napiši ustrezne set in get metode (pazi na smiselne vrednosti spremenljivk);

napiši metodo tostring, ki naj smiselno izpiše, kateri tip igrače, koliko kosov je na zalogi in kakšna

je cena;

dodaj še metodo zalogaIgrac, ki sprejme pozitivno celo število, če se je število igrač povečalo

(dobava novih) ter negativno celo število če se je število igrač zmanjšalo (prodane igrače). Temu

primerno spremeni število igrač na zalogi in pri tem pazi, da število igrač ne pade pod nič;

napiši metodo znizanaCena, ki sprejme celo število med 0 in 100 (%), to je za koliko procentov se

bo znižala cena igrače, in nastavi novo ceno igrače;

napiši glavni program, ki bo ustvaril pet tipov igrač, tri izmed njih znižal za 10, 20 in 50% ter dvema

spremenil zalogo.

V kemijskem laboratoriju večkrat preverjamo kislost snovi in jo definiramo s pH vrednostjo. Napiši razred

Snov, ki bo imel tri komponente: imeSnovi, ion in kislost. Prvi dve sta tipa string, tretja pa int. Ker je kislost zelo pomemben podatek o snovi, zagotovi, da bo zagotovo pravilen. Zato bo ustrezna spremenljivka imela privaten dostop. Sestavi tudi ustrezni get in set metodi. Pazi, da boš tudi v konstruktorju s parametri poskrbel, da ne bo prišlo do napačne nastavitve. Če bo uporabnik podal napačno kislost, ustvari objekt, kjer za prva dva podatka napišeš, da sta neobstoječa, kislino pa pustiš tako, kot jo je vnesel uporabnik. Sestavi tudi konstruktor brez parametrov, ki naredi objekt, ki predstavlja vodo. Kreiraj tabelo snovi in napiši stavke za izpis vseh objektov.

Page 137: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

137

Statične metode Za lažje razumevanje pojma statična metoda, si oglejmo metodo Sqrt razreda Math. Če pomislimo na to, kako smo jo v vseh dosedanjih primerih uporabili (poklicali), potem je v teh klicih nekaj čudnega. Metodo Sqrt smo namreč vselej poklicali tako, da smo pred njo navedli ime razreda (Math.Sqrt) in ne tako, da bi najprej naredili nov objekt tipa Math, pa potem nad njim poklicali metodo Sqrt. Kako je to možno? Pogosto se bomo srečali s primeri, ko metode ne bodo pripadale objektom (instancam) nekega razreda. To so uporabne metode, ki so tako pomembne, da so neodvisne od kateregakoli objekta. Metoda Sqrt je tipičen primer take metode. Če bila metoda Sqrt običajna metoda objekta izpeljanega iz nekega razreda, potem bi za njeno uporabo morali najprej kreirati nov objekt tipa Math, npr takole: Math m = new Math();

double d = m.Sqrt(42.24);

Tak način uporabe metode pa bi bil neroden. Vrednost, ki jo v tem primeru želimo izračunati in uporabiti, je namreč neodvisna od objekta. Podobno je pri ostalih metodah tega razreda (npr. Sin, Cos, Tan, Log, …). Razred Math prav tako vsebuje polje PI (iracionalno število Pi), za katerega uporabo bi potemtakem prav tako potrebovali nov objekt. Rešitev je v t.i. statičnih poljih oz.metodah. V C# morajo biti vse metode deklarirane znotraj razreda. Kadar pa je metoda ali pa polje deklarirano kot statično (static), lahko tako metodo ali pa polje uporabimo tako, da pred imenom polja oz. metode navedemo ime razreda. Metoda Sqrt (in seveda tudi druge metode razreda Math) je tako znotraj razreda Math deklarirana kot statična, takole: class Math

{

public static double Sqrt(double d) {

. . .

}

}

Zapomnimo pa si, da statično metodo ne kličemo tako kot objekt. Kadar definiramo statično metodo, le-ta nima dostopa do kateregakoli polja definiranega za ta razred. Uporablja lahko le polja, ki so označena kot static (statična polja). Poleg tega, lahko statična metoda kliče le tiste metode razreda, ki so prav tako označene kot statične metode. Ne-statične metode lahko, kot vemo že od prej, uporabimo le tako, da najprej kreiramo nov objekt. Primer: Napišimo razred točka in v njem statično metodo, katere naloga je le ta, da vrne string, v katerem so zapisane osnovni podatki o tem razredu

class Tocka

{

public static string Navodila()//Statična metoda

{

string stavek="Vsaka točka ima dve koordinati/polji: x je abscisa, y je ordinata!";

return stavek;

}

}

//Zato, da pokličemo statično metodo oz. statično polje NE POTREBUJEMO OBJEKTA!!! //Primer klica npr. v glavnem programu

Console.WriteLine(Tocka.Navodila());//Klic statične metode

Statična polja Tako kot obstajajo statične metode, obstajajo tudi statična polja. Včasih je npr. je potrebno, da imajo vsi objekti določenega razreda dostop do istega polja. To lahko dosežemo le tako, da tako polje deklariramo kot statično.

Page 138: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

138

class Test

{

public const double Konstanta = 20;//Statično polje

}

//Zato, da pokličemo statično metodo oz. statično polje NE POTREBUJEMO OBJEKTA!!!

Console.WriteLine(Test.Konstanta);//klic statičnega polja

Vaja:

class Nekaj

{

static int stevilo=0; //Statično polje

public Nekaj() // Konstruktor

{

stevilo++; //število objektov se je povečalo

}

public void Hello()

{

if (stevilo>3)

{

Console.WriteLine("Sedaj smo pa ţe več kot trije! Skupaj nas je ţe "+stevilo);

}

switch (stevilo)

{

case 1 : Console.WriteLine("V tej vrstici sem sam !!");

break;

case 2 : Console.WriteLine("Aha, še nekdo je tukaj, v tej vrstici sva dva!!");

break;

case 3 : Console.WriteLine("Opala, v tej vrstici smo ţe trije.");

break;

}

}

}

static void Main(string[] args)

{

Nekaj a=new Nekaj(); //konstruktor za a

a.Hello();

{

Nekaj b=new Nekaj(); //konstruktor za b

b.Hello();

{

Nekaj c=new Nekaj(); //konstruktor za c

c.Hello();

{

Nekaj d=new Nekaj(); //konstruktor za d

d.Hello();

}

}

}

} //Konec programa-> Garbage Collector poskrbi za uničenje vseh objektov

Polje razreda je lahko statično a se njegova vrednost ne more spremeniti: pri deklaraciji takega statičnega polja zapišemo besedico const (const = Constant – konstanta). Besedica static pri deklaraciji konstantnega polja NI potrebna, pa je polje še vedno statično. Konstantno polje je torej avtomatično statično in je tako dostopno preko imena razreda in ne preko imena objekta! Vrednost statičnega polja se NE da spremeniti. Tako je npr. deklarirana konstanta PI razreda Math. Primer: class Test

{

public static double kons1 = 3.14; //Statično polje

public const double kons = 3.1416; //Konstantno polje je avtomatično tudi statično

. . .

Page 139: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

139

}

//Zato, da pokličemo statično polje NE POTREBUJEMO OBJEKTA!!!

Console.WriteLine(Test.kons1);//klic statičnega polja

Console.WriteLine(Test.kons);//klic konstantnega statičnega polja

Test.kons1= Test.kons1 + 1; //OK -> Statično polje LAHKO spremenimo

Test.kons= Test.kons + 1; //NAPAKA -> Konstantnega polja ne moremo spremeniti

Dedovanje (Inheritance) – izpeljani razredi

Kaj je to dedovanje

Dedovanje (Inheritance) je ključni koncept objektno orientiranega programiranja. Smisel in pomen dedovanja je

v tem, da iz že zgrajenih razredov skušamo zgraditi bolj kompleksne, ki bodo znali narediti nekaj uporabnega.

Dedovanje je torej orodje, s katerim se izognemo ponavljanju pri definiranju različnih razredov, ki pa imajo več

ali manj značilnosti skupnih. Opredeljuje torej odnos med posameznimi razredi.

Vzemimo pojem sesalec iz biologije. Kot primer za sesalce vzemimo npr. konje in kite. Tako konji kot kiti

počnejo vse kar počnejo sesalci nasploh (dihajo zrak, skotijo žive mladiče, …), a prav tako pa imajo nekatere

svoje značilnosti (konji imajo npr. štiri noge, ki jih kiti nimajo, imajo kopita, …, obratno pa imajo npr. kiti

plavuti, ki pa jih konji nimajo, ..). V Microsoft C# bi lahko za ta primer modelirali dva razreda: prvega bi

poimenovali Sesalec, in drugega Konj, in obenem deklarirali, da je Konj podeduje (inherits) Sesalca. Na ta način

bi med sesalci in konjem vzpostavili povezavo v tem smislu, da so vsi konji sesalci (obratno pa seveda ne

velja!). Podobno lahko deklariramo razred z imenom Kit, ki je prav tako podedovan iz razreda Sesalec.

Lastnosti, kot so npr, kopita ali pa plavuti pa lahko dodatno postavimo v razred Konj oz. razred Kit.

Bazični razredi in izpeljani razredi Sintaksa, ki jo uporabimo za deklaracijo, s katero želimo povedati, da razred podeduje nek drug razred, je takale:

class IzpeljaniRazred : BazičniRazred

{

. . .

}

Izpeljani razred deduje od bazičnega razreda. Za razliko od C++, lahko razred v C# deduje največ en razred in ni

možno dedovanje dveh ali več razredov. Seveda pa je lahko razred, ki podeduje nek bazični razred, zopet

podedovan v še bolj kompleksen razred.

Primer:

Radi bi napisali razred, s katerim bi lahko predstavili točko v dvodimenzionalnem koordinatnem sistemu. Razred

poimenujmo Tocka:

class Tocka //bazični razred

{

public Tocka(int x, int y) //konstruktor

{

//telo konstruktorja

}

//telo razreda Tocka

}

Sedaj lahko definiramo razred za tridimenzionalno točko z imenom Tocka3D, s katerim bomo lahko delali objekte, ki bodo predstavljali točke v tridimenzionalnem koordinatnem sistemu in po potrebi dodamo še dodatne metode: class Tocka3D : Tocka //razred Tocka3D podeduje razred Tocka

{

//telo razreda Tocka3D – tukaj zapišemo še dodatne metode tega razreda!

Page 140: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

140

}

Klic konstruktorja bazičnega razreda Vsi razredi imajo vsaj en konstruktor (če ga ne napišemo sami, nam prevajalnik zgenerira privzeti konstruktor).

Izpeljani razred avtomatično vsebuje vsa polja bazičnega razreda, a ta polja je potrebno ob kreiranju novega

objekta inicializirati. Zaradi tega mora konstruktor v izpeljanem razredu poklicati konstruktor svojega bazičnega

razreda. V ta namen se uporablja rezervirana besedica base:

class Tocka3D : Tocka ////razred Tocka3D podeduje razred Tocka

{

public Toca3D(int z)

:base(x,y) //klic bazičnega konstruktorja Tocka(x,y)

{

//telo konstruktorja Tocka3D

}

//telo razreda Tocka3D

}

Če bazičnega konstruktorja v izpeljanem razredu ne kličemo eksplicitno (če vrstice :base(x,y) ne

napišemo!), bo prevajalnik avtomatično zgeneriral privzeti konstruktor. Ker pa vsi razredi nimajo privzetega

konstruktorja (v veliko primerih napišemo lastni konstruktor), moramo v konstruktorju znotraj izpeljanega

razreda obvezno najprej klicati bazični konstruktor (rezervirana besedica base). Če klic izpustimo (ali ga

pozabimo napisati) bo rezultat prevajanja compile-time error:

class Tocka3D : Tocka //razred Tocka3D podeduje razred Tocka

{

public Tocka3D(int z)

//NAPAKA - POZABILI smo klicati bazični konstruktor razreda Tocka

{

//telo konstruktorja Tocka3D

}

//telo razreda Tocka3D

}

Določanje oz. prirejanje razredov Poglejmo še, kako lahko kreiramo objekte iz izpeljanih razredov. Kot primer vzemimo zgornji razred Tocka in

iz njega izpeljani razred Tocka3D.

class Tocka //bazični razred

{

//telo razreda Tocka

}

class Tocka3D: Tocka //razred Tocka3D podeduje razred Tocka

{

//telo razreda Tocka3D

}

Iz razreda Tocka izpeljimo še dodatni razred Daljica

class Daljica:Tocka //razred Daljica podeduje razred Tocka

{

//telo razreda Daljica

}

Kreirajmo sedaj nekaj objektov:

Tocka A = new Tocka();

Tocka3D B = A; //NAPAKA - različni tipi

Tocka3D C = new Tocka3D ();

Tocka D = C; //OK!

Page 141: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

141

Nove metode Razredi lahko vsebujejo več ali manj metod in slej ko prej se lahko zgodi, da se pri dedovanju v izpeljanih

razredih ime metode ponovi – v izpeljanem razredu torej napišemo metodo, katere ime, število in tipi parametrov

se ujemajo z metodo bazičnega razreda. Pri prevajanju bomo zato o tem dobili ustrezno opozorilo - warning.

Metoda v izpeljanem razredu namreč v tem primeru prekrije metodo bazičnega razreda. Če npr. napišemo razred

Tocka in nato iz njega izpeljemo razred Tocka3D,

class Tocka //bazni razred

{

private int x, y; //polji razreda Tocka

public void Izpis() //metoda za izpis koordinat razreda Tocka

{

Console.WriteLine("Koordinate točke:\nx = " + x + "\ny = " + y);

}

}

class Tocka3D : Tocka //razred Tocka3D je izpeljan iz razreda Tocka

{

private int z; //dodatno polje razreda Tocka3D

//Metoda Izpis prepiše istoimensko metodo razreda Tocka

public void Izpis()

{

Console.Write("z = " + z + "\n");

}

}

nam bo prevajalnik zgeneriral opozorilo, s katerim nas obvesti, da metoda Tocka3D.Izpis prekrije metodo

Tocka.Izpis:

Program se bo sicer prevedel in tudi zagnal, a opozorilo moramo vzeti resno. Če namreč napišemo razred, ki bo

podedoval razred Tocka3D, bo uporabnik morda pričakoval, da se bo pri klicu metode Izpis pognala metoda

bazičnega razreda, a v našem primeru se bo zagnala metoda razreda Tocka3D. Problem seveda lahko rešimo

tako, da metodo Izpis v izpeljanem razredu preimenujemo (npr. Izpis1), še boljša rešitev pa je ta, da v

izpeljanem razredu eksplicitno povemo, da gre za NOVO metodo – to storimo z uporabo operatorja new.

class Tocka //bazni razred

{

private int x, y; //polji razreda Tocka

public void Izpis() //metoda za izpis koordinat razreda Tocka

{

Console.WriteLine("Koordinate točke:\nx = " + x + "\ny = " + y);

}

}

class Tocka3D : Tocka //razred Tocka3D je izpeljan iz razreda Tocka

{

private int z; //dodatno polje razreda Tocka3D

//Z operatorjem new napovemo, da ima razred Tocka3D SVOJO LASTNO metodo Izpis

new public void Izpis() {

Console.Write("z = " + z + "\n");

}

}

Vaja:

Page 142: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

142

/*Napiši razred krog z zasebnim poljem polmer in lastnostmi Premer, Obseg in Kvadratura. Iz

razreda nato izpelji razred Krogla, dodaj razredu lastno metodo Kvadratura in novo metodo

Volumen*/

class Krog //Bazični razred

{

private double polmer;//zasebno polje razreda Krog

public double Polmer //lastnost za dostop do polja polmer

{

get

{ if (polmer < 0)

return 0.00;

else

return polmer;

}

set { polmer = value;}

}

public double Premer //lastnost, ki vrne premer kroga

{

get { return Polmer * 2; }

}

public double Obseg //lastnost, ki vrne obseg kroga

{

get { return Premer * Math.PI; }

}

public double Kvadratura //lastnost, ki vrne ploščino kroga

{

get { return Math.PI*Math.Pow(Polmer,2); }

}

}

/*Kreirajmo razred Krogla ki naj podeduje razred Krog. Razred Krogla bo podedoval polje

polmer, podedoval bo lastnosti Premer in Obseg, imel bo SVOJO lastnost za izračun kvadrature,

poleg tega pa še novo lastnost za izračun prostornine krogle */

class Krogla : Krog

{

new public double Kvadratura //z operatorjem new smo označili, da ima razred krog SVOJO

//lastno lastnost kvadratuta

{

get { return 4 * Math.PI*Math.Pow(Polmer,2); }

}

public double Volumen //nova lastnost razreda Krogla

{

get { return 4 * Math.PI * Math.Pow(Polmer,2) / 3; }

}

}

//glavni program

static void Main()

{

Krog c = new Krog();//nov objekt razreda Krog

c.Polmer = 25.55;

Console.WriteLine("Karakteristike kroga");

Console.WriteLine("Polmer : {0}", c.Polmer);

Console.WriteLine("Premer : {0}", c.Premer);

Console.WriteLine("Obseg : {0,-10:F2}", c.Obseg);

Console.WriteLine("Ploščina: {0,-10:F2}", c.Kvadratura);

Krogla s = new Krogla();//nov objekt razreda Krogla

s.Polmer = 25.55;

Console.WriteLine("\nKarakteristike krogle");

Console.WriteLine("Polmer : {0}", s.Polmer);

Console.WriteLine("Premer : {0}", s.Premer);

Console.WriteLine("Obseg : {0,-10:F2}", s.Obseg);

Console.WriteLine("Površina : {0,-10:F2}", s.Kvadratura);

Console.WriteLine("Prostornina: {0,-10:F2}\n", s.Volumen);

}

Page 143: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

143

Virtualne metode Pogosto želimo metodo, ki smo je napisali v bazičnem razredu, v višjih (izpeljanih) razredih skriti in napisati

novo metodo, ki pa bo imela enako ime in enake parametre. Eden izmed načinov je uporaba operatorja new za

tako metodo, drug način pa je z uporabo rezervirane besede virtual. Metodo, za katero želimo že v bazičnem

razredu označiti, da jo bomo lahko v nadrejenih razredih nadomestili z novo metodo (jo prekriti), označimo kot

virtualno (virtual), npr.:

//virtualna metoda v bazičnem razredu – v izpeljanih razredih bo lahko prekrita(override)

public virtual void Koordinate()

{...}

V nadrejenem razredu moramo v takem primeru pri metodi z enakim imenom uporabiti rezervirano besedico

override, s katero povemo, da bo ta metoda prekrila/prepisala bazično metodo z enakim imenom in enakimi

parametri.

//izpeljani razred – besedica override pomeni, da smo s to metodo prekrili bazično metodo

public override void Koordinate()

{ ...}

Ločiti pa moramo razliko med tem, ali neka metoda prepiše bazično metodo (override) ali pa jo skrije.

Prepisovanje metode (overriding) je mehanizem, kako izvesti drugo, novo implementacijo iste metode –

virtualne in override metode so si v tem primeru sorodne, saj se pričakuje, da bodo opravljale enako nalogo, a

nad različnimi objekti (izpeljanimi iz bazičnih razredov, ali pa iz podedovanih razredov). Skrivanje metode

(hiding) pa pomeni, da želimo neko metodo nadomestiti z drugo – metode v tem primeru niso povezane in

lahko opravljajo povsem različne naloge.

Primer:

Naslednji primer prikazuje zapis virtualne metode Koordinate() v bazičnem razredu in override metode

Koordinate() v izpeljanem razredu.

class Tocka //bazični razred

{

private int x, y; //polji razreda Tocka

//metoda Koordinate je virtualna, kar pomeni, da jo lahko prepišemo (override)

public virtual void Koordinate() //metoda za izpis koordinat razreda Tocka

{

Console.WriteLine( "Koordinate točke:\nx = "+x + "\ny = " + y);

}

}

class Tocka3D : Tocka //razred Tocka3D je izpeljan iz razreda Tocka

{

private int z; //dodatno polje razreda Tocka3D

//Metoda Koordinate je označena kot override - prepiše istoimensko metodo razreda Tocka

public override void Koordinate()

{

base.Koordinate(); //klic bazične metode Koordinate razreda Tocka

Console.Write("z = "+z+"\n");

}

}

Prekrivne (Override) metode Kadar je bazičnem razredu neka metoda označena kot virtualna (virtual), jo torej lahko v nadrejenih razredih prekrijemo/povozimo (override). Pri deklaraciji takih metod (pravimo jim polimorfne metode) z uporabo rezerviranih besed virtual in override, pa se moramo držati nekaterih pomembnih pravil:

Metoda tipa virtual oz. override NE more biti zasebna (ne mre biti private);

Obe metodi, tako virtualna kot override morata biti identični: imeti morata enako ime, enako število in tip parametrov in enak tip vrednosti, ki jo vračata;

Page 144: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

144

Obe metodi morata imeti enak dostop. Če je npr. virtualna metoda označena kot javna (public), mora biti javna tudi metoda override;

Prepišemo (prekrijemo/povozimo) lahko le virtualno metodo. Če metoda ni označena kot virtualna in bomo v nadrejenem razredu skušali narediti override, bomo dobili obvestilo o napaki;

Če v nadrejenem razredu ne bomo uporabili besedice override, bazična metoda ne bo prekrita. To pa hkrati pomeni, da se bo tudi v izpeljanem razredu izvajala metoda bazičnega razreda in ne tista, ki smo napisali v izpeljanem razredu;

Če neko metodo označimo kot override, jo lahko v nadrejenih razredih ponovno prekrijemo z novo metodo.

Vaja:

Razred Tocka in razred Tocka3D, ki je izpeljan iz razreda Tocka sta implementirana v naslednjem primeru:

class Tocka //bazni razred

{

private int x, y; //polji razreda Tocka

public Tocka(int x,int y) //konstruktor

{

this.x = x;

this.y = y;

}

//metoda Koordinate je virtualna, kar pomeni, da jo lahko preobloţimo (naredimo override)

public virtual void Koordinate() //metoda za izpis koordinat razreda Tocka

{

Console.WriteLine( "Koordinate točke:\nx = "+x + "\ny = " + y);

}

}

class Tocka3D : Tocka //razred Tocka3D je izpeljan iz razreda Tocka

{

private int z; //dodatno polje razreda Tocka3D

public Tocka3D(int x,int y,int z) //konstruktor - parametra x in y potrebujemo za

: base(x,y) //dedovanje bazičnega konstruktorja razreda Tocka

{

this.z = z;

}

//Metoda Koordinate prepiše istoimensko metodo razreda Tocka

public override void Koordinate()

{

base.Koordinate(); //klic bazične metode Koordinate razreda Tocka

Console.Write("z = "+z+"\n");

}

}

static void Main(string[] args)

{

Tocka A = new Tocka(1,1); //Nov objekt razreda Tocka

A.Koordinate(); //klic metode Koordinate razreda Tocka

Tocka3D A3D = new Tocka3D(1, 2, 3); //Nov objekt razerda Tocka3D

A3D.Koordinate(); //klic preobloţene metode Koordinate razreda Tocka3D

}

Vaja:

/*Napišimo razred krog z zasebnim poljem polmer in virtualno metodo ploscina, nato pa še

razred kolobar. Razred kolobar naj deduje razred krog, dodano naj ima še eno zasebno polje

notranjiPolmer in svojo lastno(override) metodo polščina*/

class krog //bazni razred

{

private int polmer; //polje razreda krog

//metoda ploscina je virtualna, kar pomeni, da jo lahko preobloţimo (override)

public virtual double ploscina()

{

return Math.PI * polmer * polmer;

Page 145: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

145

}

public int Polmer //lastnost(property) razreda krog, za dostop in inic. polja polmer

{

get

{

return polmer;

}

set

{

polmer = value;

}

}

}

class kolobar : krog //razred kolobar podeduje razred krog

{

//ker kolobar deduje krog, ţe pozna polje polmer

private int notranjiPolmer; //dodatno polje razreda kolobar

//metoda ploscina prepiše istoimensko metodo bazičnega razreda krog

public override double ploscina()

{

return Math.PI * (Polmer * Polmer - notranjiPolmer*notranjiPolmer);

}

//ker kolobar deduje krog, ţe pozna njegovo lastnost Polmer

//dodatna lastnost (property) razreda kolobar, za dostop in inic. polja notranjiPolmer

public int NotranjiPolmer

{

get

{

return notranjiPolmer;

}

set

{

notranjiPolmer = value;

}

}

}

static void Main(string[] args)

{

Random naklj = new Random();

krog k=new krog(); //nov objekt razreda krog

k.Polmer = naklj.Next(1, 10); //polje polmer inicializiramo preko lastnosti Polmer

kolobar kol = new kolobar(); //nov objekt razreda kolobar

//tudi polje polmer objekta kol inicializiramo preko lastnosti Polmer

kol.Polmer = naklj.Next(1, 10);

//polje notranjiPolmer inicializiramo preko lastnosti notranjiPolmer

kol.NotranjiPolmer = naklj.Next(1, 10);

//izpis ploščine kroga na 4 decimalke

Console.WriteLine("Ploščina kroga: {0:F4}",k.ploscina());

//izpis ploščine kolobarja na 4 decimalke

Console.WriteLine("Ploščina kolobarja: {0:F4}", kol.ploscina());

}

Vaja:

//Deklarirajmo razred Oseba, nato pa razred Kmetovalec, ki naj podeduje razred Oseba. Razred

Oseba naj ima polje ime, razred Kmetovalec pa še dodatno polje velikostPosesti. Za oba razreda

napišimo tudi konstruktor in virtualno metodo Tostring za izpis podatkov o posameznem

objektu!*/

//glavni program

static void Main(string[] args)

{

ArrayList osebe = new ArrayList(); //zbirka oseb

Oseba mike = new Oseba("mike");

osebe.Add(mike);

Console.WriteLine("Osebe:");

Page 146: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

146

//izpišemo seznam vseh oseb

foreach (Oseba p in osebe)

Console.WriteLine(p.Tostring());

ArrayList kmetovalci = new ArrayList(); //zbirka kmetovalcev

Kmetovalec john = new Kmetovalec("john", 10);

Kmetovalec bob = new Kmetovalec("bob", 20);

kmetovalci.Add(john);

kmetovalci.Add(bob);

Console.WriteLine();

Console.WriteLine("Kmetovalci:");

//izpišemo seznam vseh kmetovalcev

foreach (Kmetovalec f in kmetovalci)

Console.WriteLine(f.Tostring());

ArrayList vsiSkupaj = new ArrayList(); //zbirka vseh skupaj

//napolnimo skupno zbirko vseh oseb

foreach (Oseba o in osebe)

vsiSkupaj.Add(o);

foreach (Kmetovalec f in kmetovalci)

vsiSkupaj.Add(f);

Console.WriteLine();

Console.WriteLine("Vsi skupaj:");

//izpišemo seznam vseh oseb

foreach (Oseba p in vsiSkupaj)

Console.WriteLine(p.Tostring());

}

public class Oseba //bazični razred

{

public string ime; //bazično polje

public Oseba(string ime) //bazični konstruktor

{

this.ime = ime;

}

public virtual string Tostring() //virtualna metoda bazičnega polja

{

return "Ime=" + ime;

}

}

public class Kmetovalec : Oseba //razred Kmetovalec deduje razred Oseba

{

int velikostPosesti; //dodatno polje razreda Kmetovalec

public Kmetovalec(string ime, int velikostPosesti) //konstruktor razreda Kmetovalec

: base(ime) //podeduje bazično polje ime

{

this.velikostPosesti = velikostPosesti;

}

public override string Tostring() //metoda Tostring prekrije bazično metodo Tostring

{

return base.Tostring() + "; Kvadratnih metorv = " +

velikostPosesti.Tostring();

}

}

Naloge:

Napiši razred Kocka tako, da bo izpeljan iz razreda Kvadrat. Razreda naj poleg svojih podatkov

vsebujeta še privzeti konstruktor, metode za postavitev vrednosti podatkov in metode za izračun površine, obsega in volumna.

Polimorfizem - mnogoličnost

Page 147: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

147

V izpeljanih razredih se srečamo še z enim temeljnim pojmom objektno orientiranega programiranja – to je pojem polimorfizem oz. mnogoličnost. Pojem polimorfizem označuje princip, da lahko različni objekti razumejo isto sporočilo in se nanj odzovejo vsak na svoj način. Pomeni tudi, da je ista operacija lahko implementirana na več različnih načinov oz. zanjo obstaja več metod. Dedovanje in polimorfizem sta lastnosti, ki predstavljata osnovo objektnega programiranja in omogočata hitrejši razvoj in lažje vzdrževanje programske kode. Kot primer za prikaz polimorfizma deklarirajmo razred OsnovniRazred, ki ima eno samo metodo z imenom

Slika. Ker želimo, da bo vsak objekt, ki bo izpeljan iz tega razreda (tudi tisti v podedovanih – izpeljanih

razredih) ohranil sebi lastno obnašanje ob klicu metode Slika, moramo uporabiti polimorfno redefinicijo.

Polimorfno redefinicijo omogočimo z že znano definicijo virtualne metode. V telesu te metode bomo za vajo in zaradi enostavnosti prikazali le neko sporočilno okno! public class OsnovniRazred //temeljni razred

{

public virtual void Slika() //polimorfna redefinicija – omogočimo jo z virtualno metodo {

Console.WriteLine("Osnovni objekt!");

}

}

Ker smo metodo Slika definirali kot virtualno, smo s tem napovedali, da bodo izpeljani objekti lahko

uporabljali svojo (prekrivno oz. override) metodo Slika, ki pa bo imela enako ime.

Razred OsnovniRazred bo naš temeljni razred, iz katerega bomo tvorili izpeljane razrede in v njih tvorili

nove objekte. Ker je metoda Slika virtualna to pomeni, da lahko v izpeljanih razredih to metodo prekrijemo

(override) z metodo, ki bo imela enako ime a drugačen pomen (drugačno vsebino). Napišimo sedaj še tri razrede, ki naj bodo izpeljani iz razreda OsnovniRazred in ki imajo svojo metodo

Slika. Pred tipom takih metod mora stati besedice override, ki označuje, da se bodo objekti izpeljani iz teh

razredov na to metodo odzivali vsak na svoj način. Osnovni pogoj pa je, da imajo take override metode enako raven zaščite (npr. vse so public) , enako ime in enake parametre kot jih ima osnovna virtualna metoda v bazičnem razredu.

public class Crta : OsnovniRazred //Crta je razred, ki podeduje razred OsnovniRazred

{

public override void Slika() //preobloţena metoda razreda Crta

{

Console.WriteLine("Črta."); //Telo override metode je seveda lahko drugačno!!!

}

}

public class Krog : OsnovniRazred //Tudi Krog je razred, ki podeduje razred OsnovniRazred

{

public override void Slika()

{

Console.WriteLine ("Krog."); //Telo override metode je seveda lahko drugačno!!!

}

}

public class Kvadrat: OsnovniRazred //Tudi Kvadrat je razred, ki podeduje razred OsnovniRazred

{

public override void Slika() //Telo override metode je seveda lahko drugačno!!!

{

Console.WriteLine ("Kvadrat.");

}

}

Poglejmo sedaj, kako bi te štiri razrede sedaj uporabili in na primeru izpeljanih objektov prikazali princip polimorfizma. V ta namen kreirajmo tabelo objektov. Ime tabele je dObj, tabela pa naj bo inicializirana tako,

da so v njej lahko štiri objekti tipa OsnovniRazred.

Page 148: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

148

OsnovniRazred[] dObj = new OsnovniRazred[4];//tabela objektov

Ker so razredi Crta, Krog in Kvadrat izpeljani iz bazičnega razreda OsnovniRazred, jih lahko

priredimo isti tabeli dObj. Če te zmožnosti ne bi bilo, bi morali za vsak nov objekt, izpeljan iz kateregakoli od

teh štirih razredov, kreirati svojo tabelo. Dedovanje pa nam omogoča, da se vsak od izpeljanih objektov obnaša tako kot njegov bazični razred. Naslednjo kodo lahko zapišemo npr. v dogodek Click nekega gumba, ali pa neko opcijo menija. dObj[0] = new Crta(); //konstruktor objekta dObj[0]

dObj[1] = new Krog(); //konstruktor objekta dObj[1]

dObj[2] = new Kvadrat(); //konstruktor objekta dObj[2]

dObj[3] = new OsnovniRazred();//konstruktor objekta dObj[3]

foreach (OsnovniRazred objektZaRisanje in dObj)

{

objektZaRisanje.Slika(); //klic metode Slika ustreznega objekta

}

Ko je tabela inicializirana, lahko npr. s foreach zanko pregledamo vsakega od objektov v tabeli. Zaradi načela polimorfizma se v zagnanem programu vsak objekt obnaša po svoje, pač odvisno od tega, iz katerega razreda je bil izpeljan. Ker smo v izpeljanih razredih prepisali virtualno metodo Slika , se ta metoda v izpeljanih objektih

izvaja različno, pač glede na njeno definicijo v izpeljanih razredih. Pri vsakem prehodu zanke bomo tako dobili drugačno sporočilno okno.

Uporaba označevalca protected Uporaba označevalcev private in public predstavlja obe skrajni možnosti dostopa do članov razreda (oz. objekta). Javna polja in metode so dostopne vsem, zasebna polja in metode pa so dostopne le znotraj razreda. Pogosto pa je koristno, da bazični razred dovoljuje izpeljanim razredom, da le-ti dostopajo do nekaterih bazičnih članov, obenem pa ne dovoljujejo dostopa razredom, ki niso del iste hierarhije. V takem primeru uporabimo za dostop označevalec protected. Nadrejeni razred torej lahko dostopa do člana bazičnega razreda, ki je označen kot protected, kar praktično pomeni, da bazični član, ki je označen kot protected, v izpeljanem razredu postane javen (public). Javen je tudi v vseh višjih razredih. V primeru, ko pa nek razred ni izpeljani razred, pa nima dostopa do članov razreda, ki so označeni kot protected – znotraj razreda, ki ni izpeljani razred, je torej član razreda, ki je označen kot protected enak zasebenemu članu (private).

Page 149: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

149

Prosojnice M. Lokar 2009

Objektno programiranje

Objekti • Objekt je skupek podatkov, s katerim želimo upravljati kot s celoto.

• Ima

– Podatke

Kakšno je stanje (state) objekta

kaj o objektu vemo/hranimo

– stalni podatki / spremenljivi podatki

– Metode

Kakšno je obnašanje (behaviour) objekta

Kaj objekt "zna"

Kakšne metode lahko izvajamo nad njem

• Vsak objekt pripada nekemu razredu. Če pripada objekt x razredu R, potem pravimo tudi da je x objekt tipa R.

Znani objekti • Nekaj primerov objektov iz standardnih knjižnic jezika C# :

– Objekt tipa StreamReader predstavlja vhodni kanal. Kadar želimo brati s

tipkovnice ali z datoteke, naredimo tak objekt in kličemo njegovo metodo ReadLine za branje ene vrstice.

– Objekt System.Console predstavlja standardni izhod. Kadar želimo kaj izpisati na zaslon, pokličemo metodo WriteLine na objektu System.Console.

– Objekt tipa Random predstavlja generator naključnih števil.

• Seveda pa so objekti uporabni predvsem zato, ker lahko programer definira nove razrede in objekte.

Objekti

• stanja:

Page 150: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

150

– Lastnosti, podatki, komponente

• “znanje” – Odzivanje na dogodke

• Združeno v celoto – Podatki in metode, ki opisujejo neko stvar/objekt

– Žoga:

Podatki: velikost, barva, položaj v prostoru, …

Metode: sprememba velikosti, sprememba položaja, …

– Ulomek:

Podatki: števec, imenovalec

Metode: spremeni števec, spremeni imenovalec, seštej, obratna vrednost, lepo izpiši, ...

Primeri objektov • Datum

– V objektih tipa datum bi npr. hranili datume

• Podatki (stanje objekta)

– Dan, mesec, leto

• Metode (obnašanje / znanje)

– Vrni mesec

– Vrni število dni med dvema datumoma

– Naslednji dan

– Prejšnji dan

– Lepo izpiši

– Je leto prestopno

– Dan v tednu na določen datum

– ...

Avto • Podatki

– tehnične značilnosti:

Page 151: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

151

najvecjaHitrost (v km/h),

velikostRezervoarja (v litrih)

povprečnaPoraba (v litrih/100 km).

– trenutno stanje avtomobila

Je avto vžgan (da/ne)

Koliko je na števcu km (npr. 234.142 km),

Trenutna hitrost (npr. 56.12 km/h)

Količina Goriva (npr. 14.325 litrov).

• "Obnašanje"

– Vžgi avto (spremeni stanje "vžganosti")

– Ugasni avto (spremeni stanje "vžganosti")

– "dej gas" (spremeni stanje hitrost)

– "bremzej"

– Natankaj gorivo

– Povej, za koliko km še zadošča gorivo

– ...

Primer problema • Napisati morate program, ki bo nadzoroval delovanje dvigal

• Objekt Dvigalo

• Podatki (stanje) objekta:

– V katerem nadstropju je,

– koliko oseb je v njemu

– kakšna je masa teh oseb.

• Delovanje dvigala ("znanje", "obnašanje")

– vstop oseb v dvigalo,

– izstop

– premik gor ali dol

Primer problema • Vodimo skladišče kontejnerjev

Page 152: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

152

• Radi bi napisali program, ki bi znal odgovoriti na določena vprašanja

– Izpis vsebine vseh polnih kontejnerjev

– Koliko je povsem praznih kontejnerjev

– Vsebina največjega kontejnerja

– …

Razred Kontejner • Tabela kontejnerjev

• Posamezni kontejner

– Nov razred

• Za vsak kontejner poznamo

– Mere

– Zapolnjenost (v %)

– Vsebino

• Kaj počnemo s posameznim kontejnerjem

– Napolni z določeno vsebino

– Poizvedi, koliko je zaseden

– Dodaj vsebino

– Izpiši vsebino

– Poizvedi po merah

Programiranje v C# • Sestavljanje razredov

– Opis lastnosti objektov

– Opis metod (“znanja” objektov)

Ustvarjanje objektov in njihova uporaba

"Ukazovanje" objektom, kaj naj počnejo

Page 153: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

153

Objekt za izpolnitev določene naloge potrebuje druge objekte in njihove metode

• Začetek

– Glavni razred (ki ima metodo Main)

– Izvajanje metode Main – ustvarjanje objektov, proženje dogodkov, odzivanje na dogodke, …

Objekti

• Objekt je kakršenkoli skupek podatkov, s katerimi želimo upravljati.

• Osnovni pristop objektnega programiranja

– objekt = podatki + metode za delo s podatki.

• Ko želimo kak podatek (objekt) obdelati, objektu (podatku) signaliziramo, kaj naj se zgodi.

– Pokličemo ustrezno metodo v objektu.

• Objekt je "črna škatla“, ki sprejema in pošilja sporočila. Jedro objekta sestavljajo njegove spremenljivke, okrog katerih se nahajajo njegove metode.

Princip črne škatle • Imamo objekt obj, ki hrani podatke o neki osebi.

• Zanima nas inteligenčni količnik te osebe.

– obj.KolikoJeIQ()

• Objekt se odzove z odgovorom

• Uporabnika ne zanima, kako je objekt prišel do odgovora, le kaj ga lahko vpraša in v kakšni obliki bo dobil odgovor!

• Ni važno, kako je objekt odgovoril – kako je prišel do IQ (je to podatek, ki je zapisan nekje v objektu, je to rezultat nekega preračunavanja …

Page 154: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

154

Prednosti "črne škatle" • Če kasneje spremenimo notranjost "črne škatle" (razreda)

• Spremembe v programih, ki razred uporabljajo, niso potrebne

– Seveda, če je način spraševanja in odgovarjanja ostal nespremenjen

– Če so metode ostale enake (imajo enaka imena, nabor parametrov in tip rezultata)

• Npr.:

– Šef ugotovi, da bi bilo smiselno, da v vaših programih pri datumu hranite še čas. Ampak stvari naj bi naredili tako, da obstoječi programi ne bi bili prizadeti (beri, da bi delali še naprej)

– Če je razred "črna" škatla, lahko naredimo spremembe tako, da ne bo "staro" prizadeto

• Pomislite samo na "običajno življenje"

– Verjetno je zgradba menjalnika danes bistveno drugačna, kot je bila pred 30 leti

– A kar se šoferja (starega ;-) ) tiče, se ni nič spremenilo – s to črno škatlo (menjalnikom) upravlja še vedno na enak način kot pred 30 leti

• Če se bodo v C# 2013 odločili, da razred Random spemenijo (spremenijo notranjost "črne škatle"), a bomo še vedno imeli metodo Next(a, b), se za programerja ne bo nič spremenilo, čeprav bo metoda Next morda "znotraj" delala drugače!

Od kje razredi? • Veliko vgrajenih (oziroma v standardnih knjižnicah) v C#

– .NET Framework

C# je .NET jezik – ima dostop do vseh razredov, definiranih v knjižnicah (zbirkah razredov) okolja .NET

Glej npr. http://msdn2.microsoft.com/sl-si/library/ms229335(en-us).aspx

– Razporejeni v imenske prostore

System, System.IO …

• Drugi viri

– Naši “stari” razredi

– Drugi programerji

• Potrebujemo dostop do ustrezne datoteke (dll, exe)

Page 155: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

155

Moj prvi razred Oglejmo si naslednji program:

using System;

public class MojR{

private string mojNiz;

public MojR(string nekNiz) {

mojNiz = nekNiz;

}

public void Izpisi() {

Console.WriteLine(mojNiz);

}

}

public class Pozdrav {

public static void Main(string[] arg) {

MojR prvi;

prvi = new MojR("Pozdravljen, moj prvi objekt v C#!");

prvi.Izpisi();

}

}

Objekt in ime spremenljivke • NekiObjekt a;

– a je naslov, kjer bo objekt (referenca na objekt)

• new NekiObjekt();

– V pomnilniku se je naredil objekt tipa NekiObjekt() / po pravilih, ki so določena

z opisom razreda NekiObjekt

• a = new NekiObjekt();

– V pomnilniku se je naredil objekt tipa NekiObjekt() in a kaže na ta novo

ustvarjeni objekt

Page 156: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

156

Ustvarjanje objektov • Razred je šablona, ki definira spremenljivke in metode skupne vsem objektom iste vrste.

• class

– Načrt objekta, šablona

• Primerek razreda (instanca) – konkretni objekt

– Ustvarimo ga z new

– Brez tega objekta NE MOREMO uporabiti

– NekiObjekt a;

Objekt NE obstaja

a je IME objekta

natančneje

– a je ime spremenljivke, kjer hranimo NASLOV objekta vrste NekiObjekt

C#in objekti • Razred (class) je opis vrste objekta (načrt, kako naj bo objekt videti) – opis ideje objekta

• Primerek razreda (instanca) – konkretni objekt

Ustvarjanje objektov – Ulomek ime = new Ulomek(3, 4);

– Rečemo:

V objektu ime je shranjen ulomek ݯ

– Točneje

V spremenljivki ime je naslov nekega objekta tipa Ulomek, ki predstavlja ulomek ݯ

Spremenljivka ime kaže na objekt tipa Ulomek, ki …

– Prvi ukaz je dejansko spoj dveh stavkov:

deklaracija

prireditev

– Ulomek ime;

– ime = new Ulomek(3, 4);

Page 157: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

157

Ustvarjanje objektov – Ulomek ime;

– ime = new Ulomek(3, 4);

Še en zgled • Poglejmo si še en zgled. Denimo, da bi radi napisali program, ki bo prebral nek

ulomek in mu prištel 1/2.

• "klasično" se bomo tega lotili takole

Page 158: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

158

Povečaj ulomek "klasično" using System;

namespace UlomkiKlasika {

class Program

{

public static void Main(string[] args)

{

// vnos podatkov

Console.Write("Števec ulomka: ");

string beri = Console.ReadLine();

int stevec = int.Parse(beri);

Console.Write("Imenovalec ulomka: ");

beri = Console.ReadLine();

int imenovalec = int.Parse(beri);

// "delo"

stevec = stevec + 1;

imenovalec = imenovalec + 2;

// izpis

Console.WriteLine("Nov ulomek je: " + stevec + " / " + imenovalec);

Console.Write("Press any key to continue . . . ");

Console.ReadKey(true);

}

}

}

Povečaj ulomek - objektno • Razred Ulomek

• Hrani podatke o svojem števcu in imenovalcu

• "se zna" povečati za drug ulomek

– Vsebuje metodo, ki ta ulomek poveča za drug ulomek

Page 159: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

159

"Objektni" program

Ulomki - objektno using System;

namespace UlomkiObjektno

{

class Ulomek {

public int stevec;

public int imenovalec;

public Ulomek(int st, int im) {

this.stevec = st;

this.imenovalec = im;

}

public void Pristej(Ulomek a) {

this.stevec = this.stevec + a.stevec;

this.imenovalec = this.imenovalec + a.imenovalec;

}

}

class Program

{

public static void Main(string[] args)

{

// vnos podatkov

Ulomki - objektno Console.Write("Števec ulomka: ");

string beri = Console.ReadLine();

int stevec = int.Parse(beri);

Console.Write("Imenovalec ulomka: ");

beri = Console.ReadLine();

int imenovalec = int.Parse(beri);

Page 160: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

160

Ulomek moj = new Ulomek(stevec, imenovalec);

// "delo"

Ulomek polovica = new Ulomek(1, 2);

moj.Pristej(polovica);

// izpis

Console.WriteLine("Nov ulomek je: " + moj.stevec + " / " +

moj.imenovalec);

Primerjava • "klasično"

// "delo"

stevec = stevec + 1;

imenovalec = imenovalec + 2;

• objektno

Ulomek polovica = new Ulomek(1, 2);

moj.Pristej(polovica);

Ulomek • Ampak – ulomke smo seštevali narobe, po "Janezkovo"!

– Saj veste ... Inšpektor reče ... Saj se ulomki ne seštevajo tako, da se posebej seštejeta števec in imenovalec ... Učitelj Janezek pa ... Saj vem, ampak tako si lažje zapomnijo ...

• Napisali pa smo že nekaj 10 programov z "janezkovim seštevanjem"

Page 161: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

161

– Klasično: preko kode vseh 10 programov in "iščemo", kje smo pravzaprav seštevali

– Objektno: le spremenimo metodo Pristej v razredu Ulomek, nato pa

Nič, to je vse ...

Objektno programiranje • Problem

– Z analizo ugotovimo, kakšne objekte potrebujemo za reševanje

– Pregledamo, ali že imamo na voljo ustrezne razrede

Standardna knjižnica

Druge knjižnice

Naši stari razredi

– Sestavimo ustrezne manjkajoče razrede

– Sestavimo “glavni” program, kjer s pomočjo objektov rešimo problem

Objekti, razredi, ... Od kje in zakaj

Združevanje podatkov • Denimo, da pišemo program, ki bo pomagal upravljati farmo

zajcev

• Za vsakega zajca poznamo:

– serijsko številko

– spol

– težo

• Kako doseči, da se podatki “držijo” skupaj

– Tabela ni ustrezna

Tabela je zbirka istovrstnih podatkov

– Sestavimo razred Zajec

– Opis podatkov, ki sestavljajo poljubnega zajca

Page 162: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

162

Moj prvi razred • Razredi za uporabo znotraj nekega programa

– "lokalni" razredi

– Kot prej (Console Application ...)

namespace PrviOOP {

public class MojR {

private string mojNiz;

public MojR(string nekNiz) {

mojNiz = nekNiz;

}

public void Izpisi(){

Console.WriteLine(mojNiz);

}

}

public class Pozdrav {

public static void Main(string[] arg) {

MojR prvi;

prvi = new MojR("Pozdravljen, moj prvi objekt v C#!");

prvi.Izpisi();

}

}

}

Knjižnica razredov • Knjižnice razredov

Page 163: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

163

Komentarji • // - vrstični komentar

• /* ... */ : splošen komentar

• /// dokumentacijski komentar

– Glej npr:

http://www.softsteel.co.uk/tutorials/cSharp/lesson19.html

http://www.winnershtriangle.com/w/Articles.XMLCommentsInCSharp.asp

http://www.csharphelp.com/archives3/archive598.html

Razred Zajec public class Zajec {

public string serijska;

public bool spol;

public double masa;

}

• S tem imamo napisan opis, kako je določen poljuben zajec

– Načrt, kakšni so zajci

– Ni to konkreten zajec

• Ni metode Main, dejansko se NIČ ne izvede, ...

• Ni namenjeno poganjanju kot program

• Hisa.cs

– Načrt, kako je videti hiša

– Kot ga je pripravil projektant

• Prevedemo • Hisa.dll (naš zgled: MojiRazredi.dll)

– Še vedno načrt, kakšna naj bo hiša

– V obliki, kot ga lahko uporabijo tisti, ki bodo po tem načrtu izdelali konkretno hišo

Page 164: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

164

Uporaba razreda Zajec • Program, kjer delamo z zajci:

– Potrebuje datoteko MojiRazredi.dll (ker vsebuje definicijo razreda Zajec)

– Napovemo uporabo te datoteke

Project / Add reference

Uporaba razreda Zajec • Program, kjer delamo z zajci:

– Potrebuje datoteko ClassLibrary1.dll (ker vsebuje definicijo razreda Zajec)

– Napovemo uporabo te datoteke

Okolje: Project / Add reference

Uporaba razreda Zajec • Program, kjer delamo z zajci:

• using MojiRazredi; oziroma

• using ClassLibrary1; ...

• Če v programu potrebujemo konkretnega zajca, ga napovemo

– Zajec rjavko

• “ustvarimo” z new – new Zajec()

– Ustvaril se je konkreten zajec po navodilih za razred Zajec (ta zajec ima torej tri podatke / lastnosti / komponente)

– Metoda je vrnila naslov, kje ta konkretni zajec je

Page 165: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

165

– Zajec rjavko = new Zajec();

V spremenljivki rjavko je naslov, kje je novo ustvarjeni zajec (objekt)

Dostop do podatkov v objektu • rjavko.spol = true;

• rjavko.serijska = “BRGH_17_A”;

• rjavko.masa = 3.2;

Razred Zajec

Še končni pogled na naslove • Potem bomo malček "popustili" ;-)

• Zajec[] tabZ;

– Ustvarili smo spremenljivko tabZ. V spremenljivki tabZ lahko shranimo naslov tabele, v katero bomo

lahko shranjevali naslove objektov tipa Zajec. Trenutno v tej spremenljivki ni nobenega naslova.

• tabZ = new Zajec[200];

– Operator new je nekje ustvaril tabelo, v kateri je prostor za 200 naslovov objektov, v katerih lahko shranimo podatke o posameznem zajcu. Naslov tega prostora za tabelo smo shranili v spremenljivko tabZ.

– V tem trenutku ne obstaja še noben objekt tipa Zajec.

• tabZ[0] = new Zajec();

– Operator new je nekje ustvaril prostor za objekt tipa Zajec. Naslov tega objekta se je shranil v 0-to celico tabele, katere naslov je shranjen v spremenljivki tabZ.

• Seveda pa bomo govorili ... V tabeli tabZ je na mestu 0 zajec ...

Page 166: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

166

Razred – shramba podatkov • public class Ulomek {

public int stevec;

public int imenovalec;

}

• Prevedemo in dobimo Ulomek.class

• Kaj sedaj?

– Uporabljamo v drugih programih (razredih)

– Ulomek x = new Ulomek();

• Kako “napolniti” stevec in imenovalec?

– x.stevec : spremenljivka tipa int!

– x.imenovalec : enako

– x.stevec = 1;

– x.imenovalec = x.stevec + 1;

Povzetek • Definicija razreda

– Običajno v knjižnici!

Page 167: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

167

public class ImeRazreda {

public podatkovni tip element1;

public podatkovni tip element2;

public podatkovni tip elementn;

}

Povzetek • Uporaba razreda

– Če potrebujemo primerek razreda

– new ImeRazreda()

– Ustvari prostor in pove, kje ta prostor je

Naslov prostora shranimo v neko spremenljivko (tipa ImeRazreda), denimo mojaSpTipaImeRazreda

– Dostop do prostorov za hranjenje

Operator .

imeObjekta.elementi

– imeObjekta.imeKomponente

» mojaSpTipaImeRazreda.starost

Konstruktorji • Ob tvorbi objekta bi radi nastavili začetno stanje

spremenljivk – in morda opravili še kaj – o tem kasneje

• Konstruktor – metoda, ki jo pokličemo ob tvorbi objekta z new

• Brez tipa rezultata!

Page 168: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

168

• Ime kot je ime razreda

• Kličemo jo skupaj z new – Klic: new Zajec();

Razred Zajec public class Zajec {

public String serijska;

public boolean spol;

public double masa;

// konstruktor

public Zajec() {

this.spol = true; // vsem zajcem na začetku določimo m. spol

this.masa = 1.0; // in tehtajo 1kg

this.serijska = “NEDOLOČENO”;

}

}

• Zajca “ustvarimo” z new – new Zajec()

– Ustvaril se je konkreten zajec po navodilih iz konstruktorja Zajec() (ta zajec ima torej tri podatke z vrednostmi, kot

je predpisano v konstruktorju)

– Kaj je this?

this • this

• Pomeni objekt, ki ga "obdelujemo"

• V konstruktorju – objekt, ki ga ustvarjamo

Page 169: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

169

• this.spol – Lastnost/komponenta spol objekta, ki se ustvarja

• Zajec rjavko = new Zajec();

• Zajec belko= new Zajec();

• Pri prvem klicu se je v konstruktorju this nanašal na rjavko, pri drugem na belko.

this v ostalih metodah • Kasneje – pisali bomo metode, ki se bodo uporabljale nad

razredi – Random ng = new Random();

– Random g2 = new Random();

– Console.WriteLine(ng.Next(1,10));

– Kako so napisali kodo metode, da se je vedelo, da pri metodi Next mislimo na uporabo generatorja ng in ne na g2?

• Kako se v metodah razreda sklicati na ta objekt (objekt, nad katerim je izvajana metoda)?

– Razred MojRazred in v njem komponenta starost. Napišimo metodo MetodaNeka(), ki izpiše starost objekta, nad katerim izvajamo metodo.

– Klici bodo npr.: objA.MetodaNeka(), objC.MetodaNeka()

– Kako v postopku za MetodaNeka povedati, da gre

Prvič za objekt z imenom objA

drugič za objekt z imenom objC

Page 170: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

170

this v ostalih metodah • Kako povedati, da naj se ob klicu objA.MetodaNeka()

uporabi starost objekta objA, ob klicu objC.MetodaNeka()

pa starost objekta objC?

– Console.WriteLine("Starost je: " +

?????.starost);

• Ob prvem klicu je ???? objA, ob drugem pa objC. To

"zamenjavo" dosežemo z this. Napišemo

– Console.WriteLine("Starost je: " +

this.starost);

• Ob prvem klicu this pomeni objA, ob drugem pa objC.

Konstruktorji

• Če konstruktorja ne napišemo (kot ga nismo prej), ga “naredi” prevajalnik sam (a metoda ne naredi nič) – public Zajec() {

}

• Lahko imamo več konstruktorjev

• Konstruktorjev ne moremo klicati posebej (kot ostale metode) – Le ko tvorimo objekt

– new

– Za vzpostavitev začetnega stanja

• Enako ime kot razred

• Nimajo tipa rezultata (tudi void ne!)

• Ni stavka return

Page 171: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

171

Več konstruktorjev • Uporabniki bi poleg privzetega zajca, radi še možnost, da bi

takoj, ko zajca naredijo, temu določili še serijsko številko. Radi bi torej konstruktor

– public Zajec(string serijskaSt)

• Poleg tega pa včasih na farmo dobijo tudi pošiljko samic. Torej potrebujejo še konstruktor public Zajec(bool spol)

• Včasih pa so zajci "nestandardni" public Zajec(string serijskaSt, bool spol, double teza)

• Potrebujemo več načinov nastavljanja začetnega stanja objekta

• Več konstruktorjev:

– Več metod z enakim imenom

– Je to možno?

Preobteževanje • Več metod z enakim imenom

– Je to možno?

• Preobteževanje

– Overloading

– Velja tudi splošno, za vse metode

• Metode se morajo razlikovati ne v imenu, ampak podpisu

• Podpis metode

– Podpis: ime + število in tipi parametrov!

public static int NekaMetoda(double x)

Podpis (poenostavljeno): NekaMetoda_double

– Tip rezultata (return tip) NI del podpisa!

– public static int NekaMetoda(double x)

Page 172: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

172

Podpisi metod • Podpis metode:

– public static int NekaMetoda(double x)

– Podpis: NekaMetoda_double

– “interno” ime

– public Zajec(string serijskaStev,

bool spol, double masa)

Podpis: Zajec_String_bool_double

• Kaj je lahko sočasno: – public static int NekaMetoda()

– public static int NekaMetoda(double y)

– public static int NekaMetoda(double x)

– public static double NekaMetoda(double x)

– public static int NekaDrugaMetoda(double y)

Konstruktorji razreda Zajec public Zajec() {

this.spol = true; // vsem zajcem na začetku določimo m. spol

this.masa = 1.0; // in tehtajo 1kg

this.serijska = “NEDOLOČENO”;

}

public Zajec(string serijskaStev):this()

{

this.serijska = serijskaStev;

}

• : this() – klic konstruktorja Zajec()

• Izvede se pred vsemi ukazi v konstruktorju

Page 173: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

173

Sklicevanje na konstruktorje public Zajec(string ser, bool sp,

double t) : this(ser)

{

this.spol = sp;

this.masa = t;

}

this • this(

– Za klic konstruktorja pred začetkom drugega konstruktorja

this()

this(<parametri>)

– Uporabimo lahko le kot PRVI stavek v konstruktorju

• this.

– Za dostop do lastnosti

– this.serijska

– Če ni možnosti zamenjave, lahko izpustimo

– serijska

– this.spol = spol;

– Obstajala je že DRUGO ime spol

Ločiti med spremenljivko spol, ki je parameter in lastnostjo objekta z

imenom spol

"Prednost" ima bolj "lokalna" stvar – torej parameter

spol = spol;

Zgled – člani športnega kluba • Denimo, da bi radi napisali program, ki vodi

evidenco o članih športnega kluba. Podatki o članu

Page 174: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

174

obsegajo ime, priimek, letnico vpisa v klub in vpisno številke (seveda je to poenostavljen primer). Torej objekt, ki predstavlja člana kluba, vsebuje štiri podatke:

Page 175: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

175

public class Clan {

public string ime;

public string priimek;

public int letoVpisa;

public string vpisnaStevilka;

}

Klub - uporaba using MojaKnjiznica;

public class TestKlub {

public static void Main(string[] args) {

Clan a = new Clan();

a.ime = "Janez";

a.priimek = "Starina";

a.letoVpisa = 2000;

a.vpisnaStevilka = "2304";

Clan b = new Clan();

b.ime = "Mojca";

b.priimek = "Mavko";

b.letoVpisa = 2001;

b.vpisnaStevilka = "4377";

Clan c = b;

c.ime = "Andreja";

Console.WriteLine("Clan a:\n" + a.ime + " " + a.priimek +

" " + a.letoVpisa + " (" + a.vpisnaStevilka + ")\n");

Page 176: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

176

Console.WriteLine("Clan b:\n" + b.ime + " " + b.priimek +

" " + b.letoVpisa + " (" + b.vpisnaStevilka + ")\n");

Console.WriteLine("Clan c:\n" + c.ime + " " + c.priimek +

" " + c.letoVpisa + " (" + c.vpisnaStevilka + ")\n");

Console.ReadLine();

}

}

Zakaj public class TestKlub {

public static void Main(string[] args) {

Clan a;

a = new Clan();

a.ime = "Janez";

a.priimek = "Starina";

a.letoVpisa = 2000;

a.vpisnaStevilka = "2304";

Clan b = new Clan();

b.ime = "Mojca";

b.priimek = "Mavko";

b.letoVpisa = 2001;

b.vpisnaStevilka = "4377";

Clan c;

c = b;

c.ime = "Andreja";

Console.WriteLine("Clan a:\n" + a.ime + " " + a.priimek +

" " + a.letoVpisa + " (" + a.vpisnaStevilka + ")\n");

Console.WriteLine("Clan b:\n" + b.ime + " " + b.priimek +

Page 177: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

177

" " + b.letoVpisa + " (" + b.vpisnaStevilka + ")\n");

Console.WriteLine("Clan c:\n" + c.ime + " " + c.priimek +

" " + c.letoVpisa + " (" + c.vpisnaStevilka + ")\n");

Console.ReadLine();

}

}

Zgled – športni klub, nadaljevanje • Spremenimo sedaj naš razred Clan tako, da bomo

uporabili konstruktor

Page 178: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

178

public class Clan {

public string ime;

public string priimek;

public int letoVpisa;

public string vpisnaStevilka;

public Clan(string i, string p, int l, string

v) {

this.ime = i;

this.priimek = p;

this.letoVpisa = l;

this.vpisnaStevilka = v;

}

}

Šprtni klub - test

• Bo naš testni program OK?

• Poženimo

• Napake!

– Kako, rekli smo, da spreminjanje razreda ne vpliva na uporabniške programe

– Spremenili smo način uporabe

– V testnem programu: Clan()

Page 179: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

179

Tega sedaj ni – C# ga naredi sam le, če nismo napisali nobenega konstruktorja

Popravljeni zgled public class Clan {

public string ime;

public string priimek;

public int letoVpisa;

public string vpisnaStevilka;

public Clan() {

this.ime = "Ne vem";

this.priimek = "Ne vem";

this.letoVpisa = 0;

this.vpisnaStevilka = "Ne vem";

}

public Clan(string i, string p, int l, string v) {

this.ime = i;

this.priimek = p;

this.letoVpisa = l;

this.vpisnaStevilka = v;

}

}

Poskus testa

• Ni težav!

Page 180: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

180

• Zakaj ves ta napor, če pa je na koncu le isto ...

• Preglednost! – In priprava za naprej

Page 181: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

181

Primerjava: brez / s konstruktiorjem public class TestKlub {

public static void Main(string[] args) {

Clan a = new Clan();

a.ime = "Janez";

a.priimek = "Starina";

a.letoVpisa = 2000;

a.vpisnaStevilka = "2304";

Clan b = new Clan();

b.ime = "Mojca";

b.priimek = "Mavko";

b.letoVpisa = 2001;

b.vpisnaStevilka = "4377";

Clan c = b;

c.ime = "Andreja";

...

public class TestClan{

public static void Main(string[] args) {

Clan a = new Clan("Janez",

"Starina", 2000, "2304");

Clan b = new Clan("Mojca",

"Mavko", 2001, "4377");

Clan c = b;

Page 182: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

182

c.ime = "Andreja";

...

Objektne metode

• V definicijo razreda običajno spadajo tudi metode

• Klic objektnih metod: – imeObjekta.imeMetode(parametri)

– System.Console.WriteLine("To naj se izpiše");

– besedilo.Equals(primerjava)

• Metoda v razredu Clan

Page 183: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

183

public string Inicialke() { return this.ime[0] + "." + this.priimek[0]

+ ".";

}

Uporaba metode using MojiRazredi; // knjižnica z razredom Clan

public class TestClan{

public static void Main(string[] args) {

Clan a = new Clan("Janez", "Starina", 2000, "2304");

String inicialkeClanaA = a.Inicialke();

Console.Write("Clan a:\n" + a.ime + " " + a.priimek +

" " + a.letoVpisa + " (" + a.vpisnaStevilka +

") ");

Console.WriteLine("ima inicialke: " + inicialkeClanaA);

}

}

Sprememba metode public class Clan {

public string ime;

Page 184: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

184

public string priimek;

public int letoVpisa;

public string vpisnaStevilka;

public Clan() {

ime = "Ne vem";

priimek = "Ne vem";

letoVpisa = 0;

vpisnaStevilka = "Ne vem";

}

public Clan(string i, string p, int l, string v) : this() {

ime = i;

priimek = p;

letoVpisa = l;

vpisnaStevilka = v;

}

public string Inicialke() {

return this.ime[0] + " " + this.priimek[0];

}

}

Metoda, ki vrača objekt iz Razreda

• Vemo, da z – a = b;

• kjer sta a in b obe spremenljivki tipa Clan, v a ne shranimo kopije objekta b, ampak sedaj a in b označujeta isti objekt.

• Metoda, ki naredi kopijo objekta.

Page 185: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

185

a = b.Kopija();

– V a je nov objekt, ki pa ima iste podatke kot b.

Page 186: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

186

Kopija

public Clan Kopija() {

Clan nov = new Clan();

nov.ime = this.ime;

nov.priimek = this.priimek;

nov.letoVpisa = this.letoVpisa;

nov.vpisnaStevilka =

this.vpisnaStevilka;

return nov;

}

Še metoda za izpis public void Izpis() {

Console.WriteLine("Clan:\n" + this.ime + " " +

this.priimek + " " + this.letoVpisa +

" (" + this.vpisnaStevilka + ")\n");

}

ali pa še

Page 187: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

187

public string Opis() {

return this.ime + " " + this.priimek + " " +

this.letoVpisa +

" (" + this.vpisnaStevilka + ");

}

Uporaba public class TestKlub {

public static void Main(string[] args) {

Clan a = new Clan("Janez", "Starina", 2000, "2304");

Clan b = new Clan("Mojca", "Mavko", 2001, "4377");

Clan c = b;

c.ime = "Andreja";

Clan d = b.Kopija();

d.ime = "Tadeja";

Console.WriteLine("Clan a"); a.Izpis();

Console.WriteLine("Clan b:\n" + b.Opis());

Console.WriteLine("Clan c:\n" + c);

Console.WriteLine("Clan d"); d.Izpis();

Console.ReadLine();

}

}

Page 188: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

188

Razred Datum • Denimo, da v naših programih pogosto delamo z datumi.

• Zato bomo sestavili ustrezni razred

• Načrt razreda: – Podatki

dan (število)

mesec (izbira: število ali pa niz)

Leto (število)

– Metode

Konstruktorji

Izpiši

Povečaj za 1 dan

Je datum smiselen

Je leto prestopno

Nov datum za toliko in toliko dni pred/za danim datumom

Dan v tednu

...

Page 189: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

189

Datum – podatki in konstruktor public class Datum {

public int dan;

public string mesec;

public int leto;

public Datum() {

dan = 1;

mesec = "januar"

leto = 2000;

} // privzeti datum je torej 1.1.2000

Dodatni konstruktorji public Datum(int leto) : this() {

this.leto = leto; // this je nujen

} // datum je torej 1.1.leto

public Datum(int d, string m, int l) : this(l)

{ // leto smo že nastavili

this.mesec = m; // this ni nujen

this.dan = d;

} // datum je torej d.m.l (na primer 12.3.2006 ali

Page 190: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

190

// 12. marec 2006)

Prestopno • Zanima nas, ali je leto prestopno

Page 191: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

191

public bool JePrestopno() {

int leto = this.leto;

if (leto % 4 != 0) return false;

if (leto % 400 == 0) return true;

if (leto % 100 == 0) return false;

return true;

}

Dodaj en dan public void PovecajZaEnDan() {

dan = dan + 1;

if (dan < 29) return;

if (dan == 29 && mesec != "februar") return;

if (dan == 29 && mesec == "februar" && this.JePrestopno())

return;

// lahko nehamo, mesec in leto sta ok

string[] meseciPo30 = {"april","junij","september",

"november"};

if (dan == 31) {

if (meseciPo30.IndexOf(mesec) > 0){

mesec = mesec + 1;

if (mesec == 13) {

mesec = 1;

leto++;

}

Page 192: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

192

return;

}

// če je 32 dni, je zagotovo

Uporaba razreda • Ugotovi, če je letošnje leto prestopno!

Page 193: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

193

using MojiRazredi;

public class JeLetosPrestopnoLeto {

Datum danes = new Datum(5, 3, 2009);

if (danes.jePrestopno()) {

Console.WriteLine("Je prestopno

leto");

} else {

Console.WriteLine("Ni prestopno

leto");

}

}

Sprememba razreda

• Imamo if (enClan.letoVpisa > drugClan.letoVpisa) { …

• Spremenimo razred Clan, tako, da vodimo datum vpisa

Page 194: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

194

public class Clan {

public string ime;

public string priimek;

public Datum datumVpisa;

public string vpisnaStevilka;

public Clan() {

ime = "Ne vem";

priimek = "Ne vem";

datumVpisa = new Datum();

vpisnaStevilka = "Ne vem";

}

Sprememba razreda public Clan(string i, string p, Datum d, string v) : this() {

ime = i;

priimek = p;

datumVpisa = d;

vpisnaStevilka = v;

}

public string Inicialke() {

return this.ime[0] + "." + this.priimek[0] + ".";

}

public Clan Kopija() {

Clan nov = new Clan();

nov.ime = this.ime;

nov.priimek = this.priimek;

nov.datumVpisa = this.datumVpisa.Kopija();

nov.vpisnaStevilka = this.vpisnaStevilka;

Page 195: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

195

return nov;

}

public void Izpis() {

Console.WriteLine("Clan:\n" + this.ime + " " + this.priimek + " " +

this.datumVpisa.OpisDat() + " (" +

this.vpisnaStevilka + ")\n");

}

Sprememba razreda public string Opis() {

return this.ime + " " + this.priimek + " " +

this.datumVpisa.OpisDat() + " (" + this.vpisnaStevilka + ");

}

public string ToString() {

return this.Opis();

}

public bool SpremeniLetoVpisa(int l) {

if ((2000 <= leto) && (leto <= 2020)) {

this.datumVpisa.leto = l;

return true; //leto je smiselno, popravimo stanje objekta in

vrnemo true

}

return false; // leto ni smsielno, ne spremnimo inč in vrnemo

false

}

}

Page 196: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

196

Sprememba razreda public Clan(string i, string p, Datum d, string v) : this() {

ime = i;

priimek = p;

datumVpisa = d;

vpisnaStevilka = v;

}

public string Inicialke() {

return this.ime[0] + "." + this.priimek[0] + ".";

}

public Clan Kopija() {

Clan nov = new Clan();

nov.ime = this.ime;

nov.priimek = this.priimek;

nov.datumVpisa = this.datumVpisa.Kopija();

nov.vpisnaStevilkaevilka = this.vpisnaStevilka;

return nov;

}

Sprememba razreda public void Izpis() {

Console.WriteLine("Clan:\n" + this.ime + " " +

this.priimek + " " + this.datumVpisa.OpisDat() + " (" +

this.vpisnaStevilka + ")\n");

}

public string Opis() {

Page 197: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

197

return this.ime + " " + this.priimek + " " +

this.datumVpisa.OpisDat() + " (" + this.vpisnaStevilka + ");

}

public bool SpremeniLetoVpisa(int l) {

if ((2000 <= leto) && (leto <= 2020)) {

this.datumVpisa.leto = l;

return true; //leto je smiselno, popravimo stanje objekta in vrnemo true

}

return false; // leto ni smiselno, ne spremenimo nič in vrnemo false

}

}

Način programiranja

• Seveda zaradi spremembe

if (enClan.letoVpisa > drugClan.letoVpisa) { …

• ne deluje več!

• Kako popraviti?

Page 198: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

198

Že v prvotnem razredu public class Clan {

public string ime;

public string priimek;

public int letoVpisa;

public string vpisnaStevilka;

public bool SpremeniLetoVpisa(int leto) {

if ((2000 <= leto) && (leto <= 2020)) {

this.letoVpisa = leto;

return true;

}

return false; // leto ni smiselno, ne spremnimo nič in vrnemo false

}

public int VrniLetoVpisa() {

return this.letoVpisa;

}

}

Ob spremembi razreda Clan • Le metodo

Page 199: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

199

public int VrniLetoVpisa() {

return this.letoVpisa;

}

• zamenjamo z

public int VrniLetoVpisa() {

return this.datumVpisa.leto;

}

Dostop do stanj objekta • Možnost, da neposredno dostopamo do stanj/lastnosti objekta

NI NAJBOLJŠI!

– Ne le, da ni najboljši, je == CENZURA ==

• Nobene kontrole nad pravilnostjo podatkov o objektu!

– rjavko.masa = -3.2;

– Ker ne moremo vedeti, ali so podatki pravilni -vsi postopki po nepotrebnem bolj zapleteni

– Objekt naj sam poskrbi, da bo v pravilnem stanju

• Težave, če moramo kasneje spremeniti način predstavitve podatkov o objektu

Page 200: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

200

Zgled public class TestClan{

public static void Main(string[] args) {

Clan novClan = new Clan();

novClan.ime = "Katarina";

novClan.letoVpisa = 208;

Dodajmo metodo

• Ni problem, napisali bomo metodo, ki bo vnos preverjala

Page 201: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

201

public class Clan {

public string ime;

public string priimek;

public int letoVpisa;

public string vpisnaStevilka;

… // konstruktorji in metode kot prej!

public bool SpremeniLetoVpisa(int leto) {

if ((2000 <= leto) && (leto <= 2020)) {

this.letoVpisa = leto;

return true; //leto je smiselno, popravimo stanje objekta in

vrnemo true

}

return false; // leto ni smiselno, ne spremenimo nič in

vrnemo false

}

}

Uporaba metode public class TestKlub {

public static void Main(string[] args) {

Clan novClan = new Clan();

novClan.ime = "Katarina";

novClan.letoVpisa = 2007;

novClan.SpremeniLetoVpisa(208);

Page 202: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

202

novClan.SpremeniLetoVpisa(2008);

Dostopi do stanj • ime_objekta.stanje

• Zakaj je to lahko problem?

– “zunanji” uporabnik nastavi napačno vrednost

z1.masa = -100.10;

– Uporabnik pozna predstavitev

Kasneje je ni mogoče spremeniti

• Načini dostopa

– public

– private

– protected

– internal

• public string serijska; private Datum datumRojstva;

• private double masa; public int[] tab;

• bool spol;

public

• Znotraj razreda NI omejitev, vedno (ne glede na način dostopa) je možen dostop do komponent.

• public

– Do lastnosti lahko dostopajo vsi, od kjerkoli (iz katerihkoli datotek (razredov))

imeObjekta.lastnost

– public int javnaLastnost; // v razredu MojObjekt

– Kdorkoli naredi objekt vrste MojObjekt

Page 203: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

203

MojObjekt x = new MojObjekt();

– lahko dostopa do javnaLastnost

x.javnaLastnost

private

• private – Do lastnosti ne more dostopati nihče, razen

metod znotraj razreda Ko pišemo načrt razreda

this.lastnost

lastnost

– private int privatnaLastnost; // v

razredu MojObjekt

– Če kdo naredi objekt vrste MojObjekt MojObjekt x = new MojObjekt();

– Pri poskusu dostopa do privatnaLastnost x.privatnaLastnost

Prevajalnik javi napako

Page 204: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

204

Razred Zajec public class Zajec {

public string serijska; // serijska stevilka zajca

public bool spol; // true = moski, false = zenska

private double masa; // masa zajca ob zadnjem pregledu

}

Razred Zajec2 public class Zajec2 {

public string serijska; // serijska stevilka zajca

public bool spol; // true = moski, false = zenska

private double masa; // masa zajca ob zadnjem pregledu

public SpremeniTezo(double x) {

this.masa = x;

}

}

Dostop do stanj/lastnost • Kako uporabiti:

– Metode za dostop do stanj

"get" metode

– Metode za nastavljanje stanj

"set" metode

• Zakaj je boljši dostop preko metod kot neposredno – Možnost kontrole pravilnosti!

– Možnost kasnejše spremembe načina predstavitve (hranjenja podatkov)

Page 205: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

205

Dostop do stanj/lastnost • Zakaj je boljši dostop preko metod kot neposredno

– Možnost oblikovanja pogleda na podatke

Podatke uporabniku posredujemo drugače, kot jih hranimo (datum – interno je mesec število (1, 2, ...), "navzven" kot ime ("januar", "februar" ...)

– Dostop do določenih lastnosti lahko omejimo

Npr. spol lahko nastavimo le, ko naredimo objekt (kasneje ne, saj se ne spreminja ... če odmislimo kakšne operacije, določene vrste živali ... seveda.)

Hranimo lahko tudi določene podatke, ki jih uporabnik sploh ne potrebuje ..

Nastavitve stanj/podatkov • Nastavitve stanj

– “prireditveni stavek”

• zajcek.SpremeniTezo(2.5);

– V bistvu isto kot zajcek.masa = 2.5;

– A metoda spremeniTezo lahko PREVERI, če je taka sprememba teže smiselna!

• zajcek.SpremeniTezo(-12.5);

– V bistvu isto kot zajcek.masa = -12.5;

– A tu bomo lahko PREPREČILI postavitev lastnosti objekta v napačno stanje!

Page 206: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

206

Razred Zajec - SpremeniTezo public void SpremeniTezo(double novaTeza) {

// smislena nova teza je le med 0 in 10 kg

if ((0 < novaTeza) && (novaTeza <= 10))

this.masa = novaTeza;

// v nasprotnem primeru NE spremenimo teže

}

public bool SpremeniTezo(double novaTeza) {

// smislena nova teza je le med 0 in 10 kg

if ((0 < novaTeza) && (novaTeza <= 10)){

this.masa = novaTeza;

return true; // sprememba uspela

}

// v nasprotnem primeru NE spremenimo teže

// in javimo, da spremembe nismo naredili

return false;

}

SpremeniTezo • Imamo lahko OBE metodi?

– NE

– Imata enak podpis (ime + tipi parametrov)

– Tip rezultata NI del podpisa!

• Metoda je seveda lahko bolj kompleksna – denimo vemo, da se teža ne more spremeniti bolj kot za 15%

Page 207: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

207

public bool SpremeniTezo(double novaTeza) {

// smislena nova teza je le med 0 in 10 kg

// in če ni več kot 15% spremembe od zadnjič

int sprememba = (int)(0.5 +

(100 * Math.abs(this.masa – novaTeza) / this.masa);

if ((0 < novaTeza) && (novaTeza <= 10) && (sprememba <= 15) ){

masa = novaTeza; // this.masa ... Lahko pa this spustimo!

return true; // sprememba uspela

}

// v nasprotnem primeru NE spremenimo teže

// in javimo, da spremembe nismo naredili

return false;

}

Teža in konstruktor • Seveda je smiselno, da zagotovimo, da je teža ustrezna že ves

čas!

– Pozor na začetno stanje: konstruktor!

– Tudi v konstruktorju preverimo, če se uporabnik "obnaša lepo"

• Pogosto na to pozabimo

– Zajec neki = new Zajec("X532", true, 105);

– 105 kilogramskega zajca verjetno ni, a uporabnik je pozabil na decimalno piko v 1.05

– Zato je kontrola smiselnosti podatkov potrebna tudi v konstruktorjih!

Page 208: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

208

SpremeniSpol,

SpremeniSerijsko

• Kaj pa SpremeniSpol – Če niste v kakšni čudni industriji ali razvoju, je ta metoda odveč ;-))

• Morda tudi metoda SpremeniSerijsko – Pozor na konstruktor brez parametrov

– Serijska je "NEDOLOČENO"

• Metoda SpremeniSerijsko naj pusti spreminjati le take serijske številke!

public bool SpremeniSerijsko(string seStev) {

// sprememba dopustna le, če serijske štev. ni

if (this.serijska.Equals("NEDLOČENO")) {

this.serijska = seStev;

return true;

}

return false; // ne smemo spremniti že obstoječe!

}

Dostop do stanj

• Poizvedba – “spremenljivka” v izrazu

• zajcek.PovejTezo() – Da bomo lahko izvedeli težo zajca

– Najenostavneje v telesu metode le return this.masa;

– Lahko stvar "oblikujemo" – recimo, da bomo uporabniku vedno povedali le težo na pol kilograma, mi pa bomo interno stvar vodili natančneje

Page 209: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

209

Razred Zajec - povejTezo public double PovejTezo() {

return this.masa; // ali return masa

}

• Ali pa prilagodimo podatke

– Težo povemo na 0.5 kg natančno

– 2.721 2.5, 2.905 3, 2.502 2.5, ...

– Vzamemo težo v celih kg in pogledamo prvo decimalko decimalnega dela

– Če je med 3 in 7, prištejemo k celim kg 0.5

– Če je več kot 7, prištejemo k celim kg 1.0

Page 210: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

210

public double PovejTezo() {

// težo bomo povedali le na 0.5 kg natančno

int tezaKg = (int)this.masa;

int decim = (int)((this.masa – tezaKg) * 10);

if (decim < 3) return tezaKg + 0.0;

if (decim < 8) return tezaKg + 0.5;

return tezaKg + 1.0;

}

Celotni razred Zajec - 1 public class Zajec {

private string serijska;

private bool spol;

private double masa;

// konstruktor

public Zajec() {

this.spol = true; // vsem zajcem na začetku določimo m.

spol

this.masa = 1.0; // in tehtajo 1kg

this.serijska = "NEDOLOČENO";

}

public Zajec(string serijskaStev):this() {

this.serijska = serijskaStev;

}

public Zajec(string serijskaStev, bool spol, double

masa):this() {

this.serijska = serijskaStev;

this.SpremeniTezo(masa); // uporabimo metodo za sprem.

Page 211: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

211

this.spol = spol;

}

Celotni razred Zajec - 2 public double PovejTezo() {

// težo bomo povedali le na 0.5 kg natančno

int tezaKg = (int)this.masa;

int decim = (int)((this.masa – tezaKg) * 10);

if (decim < 3) return tezaKg + 0.0;

if (decim < 8) return tezaKg + 0.5;

return tezaKg + 1.0;

}

public bool SpremeniTezo(double novaTeza) {

// smislena nova teza je le med 0 in 10 kg

if ((0 < novaTeza) && (novaTeza <= 10)){

masa = novaTeza; // this.masa ... Lahko pa this

spustimo!

return true; // sprememba uspela

}

// v nasprotnem primeru NE spremenimo teže

// in javimo, da spremembe nismo naredili

return false;

}

Celotni razred Zajec - 3 public string PovejSerijsko() {

return this.serijska;

Page 212: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

212

}

public void SpremeniSerijsko(string s) {

this.serijska = s;

}

public bool JeSamec() {

return this.spol;

}

// ker se spol naknadno NE spremeni, metode za

// spreminjanje spola sploh ne ponudimo uporabniku!

} // Zajec

Uporaba razreda Zajec public class ZajčnikNov {

public static void Main(string[] ar) {

Zajec z1 = new Zajec("1238-12-0", false, 0.12);

if (z1.SpremeniTezo(z1.PovejTezo() + 0.3))

Console.WriteLine("Teža je spremenjena na " +

z1.PovejTezo());

else Console.WriteLine("Teža je nespremenjena!" +

" Prirastek je prevelik! Preveri!");

Console.WriteLine("Zajec ima ser. št.:" +

z1.PovejSerijsko());

}

}

Ostale metode • Poleg get/set metod in konstruktorjev

• Metode

Page 213: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

213

– Odzivi objektov

– "znanje objektov"

• Objekti tipa string

– Znajo povedati, kje se v njih začne nek podniz:

"niz".IndexOf("i")

– Znajo vrniti spremeniti črke v male in vrniti nov niz:

nekNiz.ToLower()

– Znajo povedati, če so enaki nekemu drugemu nizu:

mojPriimek.Equals("Lokar")

• "naši" razredi

– Sprogramiramo znanje – ustrezne metode

• Zajec bo znal povedati svojo vrednost, če mu povemo ceno za kg žive teže

Page 214: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

214

Razred Zajec – dodatne metode public double Vrednost(double cenaZaKg) {

// pove vrednost zajca

// zajce tehtamo na dogovorjeno natančnost

return cenaZaKg * this.PovejTezo();

}

public bool MeNapojiti() {

// ugotovimo, če ga je smiselno

// napojiti, da bomo "ujeli" naslednjih pol kg

// dejanska teža je večja od "izmerjene"

return (this.masa – this.PovejTezo() > 0);

}

Metoda ToString Poglejmo si naslednji program:

using System;

using MojaKniznica;

namespace Izpis

{

class Program

{

public static void Main(string[] args)

{

Zajec zeko = new Zajec();

Console.WriteLine("To je zajec: " + zeko);

Page 215: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

215

Console.Write("Press any key to continue . . . ");

Console.ReadKey(true);

}

}

}

ToString • Očitno objekte lahko tudi izpišemo

• Gre seveda tudi takole – string niz = "Zajec " + zeko + "!";

• Torej se tudi objekti "obnašajo" tako kot int, double ... (se torej pretvorijo v niz)

• A s pretvorbo nismo ravno zadovoljni, radi bi kakšne bolj smiselne informacije

• V razredu, ki ga definiramo (recimo Zajec) napišemo metodo ToString

• Če na določenem mestu potrebujemo niz, a naletimo na objekt, se ta metoda pokliče avtomatično string niz = "Zajec " + zeko.ToString() + "!";

Page 216: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

216

Metoda ToString Isti program, a tako, da je v razredu Zajec metoda ToString:

using System;

using MojaKniznica;

namespace Izpis

{

class Program

{

public static void Main(string[] args)

{

Zajec zeko = new Zajec();

Console.WriteLine("To je zajec: " + zeko);

Console.Write("Press any key to continue . . . ");

Console.ReadKey(true);

}

}

}

ToString • V razred Zajec smo napisali

public override string ToString()

{

return "Zajec: " + this.PovejSerijsko();

}

• Posebnost - override

Page 217: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

217

• Zaradi "dedovanja"

– O tem kasneje

– Z dedovanjem smo avtomatično pridobili metodo ToString (zato je vedno na voljo)

– Zato override, da "povozimo " podedovano metodo

ToString in Opis • Razlika med

public override string ToString()

{

return "Zajec: " + this.PovejSerijsko() ;

}

• in

public string Opis()

{

return "Zajec: " + this.PovejSerijsko() ;

}

• Metodo Opis moramo poklicati sami, metoda ToString se kliče avtomatsko (če je potrebno)

– string niz1 = "Zajec " + zeko.ToString() + "!";

– string niz2 = "Zajec " + zeko + "!";

– string niz3 = "Zajec " + zeko.Opis() + "!";

• Vsi trije niz enaki!

• ToString obstaja, tudi, če jo ne napišemo (a verjetno z vrnjenim nizom nismo najbolj zadovoljni)

Tudi objekti so lahko del razreda • Ko sestavljamo razred, kot objektne spremenljivke lahko

uporabimo tudi spremenljivke istega razreda

• Npr.: starsi Zajca

Page 218: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

218

• Razred Clan

– public class Clan {

private string ime; private Clan gaJePriporocil; ...

Še malo o zajcih • Preden nadaljujemo

– Želimo hraniti tudi starše zajca

public class Zajec {

private double masa;

private string serijska;

private bool spol;

private Zajec oce;

private zajec mati;

– Še konstruktor

Problemi s konstruktorjem • Sestavimo privzeti konstruktor

Page 219: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

219

public Zajec() {

this.spol = true;

this.masa = 1.0;

this.serijska = "NEDOLOČENO";

this.oce = new Zajec();

this.mama = new Zajec();

}

• Ko stvar prevedemo, je vse v redu. A ob poskusu

– Zajec rjavko = new Zajec();

Problemi s konstruktorjem • Rekurzija brez ustavitve

• Tudi konstruktor je metoda

– Ustavitveni pogoj

– A pri konstruktorju brez parametrov ni parametrov, ki bi lahko določili ustavitveni pogoj

Kaj storiti • "Privzeti" zajec ne pozna staršev

• oce = null;

• mama = null;

• Če malo pomislimo – ko zajca "naredimo", morata starša že

Page 220: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

220

obstajati

– Ju nima smisla ustvarjati na novo

Razred Avto

Sprogramirajte razred Avto, ki predstavlja avtomobil z njegovimi tehničnimi značilnostmi in trenutnim

stanjem.

Razred naj vsebuje sledeče attribute:

Poleg atributa stevilka, ki omogoča ločevanje posameznih avtomobilov, so tehnične značilnosti opisane z

atributi najvecjaHitrost (v km/h), velikostRezervoarja (v litrih) in povprečnaPoraba (v

litrih/100 km). Trenutno stanje avtomobila predstavljajo atributi avtoVzgan (tipa boolean), stevecKm (npr.

234.142 km), trenutnaHitrost (npr. 56.12 km/h) in kolicinaGoriva (npr. 14.325 litrov).

Razred naj vsebuje naslednje metode:

NastaviStevilko(s): nastavi atribut stevilka na s (recimo 1),

NastaviNajvecjoHitrost(h): nastavi atribut najvecjaHitrost na h km/h (recimo 180),

NastaviVelRezervoar(l): nastavi atribut velikostRezervoarja na l litrov (recimo 48.0),

NastaviPovPorabo(l): nastavi atribut povprecnaPoraba na l litrov/100 km (recimo 7.5),

NapolniGorivo(): nastavi atribut kolicinaGoriva na vrednost velikostRezervoarja,

VzgiAvto(): nastavi atribut avtoVzgan na true, pri čemer se porabi 0.05 litra goriva (če ni zadosti

goriva se avto ne vžge)

UgasniAvto(): nastavi atribut avtoVzgan na false, pri čemer se lahko avto ugasne le, ko je njegova

hitrost enaka 0.

Vozi(t): vozi t minut s trenutno hitrostjo in vrne koliko poti je v tem času prevozil. Metoda povzroči

spremembo atributov stevecKm in kolicinaGoriva. Če je goriva premalo, naj vožnja poteka tako

dolgo, dokler ne zmanjka goriva. Avto lahko vozi le v primeru, da je vžgan (atribut avtoVzgan nastavljen

na true) in ima trenutno hitrost večjo kot 0.

SpremeniHitrost(h): spremeni trenutnaHitrost na vrednost h km/h. Hitrost avtomobila je

med 0 in najvecjaHitrost.

Ustavi(): spremeni trenutnaHitrost na vrednost 0 km/h.

NajvecjiCas(): metoda vrne podatek, koliko minut in sekund lahko še vozimo glede na trenutno

količino goriva. Trenutno stanje avtomobila se ne spremeni.

Page 221: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

221

Doseg(l): metoda vrne podatek, koliko kilometrov lahko prevozimo z največjo hitrostjo, če je v

rezervoarju še l litrov goriva. Trenutno stanje avtomobila se ne spremeni.

CasVoznje(d): metoda nam pove vrne koliko časa (v minutah in sekundah) potrebujemo, da prevozimo

d kilometrov, če vozimo z nespremenjeno hitrostjo, v rezervoarju pa imamo trenutno količino goriva. Pri

tem je treba upoštevati, da količina goriva lahko tudi ne zadošča; v tem primeru bo treba ustaviti, ugasniti

avtomobil, napolniti rezervoar, ga znova prižgati in odpeljati naprej, kar nam vzame dodatnih 15 minut.

Trenutno stanje avtomobila naj se sicer ne spremeni.

PotovalnaHitrost(d,t): metoda vrne hitrost, s katero naj avto vozi, da bi d km oddaljen kraj

dosegel v t minutah. Pri tem naj upošteva tudi eventuelna polnjenja rezervoarja, ki vzamejo dodatnih 15

minut. Trenutno stanje avtomobila se ne spremeni.

Boljsi(a): primerja trenutni avtomobil z avtomobilom a in izpiše stevilko boljšega avtomobila.

Boljši je tisti avtomobil, ki v krajšem času prevozi 1000 km, pri čemer upoštevajte tudi čas polnjenja goriva.

Izpis(): izpiše vse atribute avtomobila.

Pri vseh metodah predpostavljamo, da avto pri vseh hitrostih porabi enako količino goriva. Za generiranje avtomobilov sprogramirajte tri konstruktorje:

prvi naj bo brez parametrov in zgenerira avtomobil, ki ima vse atribute prazne (vrednost 0 ali false)

drugi naj ima le parameter s in naj zgenerira avtomobil s številko s, ostale vrednosti pa naj bodo prazne.

tretji naj ima parametre s, h, v in p, zgenerira pa naj avtomobil številke s, z največjo hitrostjo h, velikostjo

rezervoarja v in povprečno porabo p. Atributi stevecKm, trenutnaHitrost in kolicinaGoriva naj imajo

vrednost 0, avtoVzgan pa na false.

Testiranje razreda Za testiranje razreda Avto napišite razred TestAvto. Z uporabo vsakega konstruktorja definirajte po en avto. S

pomočjo metod za nastavljanje atributov dopolnite podatke za prva dva avtomobila, nato pa izpišite vse

podatke o vseh avtomobilih. Z vsemi simulirajte polnjenje goriva, vožnjo, spreminjanje hitrosti in vse

informativne izpise. Ponovno izpišite vse avtomobile ter jih primerjajte med seboj.

OOP v C++ (avtor dr. M. Krašna)

2. Razmišljanje "po objektno" Zadnjih nekaj let objektno programiranje in objektno usmerjena (objected oriented) tehnologija predstavlja precejšen bum. Kar je v sedemdesetih letih pomenilo strukturirano programiranje, v sedanjem času pripada temu pristopu. Ţal je bilo na začetku prav tako tudi področje mnogih napačnih interpretacij. Različni ljudje so različno

Page 222: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

222

interpretirali posamezne podrobnosti tega pristopa in hitro je prišlo do napačnih razlag kaj je objekt, kaj razred, kaj metode, ipd. Ključna pojma takšnega pristopa v programiranju sta objekt in razred. Kako to dvoje poveţemo v delujoče programe predstavlja mnogim na začetku zaradi nekoliko drugačnega razmišljanja teţavo. Če pogledamo naokoli (pa naj bo kar računalniška učilnica, kjer potekajo oz. so potekale vaje), lahko vidimo več objektov. Pred vami je vaš delovni računalnik, torej objekt. Računalnik med drugim vsebuje miško in tipkovnico, torej dva objekta. Tipkovnica je sestavljena iz mnoţice tipk, torej mnoţice objektov. Ko ste na vajah, sedite na stolu, torej sedite na objektu. Na vsako konkretno zadevo, na vsak predmet, na vsako bitje lahko gledamo, kakor da je objekt. Skupna značilnost vsem tem objektom je, da imajo:

stanje (ang. state), in

obnašanje oz. vedenje (ang. behaviour). Stanje je bolj široko gledan pojem (prevod besede v slovenščino zagotovo ni najbolj primeren) in lahko za računalnik recimo predstavlja moč njegovega procesorja, količino RAM pomnilnika, kapaciteto diska, hitrost CD (DVD) enote, število USB priključkov, stanje ali je računalnik priţgan ali ugasnjen, seznam priključenih enot nanj, kot recimo ali je priključen tiskalnik, ipd. Obnašanje računalnika na drugi strani je recimo formatiranje diska, zapisovanje datoteke na disk, tiskanje dokumenta, priţiganje računalnika (ali pa da ga ugasnemo), da zbrišemo datoteko na disku, da mu povečamo kapaciteto pomnilnika ipd. Poenostavljeno gledano je torej, da imamo na eni strani statične lastnosti in na drugi dinamične oz. spremenljive. preprosto pravilo oz. recept: stanje je predstavljeno s spremenljivkami, obnašanje pa z metodami (funkcije in procedure). Za računalnik bi lahko recimo imeli naslednjo tabelo stanje: - hitrost procesorja - količina RAM pomnilnika - kapaciteta diska - hitrost CD enote - število USB priključkov - priţgan ali ugasnjen računalnik - priključen tiskalnik obnašanje: - zapis datoteke na disk - tiskanje dokumenta - priţiganje računalnika - ugašanje računalnika - brisanje datoteke na disku

Page 223: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

223

- formatiranje diska - povečanje kapacitete RAM-a kar pretvorimo v spremenljivke in metode. Za spremenljivke izberemo smiselne tipe spremenljivk, kar bi za primer z računalnikom recimo lahko bilo: stanje: int HitrostProcesorja; // hitrost procesorja v megahercih int KolicinaPomnilnika; // količina RAM pomnilnika v megabajtih int KapacitetaDiska; // kapaciteta diska v megabajtih int HitrostCDEnote; // hitrost enote v faktorjih pisanja (52x, ...) int SteviloUSBPrikljuckov; // število USB priključkov bool Prizgan; // true, če je računalnik priţgan, sicer false bool PrikljucenTiskalnik; // true, če je priključen tisk., sicer false

Stanje je torej smiselno zapisano s spremenljivkami. V tem preprostem primeru so sedaj le cela števila in dvakrat logična vrednost, lahko pa je stanje opisano tudi mnogo kompleksneje (realna števila, polja, nizi, kazalci, datoteke, ipd.), kot bo kasneje v primerih iz pisnih izpitov. Nekoliko več dela je pri obnašanju. Za vsako postavko iz seznama obnašanja si moramo smiselno zamisliti ali bomo uporabili funkcijo ali proceduro, prav tako pa tudi ali je morda to specifično obnašanje odvisno od kakšnih sekundarnih faktorjev (npr. katero datoteko bomo natisnili ali zbrisali, na kateri tiskalnik bomo natisnili dokument, ipd.), kar predstavimo s parametri metod. Dobra izbira metod je ključnega pomena, večkrat pa se izkaţe, da je na tej stopnji potrebno spremeniti spremenljivke, ki predstavljajo stanje, ker si stanje nismo recimo dovolj dobro zamislili. Na primeru računalnika poglejmo, kako si lahko zamislimo metode (kar ne velja za primere, kadar vam nekdo vnaprej predpiše metode, kot bo na pisnih izpitih) za enostaven primer (nekaj postavk pri obnašanju iz zgornje tabele bo namenoma izpuščenih). Recimo, da bi obnašanje računalnika ponazorili s temi metodami: obnašanje: void ZapisiDatoteko(ImeDatoteke); // zapiše datoteko na disk void NatisniDokument(ImeDokumenta); // natisni dokument na tiskalnik void PrizgiRacunalnik(); // priţiganje računalnika void UgasniRacunalnik(); // ugasni računalnik void BrisiDatoteko(ImeDatoteke); // brisanje datoteke z diska

Zakaj na primer procedura za ugašanje računalnika? Kaj v tej proceduri sploh naredimo? Bi bila morda funkcija bolj primerna? Potrebujemo kakšno povratno informacijo, ko ugasnemo računalnik? Kakšna je stanje računalnika pred to metodo in kakšno potem oziroma kakšna je razlika stanja? Na ta in podobna vprašanja moramo pomisliti, da si lahko kvalitetno zamislimo implementacijo vseh metod za obnašanje. Pri izbiri stanja smo si zamislili, da bomo s spremenljivko Prizgan označili ali je računalnik priţgan ali ne. Ima torej vrednost true, če je priţgan in false, če je ugasnjen zaradi česar imamo pri metodi dve moţnosti: bodisi bomo ugasnili računalnik, ki je priţgan ali pa bomo ţeleli ugasniti računalnik, ki je ţe ugasnjen. Ne glede na ti dve moţnosti pa vemo, da ko bomo to metodo izvedli, bo

Page 224: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

224

računalnik ugasnjen, torej spremenljivka Prizgan bo imela vrednost false. To je hkrati tudi morebitna sprememba stanja pri tej metodi. In ker natančno vemo, da bo po tej metodi računalnik ugasnjen, ne potrebujemo nobene povratne informacije, zaradi česar lahko to metodo implementiramo kot proceduro. Celotno obnašanje računalnika je za ta zgornji primer zamišljeno s procedurami. Za preprosti primer je to morda dovolj dobro, za realnejše primere pa je včasih zaţeljeno več informacije. Na primer, ko bomo izvedli tiskanje dokumenta (torej, ko bomo v programu poklicali metodo NatisniDokument), ne vemo ali je dokument sploh moţno natisniti ali ne. Z drugimi besedami, ali je na računalnik sploh priključen tiskalnik ali ne in ali je računalnik sploh priţgan? Obnašanje metode za tiskanje dokumentov bi tako lahko bilo "boljše", če bi metoda recimo vrnila true, če je bilo tiskanje moţno, oziroma false, če tiskanje ni bilo moţno, ker na računalnik ni priključen tiskalnik ali pa ker računalnik ni priţgan. Metodo void NatisniDokument(ImeDokumenta); // natisni dokument na tiskalnik

bi lahko torej bolj primerno zapisali kot bool NatisniDokument(ImeDokumenta); // natisni dokument na tiskalnik

Prav tako bi recimo lahko izboljšali metodo za priţiganje (in tudi za ugašanje) računalnika. Ne glede na to, da vemo da bo po izvedeni metodi računalnik ugasnjen, bi nas morda vseeno lahko zanimalo ali smo računalnik zares ugasnili ali pa je ţe bil ugasnjen. Torej, če smo ga zares ugasnili, bi vrnili za povratno infomracijo true, če pa je ţe bil ugasnjen, pa false. Tako bi sedaj metodo void PrizgiRacunalnik(); // priţgi računalnik

lahko bolj primerno zapisali kot bool PrizgiRacunalnik(); // priţgi računalnik

V splošnem se lahko ravnamo po tem načelu: bolje več povratne informacije, kakor premalo, če bomo povratno informacijo sploh uporabili, je pa stvar programerja. Preden to zapišemo v programskem jeziku C++, še na hitro razlaga pojma razred. Kar je bilo do sedaj navedeno je bilo zapisano, kako bi opisali nek konkreten objekt in to imenujemo razred - zapišemo torej splošne zadeve (stanje in obnašanje), nikjer pa dejansko še ne določimo vrednosti. Ko pa so enkrat določene vrednosti tem spremenljivkam, na primer da imamo računalnik s hitrostjo 2GHz, 512MB pomnilnika, 80GB disk, ipd., potem govorimo o konkretnem primerku razreda, t.j. o objektu (v slovenski literaturi najdemo tudi "primerek" in "instanca"). V C++ bi zgoraj podani primer zapisali kot sledi v nadaljevanju: definirali bomo razred, njegovo stanje in obnašanje, s tem razredom bomo pa hkrati definirali nov tip spremenljivke (enak imenu razreda). Dogovorimo se, da bomo razrede poimenovali vedno z začetno veliko črko C kot dodatno oznako, da gre za razred (ang. class) in naš razred o računalniku torej kot CRacunalnik. Po vzoru delovanja programskega paketa iz vaj, bo definicija razreda zapisana v dveh datotekah. V datoteki Racunalnik.h je podana

Page 225: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

225

osnovna definicija razreda, v datoteki Racunalnik.cpp pa dejanska izvedba (koda) posameznih metod. Racunalnik.h: class CRacunalnik { // stanje int HitrostProcesorja; // hitrost procesorja v megahercih int KolicinaPomnilnika; // količina RAM pomnilnika v megabajtih int KapacitetaDiska; // kapaciteta diska v megabajtih int HitrostCDEnote; // hitrost enote v faktorjih pisanja (52x, ...) int SteviloUSBPrikljuckov; // število USB priključkov bool Prizgan; // true, če je računalnik priţgan, sicer false bool PrikljucenTiskalnik; // true, če je priključen tisk., sicer false // obnasanje void ZapisiDatoteko(char* ImeDatoteke); // zapiše datoteko na disk bool NatisniDokument(char* ImeDokumenta); // natisni dokument na tiskalnik bool PrizgiRacunalnik(); // priţiganje računalnika void UgasniRacunalnik(); // ugasni računalnik void BrisiDatoteko(char* ImeDatoteke); // brisanje datoteke z diska };

V *.h datoteki je podan seznam spremeljivk stanja in seznam metod za obnašanje, vendar brez dejanske kode kaj se v teh metodah zares zgodi. To sledi v naslednji datoteki s končnico *.cpp v kateri mora seznam metod ustrezati seznamu metod datoteki *.h. Vsaki metodi je dodan zapis kateremu razredu pripada (tukaj CRacunalnik::). Racunalnik.cpp: void CRacunalnik::ZapisiDatoteko(char* ImeDatoteke) { // koda te metode... } bool CRacunalnik::NatisniDokument(char* ImeDokumenta) { if (PrikljucenTiskalnik == true && Prizgan == true) return(true); else return(false); } bool CRacunalnik::PrizgiRacunalnik() { if (Prizgan == false) // ce je ugasnjen, ga prizge Prizgan = true; return(true); } return(false); // sicer vrne false, Prizgan pa je ze true } void CRacunalnik::UgasniRacunalnik() { if (Prizgan == true) // ce je prizgan, ga ugasne Prizgan = false; } void CRacunalnik::BrisiDatoteko(char* ImeDatoteke) { // koda te metode... }

V metodah se lahko uporabljajo spremenljivke razreda imenovane tudi razredne spremenljivke (kot sta na primer spremenljivki PrikljucenTiskalnik in Prizgan v zgornjem primeru), lokalne spremenljivke ali pa spremenljivke, podane kot parameter metode. Podan primer obnašanja in metode pa niso vse, kar se definira v razredu oz. kar se lahko potem dejansko počne z dejanskim objektom. Med obnašanje spadajo: - akcije, - povpraševanje po stanju, - spreminjanje stanja in

Page 226: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

226

- konstruktor ter destruktor. Primer akcije je na primer, da smo ugasnili ali pa priţgali računalnik. Povpraševanje po stanju bi recimo predstavljal primer naslednje metode: int CRacunalnik::DajHitrostProcesorja() { return(HitrostProcesorja); }

Podobno bi lahko bilo za spreminjanje stanja (direktno ali indirektno) na primer metoda void CRacunalnik::NastaviHitrostProcesorja(int NovaHitrost) { HitrostProcesorja = NovaHitrost; }

ali pa povečanje kapacitete RAM pomnilnika z metodo

void PovecajKapacitetoRAMaZa(int Koliko);// povečanje RAM pomnilnika KolicinaPomnilnika += Koliko; }

če bi bila zamišljena kot procedura, če pa bi si jo zamislili kot funkcijo, ki kot rezultat vrne novo količino pomnilnika, bi imeli: int PovecajKapacitetoRAMaZa(int Koliko);// povečanje RAM pomnilnika KolicinaPomnilnika += Koliko; return(KolicinaPomnilnika); }

"Spreminjanje stanja" je lahko torej neposredno, kot v teh dveh metodah ali pa posredno v drugih metodah (kot je na primer pri priţiganju računalnika). Posebni metodi sta pa konstruktor in destruktor in imata imeni, enaki razredu. Medtem, ko je konstruktor namenjen inicializaciji objekta, je destruktor namenjen pokončanju objekta in "čiščenju" dinamično dodeljenega prostora v času delovanja objekta. Konstruktor se avtomatsko pokliče, ko se rezervira prostor za objekt v pomnilniku, destruktor pa ko se uniči. Namen konstruktorja je tako, da določimo vrednosti razrednim spremenljivkam (ne nujno vsem, ampak kar pač potrebujemo). Ločimo osnovni konstruktor (brez kakršnih parametrov) in dodatne konstruktorje. Medtem, ko je destruktor en sam, je lahko konstruktorjev mnogo, le razlikovati se morajo med seboj v parametrih, ki jih sprejmejo. Primer osnovnega konstrukturja za računalnik bi recimo bil naslednji: CRacunalnik::CRacunalnik() { HitrostProcesorja = 2000; KolicinaPomnilnika = 512; Prizgan = false; TiskalnikPrikljucen = true; }

Iz zapisa je razvidno, da je ime metode, ki predstavlja konstruktor enako imenu razreda, ta konstruktor pa se pokliče avtomatsko, ko kreiramo objekt brez dodatnih parametrov. V pomnilniku se takrat ustvari prostor za spremenljivko tipa CRacunalnik, ki ima ţe določene vrednosti spremenljivk HitrostProcesorja (2000), KolicinaPomnilnika (512), Prizgan (false) in TiskalnikPrikljucen (true).

Page 227: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

227

Te vrednosti se torej določijo avtomatsko in nanje ne moremo vplivati. Lahko pa poskrbimo, da pri kreiranju objekta vplivamo na določene vrednosti razrednih spremenljivk in spremenljivke in tiste, na katere ţelimo vplivati, naštejemo kot parametre konstruktorja (torej parametre metode). Dva dodatna konstruktorja bi recimo bila: CRacunalnik::CRacunalnik(int HitProc) { // parameter konstruktorja je hitrost procesorja HitrostProcesorja = HitProc; KolicinaPomnilnika = 512; Prizgan = false; TiskalnikPrikljucen = true; } CRacunalnik::CRacunalnik(int HitProc, int KolPom) { // parameter konstruktorja je hitrost procesorja in količina pomnilnika HitrostProcesorja = HitProc; KolicinaPomnilnika = KolPom; Prizgan = false; TiskalnikPrikljucen = true; }

Paziti moramo, da se metode med seboj razlikujejo po tipih parametrov, ki jih prejmejo. Metoda void ime_metode(int Parameter1)

je drugačna metodi void ime_metode(double Parameter1)

medtem, ko metoda void ime_metode(int Parameter2)

predstavlja teţavo, ker prevajalnik ne bo znal ločiti med njima (med prvo in tretjo metodo); obakrat namreč gre za enak seznam parametrov (imena spremenljivk niso pomembna, le tipi). Temu, da lahko uporabljamo ista imena za metode, v katerih se pa razlikuje seznam parametrov imenujemo preobložene metode (ang. overloading). Po definiciji konstruktor ne vrača vrednosti. Na drugi strani se destruktor avtomatsko pokliče ob sproščanju prostora v pomnilniku - ob uničenju objekta. Ime je enako razredu, le da pred njim zapišemo znak ~ (tilda). Destruktor nima parametrov in prav tako ne vrača nobene vrednosti. Za naš primer bo tako zelo preprost, da ne bo niti vseboval kode, v kolikor bi pa imeli v razrednih spremenljivkah na primer kakšna dinamična polja in bi jih tekom uporabe objekta kreirali z new, v destruktorju poskrbimo (če nismo ţe morda prej), da ta dinamična polja odstranimo iz pomnilnika (stavke z delete prestavite torej v destruktor). CRacunalnik::~CRacunalnik() { }

Vsi konstruktorji in destruktorji morajo biti našteti tudi v datoteki s končnico *.h (v seznamu metod)! Prizgan = false; TiskalnikPrikljucen = true; } CRacunalnik::CRacunalnik(int HitProc, int KolPom) { // parameter konstruktorja je hitrost procesorja in količina pomnilnika HitrostProcesorja = HitProc; KolicinaPomnilnika = KolPom;

Page 228: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

228

Prizgan = false; TiskalnikPrikljucen = true; }

Paziti moramo, da se metode med seboj razlikujejo po tipih parametrov, ki jih prejmejo. Metoda void ime_metode(int Parameter1)

je drugačna metodi void ime_metode(double Parameter1)

medtem, ko metoda void ime_metode(int Parameter2)

predstavlja teţavo, ker prevajalnik ne bo znal ločiti med njima (med prvo in tretjo metodo); obakrat namreč gre za enak seznam parametrov (imena spremenljivk niso pomembna, le tipi). Temu, da lahko uporabljamo ista imena za metode, v katerih se pa razlikuje seznam parametrov imenujemo preobložene metode (ang. overloading). Po definiciji konstruktor ne vrača vrednosti. Na drugi strani se destruktor avtomatsko pokliče ob sproščanju prostora v pomnilniku - ob uničenju objekta. Ime je enako razredu, le da pred njim zapišemo znak ~ (tilda). Destruktor nima parametrov in prav tako ne vrača nobene vrednosti. Za naš primer bo tako zelo preprost, da ne bo niti vseboval kode, v kolikor bi pa imeli v razrednih spremenljivkah na primer kakšna dinamična polja in bi jih tekom uporabe objekta kreirali z new, v destruktorju poskrbimo (če nismo ţe morda prej), da ta dinamična polja odstranimo iz pomnilnika (stavke z delete prestavite torej v destruktor). CRacunalnik::~CRacunalnik() { }

Vsi konstruktorji in destruktorji morajo biti našteti tudi v datoteki s končnico *.h (v seznamu metod)!

2.1. Javnost/privatnost (enkapsulacija) Ko enkrat določimo kaj so razredne spremenljivke in kaj metode, se odločamo še o temu, kakšno naj bo skrivanje elementov razreda oziroma kaj je javnega in kaj privatnega značaja. Nekatere spremenljivke ali metode namreč ţelimo, da se lahko uporabljajo samo znotraj metod in na točno določenih mestih, ne pa popolnoma poljubno. Recimo da bi naš razred o računalniku dodatno poglobili v metodi, kjer priţgemo računalnik in sicer, da bi metoda izgledala nekako takole: bool CRacunalnik::PrizgiRacunalnik() { if (Prizgan == false) // ce je ugasnjen, ga prizge VklopiNapajalnik(); ResetirajMaticnoPlosco(); PreberiPodatkeIzBIOSa(); InicializirajVgrajeneKartice(); Prizgan = true; return(true); } return(false); // sicer vrne false, Prizgan pa je ze true }

V metodi bi torej, če bi zares priţgali računalnik, poklicali še štiri metode (VklopiNapajalnik, ResetirajMaticnoPlosco, PreberiPodatkeIzBIOSa in

Page 229: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

229

InicializirajVgrajeneKartice), ki pa imajo popolnoma privaten značaj. Z drugimi besedami: če bodo te metode ţe kdaj poklicane, ţelimo da se to zgodi samo v tem primeru, ne pa tudi recimo, da bi nekdo poklical metodo za resetiranje matične plošče po tiskanju dokumenta. Enako velja za spremenljivke; kar ne ţelimo, da bi nekdo uporabljal izven metod, določimo da je privatnega značaja. V C++ to (javnost in privatnost) določimo v datoteki *.h z besedama private: in public: in vse kar je napisano na desni (in zatem), ima status ali privatnosti ali pa javnosti. Dopolnjena datoteka Racunalnik.h, v kateri je nekaj spremenljivk deklariranih kot privatnih, bi tako recimo bila: class CRacunalnik { public: // stanje int HitrostProcesorja; // hitrost procesorja v megahercih int KolicinaPomnilnika; // količina RAM pomnilnika v megabajtih private: int KapacitetaDiska; // kapaciteta diska v megabajtih int HitrostCDEnote; // hitrost enote v faktorjih pisanja (52x, ...) int SteviloUSBPrikljuckov; // število USB priključkov public: bool Prizgan; // true, če je računalnik priţgan, sicer false bool PrikljucenTiskalnik; // true, če je priključen tisk., sicer false // obnasanje void ZapisiDatoteko(char* ImeDatoteke); // zapiše datoteko na disk bool NatisniDokument(char* ImeDokumenta); // natisni dokument na tiskalnik bool PrizgiRacunalnik(); // priţiganje računalnika void UgasniRacunalnik(); // ugasni računalnik void BrisiDatoteko(char* ImeDatoteke); // brisanje datoteke z diska CRacunalnik(); // osnovni konstruktor CRacunalnik(int HitPro); // dodatni konstruktor CRacunalnik(int HitPro, int KolPom); // še en dodatni konstruktor ~CRacunalnik(); // destruktor };

2.2. Uporaba razredov in objektov Uporabljamo jih lahko statično ali dinamično, pri čemer v praksi prevladuje uporaba dinamičnega. Kot ţe rečeno, razred definira nov tip spremenljivke in ga na tak način tudi obravnavamo. Za statično uporabo imamo torej naslednji "vzorec": CRacunalnik MojRac1; // deklaracija objekta z imenom MojRac1 MojRac1.HitrostPomnilnika = ...; // direktni dostop do javne spremenljivke MojRac1.KapacitetaDiska = ...; // to ne gre, ker je KapacitetaDiska privatna! MojRac1.PrizgiRacunalnik(); // klic javne metode

Konstruktor se avtomatsko pokliče pri deklaraciji objekta in destruktor avtomatsko, ko se konča metoda v kateri je nek objekt lokalna spremenljivka ali pa ko se konča program, če je objekt globalna spremenljivka.

Page 230: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

230

Pri MojRac1 se uporabi osnovni konstruktor, če bi pisalo pa recimo MojRac1(1000) bi se pa avtomatsko uporabil dodatni konstruktor, ki kot parameter sprejme hitrost procesorja. Nekoliko več pisanja (je pa zato morda celo bolj berljivo programerjem) je pri uporabi dinamičnega pristopa, kjer imamo naslednji "vzorec": CRacunalnik* MojRac2; // deklaracija objekta z imenom MojRac2 (tu se // še ne pokliče noben konstruktor!) MojRac2 = new CRacunalnik(); // inicializacija preko osnovnega konstrukt. MojRac2->HitrostPomnilnika = ...;// direktni dostop do javne spremenljivke MojRac2->PrizgiRacunalnik(); // klic javne metode delete MojRac2; // uničenje objekta in klic destruktorja

Ker je z razredom definiran nov tip spremenljivke je treba v datoteki, kjer se uporablja tak razred, podati kje se nahaja osnovna definicija razreda. Zato v glavi programa pri direktivah include zapišemo za naš primer: #include "Racunalnik.h"

Pomembno pri uporabi razredov je tudi, da ločimo, da vsaka spremenljivka predstavlja svoj objekt. Tako sta obe spremenljivki, MojRac1 in MojRac2 posamezna objekta iz razreda CRacunalnik in vsak zase predstavljata samostojno enoto. Če sta recimo oba računalnika ugasnjena, pokličemo pa metodo PrizgiRacunalnik pri spremenljivki MojRac1, bomo s tem priţgali samo računalnik, predstavljen kot MojRac1. Stanje računalnika, predstavljenega kot MojRac2 ostane nedotaknjeno! Vsak klic metod in dostopanje do spremenljivk je torej izvedeno samo pri objektih, katerih ime je napisano pred ločilom (ali piko . ali pa puščico ->).

2.3. Primer: razred CTocka Definirajmo razred CTocka, ki ponazarja točko v treh dimenzijah. Razred vsebuje spremenljivke X, Y in Z, ki določajo poloţaj in metodo za izpis podatkov točke (Izpis) ter nekaj metod za seštevanje poloţajev dveh točk (če bi recimo točka predstavljala krajevni vektor). Metode za seštevanje imajo vedno isto ime (Sestej), se pa razlikujejo v parametrih, ki jih sprejmejo in v rezultatu, ki ga vrnejo. Iz primera je razvidno tudi, da lahko metoda vrne nek objekt, ki je enakega tipa kot objekt, od katerega metoda se je poklicala. Dodatno ima ta razred ob osnovnem konstruktorju tudi dodatni konstruktor, v katerem lahko določimo vrednosti vsem koordinatam. V osnovnem konstruktorju pa smo jih postavili na vrednosti 0. V nadaljevanju podajamo vsebino datoteke *.h, vsebino datoteke *.cpp ter primer uporabe v glavnem programu. Tocka.h: class CTocka { public: // spremenljivke float X,Y,Z; // koordinate točke // metode CTocka(); // osnovni konstruktor

Page 231: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

231

CTocka(float X1, float Y1, float Z1); // dodatni konstruktor ~CTocka(); void Izpis(); // izpis podatkov točke void Sestej(float X1, float Y1, float Z1); void Sestej(float X1, float Y1, float Z1,float &X2, float &Y2, float &Z2); void Sestej(CTocka* T); CTocka* Sestej(float X1, float Y1, float Z1); };

Tocka.cpp: #include "Tocka.h" // osnovni konstruktor CTocka::CTocka() { X = 0; Y = 0; Z = 0; } // dodatni konstruktor CTocka::CTocka(float X1, float Y1, float Z1) { X = X1; Y = Y1; Z = Z1; } // destruktor CTocka::~CTocka() {} // izpis podatkov tocke void CTocka::Izpis() { printf("(%f,%f,%f)\n",X,Y,Z); } // pristevanje druge tocke (sprememba se pozna na razrednih spremenljivkah) void CTocka::Sestej(float X1, float Y1, float Z1) { X += X1; Y += Y1; Z += Z1; } // sestevanje z drugo tocko (sprememba je vrnjena v spremenljivkah X2,Y2,Z2 // razredne spremenljivke pa se ne spremenijo void CTocka::Sestej(float X1,float Y1,float Z1,float &X2,float &Y2,float &Z2) { X2 = X+X1; Y2 = Y+Y1; Z2 = Z+Z1; } // pristevanje druge tocke, podane kot objekt (sprememba se pozna v razrednih // spremenljivkah klicanega objekta; ne pa od T!) void CTocka::Sestej(CTocka* T) { X += T->X; Y += T->Y; Z += T->Z; } // sestevanje druge tocke (ni spremembe razrednih spremenljivk, se pa rezultat // sestevanja vrne kot objekt! CTocka* CTocka::Sestej(float X1, float Y1, float Z1) { CTocka* Rezultat = new CTocka(); Rezultat->X = X+X1; Rezultat->Y = Y+Y1; Rezultat->Z = Z+Z1; return(Rezultat); }

V zadnji metodi imamo opravka s tremi "kompleti" koordinat. Spremenljivke X, Y in Z predstavljajo razredne spremenljivke, spremenljivke X1, Y1 in Z1 predstavljajo vrednosti, podane kot parameter metode in spremenljivke Rezultat->X, Rezultat->Y ter

Page 232: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

232

Rezultat->Z predstavljajo razredne spremenljivke od objekta Rezultat, ki ga vrnemo kot rezultat metode preko stavka return. To metodo bi lahko krajše zapisali tudi kot: // sestevanje druge tocke (ni spremembe razrednih spremenljivk, se pa rezultat // sestevanja vrne kot objekt! CTocka* CTocka::Sestej(float X1, float Y1, float Z1) { return(new CTocka(X+X1,Y+Y1,Z+Z1)); }

V predzadnji metodi smo kot parameter metode uporabili objekt, oba tipa metod (predzadnja in zadnja) pa sta v objektnem programiranju zelo pogosta. Še primer uporabe v glavnem programu: #include "Tocka.h" void main() { CTocka* TockaA = new CTocka(); CTocka* TockaB = new CTocka(5,6,7); CTocka* TockaC; TockaA->Izpis(); TockaA->Y = 100; TockaA->Izpis(); TockaB->Izpis(); TockaA->Sestej(1,2,3); TockaA->Izpis(); TockaA->Sestej(TockaB); TockaA->Izpis(); TockaC = TockaA->Sestej(10,20,30); TockaC->Izpis(); TockaA->Izpis(); }

Rezultat tega je naslednji: (0,0,0) (0,100,0) (5,6,7) (1,102,3) (6,108,10) (16,128,40) (6,108,10)

2.4. Primer: razred CDvigalo Definirajmo razred, ki ponazarja delovanje dvigala. V dvigalu ves čas beleţimo v katerem nadstropju je, koliko oseb je v njemu in kakšna je masa teh oseb. Dvigalo se lahko nahaja v nadstropjih od 0 (klet) do maksimalnega, ki je v osnovnem konstruktorju avtomatsko določen na vrednost 10, sicer pa je odvisen od podane vrednosti v dodatnem konstruktorju. Delovanje dvigala je prikazano z metodami za vstop oseb v dvigalo, izstop in za premik gor ali dol. Več različnih metod bo definiranih za podobno obnašanje za ponazoritev različne implementacije obnašanja. Dvigalo.h: class CDvigalo { public: // spremenljivke int Nadstropje; // trenutno nadstropje dvigala int MaxNadstropje; // najvišje nadstropje dvigala int SteviloOseb; // trenutno število oseb v dvigalu int TezaOseb; // skupna teţa oseb v dvigalu int MaxTezaOseb; // največja dovoljena teţa vseh oseb // metode CDvigalo(); // osnovni konstruktor

Page 233: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

233

CDvigalo(int MaxNad); // dodatni konstruktor CDvigalo(int MaxNad, int MaxTeza);// dodatni konstruktor ~CDvigalo(); // destruktor bool SpustiDvigaloV(int Kam); // spuščanje dvigala bool DvigniDigaloV(int Kam); // dviganje dvigala bool JeDvigaloVKleti(); // je dvigalo trenutno v kleti? bool JeDvigaloNaVrhu(); // je dvigalo trenutno na vrhu? bool JeVDvigaluKajOseb(); // je v dvigalu kaj oseb? bool VstopOsebe(int Teza); // vstop ene osebe v dvigalo bool IzstopOsebe(int Teza); // izstop ene osebe iz dvigala bool VstopOseb(int StOseb, int Teza); // vstop več oseb v dvigalo bool IzstopOseb(int StOseb, int Teza); // izstop več oseb iz dvigala void IzpisStanjaDvigala(); // izpis aktualnih podatkov dvigala };

Dvigalo.cpp: #include "Dvigalo.h" // osnovni konstruktor CDvigalo::CDvigalo() { Nadstropje = 0; MaxNadstropje = 10; SteviloOseb = 0; TezaOseb = 0; MaxTezaOseb = 800; } // dodatni konstruktor, ki posebej nastavi najvišje nadstropje CDvigalo::CDvigalo(int MaxNad) { Nadstropje = 0; MaxNadstropje = MaxNad; SteviloOseb = 0; TezaOseb = 0; MaxTezaOseb = 800; } // dodatni konstruktor, ki posebej nastavi najvišje nadstropje in največjo // dovoljeno teţo CDvigalo::CDvigalo(int MaxNad, int MaxTeza) { Nadstropje = 0; MaxNadstropje = MaxNad; SteviloOseb = 0; TezaOseb = 0; MaxTezaOseb = MaxTeza; } // destruktor (v tem primeru je prazen) CDvigalo::~CDvigalo() {} // spuščanje dvigala v niţja nadstropja (če ţelimo spustiti niţje kot v // klet ali pa če je ţeljeno nadstropje višje od trenutnega (torej ne gre za // spuščanje dvigala) javi false, sicer javi true in temu primerno aţurira // trenutno nadstropje dvigala bool CDvigalo::SpustiDvigaloV(int Kam) { if (Kam >= Nadstropje || Kam < 0) return(false); Nadstropje = Kam; return(true); } // dviganje dvigala v niţja nadstropja (če ţelimo spustiti višje kot na // vrh ali pa če je ţeljeno nadstropje niţje od trenutnega (torej ne gre za // dviganje dvigala) javi false, sicer javi true in temu primerno aţurira // trenutno nadstropje dvigala bool CDvigalo::DvigniDvigaloV(int Kam) { if (Kam <= Nadstropje || Kam > MaxNadstropje) return(false); Nadstropje = Kam; return(true); } // vrne true, če je dvigalo v kleti, sicer vrne false bool CDvigalo::JeDvigaloVKleti() {

Page 234: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

234

if (Nadstropje == 0) return(true); return(false); } // vrne true, če je dvigalu na vrhu (na najvišjem moţnem nadstropju), sicer // vrne false bool CDvigalo::JeDvigaloNaVrhu() { if (Nadstropje == MaxNadstropje) return(true); return(false); } // vrne true, če je v dvigalu kaj oseb (metoda bi bila ekvivalentna, če bi // gledali ali je v dvigalu skupna teţa oseb večja kot 0) bool CDvigalo::JeVDvigaluKajOseb() { if (SteviloOseb > 0) return(true); return(false); } // vstop ene osebe v dvigalo z določeno teţo (če teţa presega največjo // dovoljeno skupno teţo ali pa če je nepozitivna vrednost teţe metoda javi // false, sicer true) bool VstopOsebe(int Teza) { if (TezaOseb+Teza > MaxTeza || Teza < 0) return(false); SteviloOseb++; TezaOseb += Teza; return(true); } // izstop ene osebe iz dvigala z določeno teţo (če v dvigalu ni toliko teţe // ali pa če je nepozitivna vrednost teţe metoda javi false, sicer true) bool CDvigalo::IzstopOsebe(int Teza) { if (TezaOseb > Teza || Teza < 0) return(false); SteviloOseb--; TezaOseb -= Teza; return(true); } // vstop več oseb v dvigalo z določeno teţo (če teţa presega največjo // dovoljeno skupno teţo ali pa če je nepozitivna vrednost teţe metoda javi // false, sicer true) bool VstopOseb(int StOseb, int Teza) { if (TezaOseb+Teza > MaxTeza || Teza < 0) return(false); SteviloOseb += StOseb; TezaOseb += Teza; return(true); } // izstop več oseb iz dvigala z določeno teţo (če v dvigalu ni toliko teţe // ali pa če je nepozitivna vrednost teţe metoda javi false, sicer true) bool CDvigalo::IzstopOseb(int StOseb, int Teza) { if (TezaOseb > Teza || Teza < 0) return(false); SteviloOseb -= StOseb; TezaOseb -= Teza; return(true); } // izpis aktualnih podatkov od dvigala (trenutno nadstropje, število oseb v // dvigalu in njihova skupna teţa) void CDvigalo::IzpisStanjaDvigala() { printf("trenutno v nadstropju : %d\n",Nadstropje); printf("v njem stevilo oseb : %d s skupno tezo : %d\n",SteviloOseb,TezaOseb); }

2.5. Dedovanje

Page 235: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

235

Na hitro še nekaj besed o dedovanju (na samih vajah to ne boste potrebovali, je pa to eden od pomembnejših principov objektnega programiranja!). V implementacijah se nam lahko pojavi mnoţica razredov, ki se med seboj le malo razlikujejo (dopolnjujejo v kodi ali pa drugače implementirajo del kode) in jih je smiselno zaradi urejenosti in pregleda ustrezno hierarhično urediti. Običajno se uporabljajo postopki dedovanja (ang. inheritance), kjer izpeljani razredi (in temu primerno potem tudi objekti) podedujejo nekatere lastnosti, podatke in operacije od drugih razredov. Na primer, imamo nek razred A, predstavljen z določeno mnoţico spremenljivk in metod, zatem pa ţelimo napisati nov razred B, ki je v skoraj vsem enak, le da ima še eno dodatno spremenljivko. Namesto, da bi spet pisali (kopirali ali pa pretipkali) isto kodo in le dodali eno vrstico zaradi te dodatne spremenljivke, lahko preprosteje določimo, da ima razred B vse lastnosti in metode enake kot razred A, razlika je le dodatna spremenljivka. Pravimo, da smo razred B izpeljali iz razreda A oziroma, da je razred A nadrazred razredu B. Recimo, da imamo splošen razred CLik, ki vsebuje spremenljivki X in Y, ki določata središče lika in metodo Premakni za premik lika za podane koordinate. Imamo pa tudi razred CKrog, ki ima prav tako središče kroga in še polmer in metodo za izračun površine. Da ne ponavljamo kode, zapišemo, da je razred CKrog podedoval lastnosti od razreda CLik ima pa še svojo spremenljivko Radius: class CLik { double X,Y; // koordinati središča void Premakni(int ZaX, int ZaY); // premik središča lika CLik(); ~CLik(); }; class CKrog : CLik { // nadrazredi so našteti za podpičjem! double Radius; // radius kroga double IzracunajPovrsino(); // površina kroga CKrog(); ~CKrog(); };

Vsi nadrazredi (ker lahko razred naenkrat deduje lastnosti večih razredov) so našteti za dvopičjem, pri čemer moramo biti zelo pazljivi, da nadrazredi ne vsebujejo istoimenskih spremenljivk in metod, ker sicer pride do zmede. Pri dedovanju je še posebej pomembno, kaj je v nadrazredu določeno kot privatno, kaj javno in kaj kot prijateljsko (ang. friendly), vendar to presega okvire vaj in tega teksta.

2.6. Splošne lastnosti objektnega programiranja ("na

Page 236: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

236

hitro") Lastnosti objektno usmerjenih programskih jezikov/okolij bi lahko na hitro strnili v1: - nastanek/uničenje objektov : objekti pri svojem nastanku zavzamejo prostor v pomnilniku in ga ob koncu sprostijo. Pri tem mora programski jezik za vsak nov objekt dovoliti dve proceduri: konstruktor ob nastanku in destruktor ob uničenju. - inicializacija/prirejanje : C++ dovoljuje, da vgrajenim podatkovnim tipom ob definiciji priredimo začetno vrednost. Vse vdelane tipe smemo prirejati med seboj. Enako mora veljati tudi za nove podatkovne tipe. - polimorfizem (mnogoličnost) osnovnih operatorjev : isti operator ali funkcija dela različne stvari glede na različne parametre. Obstajati mora mehanizem, da operatorje priredimo tudi za delo z novimi tipi. - dedovanje : lastnosti enega ali več razredov se prenesejo na nov razred in dopolnijo z novimi lastnostmi. - enkapsulacija : programski jezik omogoča kontrolo nad tem, kdo pozna detajle nekega razreda in kdo ga lahko uporablja samo prek za to določenih podprogramov. Objektno programiranje se dolgoročno gledano bogato obrestuje, ker je programiranje s knjiţnicami razredov cenejše, večinoma bolj učinkovito in bolj zanesljivo. 1 delno povzeto iz Objektno programiranje v C++, Stanko Dolenšek,

4. Primeri iz izpitov Zaenkrat so v nadaljevanju samo teksti iz izpitnih nalog, rešitve še sledijo.

4.1. CAlbum in CPesem Napišite razreda CAlbum in CPesem. Razred CPesem naj vsebuje tipične podatke neke pesmi (naslov pesmi, avtor), ki naj bodo shranjene v privatnih spremenljivkah. Vrednosti teh spremenljivk naj bo moţno spremeniti in (ločeno!) povprašati po njihovih vrednostih z javnimi metodami (torej 4 metode!). Razred CAlbum naj vsebuje v privatnih spremenljivkah podatke o zaloţbi albuma, število pesmi in polje s podatki o pesmih (polje objektov razreda CPesem). Velikost polja pesmi naj se določi v konstruktorju razreda CAlbum in takrat naj se v pomnilniku tudi rezervira prostor za polje pesmi. Zapišite deklaracije in definicije metod obeh razredov, v delu programa (ne pišite vsega!) pa nakaţite, kako bi "pripravili" album dvajsetih različnih avtorjev (vsak eno pesem) iz koncertov na ŠTUKu in kako bi ugotovili avtorja tretje pesmi na tem albumu. class CPesem { private: char* Naslov; char* Avtor; public: void NastaviNaslov(char* Nas); void NastaviAvtorja(char* Avt); char* DajNaslov(); char* DajAvtorja(); CPesem(); ~CPesem(); }; CPesem::CPesem() {} CPesem::~CPesem() {}

Page 237: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

237

void CPesem::NastaviNaslov(char* Nas) { Naslov = Nas; } void CPesem::NastaviAvtorja(char* Avt) { Avtor = Avt; } char* CPesem::DajNaslov() { return(Naslov); } char* CPesem::DajAvtorja() { return(Avtor); } class CAlbum { private: char* Zalozba; int SteviloPesmi; CPesem* Pesmi; public: void NastaviPesem(int ZapSt, char* Naslov, char* Avtor); CPesem DajPesem(int ZapSt); CAlbum(int StPesmi); ~CAlbum(); }; CAlbum::CAlbum(int StPesmi) { Pesmi = new CPesem[StPesmi]; SteviloPesmi = StPesmi; } CAlbum::~CAlbum() { delete[] Pesmi;} void CAlbum::NastaviPesem(int ZapSt, char* Naslov, char* Avtor) { if (ZapSt>0 && ZapSt <= SteviloPesmi) { Pesmi[ZapSt].Naslov = Naslov; Pesmi[ZapSt].Avtor = Avtor; } } CPesem CAlbum::DajPesem(int ZapSt) { if (ZapSt>0 && ZapSt <= SteviloPesmi) return(Pesmi[ZapSt]); else return(false); }

4.2. CRacun Napišite razred CRacun, ki predstavlja račun v nekem nakupu, npr. trgovini. Razred naj vsebuje metodo DodajNaRacun(char*, double, int), kjer boste "na račun" dodali novo zadevo (z nekim imenom, ceno in tipom stopnje davka: 1 za recimo 10%, 2 recimo za 20%). Prav tako naj obstaja metoda OdstraniIzRacuna(char*), kjer boste neko zadevo odstranili iz računa. Dodatna naj bo še metoda IzpisRacuna(), kjer izpišete vse zadeve, ki so se nabrale "na računu", njihovo skupno ceno in posebej, koliko zadev (število in njihova skupna cena) je bilo za stopnjo davka 1 in koliko za stopnjo davka 2. Sami razmislite o tem, s katerimi spremenljivkami boste realizirali seznam zadev "na računu" (naj jih bo recimo največ 30), za primerjavo nizov pa lahko uporabite kar vgrajeno funkcijo strcmp! Napišite deklaracijo razreda (*.h), izvedbo metod (*.cpp) in kratek primer uporabe, kjer boste uporabili te metode. class CRacun { private: char** Stvari; double* Cene; int* Davki; int SteviloStvari; public: void DodajNaRacun(char* Kaj, double Cena, int Davek); void OdstraniIzRacuna(char* Kaj); void IzpisRacuna();

Page 238: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

238

CRacun(); ~CRacun(); }; CRacun::CRacun() { Stvari = new char*[30];} for (int i=0;i<30;i++) Stvari[i] = new char*[100]; // recimo, da je ime izdelka najvec 100 znakov SteviloStvari = 0; } CRacun::~CRacun() { for (int i=0;i<30;i++) delete[] Stvari[i]; delete Stvari; } void CRacun::DodajNaRacun(char* Kaj, double Cena, int Davek) { if (SteviloStvari == 30) // je sploh se mozno dati kaj na racun? return; Stvari[SteviloStvari] = Kaj; Cene[SteviloStvari] = Cena; Davki[SteviloStvari] = Davek; SteviloStvari++; } void CRacun::OdstraniIzRacuna(char* Kaj) { for (int i=0;i<SteviloStvari;i++) { if (strcmp(Stvari[i],Kaj) == 0) { // izdelek s tem imenom najden! for (int j=i;j<SteviloStvari-1;j++) { Stvari[j] = Stvari[j+1]; Cene[j] = Cene[j+1]; Davki[j] = Davki[j+1]; } SteviloStvari--; } // if } // for i } void CRacun::IpisRacuna() { double SkupnaCena = 0.0; int KolikoDavek1 = 0; int KolikoDavek2 = 0; double SkupnaCenaDavek1 = 0.0; double SkupnaCenaDavek2 = 0.0; for (int i=0;i<SteviloStvari;i++) { SkupnaCena += Cene[i]; if (Davki[i] == 1) { KolikoDavek1++; SkupnaCenaDavek1 += Cene[i]; } else KolikoDavek2++; SkupnaCenaDavek2 += Cene[i]; } } printf("Stanje racuna:\n"); printf("stevilo izdelkov : %d\n",SteviloStvari); printf("skupna cena izdelkov : %f\n",SkupnaCena); printf("izdelkov dav. stopnje 1 : %d v skupni ceni : %f\n",KolikoDavek1, SkupnaCenaDavek1); printf("izdelkov dav. stopnje 2 : %d v skupni ceni : %f\n",KolikoDavek2, SkupnaCenaDavek2); }

4.3. CTekma Napišite razred CTekma, ki predstavlja neko poljubno tekmo, recimo nogometno, ker je

Page 239: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

239

to trenutno najbolj aktualno. Tekma se odvija med dvema moštvoma (vsako moštvo ima svoje ime), dogajanje na tekmi pa se beleţi z naslednjimi metodami: DalGol(ime_moštva), RazveljaviGol(ime_moštva), RumeniKarton(ime_moštva), RdeciKarton(ime_moštva), Prekršek(ime_moštva) in IzpisRezultata() ter IzpisStatistike(ime moštva). V teh metodah se moštvu, katerega ime je podano kot parameter: doda gol, razveljavi gol, zabeleţi dobljen rumeni in rdeči karton, zabeleţi napravljen prekršek, zadnji dve metodi pa sta namenjeni izpisu rezultata (koliko golov je na kateri strani) in statistiki tekme (rezultat, rumeni kartoni, rdeči kartoni in prekrški). Imena moštev se določita s parametroma v konstruktorju razreda. Sami razmislite o tem, s katerimi spremenljivkami boste realizirali statistiko (in rezultat tekme). Za primerjavo nizov lahko uporabite kar vgrajeno funkcijo strmcp (<string.h>)! Napišite deklaracijo razreda (*.h), izvedbo metod (*.cpp) in kratek primer uporabe, kjer boste uporabili te metode. class CTekma { public: char* ImeMostva1; char* ImeMostva2; int Gol1, Gol2; int PrekrsekRu1, PrekrsekRu2; int PrekrsekRd1, PrekrsekRd2; int Prekrsek1, Prekrsek2; void DalGol(char* Mostvo); void RazveljaviGol(char* Mostvo); void RumeniKarton(char* Mostvo); void RdeciKarton(char* Mostvo); void Prekrsek(char* Mostvo); void IzpisRezultata(); CTekma(char* Mostvo1, char* Mostvo2); ~CTekma(); }; CTekma::CTekma(char* Mostvo1, char* Mostvo2) { Gol1 = 0; Gol2 = 0; PrekrsekRu1 = 0; PrekrsekRu2 = 0; PrekrsekRd1 = 0; PrekrsekRd2 = 0; Prekrsek1 = 0; Prekrsek2 = 0; ImeMostva1 = Mostvo1; ImeMostva2 = Mostvo2; } CTekma::~CTekma() {} void CTekma::DalGol(char* Mostvo) { if (strcmp(Mostvo,ImeMostva1) == 0) Gol1++; else Gol2++; } void CTekma::RazveljaviGol(char* Mostvo) { if (strcmp(Mostvo,ImeMostva1) == 0) Gol1--; else Gol2--; } void CTekma::RumeniKarton(char* Mostvo) { if (strcmp(Mostvo,ImeMostva1) == 0) PrekrsekRu1++; else PrekrsekRu2++; } void CTekma::RdeciKarton(char* Mostvo) {

Page 240: Objektno usmjereno programiranje na slovenskom

C# . NET

Programiranje 1 – VSŠ Informatika

240

if (strcmp(Mostvo,ImeMostva1) == 0) PrekrsekRd1++; else PrekrsekRd2++; } void CTekma::Prekrsek(char* Mostvo) { if (strcmp(Mostvo,ImeMostva1) == 0) Prekrsek1++; else Prekrsek2++; } void CTekma::IzpisRezultata() { printf("Stanje na tekmi med %s in %s:\n",ImeMostva1,ImeMostva2); printf("rezultat : %d:%d\n",Gol1,Gol2); printf("prekrski : %d:%d\n",Prekrsek1,Prekrsek2); printf("rumeni kartoni : %d:%d\n",PrekrsekRu1,PrekrsekRu2); printf("rdeci kartoni : %d:%d\n",PrekrsekRd1,PrekrsekRd2); }


Recommended