Marko Mijač Ivan Švogor Boris Tomaš
Odabrana poglavlja programskog inženjerstva razvoj programa
FAKULTET ORGANIZACIJE I INFORMATIKE, VARAŽDIN KATEDRA ZA RAZVOJ INFORMACIJSKIH SUSTAVA INTERNA SKRIPTA Skriptu pripremili: Marko Mijač Ivan Švogor Boris Tomaš
Odabrana poglavlja programskog inženjerstva razvoj programa
Skriptu uredila: Lea Kuzminski
Predgovor Skripta „Odabrana poglavlja programskog inženjerstva – razvoj programa“ originalno je namijenjena studentima treće godine preddiplomskog studija kao materijal za praćenje vježbi iz kolegija Programsko inženjerstvo. Skripta svakako nije ograničena na navedenu publiku već se može primijeniti i na višim godinama te drugim fakultetima. Nastanak skripte vođen je idejom povezivanja teorijskih saznanja programskog inženjerstva s inženjerskom disciplinom programiranja – programsko inženjerstvo, više je od programiranja. Zbog toga razloga sadržaj je podijeljen u tri logičke cjeline: I) teorijske osnove i objektno orijentirana paradigma, II) razumijevanje procesa i razvoj poslovne logike i III) uvođenje u rad i trendovi programskog inženjerstva. U prvom dijelu naglasak je na razvojnoj okolini, osnovama programiranja, rukovanjem programskim pogreškama te dokumentiranju i verzioniranju programskog koda. Drugi dio ove skripte posvećen je povezivanju poslovnog procesa i poslovne logike s arhitekturom programa. Naglasak je stavljen UML (dijagram klasa, aktivnosti i sekvenci) modeliranje, modeliranje baze podataka i povezivanje s istom. Treći dio koncentriran je na specifičnosti odabrane platforme (u ovoj skripti koristi se .NET Framework iako je naglasak na paradigmi stoga se vrlo lako mogu koristiti i druge tehnologije) te korake koji slijede nakon implementacije programskog rješenja. Iza ove skripte stoje stotine stranica materijala koje su kroz godine ispisali suradnici na kolegiju Programsko inženjerstvo, Fakulteta organizacije i informatike. Naše iskustvo i iskustvo naših prethodnika, pokušali smo što vjernije pretočiti u novu i cjelovitu skriptu. Nadamo se da će čitatelju biti od koristi. Umjesto pojedinačnim zahvalama svima koji su doprinijeli, direktno ili indirektno, naša je zahvalnost predana svakome tko čita ovaj tekst.
„Let us change our traditional attitude to the construction of programs. Instead of imagining that our main task is to instruct a computer what to do, let us concentrate rather on explaining to human beings what we want a computer to do.“ - Donald Knuth -
U Varaždinu, veljača 2014
Autori
I
Sadržaj Predgovor ................................................................................................................................................. I 1.
Integrirano razvojno okruženje - IDE ........................................................................................... 1
1.1.
Uvod ........................................................................................................................................ 2
1.2.
Što je IDE?................................................................................................................................ 2
1.3.
.NET ......................................................................................................................................... 6
1.4.
MSDN....................................................................................................................................... 7
1.5.
Visual Studio 2012 – Prvi pogled ........................................................................................... 10
1.6.
C# Hello world ....................................................................................................................... 15
1.7.
Napredne mogućnosti .NET-a i dodatni materijali ................................................................ 17
1.8.
Zadaci..................................................................................................................................... 18
1.9.
Više informacija o temi .......................................................................................................... 18
1.10. 2.
3.
4.
5.
Izvori .................................................................................................................................. 19
Kratak pregled objektno orijentiranih koncepata ..................................................................... 20
2.1.
Uvod ...................................................................................................................................... 21
2.2.
Imenovanje ............................................................................................................................ 21
2.3.
Tipovi podataka ..................................................................................................................... 23
2.4.
Sekvenca, selekcija i iteracija ................................................................................................ 28
2.5.
Pitanja i zadaci ....................................................................................................................... 33
2.6.
Više informacija o temi .......................................................................................................... 34
Rukovanje greškama u programskom kodu .................................................................................. 35 3.1.
Uvod ...................................................................................................................................... 36
3.2.
Greške u C# aplikaciji ............................................................................................................. 36
3.3.
Iznimke .................................................................................................................................. 39
3.4.
Debugging .............................................................................................................................. 45
3.5.
Pitanja i zadaci ....................................................................................................................... 47
3.6.
Više informacija o temi .......................................................................................................... 49
Verzioniranje i dokumentiranje programskog koda ...................................................................... 50 4.1.
Uvod ...................................................................................................................................... 51
4.2.
Sustavi za verzioniranje ......................................................................................................... 51
4.3.
Razvoj Windows Forms aplikacija ......................................................................................... 55
4.4.
Ostale opcije Git sustava ....................................................................................................... 61
4.5.
Dokumentiranje i komentiranje kôda ................................................................................... 61
4.6.
Pitanja i zadaci ....................................................................................................................... 62
4.7.
Više informacija o temi .......................................................................................................... 64
UML dijagram klasa – koncepti i razvoj ......................................................................................... 65 II
6.
7.
8.
9.
5.1.
Uvod ...................................................................................................................................... 66
5.2.
Osnove objektno orijentiranog pristupa ............................................................................... 66
5.3.
UML dijagram klasa ............................................................................................................... 68
5.4.
Opis procesa – proizvodna traka pogona za sklapanje automobila ...................................... 70
5.5.
Implementacija dijagrama klasa u C# .................................................................................... 72
5.6.
Generiranje klasa ................................................................................................................... 77
5.7.
Pitanja i zadaci ....................................................................................................................... 81
5.8.
Više informacija o temi .......................................................................................................... 82
5.9.
Izvori ...................................................................................................................................... 82
UML dijagram aktivnosti – koncepti i razvoj ................................................................................. 83 6.1.
Uvod ...................................................................................................................................... 84
6.2.
Dijagram aktivnosti – elementi ............................................................................................. 84
6.3.
Implementacija ...................................................................................................................... 88
6.4.
Dretve .................................................................................................................................... 91
6.5.
Pitanja i zadaci ....................................................................................................................... 94
6.6.
Više informacija o temi .......................................................................................................... 95
6.7.
Prilozi ..................................................................................................................................... 96
UML dijagram slijeda – koncepti i razvoj....................................................................................... 97 7.1.
Uvod ...................................................................................................................................... 98
7.2.
Dijagram slijeda – sequence diagram.................................................................................... 98
7.3.
Dijagram slijeda primjera sustava za mrežnu komunikaciju ............................................... 101
7.4.
Implementacija .................................................................................................................... 102
7.5.
Testiranje ............................................................................................................................. 109
7.6.
Pitanja i zadaci ..................................................................................................................... 110
Rad s bazom podataka I – osnove ............................................................................................... 111 8.1.
Uvod .................................................................................................................................... 112
8.2.
ADO.NET .............................................................................................................................. 113
8.3.
Kreiranje aplikacije za evidenciju studenata ....................................................................... 115
8.4.
Pitanja i zadaci ..................................................................................................................... 127
8.5.
Više informacija o temi ........................................................................................................ 127
Rad s bazom podataka II – dataset, procedure, okidači.............................................................. 129 9.1.
Uvod .................................................................................................................................... 130
9.2.
SQL Server 2012 .................................................................................................................. 130
9.3.
Pristupanje bazi podataka korištenjem DataSet-ova. ......................................................... 135
9.4.
Pitanja .................................................................................................................................. 153
9.5.
Više informacija o temi ........................................................................................................ 153 III
10.
Rad s bazom podataka III – ORM............................................................................................. 154
10.1.
Uvod ................................................................................................................................ 155
10.2.
ADO.NET Entity Framework ............................................................................................ 155
10.3.
Kreiranje aplikacije pristupom „Prvo baza podataka“..................................................... 159
10.3.
Pitanja i zadaci ................................................................................................................. 173
10.4.
Više informacija o temi .................................................................................................... 174
11.
Testiranje programa ................................................................................................................ 175
11.1.
Uvod ................................................................................................................................ 176
11.2.
Faze u testiranju .............................................................................................................. 177
11.3.
Izrada jediničnih testova.................................................................................................. 178
11.4.
Izrada kodiranih UI testova ............................................................................................. 184
11.5.
Pitanja i zadaci ................................................................................................................. 187
11.6.
Više informacija o temi .................................................................................................... 187
12.
Vizualizacija podataka – rad s izvještajima i vlastitim kontrolama.......................................... 188
12.1.
Uvod ................................................................................................................................ 189
12.2.
Report viewer .................................................................................................................. 189
12.3.
Izrada složenijeg izvještaja .............................................................................................. 192
12.4.
Google Chart API ............................................................................................................. 198
12.5.
Vlastite kontrole .............................................................................................................. 199
12.6.
Pitanja i zadaci ................................................................................................................. 204
12.7.
Više informacija o temi .................................................................................................... 205
13.
Uvođenje programa u rad ....................................................................................................... 206
13.1.
Uvod ................................................................................................................................ 207
13.2.
Priprema aplikacije za distribuciju ................................................................................... 207
13.3.
Copy &Paste distribucija.................................................................................................. 209
13.4.
Publish ............................................................................................................................. 209
13.5.
Setup................................................................................................................................ 211
13.6.
Pitanja i zadaci ................................................................................................................. 214
14.
Zbirka zadataka........................................................................................................................ 215
Gotove datoteke zadataka koje se rade na vježbama dostupne su na: http://goo.gl/Lo01P1
IV
Odabrana poglavlja programskog inženjerstva – razvoj programa Integrirano razvojno okruženje - IDE
1 . Integrirano razvojno okruženje - IDE Sažetak Na početku poglavlja napravljen je kratak pregled o integriranom razvojnom okruženju (IDE). U uvodu je opisano nekoliko često korištenih IDE alata te su objašnjeni neki osnovni pojmovi u razvoju softvera koji će se koristiti kroz čitav kolegij. Većina pojmova koja se koriste u ovom poglavlju dolaze iz engleskog jezika, te nemaju najspretniji prijevod. Stoga, kod nekih pojmova je naveden i originalni engleski naziv, što čitatelju može poslužiti za daljnje pretraživanje i učenje „jezika struke“. Ključne riječi: IDE, MSDN, Visual Studio 2012 Product Guide
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
1
Odabrana poglavlja programskog inženjerstva – razvoj programa Integrirano razvojno okruženje - IDE
1.1. Uvod Nakon prolaska razdoblja mehaničkog programiranja uslijedilo je razdoblje razvoja softvera putem terminala ili konzole. Naredbe koje bi računalo moralo izvesti zapisivalo se kao niz instrukcija koje bi se zatim interpretirale i izvršavale. Do danas, isti principi su se zadržali no alati koji omogućuju pisanje računalnih programa znatno su napredovali s aspekta korisničke prihvatljivosti. Iako se nerijetko koriste urednici teksta koji rade putem terminala, u daljnjem tekstu koncentriramo se na razvojna okruženja sa grafičkim sučeljima – IDE.
1.2. Što je IDE? IDE (eng. integrated development environment) je softver koji programeru pruža jednostavniji razvoj softvera jer integrira uređivač teksta (eng. editor), kompajler/interpreter i debugger. U ranijim danima razvoja softvera, programer je koristio uređivač teksta (poput edit, nano, pico, Notepad++, itd.), napisao program i spremio ga na nekoj lokaciji na računalu (/home/development/my_code.cpp ili c:\development\my_code.c). Nakon toga, pomoću drugog programa, kompajlera, bi kompilirao kôd. Izlaz iz kompajlera je izvršni program i tekstualni dokument s izvještajem postupka kompilacije. Kompajler je izvršni program koji kao ulaz prima datoteku izvornog kôda, a izlaz su novi program i izvještaj kompilacije. Osnovni zadatak kompajlera je prijevod višeg programskog jezika (C++, Java, C#) u niži programski jezik (asembler, Bytecode, CIL). Današnji kompajleri obično izvršavaju više zadataka: leksička analiza (eng. lexer), parsiranje1 (eng. parser), sintaktička analiza (provjera točnosti), generiranje programskog kôda, optimizacija programskog kôda. Kako je ovo gradivo ranijih kolegija, nećemo detaljnije ulaziti u pojašnjavanje, no za više informacija preporučujemo sljedeću literaturu:
Wikipedia2 – Compilers [1]
Wikipedia – Lexical analysis, [2]
Hints on Programming Language Design, Stanford University, [3]
Programming Language Design and Implementation Course –Rochester University, [4]
Debugger je program koji se koristi za testiranje i otklanjanje pogrešaka iz programa. Izraz debug povijesno ima vrlo zanimljivu priču. Naime, starija računala nisu koristila tranzistore već relejne lampe, koje su prilikom kontakta pregorjele i trebalo ih je zamijeniti. Računala su u tom vremenu bila veličine prostorije ili nekoliko prostorija pa je bilo sasvim normalno da u prostoriju uleti leptir.
Obrada teksta, osnovno tumačenje teksta i pripremanje razumljivih cjelina Članke iz Wikipedije smo provjerili i smatramo ih dovoljno relevantnima i pouzdanima za informativnu razinu. 1 2
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
2
Odabrana poglavlja programskog inženjerstva – razvoj programa Integrirano razvojno okruženje - IDE
Leptiri vole svjetlo, pa su prilikom dodira s lampom uzrokovali kvar hardvera, a time i kvar u izvođenju softvera. Slika 1 prikazuje prvi primjer dokumentirane računalne pogreške (eng. computer bug). Debugger omogućuje pronalaženje računalnog buga, kroz animiranje programa (izvođenje korak po korak), zaustavljanja programa na specificiranim točkama (eng. breakpoint), promjenu programa za vrijeme izvođenja, praćenje stanja varijabli, praćenje stoga itd. Debugging je veliko područje i sastavni dio modernih IDE-a. Više o pronalaženju pogreški u programskom kôdu možete pročitati u trećem poglavlju.
Slika 1: Prvi dokumentirani "Bug"
Moderni IDE alati uz navedene specifikacije uključuju dodatna pomagala za razvoj softvera poput; mogućnost konekcije na bazu podataka, dovršavanje kôda prilikom razvoja (eng. code completion), predlaganje kôda, strukturiranje i organizacija kôda itd. Iako još uvijek programeri koji razvijaju programe za naredbeni redak (bez grafičkog sučelja) i preferiraju stari način bez IDE-a, prednosti IDEa najviše se primjećuju kod razvoja GUI aplikacija (primjerice; Windows Forms, Java SWT, WPF, wxWidgets, qt, itd) i kod razvoja složenih aplikacija (20-ak i više klasa), gdje se izražajno povećava produktivnost.
Često korišteni IDE alati Eclipse Eclipse je jedan od najrasprostranjenijih IDE-a (Slika 2). Za njega postoji veliki broj dodataka (eng. extensions) čime je omogućen razvoj u više od 20 programskih jezika i to na Windows, Linux i MacOS operacijskim sustavima. Iako je Eclipse besplatan, postoji nekoliko komercijalnih izdanja specijaliziranih za razvoj u nekoj okolini (primjerice, MyEclipse Blue). Eclipse je dosta zastupljen u akademskoj zajednici, u razvoju web aplikacija, u razvoju poslovnih aplikacija te u industrijskom programskom inženjerstvu Slika 2: Eclipse - glavni prozor (primjerice, vojska, strojarstvo, autoindustrija). Osnovna izdanja Eclipse-a uključuju Eclipse za Java, C/C++, Eclipse za razvoj paralelnih aplikacija, Eclipse za auto industriju itd. Neki od poznatijih dodataka (eng. plug-in) za Eclipse su UMLet, Subclipse, PyDev, EMF (koji omogućuje MDD, eng. model driven development), Papyrus itd.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
3
Odabrana poglavlja programskog inženjerstva – razvoj programa Integrirano razvojno okruženje - IDE
NetBeans NetBeans je vrlo popularan za razvoj web aplikacija i Java GUI aplikacija (Slika 3). Omogućuje razvoj s Java, PHP, C/C++, Groovy, Ruby itd. Kao i Eclipse, alat je besplatan te sadrži mnogo dodataka i primijenjen je u mnogim industrijama; obrana (NATO MICE Console, Tactical Geographical Information System), zračna industrija (Boeing Shared Platform, AirIT Flight IS), glazba (Music IDE), financije (PowerRate, Experian Banking Software), razvoj softvera (software EKG, Cisco localisation toolkit) itd.
Slika 3: NetBeans - glavni prozor
Embarcadero RAD studio/Borland Builder Embarcadero je proizvođač istoimenog IDE alata (Slika 4). Embarcadero je 2007. godine kupio Borland Software Corporation, te su zajedno ušli u razvoj. Embarcadero IDE je moćan komercijalan alat koji omogućuje razvoj za više programskih jezika, no najpoznatije komponente su JBuilder, RAD Studio, Delphi (jezik) i C++ Builder. Slika 4: Embarcadero RAD Studio - glavni prozor
PyCharm PyCharm (Slika 5), za razliku od prethodna dva IDE-a je vrlo specijalizirani alat za korištenje programskog jezika Python. Omogućuje razvoj konzolnih, grafičkih i web aplikacija. Jedna od vrlo dobrih karakteristika ovog IDE-a jest automatizirano povezivanje biblioteka. Primjerice, ako vam zatreba neki Python modul (primjerice NumPy, PyGame), nakon instalacije istog, PyCharm će sam uočiti promjene, te će ga moći koristiti. Alat je komercijalan, no za akademske svrhe, moguće ga je nabaviti bez novčane naknade.
Slika 5: PyCharm - glavni prozor
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
4
Odabrana poglavlja programskog inženjerstva – razvoj programa Integrirano razvojno okruženje - IDE
Xcode Xcode je IDE koji se najčešće koristi za razvoj programa na Mac računalima te „i“ porodicu proizvoda (iPhone, iPad). Xcode (Slika 6) omogućuje razvoj s Cocoa i Cocoa Touch okvirima (eng. framework) koji su okosnica razvoja za Mac OS. Xcode podržava C, C++, Objective-C, Objective-C++, Java, Python, Ruby, itd.
Slika 6: Xcode
Visual Studio Visual Studio je Microsoftov komercijalni IDE (Slika 7). Za akademsku zajednicu s MSDNAA repozitorija može se preuzeti Premium verzija licencirana za akademsku primjenu. Različiti paketi (Ultimate, Premium, Professional, Test Professional, Team Foundation Server) omogućuju različite funkcionalnosti, no besplatna i najosnovnija verzija je Visual Studio Express Edition. Visual Studio podržava razvoj aplikacija za sve Microsoft platforme i Slika 7: Visual Studio 2012 jezike (C#, F#, VisualBasic, C++). Također omogućuje korištenje jezika za web (HTML5, Javascript). Uz novi poslovni pravac i novi način razvoja aplikacija, Microsoft je uz Visual Studio uveo i posebni alat Blend for Visual Studio (Slika 8). Blend omogućuje razvoj korisničkog sučelja i namijenjen je dizajnerima. Iz korisničkog sučelja kojeg dizajner napravi u Blendu, generira se XAML kôd (eng. Extensible Application Markup Language). Generirani kôd Visual Studio učitava i prikazuje korisničko sučelje, a dodatno omogućuje uređivanje XAML kôda (za dodavanje dodatnih svojstava, programskih događaja (eng. event)). Ideja Microsofta jest potpuno razdvajanje razvoja korisničkog sučelja od programske logike. Programski kôd razvijao bi „developer“, dok bi dizajn razvijao „designer“. Time je nastala kovanica „deviner“, odnosno novi član razvojnog tima, dizajner koji razumije XAML kôd. Za vježbe na kolegiju Programsko inženjerstvo, koristit ćemo Visual Studio 2012 te ćemo ga detaljno upoznati. Slika 8: Microsoft Blend
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
5
Odabrana poglavlja programskog inženjerstva – razvoj programa Integrirano razvojno okruženje - IDE
1.3. .NET Visual Studio primarno služi za razvoj .NET aplikacija. .NET je Microsoftov okvir, odnosno skup biblioteka koji pojednostavljuje razvoj aplikacije namijenjene za Microsoft tehnologije; Windows, Windows Store, Windows Phone, Windows Server i Windows Azure. Slika 9 prikazuje strukturu .NET okvira. Trenutno najniža verzija koju Microsoft podržava je 2.0. Promotrimo primjer jedne WinForms aplikacije .NET 2.0 razine. Ona se sastoji od prozora, tekstualnog polja i nekoliko gumbova (primjerice kalkulator na Windows-u). Prozor aplikacije dolazi iz biblioteke System.Windows.Forms. Ona sadrži sve potrebne elemente za iscrtavanje forme na radnoj površini Windows-a, za praćenje događaja (pritisak tipke tipkovnice ili klik miša) i ostalih potrebnih mehanizama. Taj prozor nazivamo formom. Skup objekata koji možemo prikazati na formi (gumbi, tekstualna polja, polje za datum itd.) nazivamo kontrolama. Sve kontrole koje ćemo trebati za razvoj Windows Forms aplikacije nalaze se u WinForms Slika 9: .NET 4.0 aplikacijski stog, komponenti. Ako neka komponenta nedostaje, možemo Izvor: Wikipedia uzeti apstraktnu klasu Control i proširiti je na način koji je nama potreban (o tome će više riječi biti kasnije). Kada ćemo trebati vezanu listu, stablo, graf ili neku drugu kolekciju (kolekcija = ATP = apstraktni tip podataka), koristit ćemo biblioteku Collections koja se nalazi u BCL-u (eng. Base Class Library – vidi sliku). BCL također sadrži mnoge druge fundamentalne klase koje se koriste u razvoju (primjerice, System, Diagnostics, IO, Reflection, Runtime, Security, Threading, itd.). Prilikom kompilacije programa napisanog za .NET okvir (primjerice u jeziku C#), koristi se zajednička infrastruktura (eng. Common Language Infrastructure) za prijevod na nižu programsku razinu. Svi jezici .NET-a se provode u zajednički posrednički jezik CIL (eng. Common Intermediate Language). CIL je neovisan o platformi. Program preveden u CIL se nakon toga izvršava zajedničkoj infrastrukturi, CLR (eng. Common Language Runtime), koja je ovisna o platformi (centralni procesor, grafički procesor, operacijski sustav, itd.). Na razinama iznad .NET 2.0, postoje razne nadogradnje. Verzija 3.0, je uvela 4 nova važna koncepta u razvoj, WPF (Windows Presentation Foundation – koji služi za razvoj bogatih korisničkih sučelja pomoću XAML-a), WCF (Windows Communication Foundation) – koji služi za implementaciju servisno orijentiranih arhitektura, distribuiranog računarstva i općenito komunikacije među aplikacijama. WCS (Windows Card Space) koji služi za pohranu identiteta (taj projekt je trenutno prekinut) i WF (Windows Workflow Fundation) koji služi za opis procesa i izvršavanje istog.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
6
Odabrana poglavlja programskog inženjerstva – razvoj programa Integrirano razvojno okruženje - IDE
Verzija 3.5 donosi dodatak za ADO.NET nazvan Entity Framework, koji je Microsoftov ORM (eng. object relational mapping) sustav. LINQ (Language Integrated Query) omogućuje izražavanje upita sličnih kao SQL nad kolekcijama, XML-u, Microsoftovim relacijskim bazama i ostalim bazama podataka za koje se koristi ADO.NET pristup. .NET 4.0 uvodi paralelizam s obzirom na ubrzani razvoj višejezgrenih i heterogenih platformi, a verzija 4.5 uvodi podršku za razvoj Windows 8 Metro Style aplikacija, podršku za implementaciju WebSocket-a, podršku za OAuth i OpenID, MVC za Web Forms te ostala proširenja na postojeće komponente iz verzije 4.0. Novosti .NET 4.5 (Slika 10) Izvor3.
Slika 10: .NET 4.5 novosti
1.4. MSDN Već je spomenuto da .NET okvir sadrži više od 45.000 klasa. Prvi dojam odaje veličinu čitavog sustava i strah prema shvaćanju svih pojedinih elemenata. S vremenom, neke klase zastare (eng. deprecated) i izbace se iz upotrebe, a zamjene ih nove. Kako bi se sve to moglo pratiti, učiti i vidjeti u primjeni, Microsoft održava mrežu programera MSDN (Slika 11) te dokumentacijski repozitorij MSDN (eng. Microsoft Development Network) Library.
Slika 11: Početna strana MSDN-a
3
http://www.heikniemi.net/hardcoded/wp-content/uploads/2011/10/WhatsNewNET45-en.png
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
7
Odabrana poglavlja programskog inženjerstva – razvoj programa Integrirano razvojno okruženje - IDE
MSDN Library je prva postaja kod razvoja aplikacija na Microsoftovim tehnologijama. Primjerice, želimo napraviti web aplikaciju i zanima nas kako se spojiti na MySQL bazu podataka. U polje za pretraživanje možemo napisati „web application development“, da bi saznali kako se razvijaju web aplikacije (Slika 12).
Slika 12: MSDN pretraživanje: web application development
Prema prikazanom, zaključujemo da se često ponavlja ASP.NET, a jedan od izvora navodi eng. Creating ASP.NET Web Applications. Sada ćemo provjeriti što je točno ASP.NET, tako da napišemo u pretraživanje samo ključnu riječ „asp.net“ (Slika 13).
Slika 13: MSDN pretraživanje: asp.net
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
8
Odabrana poglavlja programskog inženjerstva – razvoj programa Integrirano razvojno okruženje - IDE
Rezultati daju naslutiti da se radi o skupu biblioteka za razvoj web aplikacija. Sada slijedi čitanje rezultata da bismo bili sigurni. Kada smo se uvjerili, znamo da trebamo ASP.NET, no još uvijek se želimo spojiti na MySQL bazu podataka. Sada ćemo pretražiti „ASP.NET connect to MySQL database“ (Slika 14).
Slika 14: Konačan upit: asp.net connect to mysql database
Prema rezultatima, može se primijetiti da nas MSDN Library upućuje na druge izvore, nekoliko najpoznatijih foruma za programere: CodeProject, Stack Overflow, općeniti forumi, Microsoftova baza znanja i MSDN Library. Možemo koristiti neki od tih izvora, tako da ga označimo i time eliminiramo ostalo. Sada je trenutak kada trebamo odlučiti o kvaliteti izvora i odlučiti se za neko rješenje. Napomena Izvor i autor podataka na Internetu su najvažniji i trebaju biti pouzdani. Često nas pretraživanje može odvesti na neki zastarjeli izvor i zastarjelu informaciju. Stoga, uvijek je dobro pretraživati službene Microsoftove izvore. U gornjem primjeru, u službenoj dokumentaciji Microsoft ne navodi opis jer MySQL nije njihov proizvod, već se preporučuje MS SQL Server. Pomoć kod pretraživanja je pojam najbolje prakse (eng. best practices, primjerice tražimo: best practice for web socket implementation).
Za vježbu, korištenjem MSDN Library pronađite odgovore na sljedeća pitanja: 1. 2. 3. 4.
Što je SQL Server connection pool? Što je to Lightswitch i kako pomoću „njega/nje“ implementirati više naprama više vezu. Kako u Visual Studio-u 2012 dodati referencu na projekt i što je referenca? Što su to regularni izrazi (eng. regular expressions)? © University of Zagreb, Faculty of Organization and Informatics, Varaždin
9
Odabrana poglavlja programskog inženjerstva – razvoj programa Integrirano razvojno okruženje - IDE
1.5. Visual Studio 2012 – Prvi pogled Da bismo malo upoznali Visual Studio (VS) i neke njegove zanimljive mogućnosti, napravit ćemo jedan klasičan zadatak iz ranijih kolegija u C++-u. Cilj zadatka je napraviti jednu klasu, čiji se elementi pohranjuju u vezanu listu. Korisniku mora biti omogućeno dodavanje klasa u listu i ispis čitave liste. Prvi korak pri razvoju ovog rješenja je otvaranje prozora s izborom projekata. Iz izbornika File odaberemo New pa Project. U otvorenom prozoru, odabiremo vrstu i tip projekta (Slika 15). 1. 2. 3. 4. 5. 6.
Popis programskih jezika koje Visual Studio podržava. Ubuduće ćemo koristiti Visual C# projekte, no sada je potrebno odabrati Visual C++ u podizborniku Other Languages. Za svaki jezik moguće je odabrati nekoliko tipova projekta (obrasci projekta). Za svaki projekt, potrebno je navesti ime, lokaciju na datotečnom sustavu te naziv rješenja. Odabir verzije .NET okvira. Koristit ćemo .NET 4.5 Odabir vrste projekta. Koristit ćemo Wind32 Console Application. Odabrat ćemo da želimo kreirati novi direktorij u koji će se smjestiti projekt. Projekt (Name) i rješenje (Solution name) ćemo nazvati VSIntro.
1 4
2
5
3
6
Slika 15: Visual Studio New Project prozor
Nakon pritiska na OK, Next, na posljednjem prozoru odaberemo Empty project. SDL treba isključiti.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
10
Odabrana poglavlja programskog inženjerstva – razvoj programa Integrirano razvojno okruženje - IDE
1
2
3 4
5
Slika 16: Visual Studio 2012 - glavni zaslon
Slika 16 prikazuje tipičan izgled .NET okruženja, koja se ovisno o korištenom tipu projekta ili programskog jezika mijenja. Za sada ćemo se zadržati na C++ okruženju, međutim, pogledat ćemo i okruženje za C# jer ćemo ga ubuduće koristiti. 1. Glavni izbornik – agregira sve mogućnosti IDE-a na jednom mjestu i dinamički se mijenja ovisno o tipu i vrsti projekta. Izbornik File sadrži mogućnosti spremanja projekta, učitavanja, ispisa, verzioniranje itd. Izbornik Edit sadrži mogućnost Copy, Cut, Paste itd. Može se koristiti za refaktoriranje kôda. View izbornik služi za prikazivanje pogleda i prozora (primjerice prozor Solution explorer), Build sadrži opcije za kompilaciju projekta, Debug sadrži mogućnosti za otklanjanje pogrešaka, SQL/Data za spajanje na bazu podataka. Tools, Test i Analyze istražite sami. 2. Alatna traka – sadrži prečace do elemenata iz glavnog izbornika, primjerice za kreiranje projekta, opciju kompilacije, spremanje itd. 3. Preglednik rješenja (eng. Solution Explorer) sadrži datotečno stablo projekta. 4. Izvještajni prozor (eng. Error List) također ima tabularni prikaz. Tri glavna taba su Errors u kojoj se ispisuju sve pogreške u kôdu i koje su uzrok zašto se program ne može kompilirati. Warnings tab ispisuje upozorenja, a Messages tab ispisuje preporuke i poruke VS-a. 5. Glavni prostor – ovaj prostor namijenjen je za pisanje programskog kôda. Na vrhu prostora primijetit ćete tabularnu organizaciju datoteka izvršnog kôda.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
11
Odabrana poglavlja programskog inženjerstva – razvoj programa Integrirano razvojno okruženje - IDE
Upravljanje prozorima Kada želimo dodati još koji prozor na pogled glavnog zaslona VS-a, možemo to učiniti iz izbornika View. Možemo mijenjati raspored onako kako nam najviše odgovara, tako da mišom uhvatimo tab prozora koji želimo pomaknuti te isti pomaknemo (Slika 17).
Slika 17: Visual Studio 2012, premještanje prozora
Na zaslonu se pojavljuje pomoćni prikaz te ako iznad njega prođemo mišem (još uvijek držimo prozor), VS će nam prikazati gdje će smjestiti prozor.
Prvi program u VS 2012 Možemo započeti s pisanjem kôda. Najprije ćemo u strukturu projekta dodati novu .cpp datoteku. Desnim klikom na VSIntro (korijen strukture u pregledniku rješenja), pojavit će se kontekstni izbornik iz kojeg ćemo odabrati opciju Add i onda New Item. Otvorit će se prozor u kojem ćemo odabrati C++ File (.cpp), nazvat ćemo ga Source.cpp i pritisnuti Add. Za početak, moramo dodati osnovnu biblioteku C++-a. Počnemo pisati #include
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
12
Odabrana poglavlja programskog inženjerstva – razvoj programa Integrirano razvojno okruženje - IDE
Slika 18: IntelliSense
Implementirat ćemo klasu Automobil s atributima broja šasije, marke automobila, naziva automobila, iznos CO2 ispušnih plinova te pokazivač na sljedeći element liste. Zadatak Napravite vezanu listu i beskonačnu petlju koja omogućuje dodavanje elemenata i ispis elemenata iz liste. Element koji se upisuje je klasa Automobil.
Napomena Kod punjenja liste, primijetite da svaki puta nakon ->, vam VS ponudi listu mogućih atributa koji se nalaze u vašoj klasi Automobil.
Error! Reference source not found. #include
using namespace std; class Automobil{ public: int Sasija; char MarkaAutomobila[20]; char NazivAutomobila[20]; double CO2; Automobil *pokazivacNaSlijedeci; void unos(){ cout<<"Broj sasije automobila: "; cin>>this->Sasija; cout<<"Marka: "; cin.ignore(); cin>>this->MarkaAutomobila; cout<<"Model: "; cin.ignore(); cin>>this->NazivAutomobila; cout<<"Ispusni plinovi CO2 (g/km): "; cin>>this->CO2; this->pokazivacNaSlijedeci = NULL;
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
13
Odabrana poglavlja programskog inženjerstva – razvoj programa Integrirano razvojno okruženje - IDE
} void ispis(){ cout<<"Broj sasije: "<Sasija<MarkaAutomobila<<" "<NazivAutomobila<CO2<unos(); Automobil *posljednjiUListi = list; while(posljednjiUListi->pokazivacNaSlijedeci) posljednjiUListi = posljednjiUListi->pokazivacNaSlijedeci; posljednjiUListi->pokazivacNaSlijedeci = noviElementListe; } void ispisiAutomobile(Automobil *list){ Automobil *trenutniUListi = list->pokazivacNaSlijedeci; int biggest = 0; while(trenutniUListi){ trenutniUListi->ispis(); trenutniUListi = trenutniUListi->pokazivacNaSlijedeci; } } void main(){ int choice = -1; int N = 100; Automobil *listaAutomobila = new Automobil(); while(true){ system("cls"); cout<<"What to do?"<>choice; switch(choice){ case 0: exit(1); break; case 1: unosAutomobilaUListu(listaAutomobila); break; case 2: ispisiAutomobile(listaAutomobila); break; } system("pause"); } }
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
14
Odabrana poglavlja programskog inženjerstva – razvoj programa Integrirano razvojno okruženje - IDE
Pretraživanje projekta Kako ćemo u projektima često susresti tisuće, desetke tisuća i u velikim projektima stotine tisuća linija kôda, za lakše snalaženje potrebna je mogućnost pretraživanja. U VS-u, iz izbornika Edit, odaberimo Find and Replace opciju i unutar nje Quick Find.
Slika 19: Prozor za brzo pretraživanje
Pojavljuje se izbornik s poljem za pretraživanje u kojem možemo odabrati želimo li osjetljivost na mala ili velika slova, pretraživanje cijele riječi ili dijela riječi, pretraživanje trenutnog dokumenta, trenutnog projekta, trenutno rješenje ili svih otvorenih dokumenata (Slika 19). Nije rijetka pojava da se jedno rješenje sastoji od više projekata, pisanih u više jezika. Upravo zato dodano je i svojstvo pretraživanja pomoću regularnih izraza (eng. regular expressions, RegEx). U izborniku Find and Replace također možemo odabrati i složenije pretraživanje; Find in Files. Odabirom te opcije otvara se novi prozor s mnoštvom opcija.
1.6. C# Hello world U .NET okviru razlikujemo dva osnovna tipa programskih jezika, oni koji se izvršavaju na razini operacijskog sustava (eng. unmanaged), odnosno nenadzirani programski jezici. Njima je moguće pristupati direktno do memorijskog adresnog prostora. U .NET-u to je C++. S druge strane postoje nadzirani programski jezici (eng. managed) koji se izvode na virtualnom stroju .NET okvira. Toj kategoriji pripadaju C#, VisualBasic, F# itd. Tijekom razvoja .NET-a, tako se C# nazivao Managed C, no kasnije mu je autor Andreas Hejlsberg dao naziv Cool (eng. C-like object oriented language). S obzirom da je to ime zauzeto, na Microsoft PDC4 konferenciji dobio je naziv prema glazbenom tonu, C#, odnosno C s povisilicom, koji simbolizira razinu više od C-a.
4
Professional development conference
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
15
Odabrana poglavlja programskog inženjerstva – razvoj programa Integrirano razvojno okruženje - IDE
C# je danas programski jezik opće namjene. Uključuje mogućnost jakih i dinamičnih tipova, imperativno, deklarativno, funkcionalno, generičko, objektno i komponentno orijentirano programiranje. Jezik također sadrži generiku, parcijalne tipove, anonimne metode, iteratore, anonimne tipove, upitne izraze, lambda izraze, parcijalne metode, dinamičko povezivanje, ugrađene tipove za interoperabilnost, asinkrone metode, podršku za paralelizam itd. Sada ćemo napraviti novi C# projekt. Koristit ćemo Console Application i projekt ćemo nazvati HelloCSharpWorld. Jednostavno ćemo ispisati poruku „Hello world“ (Slika 20). Prvih 5 linija kôda je deklarativnog tipa i služi nam za učitavanje osnovnih biblioteka. Primijetite pored kôda mali znak „-„, klikom na isti, taj će se dio sakriti. Trenutno ne trebamo ni jednu biblioteku osim System, stoga ostale možemo ukloniti. U liniji 7 deklarirani je imenski prostor (eng. namespace). Imenski prostori obično služe za grupiranje niza klasa Slika 20: Hello world aplikacija slične funkcionalnosti. Trenutno imamo samo jednu klasu koja se zove Program i ona sadrži samo jednu metodu koja se zove Main. Iz statičnog objekta Console, pozvat ćemo metodu WriteLine za ispis teksta, te metodu Read, zbog toga da se prozor terminala ne ugasi odmah nakon ispisa. Za neke specifične slučajeve, .NET ne sadrži gotova rješenja (biblioteka za naprednu matematiku, strukturu podataka matrice, gotov algoritam neuronske mreže itd.) stoga moramo koristiti gotova rješenja treće strane ili razviti vlastite biblioteke, zapakirati ih u prenosivu komponentu kako bismo je u budućnosti mogli koristiti. Kako bi je koristili u projektu, moramo dodati referencu, a u C#-u najčešća vrsta komponente je DLL (eng. dynamic link library). U demonstracijske svrhe, pripremili smo PIComponent.dll, koji ćemo sada referencirati u naš projekt i pomoću IntelliSense-a saznati koje metode možemo koristiti. Da bismo dodali referencu moramo odabrati opciju Project pa Add Reference (iz glavnog izbornika). Odabrat ćemo opciju Browse i pronaći PIComponent.dll na datotečnom sustavu. Primijetite da je još moguće referencirati druge projekte, komponente .NET-a te COM-a , koji sadrži mnoge zanimljive biblioteke aplikacija koje su instalirane na računalu, primjerice Google Talk API (eng. Application program interface). Sada ćemo u novi redak upisati PIComponent. i pogledati koje nam mogućnosti nudi Visual Studio.
Slika 21: Istraživanje građe objekta pomoću IntelliSense
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
16
Odabrana poglavlja programskog inženjerstva – razvoj programa Integrirano razvojno okruženje - IDE
Radi se o klasi IDClass (Slika 21) koja sadrži metodu OMeni. Metoda OMeni nam vraća tip podataka string. Da bismo to provjerili, zadržavanjem miša iznad metode, pojavit će se kontekstni izbornik s njenim proširenim opisom. Napomena Ako IntelliSense ne nudi nikakav ispis, a dio kôda je podcrtan crvenom linijom, nešto nije dobro napravljeno. Najčešće se uzrok pogreške može pročitati u izvještajnom prozoru.
Slika 22: Prikaz građe metode
Povratni tip string i metoda ne prima parametre (Slika 22). Ovaj postupak vrijedi za sve objekte unutar .NET-a. Ispišite povratni tekst na zaslon u konzoli.
1.7. Napredne mogućnosti .NET-a i dodatni materijali Visual Studio omogućuje mnogo više nego smo ovdje prikazali, a i više od onoga što ćemo obuhvatiti na ovom kolegiju. Sada ćemo spomenuti neke dodatne funkcionalnosti koje vam mogu biti korisne u budućnosti, a i za one koji žele znati više. Kod traženja originalnim Microsoftovih dokumenta, dobro je tražiti „white paper“, primjerice „white paper on visual studio 2012“. Za najbolji pregled i razumijevanje mogućnosti koje pruža VS, preporučujemo Visual Studio 2012 Product Guide. 1. Razvoj za Windows 8 Windows 8 nosi novo korisničko sučelje nazvano Metro. Za razvoj Metro aplikacija VS pruža posebne mogućnosti za razvoj istih. Te mogućnosti uključuju podršku za HTML5, CSS3 i JavaScript kojima Web programeri mogu razvijati Widnows 8 aplikacije, za .NET programere postoji podrška za XAML, C# i VisualBasic. Dodane su opcije za korištenje DirectX 11 grafičkog repozitorija, mogućnost razvoja upravljačkog softvera (eng. driver). Uz sve navedeno programer ima mogućnost objave svoje aplikacije na Windows Store. 2. Razvoj za Web Za web programere, već smo ranije naveli da postoji podrška za najnoviji HTML i CSS, a također je pojednostavljen razvoj, testiranje i otklanjanje pogreške. Olakšano je korištenje OAuth-e, dodana je podrška za mobilne telefone u obliku pregleda web stranice s ograničenjima mobilnog telefona.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
17
Odabrana poglavlja programskog inženjerstva – razvoj programa Integrirano razvojno okruženje - IDE
3. Razvoj za oblak Visual Studio sadrži podršku za Windows Azure, Microsoftov oblak. Olakšan je razvoj, pronalaženje greški, objava aplikacije, korištenje baze podataka itd. 4. Razvoj za SharePoint SharePoint je Microsoftov servis koji omogućuje suradnju poslovnih entiteta unutar organizacije. VS 2012 sadrži nešto bolju podršku razvoju dodataka za SharePoint. 5. ALM (eng. Application Lifecycle Management) – upravljanje životnim ciklusom aplikacije Team Foundation Server je posebni dodatak za VS koji omogućuje kontinuirano praćenje i integraciju rješenja unutar tima. ALM je sustav koji omogućuje nadzor čitavog tog sustava. Svatko ima sve potrebno za svoju ulogu, tako primjerice programer u kôdu koristi anotacije za naznaku do koje razine je stigao, da bi voditelj projekta mogao pratiti napredak u svom pogledu na projekt. ALM je vrlo moćno rješenje za timove, može znatno smanjiti troškove razvoja uz bolje praćenje te izvještavanje.
1.8. Zadaci 2. Korištenjem standardne biblioteke STL, implementirajte zadatak s vezanom listom korištenjem gotove liste list iz zaglavlja . 3. Proučiti prozor složenog pretraživanja. 4. Korištenjem C# programskog jezika implementirati zadatak 1, uz klasu List.
1.9. Više informacija o temi 1. „What's New in Visual Studio 2012“? (http://msdn.microsoft.com/enus/library/vstudio/bb386063.aspx), dostupno: prosinac, 2013. 2. Visual Studio 2012 Product Guide (http://www.microway.com.au/microsoft/Visual-Studio-2012Product-Guide.pdf), dostupno: prosinac, 2013. 3. Visual Studio Training (http://www.microsoft.com/learning/en/us/visual-studio-training.aspx), dostupno: prosinac, 2013.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
18
Odabrana poglavlja programskog inženjerstva – razvoj programa Integrirano razvojno okruženje - IDE
1.10. Izvori
[1] Wikipedia, 2 2013. [Mrežno]. Available: http://en.wikipedia.org/wiki/Compiler. [2] Wikipedia, 2 2013. [Mrežno]. Available: http://en.wikipedia.org/wiki/Lexical_analysis. [3] C. Hoare, December 1973. [Mrežno]. Available: http://www.cs.berkeley.edu/~necula/cs263/handouts/hoarehints.pdf. [4] M. Scott, 2012. [Mrežno]. Available: http://www.cs.rochester.edu/u/scott/254/ .
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
19
Odabrana poglavlja programskog inženjerstva – razvoj programa Kratak pregled objektno orijentiranih koncepata
2 . Kratak pregled objektno orijentiranih koncepata Sažetak Opisani su osnovni pojmovi programiranja poput varijable, sekvence, selekcije i iteracije. Kroz primjere prikazani su osnovni koncepti programiranja u duhu objektno orijentiranog pristupa. Nadalje, definirane su smjernice pisanja dobrog kôda poput imenovanja i pozicioniranja elemenata. Ključne riječi: osnove, imenovanje, objektno orijentirani pristup, sekvenca, selekcija, iteracija
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
20
Odabrana poglavlja programskog inženjerstva – razvoj programa Kratak pregled objektno orijentiranih koncepata
2.1. Uvod Programiranje je proces stvaranja kreativnog rješenja nekog problema. Danas postoji veliki broj razvojnih alata prilagođenih velikom broj tipova problema odnosno rješenjima. No, suština svega je tehnika programiranja, strukturni i kritički način razmišljanja. Bill Gates je jednom prilikom izjavio: „I choose a lazy person to do a hard job. Because a lazy person will find an easy way to do it.“ U ovom poglavlju prisjetit ćemo se osnovnih koncepata vezanih uz programiranje kako bi bili spremni za rješavanje kompleksnijih problema na što jednostavniji način. Napomena Sve primjere radimo na konzolnoj aplikaciji koja se vizualno i sadržajno razlikuje od Windows Forms aplikacija
2.2. Imenovanje Postoji nekoliko standarda imenovanja elemenata programskog kôda, što ovisi o jeziku i tehnologiji, ali također ovisi i o samim programerima. Stoga, postoje organizacije koje koriste svoje standarde. S obzirom na to da mi koristimo zadnju verziju .Net razvojne okoline, u pravilu ćemo se pridržavat .Net smjernica za imenovanja s malim izmjenama prilagođenima našim potrebama.
Notacije 1. Pascal Ova notacija nalaže da se svaka riječ u nazivu elementa piše velikim početnim slovom bez razmaka. Npr. OvoJeIme 2. Camel Case Za razliku od Pascal notacije ovdje se prva riječ piše malim početnim slovom dok se ostale pišu velikim početnim slovom. Npr. ovoJeIme 3. Mađarska Prije mnogo godina programiranje je bilo kompliciranije iz više razloga: imena varijabli su trebala biti kratka jer su duga imena utjecala na veličinu same izvršne datoteke. Osim toga nisu postojali dobri razvojni alati koji tokom programiranja ukazuju na meta karakteristike elemenata koji se koriste (kao što to radi intellisense ili tooltipovi). Zbog toga je uvedena mađarska notacija koja nalaže da ime bude kratko, idealno do 8 znakova, iako je u praksi taj limit zanemaren. Osim toga nalaže se obavezni prefiks koji označava tip podataka npr. varijabla: dbRadius – db označava double tip podataka dok je Radius stvarno ime varijable.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
21
Odabrana poglavlja programskog inženjerstva – razvoj programa Kratak pregled objektno orijentiranih koncepata
U modernim razvojnim okolinama nije potrebno navoditi tip podatka u ime elementa iz razloga što je tip vidljiv kroz sučelje razvojne okoline samim korištenjem elementa (intellisense ili tooltipovi). Od navedenih notacija koristiti ćemo Pascal i Camel Case notacije za definiranje naziva elemenata kôda. Tablica 1: Notacija Pascal i Camel Case
Imenovanje po elementima Element
Notacija
Napomena
Class
Pascal
U pravilu je to imenica.
Enum
Pascal
Ne smije imati sufiks Enum
Event
Pascal
Exception class
Pascal
Uvijek ima sufiks Exception
Interface
Pascal
Uvijek ima prefiks I (veliko slovo i)
Method
Pascal
U pravilu je to glagol.
EventHandler delegate
Pascal
Objekt uvijek ima sufiks EventArgs
Parameter
Camel Case
Property
Pascal
Private field
Camel Case
Public field
Pascal
Variable
Camel Case
Tablica 1 prikazuje korištenje pojedinih notacija za imenovanje elemenata programskog kôda.
Skraćenice Generalno skraćenice nisu dozvoljene jer mogu proizvesti zabune kod korištenja elemenata te sami programeri mogu ponekad pobrkati iste. Naravno, neke skraćenice su dozvoljene, poput: ID, XML, FTP i sl. One se pišu velikim slovima samo ako su kraće od 2 znaka. Skraćenice od 3 ili više znaka se pišu Pascal notacijom. Npr. XmlDataReader ili UserID. Kako više nije bitna veličina kôda preporučuje se za nazive koristiti pune izraze jer su lakše čitljivi i razumljiviji. S druge strane preveliki nazivi mogu kôd učiniti teško čitljivim. Stoga je potrebno razumno nazivati elemente npr.
VarijablaZaBrojanjaKorakaWhilePetlje – dobro opisani naziv ali predug, loša praksa. Brojac – dobar primjer naziva elementa. Za razliku od prethodnog veličine je samo jedne riječi. Postavlja se pitanje, a kako znati za što je taj brojač? Odgovor je iz konteksta. Ova varijabla će, u pravilu, biti korištena u while petlji pa će se znati njena izvorna svrha.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
22
Odabrana poglavlja programskog inženjerstva – razvoj programa Kratak pregled objektno orijentiranih koncepata
2.3. Tipovi podataka Suština programiranja je logička obrada podataka različitih tipova. Tip podatka definira izgled memorijskog zapisa podatka u memoriji računala. Osim toga tip podatka definira moguće operacije (logičke obrade) koje je moguće odraditi nad podatkom. Npr. podatke tipa tekst ne možemo množiti kao što to možemo raditi s podacima cjelobrojnog tipa. Postoje dvije vrste tipova podataka:
Jednostavni Složeni
Jednostavni tipovi podataka Različiti programski jezici definiraju različite jednostavne tipove podataka. Tablica 2 opisuje osnovne jednostavne tipove podataka. Tablica 2: Jednostavni tipovi podataka
Tip
Opis
Numerički tipovi Byte
Raspon vrijednosti: 0 do 255.
Sbyte
Signed byte, raspon vrijednosti: -128 do +127.
Decimal
Raspon vrijednosti: -7.9*1028 do +7.9*1028, vrijednost ima opcionalni sufiks „m“.
Double
Raspon vrijednosti: ±5.0*10−324 do ±1.7*10308, vrijednost ima opcionalni sufiks „D“.
Float
Raspon vrijednosti: -3.4*1038 do +3.4*1038, vrijednost ima opcionalni sufiks „F“.
Int
Raspon vrijednosti: -2 147 483 648 do +2 147 483 647.
Uint
Unsigned int, Raspon vrijednosti: 0 do 4 294 967 295, vrijednost ima opcionalni sufiks „U“.
Long
Raspon vrijednosti: –9 223 372 036 854 775 808 do +9 223 372 036 854 775 807, vrijednost ima opcionalni sufiks „L“.
Ulong
Unsigned long, Raspon vrijednosti: 0 do 18 446 744 073 709 551 615, vrijednost ima opcionalni sufiks „UL“.
Short
Raspon vrijednosti: -32 768 do +32 767.
Ushort
Unsigned short, Raspon vrijednosti: 0 do 65 535.
Ostali tipovi Bool
Logički tip podataka, može imati dvije vrijednosti: True ili False.
Object
Bazični tip podataka za sve ostale tipove podataka. To znači da su svi objekti ili varijable nekog tipa ujedno i tipa object.
Void
Tehnički ovo nije tip podataka nego samo označava da npr. neka metoda nema povratni tip podataka, interpretirati se može kao „bez tipa“, „nikakvi tip“.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
23
Odabrana poglavlja programskog inženjerstva – razvoj programa Kratak pregled objektno orijentiranih koncepata
Znakovni tipovi Char
Predstavlja vrijednost jednog znaka. Npr 'A'.
String
Predstavlja sekvencu od 0 ili više znakova. Npr: „ovo je string“.
U tablici su podebljani oni tipovi podataka koje ćemo koristiti. Odabir tipa podataka, posebno numeričkih, ovisi o primjeni. Zbog racionalnog korištenja resursa računala, poželjno je koristiti minimalni tip podataka potreban da se pohrane sve moguće vrijednosti. Kôd 1: Primjer korištenja sufiksa float varijabla = 2.3F;
Složeni tipovi podataka Složeni tipovi podataka sastoje se od više različitih ili istih jednostavnih (ili složenih) tipova podataka, a dijele se na: Polja Sastoje se od N elemenata od jednog jednostavnog (ili složenog) tipa podataka. Npr. Polje brojeva tipa int dužine 7 elemenata: 1 2 3 4 4 5 6
Napomena Redni brojevi elemenata u nekom polju počinju od 0 (nule). Generalno u .Net-u indeksi elemenata polja, kolekcija, nizova,… počinu od 0 (nule) to je tzv. Zero based indexing
Strukture i klase Za razliku od polja strukture i klase mogu imati elemente različitih jednostavnih (ili složenih) tipova podatka. Razlika između struktura i klasa je u činjenici da klase u sebi mogu imati programsku logiku i podatke dok strukture mogu imati samo podatkovni dio.5 Na primjer: Tablica 3
Struktura Slika Naziv, tipa string VrijednostKune, tipa int
5
„Vrisak“ 1452
Iako, u novijim programskim jezicima ta razlika praktički ne postoji.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
24
Odabrana poglavlja programskog inženjerstva – razvoj programa Kratak pregled objektno orijentiranih koncepata
Tablica 4
Klasa Slika Naziv, tipa string VrijednostKune, tipa int Operacija ProdajSliku()
„Vrisak“ 1452
Tablica 3 definira strukturu dok Tablica 4 definira klasu za umjetničke slike. Prva kolona opisuje tip dok druga definira primjer vrijednost konkretnog objekta. Klasa Slika ima jednu operaciju koja se zove ProdajSliku(). Ovu operaciju struktura ne može imati.
Varijable, reference, objekti i polja Varijable su elementi programskog kôda u koje pohranjujemo podatke. Svaka varijabla mora imati definirani svoj tip podataka i ime (u skladu sa smjernicama imenovanja). Dobra praksa je varijable deklarirati na početku bloka u kojem se koriste, npr. početak klase ili na početku metode, ako se radi o varijabli koja će se koristiti samo u toj metodi. Deklariranu varijablu je moguće koristiti, u pravilu, na dva
Kôd 3: Deklaracija varijable int varijabla;
načina: čitanje varijable i zapisivanje u varijablu. Čitanje se radi samim „spominjanjem“ imena varijable. varijabla = 2; Vrijednost se u varijablu zapisuje korištenjem znaka „=“, uvijek se s lijeve strane nalazi element u koji pohranjujemo vrijednost navedenu s desne strane znaka „=“. Kôd 2: Zapisivanje u varijablu
Moguće je prilikom same deklaracije pohraniti inicijalnu vrijednost, ta procedura se zove inicijalizacija varijable. Reference su varijable složenog tipa. Reference možemo promatrati kao pokazivače iz C/C++ jezika, ali puno jednostavnije. Primjer korištenja referenci pokazat ćemo na primjeru klase Slika. Za početak definirajmo klasu Slika. Ovdje nećemo ulaziti u načine definiranje klase, to je tema jednih od sljedećih vježbi, samo je bitno da smo definirali jedan složeni tip podataka koji se zove Slika. Deklariranje reference tipa klase Slika se radi na isti način kao i deklariranje varijable.
Kôd 4: Inicijalizacija varijable int varijabla = 2;
Kôd 5: Definiranje klase Slika class Slika { string Naziv; int VrijednostKune; void ProdajSliku() { } public Slika() { } }
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
25
Odabrana poglavlja programskog inženjerstva – razvoj programa Kratak pregled objektno orijentiranih koncepata
Kôd 6: Deklariranje reference Slika referenca;
Samim deklariranjem reference, Visual Studio će nas upozoriti da smo deklarirali varijablu, a da istu nismo koristili. Upozorenje nestaje, ako inicijaliziramo referencu. Referenci smo dodijelili vrijednost null koja označava praznu vrijednost, ništa, stoga je vrijednost inicijalizirana u ništa. Sadržaj varijable (reference) koja se zove „referenca“ je ništa odnosno Kôd 7: Inicijalizacija reference null. Dobra praksa je prazne reference inicijalizirati s null. Slika referenca = null;
Što je onda referenca? -
To je pokazivač na objekt odnosno instancu neke klase i služi nam za manipuliranje karakteristikama i metodama objekata neke klase.
Objekt je instanca neke klase. Instanciranje odnosno stvaranje objekta neke klase se radi korištenjem ključne riječi new. Nakon riječi new slijedi poziv konstruktora.
Kôd 8: Instanciranje objekta klase referenca = new Slika();
Ovime naša referenca više nema vrijednost null nego stvarnu vrijednost konkretnog objekta6. U memoriji (u stvarnosti) postoji samo jedan novi objekt klase Slika. Kôd 9: Instanciranje više klasa Slika referenca = new Slika(); Slika referenca2 = referenca; Slika referenca3 = new Slika();
Ovdje je prikazano instanciranje dva objekta klase Slika. Međutim tu su deklarirane i tri reference tipa Slika.
Reference: referenca i referenca2 pokazuju na prvi objekt klase Slika. Referenca referenca3 pokazuje na drugi objekt klase Slika. Mijenjanjem stanja objekta identificiranog referenca sve izmjene će istog trena biti dostupne i korištenjem reference referenca2. Strukturni objekti su instance strukture. Ranije smo opisali razliku između strukture i klase. Instanciranje objekata struktura se radi na isti način kao i instanciranje objekata neke klase. Međutim, strukture su „niži“ koncept od klase pa postoji i mogućnost deklariranja struktura kao i jednostavne varijable koristeći sintaksu TipStrukture ImeStrukture. U oba slučaja u memoriji će biti instanciran samo jedan objekt strukture. Polja su složeni tip podataka koje također treba instancirati na sličan način kao i klase. Kôd 10: Instanciranje polja Točnije njegovu memorijsku adresu, ali radi jednostavnosti smatramo to vrijednošću novog objekta. Štoviše vrlo često ćemo o „referenci na objekt neke klase“ govoriti samo „objekt neke klase“. 6
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
26
Odabrana poglavlja programskog inženjerstva – razvoj programa Kratak pregled objektno orijentiranih koncepata
int[] polje = new int[7];
Ovdje je instancirano polje brojeva u koje stane 7 elemenata.
Vrijednost prvom i drugom elementu polja se radi ovako: Kôd 11: Dodjeljivanje vrijednosti elementima polja polje[0] = 3; polje[1] = 1;
Primijetimo da se elementima polja pristupa preko indeksa koji se definira uglatim zagradama[] nakon imena varijable odnosno reference.
Napomena Brisanje objekata: U većini slučajeva nije nužno voditi brigu o brisanju objekta jer .Net koristi Garbage Collector za dealociranje (brisanje) svih objekata i varijabli koje se više ne koriste. Ako na neki objekt ne pokazuje niti jedna referenca tada će on automatski biti dealociran odnosno obrisan.
Pitanje za razmišljanje (1) int a = 1; int b = 3; Suma svih brojeva od a do b je 6. Kolika bi bila suma svih brojeva od a do b ako su a i b tipa float?
2.3.1.
Promjena tipa podataka (cast)
Često ćemo se naći u prilici da moramo promijeniti tip podataka nekoj varijabli ili objektu. Takav proces zadaje puno problema zato što nisu svi tipovi kompatibilni i kod promjene tipa može doći do gubitka informacija. Na primjer: U varijablu tipa float pohranjujemo decimalni broj. Kôd 12: Promjena tipa podataka Ako toj varijabli promijenimo tip u int tada će doći float a = 3.45F; do gubitka informacije, konkretno izgubit će se čitavi int b = (int)a; decimalni dio jer int tip podataka samo pohranjuje cijele brojeve. U konkretnom primjeru vrijednost varijable a je 3.45 dok je vrijednost varijable b 3. Sintaksa promjene tipa podatka je takva da ispred varijable u zagradama ( ) navedemo novi tip podataka kojeg želimo privremeno dodijeliti toj varijabli. Referentni tipovi (klase i strukture) imaju mogućnost korištenja i drugačije sintakse: © University of Zagreb, Faculty of Organization and Informatics, Varaždin
27
Odabrana poglavlja programskog inženjerstva – razvoj programa Kratak pregled objektno orijentiranih koncepata
(objekt as NewType) - sve unutar zagrade, ispred as će biti promatrano kao objekt tipa NewType. Napomena Nisu svi tipovi kompatibilni i prikladni za međusobne promjene. Klase i potklase imaju hijerarhiju koju je lako pratiti. Nije moguće npr. napraviti konverziju između tipa string i tipa Slika. Zato što nisu kompatibilni tipovi odnosno nisu u nikakvom odnosu.
Pitanje za razmišljanje (2) U koji tip podataka uvijek možemo promijeniti bilo koju varijablu bio kojeg tipa?
2.4. Sekvenca, selekcija i iteracija Sada kada smo upoznati s podacima i definiranjem podataka u programskom kôdu, možemo se upoznati s kontrolnim strukturama. Sekvenca Sekvenca je prva i osnovna struktura. Programski kôd se izvodi slijedno, linearno, jedna naredba iza druge. Pojedina naredba može biti alias za blok veliki blok drugih naredbi koje se opet izvode slijedno. Nakon završetka izvođenja jedne naredbe (koja se može sastojati od više drugih naredbi) izvođenje se nastavlja nakon naredbe koja se prethodno izvršila. Sa sekvencom smo se već susreli u isječcima programskog kôda u ovom poglavlju. Kôd 13: Primjer sekvence int prviBroj; int drugiBroj; int rezultat; prviBroj = 3; drugiBroj = 4; rezultat = prviBroj + drugiBroj;
Ovaj programski isječak deklarira tri varijable. U prve dvije se pohranjuje konkretna vrijednost dok se u treću pohranjuje rezultat zbrajanja vrijednosti iz prve dvije varijable.
Selekcija Selekcija je grananje programskog kôda na 0 ili više dijelova. © University of Zagreb, Faculty of Organization and Informatics, Varaždin
28
Odabrana poglavlja programskog inženjerstva – razvoj programa Kratak pregled objektno orijentiranih koncepata
Kôd 14: Primjer selekcije – if(…){…}else{…} static void Main(string[] args) { int prviBroj; int drugiBroj; int rezultat; prviBroj = 3; drugiBroj = 4; if (prviBroj == 10) { // neće se izvršiti rezultat = 10; } else { Rezultat = prviBroj + drugiBroj; } }
U ovom primjeru i ostalima na dalje, je prikazana čitava Main metoda. To je prva metoda koja se izvrši prilikom pokretanja programa. Selekcija je u ovom primjeru prikazana koristeći if {..} else {…} naredbe. U zagradama if naredbe se nalazi logički izraz (koji je lažan). Ako je logički izraz istina, izvršit će se kôd ili blok kôda iza if naredbe (crveno). Ako je logički izraz lažan (u primjeru je lažan) izvršit će se kôd ili blok kôda iza else naredbe (zeleno).
Napomena Blok kôda je bilo koji set naredbi jedna iza druge definiran s vitičastim zagradama { }. U prethodnom primjeru nismo trebali koristiti vitičaste zagrade za selekciju zato što se radi o jednoj naredbi, međutim dobra praksa je uvijek staviti vitičaste zagrade radi lakše čitljivosti kôda.
Kôd 15: Primjer selekcije – switch
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
29
Odabrana poglavlja programskog inženjerstva – razvoj programa Kratak pregled objektno orijentiranih koncepata
static void Main(string[] args) { int prviBroj; int rezultat; prviBroj = 1; switch (prviBroj) { case 1: { rezultat = 11; break; } case 2: { rezultat = 12; break; } default: { rezultat = 10; break; } } }
Switch omogućava višestruko grananje gdje odabir grane ovisi o vrijednosti varijable unutar zagrada switch naredbe, u ovom slučaju je to vrijednost varijable prviBroj. Ako je prvi broj jednak 1 tada će se izvršiti blok kôda definiran iza oznake case 1: tj. u varijablu rezultat će se pohraniti vrijednost 11. Ako je prvi broj jednak 2 tada će se izvršiti blok kôda definiran iza oznake case 2: tj. u varijablu rezultat će se pohraniti vrijednost 12. Ako prviBroj ima vrijednost koja nije definirana niti jednom case oznakom tada će se izvršiti blok kôda definiran default: oznakom tj. u varijablu rezultat će se pohraniti 10.
Napomena Break: Iza svake case oznake treba biti minimalno jedna naredba, a to je break naredba koja govori da se izvođenje mora zaustaviti na mjestu gdje se ista nalazi. Break naredba je i obavezna na kraju svakog bloka kôda koji se treba izvesti nakon case (ili default) oznake. Nadalje, moguće je koristiti tu naredbu za „prisilno“ prekidanje iteracija neovisno o definiranom uvjetu izlaska iz petlje.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
30
Odabrana poglavlja programskog inženjerstva – razvoj programa Kratak pregled objektno orijentiranih koncepata
Kôd 16: Primjer selekcije – inline static void Main(string[] args) { int prviBroj; int rezultat; prviBroj = 1; rezultat = (prviBroj == 1) ? 11 : 10; }
Ovaj primjer selekcije je često korišten kod malih grananja, u pravilu samo za definiranje vrijednosti neke varijable. Kôd provjerava varijablu prviBroj i ako je ona 1 tada se u varijablu rezultat zapisuje 11 inače se zapisuje 10.
Sintaksa ove selekcije bi bila: objekt = (logički izraz)? „vrijednost ako je izraz istina“ : „vrijednost ako je izraz neistina“ Vrijednost ne mora biti konkretna vrijednost nego može biti i izraz (programski kôd) koji vraća neku vrijednost. Iteracija Iteracija definira koncept ponavljanja bloka programskog kôda. Iteraciju je izmislio programer koji odgovara profilu osobe u izjavi u Uvodu. Ideja je da umjesto 100 linija istog kôda napravimo petlju koja će uvelike pojednostaviti kôd, a samim time i skratiti pisanje tog kôda. Postoji nekoliko načina definiranja iteracija, koje je najjednostavnije objasniti na primjeru zbrajanja prvih N prirodnih brojeva. Kôd 17: Primjer iteracije – for static void Main(string[] args) { int n = 10; int suma = 0; for (int i = 0; i <= n; i++) { suma += i; //suma = suma + i; //isto } }
Prvi primjer iteracije je for petlja koja ima sintaksu: for(„inicijalizacija“ ; “logički izraz“ ; “korak petlje“) {…} Inicijalizacija i korak petlje mogu biti programski izrazi. U praksi se pod inicijalizacijom podrazumijeva deklariranje i inicijalizacija brojača (i). U koraku petlje definiramo promjene nad elementima definiranim u inicijalizaciji. Logički izraz služi za ispitivanje uvjeta izlaska iz petlje, ako je on false tada se izvođenje petlje zaustavlja.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
31
Odabrana poglavlja programskog inženjerstva – razvoj programa Kratak pregled objektno orijentiranih koncepata
Kôd 18: Primjer iteracije – while static void Main(string[] args) { int n = 10; int suma = 0; int brojac = 0; while (brojac <= n) { suma += brojac; //brojac = brojac + 1; brojac++; } }
Drugi primjer petlje je while petlja koja ima sintaksu: while(„logički izraz“) {…}
U prikazanom primjeru možemo vidjeti korištenje while petlje. U svakom koraku petlje, kao i kod for petlje, izvodi se blok kôda. U konkretnom primjeru zbrajaju se svi brojevi od 0 do 10. Rezultat zbrajanja se pohranjuje u varijablu suma. Primijetimo kako smo morali voditi računa o uvjetu izlaska iz petlje, za to smo napravili jednu pomoćnu varijablu brojac koja nam služi za brojanje koraka petlje. Na kraju petlje brojac povećavamo za 1. U trenutku kada vrijednost brojača dosegne broj n tada prestaje izvođenje petlje. Petlja će se izvoditi sve dok je logički izraz istina. Na taj način je vrlo lako napraviti beskonačnu petlju. Izvođenje programa s beskonačnom petljom nikada neće samostalno završiti.
Kôd 19: Beskonačna while petlja static void Main(string[] args) { while (true) { //beskonačna petlja } }
Pitanje za razmišljanje (3) Kako biste implementirali istu funkcionalnost zbrajanja prvih N prirodnih brojeva ali u formalno beskonačnoj while petlji?
Kôd 20: Primjer iteracije – do .. while static void Main(string[] args) { int n = 10; int suma = 0; int brojac = 0; do { suma += brojac; brojac++; } while (brojac <= n); }
Do..while iteracija je slična while iteraciji s jednom razlikom. Uvijek će se izvršiti minimalno jedan korak petlje. To je zato što se logička provjera događa na kraju petlje, dok se kod while petlje provjera događa na samom početku pa je moguće da se korak while petlja niti jednom ne izvrši.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
32
Odabrana poglavlja programskog inženjerstva – razvoj programa Kratak pregled objektno orijentiranih koncepata
Ovo poglavlje je bilo kratko i sažeto ponavljanje osnovnih koncepata programskog kôda. Teoretski je s ovim znanjem moguće napraviti bilo koju aplikaciju uz dovoljno truda i vremena. Ponovili smo kako se implementiraju petlje i grananja u aplikacijama. Naučili smo puno toga o različitim tipovima podataka, o jednostavnim ali i onim složenima poput struktura, klasa i polja. Dogovorili smo načine imenovanja elemenata programskog kôda. Bitno je pridržavati se jednog načina imenovanja zato što će nam biti puno intuitivnije razumjeti tuđi, ali i vlastiti kôd.
2.5. Pitanja i zadaci Samostalni zadatak Napravite konzolnu aplikaciju koja izračunava umnožak svih prirodnih brojeva od N=10 do M=30. Kako bi ispisali rezultat izračuna na ekran, koristite naredbu Console.WriteLine(…) Umjesto vrijednosti „tekst za ispis“ možete Kôd 21: Naredba za ispis na ekran Console.WriteLine(„tekst za ispis“); Console.ReadLine();
staviti bilo koju drugu vrijednost, ali i bilo koju varijablu. Naredba će prepoznati da se radi o varijabli i ispisati njenu vrijednost.
Ovu naredbu stavite na kraj vaše Main funkcije kako se aplikacija ne bi zatvorila prije nego fizički vidite rezultat. U suštini ova naredba “čeka” da korisnik pritisne bilo koju tipku. Kako se radi o sekvenci naredbi nakon što korisnik stisne tipku izvršiti će se sljedeća naredba; koje nema, zatim će aplikacija završiti i konzolni prozor će se zatvoriti.
Kôd 22: Naredba za čekanje Console.ReadLine();
Zadaci 1. Napravite konzolnu aplikaciju koja na ekran korisnika ispisuje N puta riječi „crno“ zatim M puta riječ bijelo. 2. Napravite aplikaciju koja od korisnika traži unos dva decimalna zbroja te računa njihov zbroj. U aplikaciji se za pohranu smije koristiti samo int tip podataka. Zbog ovog ograničenja najjednostavnije je napraviti strukturu/klasu za pohranu decimalnih brojeva.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
33
Odabrana poglavlja programskog inženjerstva – razvoj programa Kratak pregled objektno orijentiranih koncepata
Pitanja 1. Što je to iteracija? 2. Objasnite osnovnu razliku između do-while i while petlje. 3. Koja je svrha drugog segmenta for petlje? for(1.segment; 2.segment;3.segment) 4. Koja je razlika između strukture i klase? 5. Što su reference? 6. Što je to tip varijable? 7. Navedite najkraći mogući logički izraz (bez varijabi) koji se može koristiti u programskom kôdu? 8. Objasnite break naredbu i njeno korištenje. 9. Objasnite continue naredbu. (odgovor potražiti na Internetu) 10. Navedite primjer inline grananja. 11. Objasnite razliku između float i double tipa. 12. Objasnite razliku između char i string tipa. 13. Koja je svrha default naredbe? 14. Zašto korištenje mađarske notacije nema smisla? 15. Kada koristimo camelCase notaciju? 16. Kako ispravno imenujemo sučelja (interface) i njihove reference? 17. Što je objekt neke klase? 18. Što je object? 19. Napišite kôd za deklariranje polja znakova s 3 elementa.
2.6. Više informacija o temi 1. Imenovanje (http://msdn.microsoft.com/en-us/library/vstudio/ms229045(v=vs.100).aspx)),( http://www.akadia.com/services/naming_conventions.html), dostupno: prosinac, 2013. 2. Tipovi podataka (http://msdn.microsoft.com/en-us/library/ya5y69ds.aspx), dostupno: prosinac, 2013.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
34
Odabrana poglavlja programskog inženjerstva – razvoj programa Rukovanje greškama u programskom kodu
3. Rukovanje greškama u programskom kodu Sažetak U ovom poglavlju opisane su moguće greške u C# aplikacijama. Nadalje, opisani su načini kako možemo iskoristiti ugrađene elemente Visual Studio-a i .NET-a da minimiziramo mogućnost pojave grešaka. Obrađene su teme kao što su tipovi grešaka, iznimke, rukovanje iznimkama, debugging itd. Ključne riječi: greške u C#, tipovi grešaka, iznimke, try-catch-finally, debugging
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
35
Odabrana poglavlja programskog inženjerstva – razvoj programa Rukovanje greškama u programskom kodu
3.1. Uvod Pisanje računalnih programa je složen posao, te je stoga vrlo osjetljiv na pogreške. S porastom složenosti i veličine računalnih programa raste i vjerojatnost pogreške. Koliko god se trudili u tome, vjerojatnost da ćemo napisati netrivijalan program koji nema ni jednu grešku je vrlo mala. Često pojava greške niti ne ovisi samo o nama programerima, pa osim što se trebamo truditi pisati kôd sa što manje grešaka, moramo i pisati kôd koji će biti otporan na greške, tj. ako se greška i dogodi - da program na odgovarajući način reagira na nju. Postoje različiti tipovi grešaka koje se mogu pojaviti, ovisno o ozbiljnosti greške, fazi u kojoj se greška pojavljuje, uzročniku greške itd. Visual Studio razvojno okruženje, kao i sam .NET i C# programski jezik nude nam različite mehanizme za pomoć u pravovremenom identificiranju i adekvatnom rukovanju greškama.
3.2. Greške u C# aplikaciji U .NET-u a samim tim i u C# programskom jeziku postoji nekoliko vrsta grešaka:
design-time greške run-time greške logičke greške. Napomena
O pojavi grešaka nas Visual Studio obavještava kroz panel Error List, koji možete uključiti kroz alatnu traku: View -> Error List.
Design-time greške Kôd 23: Primjeri design-time grešaka //Pogrešno napisana ključna riječ public classe TestnaKlasa {} //Uporaba nedeklarirane varijable. varijab = 10; //Pogrešna uporaba operatora if (varijabla = 10){} //Izostavljen znak točka i zarez na kraju izraza (;) int varijabla; varijabla = 10
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
36
Odabrana poglavlja programskog inženjerstva – razvoj programa Rukovanje greškama u programskom kodu
Design-time greške su greške koje se pojavljuju dok pišemo kôd u editoru, prije nego pokrenemo program. Zapravo, ako imamo grešku ovog tipa nećemo ni moći pokrenuti program jer Visual Studio neće moći kompilirati kôd. Design-time greške je relativno lako uočiti, jer ih sam Visual Studio podcrta čim se pojave.
Crvenom crtom će biti podcrtane greške u sintaksi, npr. tipfeleri, nepropisna uporaba jezičnih elemenata, uporaba nepostojećih elemenata. Zelenom crtom će biti podcrtana upozorenja, koja sama po sebi ne moraju biti greške, ali potencijalno mogu uzrokovati probleme.
Osim što podcrta greške, Visual Studio nas o svakoj od ovih grešaka obavijesti i kroz panel Error List (Slika 23), prikazujući nam pri tome opis greške i točnu lokaciju. Dvostrukim klikom na neku od grešaka Visual Studio nas pozicionira na lokaciju greške u programskom kôdu.
Slika 23: Error List panel sa popisom grešaka
Osim što olakšava i ubrzava proces programiranja, Intellisense opcija Visual Studio-a smanjuje i mogućnost pojave sintaktičkih grešaka, jer nam u svakom trenutku pisanja kôda predlaže moguće jezične konstrukte, imena varijabli i slično. Kôd 24: Primjeri run-time grešaka //Pokušaj uporabe objekta sa vrijednošću null Form1 novaForma = null; novaForma.ShowDialog(); //Pokušaj dijeljenja sa nulom. int a = 10; int b = 0; int c = a / b; //Pokušaj otvaranja nepostojeće datoteke. FileInfo datoteka = new FileInfo(@"C:\NepostojecaDatoteka.txt"); datoteka.Open(FileMode.Open);
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
37
Odabrana poglavlja programskog inženjerstva – razvoj programa Rukovanje greškama u programskom kodu
Run-time greške Run-time greške su one koje se pojave tek nakon što kompiliramo kôd i pokrenemo ili pokušamo pokrenuti aplikaciju. Ove greške uzrokuju „pucanje“ programa i o njima nas Visual Studio obavještava kroz iznimke (eng. exceptions). Za razliku od design-time grešaka, run-time greške često nije lako uočiti jer se ponekad pojavljuju u samo određenim scenarijima korištenja programa. Zbog toga su one i mnogo opasnije. O run-time greški nas Visual Studio obavještava kroz poruku o iznimci (Slika 25). Ako iznimka nije na odgovarajući način obrađena, možemo očekivati „pucanje“ programa.
Slika 24: Pokušaj otvaranja nepostojeće datoteke
Slika 25: Pokušaj otvaranja nepostojeće datoteke (debug)
Logičke greške Logičke greške je najteže uočiti. Njih Visual Studio neće prepoznati dok pišemo kôd, niti će iste nužno uzrokovati „pucanje“ programa. To su pogreške u logici programskog kôda, koje je napravio programer, te rezultiraju netočnim i neočekivanim rezultatom izvođenja programa. Nažalost, u ovakvom slučaju nam Visual Studio ne može mnogo pomoći. Osim temeljitog testiranja programa i njegovih rezultata ne postoji drugi način da pronađemo i otkrijemo logičke greške. Kôd 25: Primjeri logičkih grešaka private double IzracunajPovrsinuKruga(double polumjer) { //Logička greška kod izračunavanja površine kruga (netočna formula) double povrsina = 0; povrsina = polumjer * Math.PI; return povrsina; }
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
38
Odabrana poglavlja programskog inženjerstva – razvoj programa Rukovanje greškama u programskom kodu
3.3. Iznimke Iznimke smo već spomenuli kada smo govorili o run-time greškama te smo rekli da će greška za vrijeme rada s programom u pravilu rezultirati iznimkom. U vremenu prije uvođenja iznimaka postojalo je više tehnika za rad s greškama. U pravilu je to ovisilo o programeru i aplikaciji koja se razvija. Većina tih tehnika se temeljila na definiranju numeričkih ili alfanumeričkih konstanti koje su predstavljale identifikator greške. Osim što ovakav pristup nije bio standardiziran, sam identifikator nije mnogo govorio o samoj greški. Uvođenjem mehanizma iznimaka pokušalo se unificirati pristup rada s greškama, te ponuditi više informacija o greški od samog identifikatora greške. U skladu s objektnim principima C# programskog jezika, i iznimke predstavljaju objekte koji sadržavaju iscrpne informacije o greški koja se dogodila, uključujući jasan opis, izvor, pomoć itd. U .NET platformi postoji veliki broj predefiniranih iznimaka koje možemo koristiti, a naravno možemo kreirati i vlastite iznimke. Tablica 5: Tipovi iznimaka
Česti tipovi iznimaka Iznimka
Opis
Exception
Generička iznimka, ovu iznimku nasljeđuju sve ostale.
ArgumentException
Pojavljuje se kada pozivamo metodu u kojoj jedan od parametara nije ispravan.
ArgumentNullException
Pojavljuje se kada pozivamo metodu u kojoj je jedan od parametara null vrijednost.
ArgumentOutOfRangeException
Pojavljuje se kada pozivamo metodu u kojoj jedan od parametara nije u zadanom rasponu vrijednosti.
ArithmeticException
Pojavljuje se kada je generirana matematička greška.
DivideByZeroException
Pojavljuje se ako pokušamo dijeliti sa nulom.
FormatException
Pojavljuje se ako format parametra nije ispravan.
IndexOutOfRangeException
Pojavljuje se ako pokušamo pristupiti indeksu polja koji je izvan granica polja.
InsufficientMemoryException
Pojavljuje se ako nema dovoljno dostupne memorije.
InvalidCastException
Pojavljuje se ako pokušamo izvršiti nepodržanu pretvorbu vrijednosti jednog tipa podataka u drugi.
NotImplementedException
Pojavljuje se ako pokušamo koristiti metodu ili svojstvo koje nije implementirano.
NotSupportedException
Pojavljuje se ako pokušamo koristiti metodu sučelja koja nije podržana.
NullReferenceException
Pojavljuje se kada pokušamo pozvati metodu ili pristupiti svojstvu objekta koji ima vrijednost null.
OutOfMemoryException
Slično kao i InsufficientMemoryException.
OverflowException
Pojavljuje se kada pokušamo izvršiti nedozvoljene numeričke operacije, kao što je zbrajanje prevelikih brojeva korištenjem
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
39
Odabrana poglavlja programskog inženjerstva – razvoj programa Rukovanje greškama u programskom kodu
32-bitnog cjelobrojnog tipa podataka. StackOverflowException
Pojavljuje se kada je stog pozvanih metoda pun, obično kao rezultati beskonačne rekurzije.
SystemException
Podiže je operacijski sustav.
Struktura iznimke
IndexOutOfRangeException
Ono što je zajedničko svim iznimkama je da nasljeđuju temeljnu klasu System.Exception, te tako tvore svojevrsnu hijerarhiju iznimaka (Slika 26). Iznimka sadrži različite informacije o greški koja se pojavila. Neke od tih informacija su dostupne u obliku svojstava iznimke.
NullReferenceException SystemException System.Exception
StackOverflowException ApplicationException ...
Slika 26: Hijerarhija iznimaka u .NET-u
Tablica 6: Svojstva iznimke
Izdvojena svojstva iznimke Svojstvo
Tip
Opis
Message
String
Tekst greške koji opisuje uzrok iznimke.
StackTrace
String
Informacije o tome gdje se greška dogodila. (u redoslijedu poziva metoda + broj linije kôda.)
InnerException
Exception
Ako je trenutna iznimka uzrokovana nekom prethodnom iznimkom, ovo svojstvo sadrži referencu prema toj izvornoj iznimci.
HelpLink
String
Sadrži URL na kojem se može pronaći pomoć o ovoj iznimci.
Source
String
Naziv projekta u kojem se desila greška.
Rukovanje iznimkama Kao što smo već spomenuli, iznimka koja nije na odgovarajući način obrađena može uzrokovati „pucanje“ programa. Zbog toga je vrlo bitno predvidjeti mogućnost pojave iznimke te se na to pripremiti. Rukovanje iznimkama se u C# programskom jeziku odvija uz pomoć try-catch-finally bloka naredbi. Struktura try-catch-finally bloka naredbi Try
Try blok sadrži naredbe za koje smatramo da bi mogle
{
uzrokovati pojavu iznimke. Ovaj dio je obavezan. naredbe
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
40
Odabrana poglavlja programskog inženjerstva – razvoj programa Rukovanje greškama u programskom kodu
} catch(…)
Catch blok sadrži jedan ili više catch izraza u kojima se rukuje
{
iznimkama koje su se pojavile u try bloku. Zbog toga se taj blok naredbe
zove rukovatelj iznimkama (eng. exception handler). Ovaj blok je
}
obavezan, ako ne postoji definiran finally blok.
catch … Finally
U finally blok stavljamo kôd koji će se izvršiti u bilo kojem
{
slučaju, neovisno o tome je li se pojavila iznimka u try bloku ili naredbe
nije. Ovaj blok je obavezan, ako ne postoji definiran catch blok.
}
Finally blok omogućava programeru da očisti zaprljano stanje sustava koje je rezultat pojave iznimke u try bloku ili da oslobodi vanjske resurse kao što su datoteke, baze podataka itd.
Uporaba try i finally bloka je relativno jednostavna, međutim catch blok je nešto složeniji. Kao što smo već spomenuli catch blok rukuje iznimkama koje se pojavljuju u try bloku. Postoje tri oblika catch izraza: općeniti catch izraz, specifični catch izraz, i specifični catch izraz s objektom. U sljedećoj tablici možemo vidjeti što svaki od tih oblika predstavlja. Tablica 7: Oblici catch izraza
Oblici catch izraza Catch
Općeniti catch izraz, ne sadrži parametar, rukuje bilo kojom iznimkom,
{
ali ne može odrediti tip iznimke. naredbe
} catch(TipIznimke)
Specifični catch izraz, prihvaća tip iznimke kao parametar i može
{
rukovati bilo kojom iznimkom navedenog tipa. naredbe
} catch(TipIznimke ex)
Specifični catch izraz s objektom, daje najviše podataka o iznimci. Osim
{
što definira tip iznimke kojom može rukovati, daje nam i instancu naredbe
}
iznimke (eng. exception variable) koja se ponaša kao lokalna varijabla unutar catch izraza, iz koje možemo saznati sve potrebne informacije o greški koja se dogodila.
Kada smo govorili o run-time greškama naveli smo par primjera koji su rezultirali pojavom iznimaka. Iznimkama nismo na odgovarajući način rukovali, pa je zbog toga došlo do „pucanja“ programa i prikaza ne baš user-friendly poruke. Sada ćemo pokazati kako uz pomoć try-catch-finally bloka rukovati tim iznimkama, tj. reagirati na pojavu iznimke.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
41
Odabrana poglavlja programskog inženjerstva – razvoj programa Rukovanje greškama u programskom kodu
Na primjeru rukovanja iznimkom FileNotFoundException možemo vidjeti kako smo identificirali potencijalno opasan dio kôda – otvaranje datoteke. Prepoznali smo da postoji mogućnost da datoteka koju želimo otvoriti ne postoji te da će se pojaviti iznimka. Zbog toga smo taj dio kôda stavili u try blok. S druge strane, ako se pojavi iznimka da datoteka ne postoji, želimo je uhvatiti i na odgovarajući način rukovati s njom u catch bloku – tako da kreiramo datoteku. I na kraju u finally bloku osiguravamo dealociranje resursa neovisno je li se iznimka pojavila ili ne. Kôd 26: Primjer rukovanja iznimkom FileNotFoundException FileInfo datoteka; FileStream stream; try { //Pokušaj otvaranja nepostojeće datoteke. datoteka = new FileInfo(@"C:\NepostojecaDatoteka.txt"); stream = datoteka.OpenRead(); } catch (FileNotFoundException) { //Kreiranje datoteke ako nije pronađena File.Create(@"C:\NepostojecaDatoteka.txt"); datoteka = new FileInfo(@"C:\NepostojecaDatoteka.txt"); stream = datoteka.OpenRead(); } finally { //Dealociranje resursa neovisno o pojavi iznimke. datoteka = null; stream = null; }
Na sljedećem primjeru možemo vidjeti rukovanje iznimkom dijeljenja s nulom. U ovom slučaju na pojavu iznimke smo reagirali samo prikazom upozorenja korisniku. Osim toga, ovdje možemo vidjeti i uporabu objekta iznimke „ex“, preko kojeg smo došli do izvorne poruke o iznimci. Kôd 27: Primjer rukovanja iznimkom DivideByZeroException Try { //Pokušaj dijeljenja sa nulom. int a = 10; int b = 0; int c = a / b; } catch (DivideByZeroException ex) {
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
42
Odabrana poglavlja programskog inženjerstva – razvoj programa Rukovanje greškama u programskom kodu
//Ovaj put samo prikazujemo upozorenje o iznimci. MessageBox.Show(ex.Message, "Dijeljenje sa nulom!"); }
Također, bitno je primijetiti da u navedenim primjerima u catch bloku hvatamo samo jednu vrstu iznimke (FileNotFoundException ili DivideByZeroException). U slučaju da se pojavila bilo koja druga iznimka, došlo bi do „pucanja“ programa. Zbog toga nam C# omogućava pisanje više catch izraza s kojim možemo obuhvatiti više vrsta iznimaka. Uvijek se preporučuje upotrebljavati što je moguće specifičnije iznimke, međutim ako nismo sigurni koje se sve iznimke mogu pojaviti možemo iskoristiti i temeljnu System.Exception iznimku. Ako imamo više catch izraza u jednom bloku, pravilo je da se prvo hvataju specifične iznimke, a na kraju općenitije. To možemo vidjeti na sljedećem primjeru. Kôd 28: Primjer rukovanja više iznimaka Try { //neki kod koji može uzrokovati različite iznimke... } catch (DivideByZeroException) { //prvo provjeravamo DivideByZeroException } catch (FileNotFoundException) { //zatim FileNotFoundException } catch (Exception) { //zatim najopćenitiju iznimku Exception }
Kreiranje vlastitih iznimaka U .NET-u postoji mnogo predefiniranih iznimaka, neke od njih smo i naveli, međutim često će se javiti potreba za korisnički definiranim iznimkama koje će pokrivati specifične potrebe naše aplikacije. S obzirom na to da su iznimke u .NET-u definirane kao klase, vrlo lako možemo kreirati svoje iznimke i nasljeđivati postojeće. Preporučuje se da korisničke iznimke nasljeđuju iznimku System.ApplicationException, a ne System.Exception jer ćemo tako jače naglasiti razliku između sistemski generiranih iznimki, i naših, korisnički definiranih iznimki. Jedna od razlika između korisničke iznimke i iznimke .NET-a je ta da za korisničku iznimku mi sami moramo „baciti“ (eng. throw). Da bi to napravili koristit ćemo ključnu riječ throw. Naravno, ništa nas ne sprječava da to isto napravimo i za predefinirane iznimke. Na sljedećem primjeru možemo vidjeti kako se kreira nova iznimka. Pretpostavimo da razvijamo aplikaciju koja radi izračune svojstava geometrijskih likova i da većina tih svojstava mora imati vrijednost veću od nule. Napravit ćemo iznimku koja će javljati grešku kada se pojavi svojstvo koje ima nedozvoljenu vrijednost.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
43
Odabrana poglavlja programskog inženjerstva – razvoj programa Rukovanje greškama u programskom kodu
Kôd 29: Primjer kreiranja vlastite iznimke //Nova korisnička iznimka koja nasljeđuje ApplicationException iznimku. public class NotAllowedValueException : ApplicationException { public NotAllowedValueException(string poruka) { DodatnaPoruka = poruka; } public string DodatnaPoruka { get; set; } } //Metoda u kojoj "bacamo" iznimku. private double IzracunajPovrsinuPravokutnika(double a, double b) { double povrsina = 0; if (a <= 0 || b <= 0) { throw new NotAllowedValueException("Stranice moraju biti veće od nule!"); } povrsina = a * b; return povrsina; } //Hvatanje iznimke double stranicaA = 10; double stranicaB = -5; try { double povrsina = IzracunajPovrsinuPravokutnika(stranicaA, stranicaB); } catch (NotAllowedValueException ex) { MessageBox.Show(ex.DodatnaPoruka); }
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
44
Odabrana poglavlja programskog inženjerstva – razvoj programa Rukovanje greškama u programskom kodu
3.4. Debugging Pod pojmom debugging podrazumijevamo proces pronalaženja i ispravljanja run-time i logičkih grešaka programa. Ovisno o kompleksnosti aplikacije, ali i kvaliteti kôda, taj proces može biti vrlo zahtjevan. Mnoga razvojna okruženja ne pružaju napredne opcije debugging-a, te zbog toga velik broj programera koristi neku vrstu ispisa ili poruka da bi znao do kojeg dijela kôda se program izvršio ili koja je vrijednost neke varijable u određenom trenutku. Na sreću, Visual Studio kao razvojno okruženje posjeduje niz ugrađenih mehanizama kako bi nam proces debugging-a olakšao. U fazi razvoja aplikacije, dok još aktivno pišemo ili mijenjamo kôd, obično aplikaciju pokrećemo u Debug načinu (F5 ili u izborniku Debug->Start Debugging ili u alatnoj traci Start). Aplikacija se tada normalno izvršava, ali imamo neke dodatne mogućnosti kao što su: zaustavljanja izvršavanja kôda u bilo kojem trenutku, izvršavanja pojedinačnih linija kôda, pregled vrijednosti varijabli, promjena vrijednosti varijabli itd. Breakpoints Da bi izvršavanje aplikacije zaustavili na željenom mjestu možemo koristiti točke zaustavljanja (eng. breakpoints). Ova mogućnost je vrlo korisna kada sumnjamo da određeni dio kôda ne radi ispravno, i želimo detaljno promotriti na koji način se taj Slika 27: Primjer točke zaustavljanja kôd izvršava (Slika 27). Točke zaustavljanja je lako postaviti: kliknemo na lijevi rub prozora s kôdom, ispred linije kôda na kojoj se želimo zaustaviti (alternativa je pozicionirati se na odgovarajuću liniju kôda i pritisnuti tipku F9 na tipkovnici). Kada pokrenemo aplikaciju, ona će se zaustaviti na definiranoj točki zaustavljanja. Naravno, dozvoljeno je definirati više takvih točaka. Točku zaustavljanja možemo isključiti ponovnim klikom na crveni kružić ili ponovnim pritiskom na tipku F9. Moguće je definirati i naprednije opcije za točku zaustavljanja, desnim klikom miša na crveni kružić. Neke od tih opcija su Condition (uvjetno zaustavljanje izvršavanja), Hit count (program se neće zaustaviti dok točka zaustavljanja nije već određeni broj puta pređena), When hit (definira alternativno ponašanje programa kada se dođe na točku zaustavljanja). Prolazak kroz kôd Kada zaustavimo izvršavanje programa, možemo izvršavati program liniju po liniju kôda i nakon svake linije kôda provjeravati stanje varijabli. Postoji nekoliko naredbi koje nam omogućavaju prolazak kroz kôd u debug načinu rada, najbitnije su: Step Into, Step Over, Step Out (Slika 28). Sve opcije su dostupne i kroz izbornik Debug kao i kroz alatnu traku.
Slika 28: Debug opcije
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
45
Odabrana poglavlja programskog inženjerstva – razvoj programa Rukovanje greškama u programskom kodu
Tablica 8: Debug opcije
Debug opcije Opcija
Tipkovnička kratica
Opis
Step Over
F10
Izvršava trenutnu liniju kôda i zaustavlja se na sljedećoj.
Step Into
F11
Izvršava trenutnu liniju kôda i zaustavlja se na sljedećoj, osim ako je trenutna linija kôda poziv metode. U tom slučaju će debugger ući u metodu koja se poziva i zaustaviti se na prvoj liniji kôda te metode.
Step Out
Shift + F11
Izlazi iz pozvane metode i vraća se na mjesto odakle je metoda pozvana.
Show Next Statement
Alt + Num *
Vraća nas na liniju kôda koja je sljedeća za izvršavanje.
Set Next Statement
Ctrl + Shift + F10
Definira koja će se linija kôda sljedeća izvršiti.
Run To Cursor
Ctrl + F10
Izvršava sve linije kôda do linije na kojoj je postavljen kursor.
Continue
F5
Nastavlja s izvođenjem programa i izlazi iz debug načina rada.
Stop Debugging
Shift + F5
Prekida izvođenje programa.
Restart
Ctrl + Shift + F5
Ponovno pokreće program.
Debug prozori U debug načinu rada dostupno je nekoliko prozora/panela koji nude korisne informacije o stanju programa u danom trenutku. Oni su nam dostupni kada smo u debug načinu rada i kada zaustavimo aplikaciju uz pomoć točke zaustavljanja. Ako nisu automatski otvoreni, možemo ih pronaći u izborniku Debug->Windows. Locals prozor (Slika 29) nam pokazuje sve lokalno dostupne varijable i informacije o njima, uključujući i vrijednosti varijable. Osim što možemo pregledavati vrijednosti varijabli, možemo ih i mijenjati.
Slika 29: Izgled Locals prozora
Watch prozor je sličan kao i Locals prozor, samo što se kod Watch prozora prikazuju varijable za koje smo eksplicitno definirali da ih želimo pratiti. Praćenje varijabli Slika 30: Izgled Call Stack prozora
definiramo desnim klikom miša na varijablu i odabirom opcije Add Watch.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
46
Odabrana poglavlja programskog inženjerstva – razvoj programa Rukovanje greškama u programskom kodu
Call Stack prozor prikazuje stog s trenutnom hijerarhijom poziva metoda, uključujući i njihove parametre. Immediate prozor nam omogućava direktno izvršavanje kôda u trenutnom kontekstu aplikacije. To može biti čitanje varijabli, izmjena varijabli ili neki drugi kôd.
3.5. Pitanja i zadaci Samostalni zadatak Upišite sljedeće primjere kôda u Visual Studio i pronađite u čemu je greška. Koju grešku vam prikazuje Visual Studio? Kôd 30: Sintaktičke greške //Primjer 1. int a = 10
//Primjer 5. private void Metoda2() { int a, b, c; c = a + b; } //Primjer 6. private int Metoda3() { int a = 10, b = 20, c = 0; c = a + b; }
//Primjer 2. int a = "10"; //Primjer 3 varijabla = 10 //Primjer 4. private Metoda1() {}
Upišite sljedeće primjere kôda i provjerite kojom iznimkom će rezultirati. Zašto su se iznimke dogodile, što možemo napraviti da spriječimo pojavu tih iznimaka? Napravite kôd za rukovanje tim iznimkama uz pomoć try catch-finally bloka naredbi. Napravite 3 metode navedene u sljedećem primjeru. Napravite aplikaciju koja će prilikom svojeg
pokretanja
pozvati
prvu
Kôd 31: Iznimke //Primjer 1. int a = Convert.ToInt32("Tekst"); //Primjer 2. int a = 10, b = 0, c = 0; c = a / b; //Primjer 3. int[] polje = new int[10]; polje[11] = 1;
metodu
(GlavnaMetoda). Stavite točku zaustavljanja (breakpoint) na prvu liniju kôda prve metode (GlavnaMetoda), zatim pokrenite program i izvršavajte liniju po liniju kôda (ulazeći pri tome i u pozvane metode), dok ne izvršite cijelu metodu. Za to vrijeme pratite vrijednost varijabli u Locals prozoru. Dodajte varijable zbroj i razlika u Watch prozor i pratite njihove vrijednosti. © University of Zagreb, Faculty of Organization and Informatics, Varaždin
47
Odabrana poglavlja programskog inženjerstva – razvoj programa Rukovanje greškama u programskom kodu
Kôd 32: Debugging private void { int a = zbroj = razlika }
GlavnaMetoda() 10, b = 20, zbroj, razlika; IzracunajZbroj(a, b); = IzracunajRazliku(a, b);
private int IzracunajRazliku(int x, int y) { int razlika = 0; razlika = x - y; return razlika; }
//Izračunava zbroj vrijednosti u intervalu //od broj x do y uključujući i njih private int IzracunajZbroj(int x, int y) { int zbroj = 0; for (int i = x; i < y-1; i++) { zbroj += i; } return zbroj; }
Pitanja 1. Koji tipovi grešaka postoje i koje su karakteristike tih grešaka? 2. Koje tipove grešaka je najlakše a koje najteže pronaći? 3. Što su to iznimke, koje informacije sadrže i kako se manifestiraju u aplikaciji? 4. Koje su najčešće predefinirane .NET iznimke? 5. Za što nam služe korisnički definirane iznimke, i po čemu se razlikuju od predefiniranih? 6. Zašto je važno rukovati iznimkama? 7. Objasnite na koji način rukujemo iznimkama. Za što služe try, catch i finally blok? 8. Što podrazumijevamo pod pojmom debugging? 9. Koje mehanizme Visual Studio ima za olakšavanje pronalaženja grešaka (debugging)? 10. Što su to točke zaustavljanja (breakpoints)? 11. Na koji način možemo vidjeti stanje varijabli programa u određenom trenutku? 12. Napravite nekoliko sintaktičkih grešaka, i pokažite na koji način je Visual Studio identificirao te greške. 13. Napišite programski kôd koji uzrokuje iznimku FormatException. Na koji način bi spriječili pojavu te iznimke? 14. Napravite for petlju koja iterira 1000 puta. Zaustavite uz pomoć uvjetne točke zaustavljanja (Conditional breakpoint) izvršavanje petlje na 500-toj iteraciji. 15. Napravite aplikaciju koja deklarira 3 integer varijable, dvjema dodjeljuje vrijednosti, te zbroj dodjeljuje trećoj varijabli. Zaustavite aplikaciju u trenutku zbrajanja prva dva broja. Pogledajte stanje varijabli u Locals prozoru. U Watch prozor dodajte (Add Watch) prvu i drugu varijablu. © University of Zagreb, Faculty of Organization and Informatics, Varaždin
48
Odabrana poglavlja programskog inženjerstva – razvoj programa Rukovanje greškama u programskom kodu
3.6. Više informacija o temi 1. Errors and exceptions (http://www.functionx.com/csharp/exceptions/ied.htm), dostupno: prosinac, 2013. 2. 10+ powerful debugging tricks with Visual Studio (http://www.codeproject.com/Articles/359801/10plus-powerful-debugging-tricks-with-VisualStudi), dostupno: prosinac, 2013. 3. Debugging your C# Apps, (http://www.homeandlearn.co.uk/csharp/csharp_s5p1.html), dostupno: prosinac, 2013. 4. Exception Handling for C# Beginners (http://www.codeproject.com/Articles/125470/ExceptionHandling-for-C-Beginners), dostupno: prosinac, 2013. 5. Introduction to Exception Handling (http://csharp-station.com/Tutorial/CSharp/Lesson15), dostupno prosinac, 2013. 6. Exception Handling Best Practices in .NET (http://www.codeproject.com/Articles/9538/Exception-Handling-Best-Practices-in-NET), dostupno: prosinac, 2013. 7. Advanced Debugging in Visual Studio (http://www.codeproject.com/Articles/309781/AdvancedDebugging-in-Visual-Studio), dostupno: prosinac, 2013.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
49
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram klasa –koncepti i razvoj
4. Verzioniranje i dokumentiranje programskog koda Sažetak Poglavlje započinje sa sustavima za verzioniranje te njihovom uporabom u verzioniranju kôda. Pokazane su osnove verzioniranja (lokalnog i udaljenog) u Git sustavu. Prikazan je primjer jednostavne Windows Forms aplikacije, pa su obrađene i osnove grafičkog sučelja aplikacije. Dotaknuta je tema pisanja komentara u kôdu te generiranja dokumentacije na temelju XML komentara. Ključne riječi: Verzioniranje kôda, GIT, generiranje dokumentacije, doxygen, komentari. WinForms
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
50
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram klasa –koncepti i razvoj
4.1. Uvod U ovom poglavlju obrađeno je nekoliko važnih tema. Poglavlje počinje sa sustavima za verzioniranje (eng. Version Control Systems), koji danas predstavljaju neizostavnu pomoć programerskim timovima u praćenju promjena te suradnji među članovima tima. Dokumentiranje kôda i tehnička dokumentacija važni su elementi svake netrivijalne aplikacije, pa su objašnjeni načini komentiranja kôda te mogućnost generiranja tehničke dokumentacije iz tzv. XML komentara. Windows Forms aplikacije i danas su najzastupljenije (desktop) aplikacije .NET platforme stoga je jedan dio poglavlja posvećen osnovama izrade aplikacija s grafičkim sučeljem.
4.2. Sustavi za verzioniranje Sustav za verzioniranje ili sustav za kontrolu verzija (eng. Version Control System –VCS, Revision Control, itd.) je aplikacija koji nam omogućuje upravljanje promjenama datoteka na računalu. Takav sustav se temelji na kreiranju tzv. revizija, koje predstavljaju stanje skupa datoteka u određenom vremenskom trenutku. Osim što predstavlja zamjenu za kreiranje sigurnosnih kopija i omogućuje vraćanje sustava u bilo koje prethodno stanje definirano nekom revizijom, sustav za verzioniranje nam omogućuje i istovremenu suradnju prostorno dislociranog projektnog tima, čak i na istoj datoteci. Iako se mogu koristiti za verzioniranje različitih tipova datoteka (tekstualnih, zvučnih, video, slikovnih, izvršnih, itd.) posebno su korisni za verzioniranje datoteka izvornog kôda, a to znači da su neizostavan alat programerskih timova. Datoteke izvornog kôda načelno su tekstualne datoteke čiji sadržaj sustav za verzioniranje može razumjeti, odnosno može razumjeti koje su se promjene dogodile i na kojem mjestu u datoteci. S druge strane, kod binarnih datoteka to nije moguće jer način zapisa to ne omogućuje. Ukratko, neke od funkcionalnosti sustava za verzioniranje koji će nam biti od pomoći su: (a) vraćanje datoteka u neko od prethodnih stanja, (b) vraćanje čitavog projekta u neko od prethodnih stanja, (c) lakše identificiranje grešaka, (d) pregledavanje promjena kroz vrijeme na datotekama ili cijelom projektu, (e) pregledavanje tko je od članova tima napravio što, koliko, kada, itd.
Tipovi sustava za verzioniranje Sustavi za verzioniranje nisu novitet i već duže vrijeme ih softverski inženjeri koriste. Većinom se radi o samostalnim aplikacijama, no neki su ugrađeni u druge vrste softvera kao npr. tekstualne procesore, tablične kalkulatore, CMS-ove (eng. content management system) itd. Svim sustavima za verzioniranje, neovisno o tipu, karakteristično je postojanje radne kopije projekta (kopija koju korisnik mijenja i dorađuje), i repozitorija u koji korisnik sprema pojedinačne verzije (revizije) projekta. Možemo reći da je repozitorij slika sustava kojeg razvijamo. © University of Zagreb, Faculty of Organization and Informatics, Varaždin
51
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram klasa –koncepti i razvoj
U ovisnosti o lokaciji repozitorija možemo govoriti o 3 osnovna tipa sustava za verzioniranje: lokalni sustav, centralizirani sustav, distribuirani sustav. Lokalni sustav za verzioniranje podrazumijeva da se i radna kopija projekta i repozitorij nalaze lokalno, odnosno na računalu gdje se razvija softver (Slika 31). Danas većina sustava za verzioniranje omogućava podizanje lokalnog repozitorija, međutim teško ćemo u uporabi naći sustav koji dozvoljava samo taj način rada. Ova opcija je dobra, ako na projektu radi samo jedna osoba i ako ne želimo ovisiti o internetskoj vezi. S druge strane, ako na projektu radi više osoba, suradnju nije lako ostvariti. Također postoji mogućnost gubitka podataka jer nam se repozitorij nalazi na samo jednom mjestu (eng. single point of failure).
Slika 31: Lokalni sustav za verzioniranje
Centralizirani sustav za verzioniranje pretpostavlja postojanje repozitorija na centralnom serveru, kojem pristupa jedan ili više klijenata (Slika 32). Ovim pristupom je riješen problem suradnje s ostalim članovima tima. Svaki član tima povlači iz repozitorija na serveru ažurnu verziju projekta u svoju lokalnu radnu kopiju, dorađuje je te šalje izmijenjenu verziju natrag na server u obliku nove revizije. Međutim, i dalje je ostao problem ranjivosti zbog postojanja samo jednog repozitorija, ali i problem ovisnosti o mrežnim resursima. Ako računala ostanu bez mrežne konekcije, timovi ne mogu surađivati. Ipak, centralizirani sustavi za verzioniranje nude mnoge prednosti, te se danas vrlo često koriste. Neki od predstavnika takvih sustava su: CVS, Subversion, Perforce itd. U posljednjih nekoliko godina sve se više koriste distribuirani sustavi za verzioniranje. Oni su pokušali riješiti problem ranjivosti centraliziranih sustava tako da ne postoji samo jedan centralni repozitorij, nego svako klijentsko računalo sadrži cijeli repozitorij. Ako Slika 32: Centralizirani sustav za verzioniranje na jednom računalu dođe do gubitka podataka, podaci se vrlo lako vrate iz repozitorija s bilo kojeg drugog računala. Također, s obzirom na to da svako računalo ima repozitorij lokalno, korisnik ne ovisi o mrežnoj konekciji da bi mogao pristupiti ranijim revizijama projekta, nego samo onda ako želi dohvatiti nove revizije od drugih ili ako želi svoje izmjene učiniti drugima dostupne u obliku revizije. S druge strane, nedostatak ovakvog pristupa je da svako računalo mora sadržavati cjelokupan repozitorij, a ne samo zadnju aktualnu reviziju, što naravno može zauzimati dosta memorijskog prostora. Predstavnici distribuiranih sustava za verzioniranje su: Git, Mercurial, Bazaar, Darcs. © University of Zagreb, Faculty of Organization and Informatics, Varaždin
52
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram klasa –koncepti i razvoj
Git Mi ćemo koristiti Git, distribuirani sustav za verzioniranje. To ne znači da je to jedini ili najbolji sustav. Kao što smo već rekli postoje i drugi sustavi, koji su uglavnom open-source, te ih možete besplatno koristiti. Primarni razlog za naš odabir Git-a je izuzetna popularnost u posljednjih nekoliko godina, a uz to je besplatan i dostupan za sve platforme. Git je besplatan, otvoren (eng. open source), distribuirani sustav za verzioniranje, koji je sposoban upravljati malim i velikim projektima (razvijen je 2005. godine kao sustav za verzioniranje Linux kernela). Iako podržava različite načine rada i sadrži jako velik broj opcija inicijalno dostupnih preko Git Bash terminala, mi ćemo se fokusirati na nekoliko najvažnijih funkcionalnosti dostupnih preko grafičkog sučelja. Za one koji žele znati više, postoje dodatni materijali navedeni na kraju ovog poglavlja. Jedno od svojstava po kojem se Git kao distribuirani sustav razlikuje od centraliziranog sustava (kao što je npr. popularni Subversion) je način pohrane promjena, tj. verzija projekta. Subversion sadrži inicijalnu verziju projekta (Slika 33 verzija 1), a sve ostale promjene na datotekama sprema upravo kao promjene tih inicijalnih Slika 33: Verzioniranje u Subversion sustavu datoteka. Npr. verzija 2 ne sadrži inicijalne datoteke iz verzije 1, nego samo promjene koje su napravljene na tim datotekama. Nadalje, Git funkcionira tako da stvara snimke cjelokupnog sustava u danom trenutku i to sprema kao novu verziju (Slika 34). Ipak, da se ne bi razbacivao s memorijskim prostorom, ne sprema u novu verziju datoteke koje nisu bile mijenjane u odnosu na prethodnu verziju, nego samo postavi referencu prema tim datotekama u prethodnoj verziji. Već smo spomenuli da Git ima lokalni repozitorij, što znači da je čitava povijest promjena projekta dostupna lokalno na vašem računalu. Zbog toga većinu operacija kao što je kreiranje nove revizije (eng.commit), vraćanje projekta na neko od prethodnih stanja (eng. revert, rollback), pregled povijesti promjena na projektu, možete napraviti vrlo brzo i to bez potrebe veze prema centralnom repozitoriju. Ako nemate potrebu za suradnju s drugima, ovo je većina operacija koje ćete trebati. Ipak, mi ćemo se orijentirati na timske projekte i pokazat ćemo kako je moguće ostvariti suradnju uz pomoć Git-a. Slika 34: Verzioniranje u Git sustavu
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
53
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram klasa –koncepti i razvoj
Rad s Git sustavom Kako bi lakše shvatili kako Git radi, pogledajmo sliku lokalnog Git sustava (Slika 35). Radni direktorij (eng. Working directory) je kopija zadnje verzije projekta, na kojoj trenutno radimo. Nakon što smo gotovi s izmjenama, ažurirane datoteke koje želimo uvrstiti u novu verziju (reviziju) prvo šaljemo u tzv. staging područje, a zatim pokrećemo naredbu commit, koja od tih datoteka kreira novu verziju i sprema je u lokalni repozitorij.
Slika 35: Lokalne operacije u Git sustavu
Ovo su operacije koje moramo napraviti ako radimo u Git Bash terminalu. Međutim, mi ćemo koristiti grafičku klijentsku aplikaciju, koja će nam olakšati i Slika 36: Centralizirani način rada u Git-u pojednostaviti rad. Također, s obzirom na to da želimo ostvariti i suradnju između članova tima, osim lokalnih klijentskih repozitorija svakog člana tima, uvest ćemo i jedan serverski repozitorij, koji će omogućiti suradnju. Postoje različiti načini na koje možemo ostvariti suradnju u Git sustavu, a mi smo se odlučili za najjednostavniji način. Svaki član tima ima svoju radnu kopiju sustava, lokalni repozitorij u koji sprema revizije projekta, te se spaja na dijeljeni serverski repozitorij s kojim se sinkronizira, tj. šalje revizije koje je on napravio (eng. push), a povlači revizije koje su napravili drugi članovi tima (eng. pull). Praktičan rad s Git sustavom ćemo pokazati u sljedećem poglavlju kroz razvoj WinForms aplikacije. Napomena Git je potrebno instalirati, kako bismo ga mogli lokalno koristiti. Instalacijsku datoteku možete preuzeti na sljedećoj poveznici http://git-scm.com/download. Klijentsku GUI aplikaciju GitHub for Windows koju ćemo koristiti možete preuzeti ovdje: http://windows.github.com/. Ako želite imati centralni dijeljeni repozitorij na nekom serveru koji će vam olakšati suradnju u timu, možete koristiti postojeće servise. Postoji više takvih servisa dostupnih na internetu. Jedan od njih koji imaju besplatnu ponudu i koji ćemo mi koristiti je GitHub (https://github.com/). Naravno uvijek možete sami kreirati vlastiti Git server i njega koristiti kao centralni repozitorij. Za potrebe vježbi iz programskog inženjerstva smo kreirali račun na GitHub servisu s korisničkim imenom PI2013FOI. Instalirana je i GUI klijentska aplikacija GitHub for Windows, koja je već spojena na repozitorij na GitHub-u.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
54
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram klasa –koncepti i razvoj
4.3. Razvoj Windows Forms aplikacija U prvom poglavlju spomenuti su različiti tipovima projekata u Visual Studio-u, te je spomenuti i projekt za razvoj Windows Forms aplikacija. Windows Forms aplikacije su uglavnom stolne aplikacije s grafičkim korisničkim sučeljem u obliku prozora, na kojima se nalaze različite predefinirane .NET ili korisničke kontrole i komponente. Sve što se odnosi na razvoj WinForms aplikacija se nalazi u dinamičkoj biblioteci Systems.Windows.Forms.dll, kao što su npr. forme (prozori), gumbi, polja za unos, tablični prikazi, liste za odabir, alatne trake i ostale kontrole i komponente. Napomena Za razliku od konzolnih aplikacija kod kojih se rad s aplikacijom odvija po točno određenom toku, WinForms aplikacije su mnogo fleksibilnije i njihov tok je određen događajima (eng. events) uzrokovanim interakcijom korisnika ili porukom drugog sustava ili dretve. Zbog toga se programiranje WinForms aplikacija naziva event-driven development ili event-based programming.
Za primjer Windows Forms aplikacije, kreirat ćemo sljedeću jednostavnu aplikaciju: Aplikacija izračunava geometrijska svojstva kruga (polumjer, opseg, površinu). Na formi će se nalaziti tri TextBox kontrole za svaki od navedenih svojstava, 3 njima pripadajuće Label kontrole, jedna ListBox kontrola, jedna CheckBox kontrola i dva gumba (Button). Korisnik unosi vrijednost polumjera kruga u odgovarajuće polje za unos (TextBox), a klikom na gumb Izračunaj, ostala svojstva se izračunaju i prikažu u pripadajućim TextBox kontrolama. Ako je u trenutku klika na gumb Izračunaj označen i Checkbox, tada će se vrijednost polumjera kruga dodati u ListBox. Dvostrukim klikom na element ListBox-a ponovno će se izračunati podaci za odabrani polumjer kruga. Također, na formi ćemo imati gumb Očisti, koji će obrisati izračunate vrijednosti iz TextBox kontrola i postaviti ih na vrijednost „0,00“. Aplikacija mora sadržavati i odgovarajuću validaciju korisničkog unosa podataka, onemogućavajući korisnika da vrši izračune nad slovima ili brojevima manjim od 0.
Kreiranje WinForms aplikacije u lokalnom repozitoriju Kako bi kreirali novu Windows Forms aplikaciju otvorimo Visual Studio te iz alatne trake odaberimo opciju File->New->Project… Nakon toga nam se otvara dijaloški okvir New Project u kojem odaberimo predložak Windows Forms Application te upišimo naziv projekta (Name) Vjezbe03_ImePrezime i kliknimo na OK. Visual Studio nam sada kreira novo rješenje (eng. Solution) s Windows Forms projektom unutar rješenja. Također unutar projekta nam je Visual Studio automatski kreirao i jednu formu (Form1.cs).
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
55
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram klasa –koncepti i razvoj
To će predstavljati našu početnu verziju projekta, pa ćemo je kao takvu spremiti u lokalni Git repozitorij u obliku revizije. Za sada ćemo raditi samo s lokalnim repozitorijem. Da bi to napravili potrebno je otvoriti GitHub for Windows klijentsku aplikaciju. Otvaranje aplikacije će nam otkriti da nemamo trenutno kreiranih lokalnih repozitorija, ali da na GitHub servisu pod korisničkim imenom PI2013FOI imamo kreiran jedan repozitorij (Slika 37).
Slika 37: Izgled GitHub for Windows aplikacije
Nama trenutno treba lokalni repozitorij, pa se vratimo na stavku local, te iz alatne trake odaberimo opciju add. U sljedećem prozoru pod naziv repozitorija upišite točan naziv Visual Studio projekta, stavite proizvoljan opis te odaberite direktorij u kojem se nalazi navedeni projekt. Opcija Push to github mora biti isključena (Slika 38). Klikom na gumb Create kreirat će se novi lokalni repozitorij.
Slika 38: Kreiranje novog repozitorija
Iako smo kreirali repozitorij, datoteke našeg projekta se nalaze samo u radnoj kopiji (eng. working copy), ali još uvijek nisu spremljene u repozitorij kao revizija. Zato otvorimo repozitorij klikom na strelicu open this repo (Slika 39). Novootvoreni prozor će pokazati da Slika 39: Otvaranje repozitorija imamo 13 novih datoteka u radnoj kopiji koje nisu poslane u repozitorij (eng. uncommited changes), stoga ćemo upisati poruku i opis revizije, te kliknuti na gumb Commit (Slika 40).
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
56
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram klasa –koncepti i razvoj
Git će spremiti navedene promjene kao početnu reviziju našeg projekta te će nas obavijestiti da nema više promjena u radnoj kopiji projekta. S obzirom na to da je naš projekt razvojni projekt, želimo u repozitorij spremiti samo datoteke izvršnog kôda i potrebne konfiguracijske datoteke. Ostale datoteke, kao što su izvršne i privremene datoteke u bin i debug direktorijima ne želimo u repozitoriju, tj. želimo ih ignorirati. Za tu svrhu u Git-u postoje tzv. ignore liste, gdje možemo definirati koje tipove datoteka će se ignorirati prilikom kreiranja revizija u repozitoriju. Git sustav je Slika 40: Commit promjena
prepoznao naš projekt kao razvojni C# projekt, te je automatski kreirao ignore listu, tako da to ne trebamo sami raditi.
Izrada korisničkog sučelja aplikacije U sljedećoj iteraciji ćemo napraviti korisničko sučelje naše WinForms aplikacije. Ono se sastoji od sljedećih elemenata. Tablica 9: Elementi korisničkog sučelja WinForms aplikacije
Elementi korisničkog sučelja WinForms aplikacije Naziv kontrole
Tip kontrole
Svojstva
Form
Naziv datoteke = FrmGlavna
(Name svojstvo) FrmGlavna
Name = FrmGlavna Text = „Glavna forma aplikacije“ Size = 400;250 StartPosition = CenterScreen lblPolumjer
Label
Name = lblPolumjer Text = „Polumjer:“
lblPovrsina
Label
Name = lblPovrsina Text = „Površina:“
lblOpseg
Label
Name = lblOpseg Text = „Opseg:“
txtPolumjer
TextBox
Name = txtPolumjer Text = „0,00“ TextAlign = Right
txtPovrsina
TextBox
Name = txtPovrsina Text = „0,00“ TextAlign = Right
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
57
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram klasa –koncepti i razvoj
ReadOnly = True txtOpseg
TextBox
Name = txtOpseg Text = „0,00“ TextAlign = Right ReadOnly = True
chkSpremi
CheckBox
Name = chkSpremi Text = „Spremi u listu“
lstRezultati
ListBox
Name = lstRezultati
btnIzracunaj
Button
Name = btnIzracunaj Text = „Izračunaj“
btnOcisti
Button
Name = btnOcisti Text = „Očisti“
Završni izgled korisničkog sučelja (Slika 41). Pokrenite aplikaciju, primijetite da osim što imamo gotovo sučelje, naša aplikacija nema nikakvu funkcionalnost. Prije nego krenemo za implementiranjem funkcionalnosti spremit ćemo dosadašnje promjene kao sljedeću reviziju u Git sustav. Otvorimo naš lokalni repozitorij te primijetimo da je Git prepoznao napravljene promjene (uncommitted changes), te nam nudi da iste spremimo u repozitorij. Pod Commit Message upišimo „Napravljeno korisničko sučelje aplikacije“, opis možemo ostaviti prazan i kliknimo na Commit. Sada se u repozitoriju nalaze dvije revizije, te nas Git obavještava da u radnoj kopiji projekta više nema neverzioniranih promjena. Slika 41: Izgled korisničkog sučelja aplikacije
Izrada programske logike na formi Sada nam je još preostalo napisati programsku logiku aplikacije u pozadini forme. Najveći dio funkcionalnosti će se odraditi klikom na gumb btnIzracunaj. Kada korisnik klikne neki gumb, taj gumb podiže događaj Click (eng. event), koji signalizira da je korisnik kliknuo na njega. Mi možemo definirati metode (eng. Event Handlers) koje će reagirati na taj događaj. Da bi napravili metodu koja će rukovati događajem da je korisnik kliknuo na gumb, potrebno je u dizajnerskom prikazu naše forme dvostruko kliknuti na gumb btnIzracunaj. Visual Studio je automatski kreirao metodu za rukovanje događajem, prebacio se u kôdni prikaz forme, te se pozicionirao na novokreiranu metodu.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
58
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram klasa –koncepti i razvoj
Napomena Obratite pažnju na format kreirane metode. Ime metode se sastoji od imena objekta čijim događajem rukujemo, znaka „_“ te imena događaja kojim rukujemo. U ovom slučaju znamo da metoda rukuje događajem Click gumba btnIzracunaj. Prvi parametar (sender), je uvijek referenca na objekt čijim događajem rukujemo (u ovom slučaju to jer referenca na gumb btnIzracunaj), a drugi parametar sadržava dodatne korisne podatke o događaju (ovisi o kojem se događaju radi). Ovo je posebna vrsta metode koja se zove rukovatelj događajem (eng. Event Handler). private void btnIzracunaj_Click(object sender, EventArgs e)
Na temelju zahtjeva navedenih ranije, implementirat ćemo funkciju izračunavanja. Konačan kôd funkcije izgleda ovako: Kôd 33: Implementacija metode btnIzracunaj_Click private void btnIzracunaj_Click(object sender, EventArgs e) { //Deklaracija lokalnih varijabli float polumjer = 0, povrsina = 0, opseg = 0; //Dohvaćanje vrijednosti iz TextBox kontrola u varijablu, //parsiranje automatski onemogućava i pojavu nenumeričkih vrijednosti float.TryParse(txtPolumjer.Text, out polumjer); //Onemogućavanje vrijednosti manjih od nule if (polumjer < 0) polumjer = 0; //Izračunavanje površine i opsega kruga. povrsina = polumjer * polumjer * (float)Math.PI; opseg = 2 * polumjer * (float)Math.PI; //Pohrana vrijednosti iz varijabli natrag u TextBox kontrole. txtPolumjer.Text = polumjer.ToString(); txtPovrsina.Text = Math.Round(povrsina, 2).ToString(); txtOpseg.Text = Math.Round(opseg, 2).ToString(); //Spremanje vrijednosti polumjera u ListBox kontrolu //ako je CheckBox uključen if (chkSpremi.Checked == true) { lstRezultati.Items.Add(polumjer); } }
S obzirom na to da smo napravili značajan dio funkcionalnosti aplikacije, spremit ćemo ga u repozitorij kao novu reviziju s porukom „Napravljena funkcionalnost za gumb btnIzracunaj“. To je za sada treća revizija naše aplikacije u repozitoriju.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
59
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram klasa –koncepti i razvoj
Za sljedeću iteraciju aplikacije, napravit ćemo funkcionalnost iza gumba btnOcisti te ponovno izračunavanje vrijednosti dvostrukim klikom na polumjer u ListBox-u. Da bi napravili metodu koja će odgovoriti na Click događaj gumba btnOcisti označimo gumb na formi, u Properties panelu se prebacimo na prikaz događaja objekta te dvostruko kliknimo na događaj Click. Na isti način kao i za gumb btnIzracunaj Visual Studio će kreirati metodu koja će rukovati događajem. Prema zahtjevima iz zadatka, implementirat ćemo logiku u toj metodi. Kôd 34: Implementacija metode btnOcisti_Click private void btnOcisti_Click(object sender, EventArgs e) { //Postavlja sadržaj TextBox-ova na zadane vrijednosti txtPolumjer.Text = "0,00"; txtPovrsina.Text = "0,00"; txtOpseg.Text = "0,00"; //Briše elemente iz ListBox-a lstRezultati.Items.Clear(); }
Osim toga želimo dvostrukim klikom na element u ListBox-u ponovno izračunati podatke za odabrani polumjer. Da bi to napravili, označimo ListBox na formi, u Properties panelu među događajima pronađimo DoubleClick te dvostruko kliknimo na njega za stvaranje metode koje će njime rukovati. Prema zahtjevima zadatka, metodu ćemo implementirati na sljedeći način: Kôd 35: Implementacija metode lstRezultati_DoubleClick private void lstRezultati_DoubleClick(object sender, EventArgs e) { //Dohvaćamo vrijednost polumjera iz ListBox-a u varijablu float polumjer = 0; float.TryParse(lstRezultati.SelectedItem.ToString(), out polumjer); //Postavljamo vrijednost polumjera u TextBox txtPolumjer.Text = polumjer.ToString(); //Pozivamo ponovno izvršavanje izračuna btnIzracunaj_Click(null, null); }
Pitanje za razmišljanje Koji su nedostaci kôda prikazane aplikacije i zašto (npr. miješanje logike programa i korisničkog sučelja, eksplicitno pozivanje metode za rukovanje događajima…)? Na koji način bismo mogli unaprijediti postojeći kôd?
Učinjene promjene na aplikaciji, spremit ćemo i u repozitorij kao novu reviziju s porukom „Drugi dio programske logike“. Sada naš lokalni repozitorij sadrži 4 revizije. © University of Zagreb, Faculty of Organization and Informatics, Varaždin
60
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram klasa –koncepti i razvoj
4.4. Ostale opcije Git sustava Do sada smo prilikom izrade WinForms aplikacije izravno koristili samo naredbu Commit, uz pomoć koje smo promjene iz radne kopije projekta spremili u lokalni repozitorij kao reviziju. Svi klijentski programi za rad s Git sustavom omogućavaju i mnoge druge korisne operacije, a neke od njih koje omogućava klijentski program GitHub for Windows, su navedene u Tablica 10. Dodatne informacije o Git sustavu potražite na kraju poglavlja. Tablica 10: Opcije Git sustava
Ostale korisne opcije Git sustava Opcija
Opis
Pregled stanja
Opcija je dostupna jednostavnim otvaranjem repozitorija (open this repo).
repozitorija
Omogućuje prikaz općenitog stanja repozitorija: da li ima neverzioniranih promjena u radnoj kopiji, prikaz postojećih revizija projekta s podacima o vremenu kreiranja revizije, opisom revizije i korisnikom koji je napravio reviziju.
Pregled
sadržaja
pojedine revizije
Odabirom pojedine revizije pojavljuju se detaljni podaci o reviziji, kao i popis svih datoteka čije su promjene ušle u tu reviziju. Za svaku datoteku iz revizije možemo točno vidjeti promjene koje su na njoj napravljene.
Revert commit opcija
Odabirom jedne revizije, postaje nam dostupna opcija Revert commit, koja kreira novu reviziju koja poništava promjene napravljene u odabranoj reviziji. U suštini zadnje stanje u repozitoriju će biti kao prije odabrane revizije.
Roll back this commit
Ova opcija će obrisati iz repozitorija odabranu reviziju i sve revizije poslije nje, ali će promjene koje su se nalazile u obrisanim revizijama biti u radnoj kopiji projekta.
Discard changes
Ako smo napravili neke promjene na radnoj kopiji projekta, za koje smo se ipak odlučili da ih ne želimo, možemo iskoristiti ovu opciju, te vratiti stanje projekta u radnoj kopiji na stanje zadnje revizije.
Push to GitHub
Ako imate registriran račun na GitHub-u, ova opcija vam omogućava da lokalni repozitorij prebacite na GitHub servere.
4.5. Dokumentiranje i komentiranje kôda Pisanje komentara je važna stavka u programiranju koja nam pomaže u lakšem razumijevanju kôda. Prilikom razvoja većih i složenijih aplikacija često ćemo za potrebe ispravljanja grešaka ili dorade aplikacije, vraćati se na kôd koji smo programirali nekoliko mjeseci ili čak nekoliko godina prije. Ako smo taj kôd adekvatno komentirali, sigurno ćemo ga lakše i brže razumjeti. Važnost komentara se dodatno povećava u timskim projektima.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
61
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram klasa –koncepti i razvoj
Ako pokušavamo razumjeti kompleksan kôd druge osobe, koja ima različit stil pisanja od nas, a komentari ne postoje, vrlo vjerojatno ćemo potrošiti jako mnogo vremena. S druge strane, s komentarima ne treba pretjerivati. Nije potrebno objašnjavati trivijalne dijelove kôda (npr. deklaraciju varijabli, jednostavne operacije), nego samo onaj dio kôda za koji smatramo da je to zaista potrebno. Potrebu za pretjeranim pisanjem komentara i dodatnim objašnjenjima smanjuje uredan stil pisanja kôda. U C# programskom jeziku komentare od kôda odvajamo za oznakom // , ili ako želimo pisati komentare u više redova koristimo oznake /* za početak komentara, i */ za završetak komentara.
Slika 42: Primjer pisanja XML komentara
Osim ovih standardnih komentara, postoje i tzv. XML komentari. Njih obično aktiviramo iznad deklaracije klase, metode, svojstva koje opisujemo, tako da upišemo oznaku ///. XML komentari se u kôdu zatim prikazuju kao ToolTip pomoć (Slika 43), ali nam također kasnije omogućavaju i automatsko generiranje dokumentacije u MSDN stilu. Dokumentiranje uz pomoć XML komentara je jako dobra praksa, te se preporuča da tako opisujemo svaku klasu i sve njene elemente (metode i njene parametre, svojstva, događaje…).
Slika 43: Primjer ToolTip pomoći koja se generira iz XML komentara
Ako smo aplikaciju dokumentirali uz pomoć XML komentara, možemo automatski generirati dokumentaciju uz pomoć alata kao što su Microsoft Sandcastle i DoxyGen.
4.6. Pitanja i zadaci Samostalni zadatak Na ovom zadatku ćete vidjeti kako se uz pomoć Git sustava može realizirati suradnja više članova tima. Na GitHub serveru se nalazi repozitorij pod nazivom Win_Forms_Collaboration s početnom verzijom aplikacije. Da bismo počeli raditi na toj aplikaciji odaberite PI2013FOI, te na
repozitoriju
pod
nazivom
Win_Forms_Collaboration
odaberite
opciju
kloniranja
repozitorija (Clone). Sada imamo i lokalni repozitorij za aplikaciju Win_Forms_Collaboration.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
62
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram klasa –koncepti i razvoj
Pronađimo i pokrenimo projekt u Visual Studio-u. Projekt se sastoji od jedne glavne forme na kojoj se nalazi gumb za svakog studenta (broj studenta se odnosni na broj računala za kojim student sjedi) i klikom na taj gumb se poziva odgovarajuća forma. Zadatak je sljedeći: Za svakog studenta je kreirana forma pod nazivom FrmIzracun_StudentX (gdje je X broj računala na kojem student sjedi) koja se treba pokrenuti klikom na odgovarajući gumb forme FrmPocetna, i na kojoj će se izračunavati svojstva pravokutnika. Korisnik na formi unosi stranicu A i stranicu B pravokutnika, a izračunavaju se površina i opseg pravokutnika. Elementi koje forma treba sadržavati nalaze se u tablici. Nakon što student napravi zahtijevanu funkcionalnost spremiti će promjene kao novu reviziju (commit) u svoj lokalni repozitorij s porukom „Zadatak izvršen (Ime i Prezime)“. Da bi napravljene promjene poslali na GitHub centralni repozitorij potrebno je pozvati metodu sync. Analizirajte revizije koje ste napravili sami i koje su napravili ostali studenti. Tablica 11: Elementi korisničkog sučelja Win_Forms_COllaboration aplikacije
Elementi korisničkog sučelja Win_Forms_Collaboration aplikacije Naziv kontrole
Tip kontrole
Svojstva
Form
Naziv datoteke = FrmIzracun_StudentX
(Name svojstvo) FrmIzracun_StudentX (X je broj računala)
Name = FrmIzracun_StudentX Text = „Aplikaciju izradio/la Ime i Prezime“ Size = 400;250 StartPosition = CenterScreen
lblStranicaA
Label
Name = lblStranicaA; Text = „Stranica A:“
lblStranicaB
Label
Name = lblStranicaB; Text = „Stranica B:“
lblOpseg
Label
Name = lblOpseg; Text = „Opseg:“
lblPovrsina
Label
Name = lblPovrsina; Text = „Površina:“
txtStranicaA
TextBox
Name = txtStranicaA; Text = „0,00“; TextAlign = Right
txtStranicaA
TextBox
Name = txtStranicaA; Text = „0,00“; TextAlign = Right
txtPovrsina
TextBox
Name = txtPovrsina; Text = „0,00“; TextAlign = Right; ReadOnly = True
txtOpseg
TextBox
Name = txtOpseg; Text = „0,00“; TextAlign = Right; ReadOnly = True
btnIzracunaj
Button
Name = btnIzracunaj; Text = „Izračunaj“
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
63
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram klasa –koncepti i razvoj
Pitanja 8. Što su to sustavi za verzioniranje, za što nam oni služe? 9. Koje vrste sustava za verzioniranje postoje? Nabrojite neke sustave za verzioniranje? 10. Koja je razlika između klijent-server i distribuiranih sustava za verzioniranje? 11. Koje su osnovne operacije sustava za verzioniranje? 12. Na koji način sustavi za verzioniranje evidentiraju promjene u projektu? Na koji način to radi Git sustav? 13. Objasnite pojmove radna kopija projekta (eng. Working copy), revizija, repozitorij? 14. Na koji način možemo ostvariti suradnju između članova tima uz pomoć sustava za verzioniranje? 15. Zbog čega su sustavi za verzioniranje posebno korisni u razvoju softvera? 16. Što su to Windows Forms aplikacije, i koje su karakteristike takvih aplikacija? 17. Navedite nekoliko kontrola koje se koriste u izradi grafičkog sučelja i objasnite njihovu tipičnu uporabu. 18. Što su to Event Handler metode? Objasnite format deklaracije takve metode. 19. Zašto kažemo da su forme parcijalne klase, i po čemu ih možemo prepoznati? 20. Zbog čega je bitno adekvatno komentirati kôd? 21. Na koji način komentare u C# odvajamo od ostalog kôda? 22. Što su to XML komentari i za što nam oni služe?
4.7. Više informacija o temi 1. Službena stranica Git sustava za verzioniranje (http://git-scm.com/), dostupno: prosinac, 2013. 2. Besplatna online knjiga „Pro Git“ o Git sustavu (http://git-scm.com/book), dostupno: prosinac, 2013. 3. Opsežan YouTube tutorial o Git sustavu u 4 dijela (http://www.youtube.com/watch?v=mYjZtU1u9Y), dostupno: prosinac, 2013. 4. GitHub For Windows klijentska aplikacija (http://windows.github.com/), dostupno: prosinac, 2013. 5. Nekoliko besplatnih C# knjiga (http://www.csharpcourse.com/), dostupno: prosinac, 2013. 6. Online knjiga C# Essentials (http://www.techotopia.com/index.php/C_Sharp_Essentials), dostupno: prosinac, 2013. 7. Nekoliko zanimljivih tutoriala na MSDN-u (http://msdn.microsoft.com/enus/library/vstudio/dd492171.aspx), dostupno: prosinac, 2013. 8. C# Docummenting and Commenting (http://www.codeproject.com/Articles/3009/CDocumenting-and-Commenting), dostupno: prosinac, 2013. 9. Write Comments that matter (http://www.codeproject.com/Tips/467657/Write-comments-thatmatter), dostupno: prosinac, 2013.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
64
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram klasa –koncepti i razvoj
5. UML dijagram klasa – koncepti i razvoj Sažetak U ovom poglavlju opisan je procesno orijentirani sustav pogonske trake za sklapanje automobila. Opisane su i klasificirane komponente, koje prolaze kroz pogonsku traku. Nadalje, opisan je UML dijagram klasa koji priprada strukturnom skupu dijagrama. Istaknuti su osnovni koncepti objektno orijentiranog pristupa kroz razvoj UML dijagrama klasa. Ključne riječi: UML dijagram klasa, vidljivost, nasljeđivanje, enkapsulacija, polimorfizam, apstraktna klasa, programsko sučelje
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
65
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram klasa –koncepti i razvoj
5.1. Uvod Objektno orijentiran pritup nastao je zapažanjem objektnog svijeta i preslikava ga na računalo na način kojim čovjek percipira svijet – u obliku objekata. Objektni pristup omogućuje ponovnu iskoristivost programskih komponenata i sastavljanje softvera u obliku niza „kockica“. U daljnjem tekstu predstaviti ćemo neke teorijske osnove objektnog modeliranja i prijenosa istog modela u programski kod.
5.2. Osnove objektno orijentiranog pristupa U vremenu pojave softvera kôd se organizirao u programske sekvence, selekcije i ponavljanja. Prilikom razvoja pažnja je bila posvećena prema razvoju funkcija, programskih blokova i petlji. Izvođenje softvera je slijedno i prati dijagram toka, koristi top-down pristup. Često su se koristili i programski skokovi (eng. goto). Danas, ovaj pristup više nije dovoljan te je pažnja posvećena novom konceptu – objektu. Objektno orijentirano programiranje pojavilo se šezdesetih godina dvadesetog stoljeća, a glavne prednosti su7: -
Ponovna iskoristivost/brži razvoj, omogućuje iskorištavanje već napisanog programskog kôda.
-
Povećana kvaliteta, zbog većih mogućnosti testiranja. Ako je 90% aplikacije sastavljeno od kôda koji je ranije provjeren, potrebno je testiranje samo tih novih 10% i sustava kao cjeline.
-
Komponentna arhitektura, koja omogućuje ponovnu iskoristivost i razdvajanje briga (eng. separation of concerns).
-
Bolja povezanost s realnim svijetom, koja je posljedica uvođenja novih koncepata i mogućnosti opisivanja objekata iz realnog svijeta u programskom kôdu, zajedno s njihovim ponašanjem.
-
Koncepti8 o Objekt – predstavlja entitet iz realnog svijeta. Objekt je Mercedes C-klasa registarske oznake VŽ-042-VZ. o Klasa – ranije navedeni objekt pripada klasi automobila. Klasa je obrazac kojim su opisana svojstva i djelomično ponašanje objekta. Svaki automobil ima visinu, dužinu, širinu, zapremninu, snagu itd. Objekt je primjerak/instanca klase. Klasa automobil, svim instancama nalaže svojstva, pa tako objekt Fiat 500 registarske oznake ČK-040-
7
http://www.westga.edu/~bquest/1997/object.html
8
http://www.codeproject.com/Articles/15874/Understanding-Object-Oriented-Concepts
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
66
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram klasa –koncepti i razvoj
o
o
o
o
o
CK ima jednake atribute. Registarsku oznaku navodimo jer uz nju točno znamo na koji objekt mislimo. Apstrakcija – ranije navedena klasa automobil, može se instancirati u obliku konkretnog vozila, pa kažemo da se radi o konkretnoj klasi. Ako od klase ne možemo napraviti konkretan objekt, klasu nazivamo apstraktnom. Za raniji primjer, apstraktna klasa je vozilo. Od vozila ne možemo napraviti objekt jer u hijerarhiji klasa, pod vozilom, može biti automobil, kamion, itd. Sva zajednička svojstva klasa, pomičemo sve više u hijerarhiju. Nasljeđivanje – nove klase mogu nasljeđivati svojstva svojih roditeljskih klasa. Tako bi po ranijem primjeru od klase vozilo, njena klasa dijete (eng. child class) automobil, mogla naslijediti svojstvo visine, širine, zapremnine itd. Međutim, s obzirom na to da vozilo ne može biti konkretna klasa, klasa automobil mora imati specifična svojstva ili ponašanje, kako bi mogla postojati i kako bismo mogli kreirati objekte. Enkapsulacija/skrivanje podataka – opisuje dva koncepta. Prvi se odnosi na restrikciju pristupanja pojedinim dijelovima objekta, a drugi se odnosi na ugnježđivanje svih potrebnih resursa (metoda i podataka) unutar objekta. Polimorfizam - omogućuje objektima primanje više oblika, odnosno isto sučelje može obrađivati više različitih tipova podataka. Ovakvu višeobličnost omogućuje nasljeđivanje. Roditeljska klasa A (eng. parent class) implementira skup metoda i svojstava, a njih nasljeđuje neka druga klasa B (kažemo da je B specijalizacija od A, a A je generalizacija od B). Objekt klase B može se tretirati kao objekt klase A. Bazne klase (eng. base class) može implementirati virtualne metode, koje, nakon što ih druga klasa naslijedi, mogu se prevazići i ponovno implementirati. Primjerice, apstraktna klasa vozilo, definira postupak paljenja (metoda). No, za klasu moped, taj se postupak (metoda) razlikuje stoga ga klasa moped može prevazići implementirati novi postupak. Jedini uvjet je da povratni tip i argumenti metode moraju biti jednaki. Postoje dva oblika polimorfizma: Za vrijeme pisanja programa (eng. design time) Za vrijeme izvođenja programa (eng. runtime)
Objektno orijentirani razvoj mogući je na većini modernih programskih jezika (C#, Java, PHP, Python, C++ itd.), a prvi jezik na kojem su uspješno primijenjeni svi navedeni koncepti bio je Smalltalk. Uz strukturnu i objektno orijentiranu paradigmu razvoja postoji još nekoliko poznatijih: -
Funkcionalno programiranje
-
Logičko programiranje
-
Imperativno programiranje
-
Deklarativno programiranje
-
Aspektu orijentirano programiranje
-
Programiranje orijentirano na automate
-
Komponentno orijentirano programiranje
-
Podatkovno orijentirano programiranje © University of Zagreb, Faculty of Organization and Informatics, Varaždin
67
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram klasa –koncepti i razvoj
-
Programiranje orijentirano na događaje
-
Itd.
5.3. UML dijagram klasa UML dijagram klasa pripada strukturnom skupu dijagrama i u objektno orijentiranom razvoju služi za vizualno modeliranje strukture klasa, prikaza atributa i metoda te odnosa među klasama. Prema UML superspecifikaciji [1] dijagram klasa sastoji se od sljedećih elemenata: Klasa – klasificira objekte (razvrstava u razrede/klase) i sadržava opise svojstva koja karakteriziraju strukturu i ponašanje objekta. Svaka instanca klase (objekt) mora imati vrijednosti svih svojstava. Ako u početku životnog vijeka objekta nema vrijednosti za neko svojstvo, dodjeljuju se zadane (eng. default) vrijednosti. Objekt može pozivati (eng. invoke) operacije prilikom čega je moguća promjena nekih svojstava objekta.
Programsko sučelje – je stereotipizirana klasa koji se sastoji od deklaracija javnih svojstava. Sučelje se ne može konkretizirati i ono je svojevrsni ugovor kojeg sve klase, koje realiziraju to sučelje, moraju slijediti. Specifikacija instance – predstavlja instancu klase, odnosno opisuje neki entitet. Svrha specifikacije instance je prikaz svojstava entiteta u sustavu koji se modelira. Ne može se koristiti za općeniti prikaz sustava već za konkretnu točku u vremenu. Paket – se koristi za grupiranje elemenata i daje imenski prostor (eng. namespace) za grupu elemenata.
Agregacija – tip asocijacije koji daje dodatnu informaciju o odnosu klasa i njihovim životnim ciklusima. Ako postoji agregacija od klase A prema klasi B, objekt klase A nije uvjetovan životnim ciklusom klase B. Objekt klase B samo sadrži (agregira) objekte klase A. Asocijacija – najopćenitija veza koja označava interakciju između
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
68
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram klasa –koncepti i razvoj
dvije klase. Kompozicija – tip asocijacije, sličan kao i agregacija. U slučaju kompozicije, objekt klase A sastavni je dio klase B. Prestankom životnog ciklusa objekta B, prestaje i životni ciklus klase A. Slično tome, nastankom objekta klase B, nastaje i objekt klase A.
Zavisnost – je tip asocijacije koja označava da određen skup elemenata modela za svoju implementaciju ili specifikaciju treba neki drugi skup elemenata modela. Zavisnost definira vezu poslužitelj/klijent, pri kojoj promjene u poslužitelju mogu utjecati na klijenta. Generalizacija – je poseban tip asocijacije koji prikazuje podrijetlo klasa. Veza agregacije od klase A prema klasi B, ukazuje da je A generalizacija (općenitija klasa), a B je specijalizacija klase A. Realizacija programskog sučelja – je specifični tip veze koji se ostvaruje između programskog sučelja i klase koja ga implementira. Ovime je naznačeno da je klasa koja implementira sučelje usklađena s „ugovorom“ koji propisuje sučelje. Realizacija – generalizirani slučaj realizacije programskog sučelja. Generički ukazuje na odnos između poslužiteljske klase i klijentske klase kojim poslužiteljska klasa sadrži specifikacije koje klijentska klasa implementira (primijetite da je simbol jednak, no semantika je nešto općenitija). Korištenje – je vrsta veze kod koje jedan element treba drugi element modela kako bi njegova implementacija bila potpuna ili upotreba omogućena. Spajanje paketa – prikazuje na koji način se sadržaj jednog paketa proširuje sadržajem drugog paketa. Ovaj mehanizam podrazumijeva skup transformacija kojima se sadržaj jednog paketa spaja sa sadržajem drugog paketa. U slučajevima gdje elementi dva paketa prikazuju isti entitet, njihovi sadržaji se konceptualno spajaju u jedan element sukladan formalnim pravilima spajanja. Javni uvoz paketa – je vrsta odnosa između paketa koja omogućuje
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
69
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram klasa –koncepti i razvoj
korištenje elemenata koji su definirani u uvezenom paketu. Uvezeni elementi će biti vidljivi i drugim paketima. Privatni uvoz paketa – jednak odnos kao i ranije, samo uvezeni elementi neće biti vidljivi drugim paketima.
Kardinalnost UML dijagram klasa dopušta određivanje kardinalnost veza između klasa. Tablica 12: Kardinalnost
0..1 1 0..* 1..* m..n
Instanca ne mora postojati ili mora postojati točno jedna. Mora postojati jedna instanca. Instance ne mora postojati ili ih može biti više. Mora postojati barem jedna ili više instanci. Mora postojati toliko broj instanci koji se nalazi u intervalu od m do n.
5.4. Opis procesa – proizvodna traka pogona za sklapanje automobila Potrebno je razviti aplikaciju koja djelatnicima pogonske trake za sklapanje automobila omogućuje evidentiranje dijelova u automobilu (Slika 44). Na istoj traci možemo sklapati automobile i motocikle. U prvom koraku, za automobil se upisuju specifikacije (dužina, visina, težina, međuosovinski razmak itd.), nakon toga odlazi na pogonsku traku i ugrađuju se dijelovi. Upravo taj proces mora obuhvatiti aplikacija. Slika 45 prikazuje UML dijagram klasa za navedenu aplikaciju.
Slika 44: Dijelovi automobila
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
70
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram klasa –koncepti i razvoj
Slika 45: UML dijagram klasa
Dijagram se sastoji od sedam klasa, četiri enumeracije i jednog programskog sučelja. Klasa Vozilo je apstraktna i time znamo da je nije moguće instancirati. Njene metode i svojstva nasljeđuju klase Automobil i Motocikl, te je za iste moguće napraviti instancu. Vozilo također sadrži klasu VIN u odnosu kompozicije, čime arhitektu sustava daje do znanja da je životni ciklus instance klase VIN vezan za životni ciklus specijaliziranih klasa apstraktne klase Vozilo. Uz to, Vozilo agregira dijelove, objekte klase Dio. Dio ne ovisi o životnom ciklusu instance specijaliziranih klasa od klase Vozilo. Obično je odnos Dio-Cjelina prikaz kompozicije, no ovdje želimo naglasiti da je važnost u interpretaciji i shvaćanju sustava. Zašto je Dio agregirani u Vozilu? – Jer za vrijeme trajanja životnog ciklusa objekta iz klase Automobil, objekt iz klase Dio ne mora nužno postojati. Takav je zahtjev sustava i tako treba implementirati. Klasa Dio ujedno je specifični tip klase; sučelje. Dio sadrži svojstva i operacije koje će klase Motor i Mjenjac realizirati. Klasa EnumHelper sadrži četiri ugniježđene enumeracije koje koristimo za definiranje specijalnih tipova. Primijetite specifičnu vrstu veze koja nije strogo navedena u UML specifikaciji, a interpretira se kao „sadržavanje“. U boljoj arhitekturi softvera, te četiri enumeracije bile bi posebne klase koje predstavljaju entitete, a njihove se vrijednosti preuzimaju iz baze podataka. Primijetite da UML dijagram iz Slika 45 ne daje nikakve restrikcije na brojnosti (kardinalnost) veza između klasa.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
71
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram klasa –koncepti i razvoj
5.5. Implementacija dijagrama klasa u C# Programiranje klasa Najprije ćemo prikazati implementaciju jednog primjera a nakon toga ćete samostalno dovršiti primjer. Krenut ćemo od klase VIN. Ona nema zavisnost o drugim klasama te je logično i s njome započeti. Primijetite da klasa ima tri privatna svojstva tipa int, jednu javnu metodu kojom se generira i u obliku string vraća VIN, te konstruktor. U strukturi projekta ćemo napraviti novi direktorij i nazvati ga AutomotiveClasses, a unutar njega novu klasu VIN. Kôd 36 using using using using using
System; System.Collections.Generic; System.Linq; System.Text; System.Threading.Tasks;
namespace vjclass.AutomotiveClasses { class VIN { private int wmi; private int vds; private int vis; public VIN(int { this.wmi = this.vds = this.vis = }
wmi, int vds, int vis) wmi; vds; vis;
public string getVin() { return wmi + "" + vds + "" + vis; } } }
Sljedeća klasa koja nema ovisnosti je sučelje Dio. Kako sučelje ne sadrži implementaciju, već samo specifikaciju metoda i svojstava, kôd je prilično jednostavan: Kôd 37 using System; using System.Collections.Generic; using System.Linq;
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
72
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram klasa –koncepti i razvoj
using System.Text; using System.Threading.Tasks; namespace vjclass.AutomotiveClasses { interface Dio { string naziv { get; set; } int sifra { get; set; } string printAttributes(); } }
Primijetite korištenje operatora get i set. Visual Studio razlikuje klasične varijable (eng. field) od svojstava klasa (eng. property) i to tako da se svojstva klase zapisuju u obliku metode. To nije ništa drugo nego klasična varijabla, no njena vrijednost se postavlja preko metode (set, eng. mutator) i dohvaća preko metode (get, eng. accessor). Ako nema posebnog postupka za dohvaćanje i postavljanje, može se pisati skraćeni zapis, kao što je u prethodnom kôdu. Getter i setter koriste se za finiju granulaciju pristupa kôdu. Kompletni zapis izgledao bi kao što slijedi: Kôd 38 public string _svojstvo; public string Svojstvo { get { return _svojstvo; } set { _svojstvo = value; } }
Iz prethodnog primjera vidimo, da pristup do neke varijable možemo zaštititi ili mijenjati. Ključna riječ value implicira proslijeđenu vrijednost. UML ne razlikuje svojstvo od klasične varijable, no gotovo uvijek kada se radi u objektnom pristupu, sve treba oblikovati kao objekt. Sada kada imamo VIN i Dio klase, možemo implementirati apstraktnu klasu Vozilo. Vozilo ujedno sadrži odnosno ima kompozicijsku vezu prema klasi VIN te agregacijsku vezu prema sučelju Dio. Kôd 39 abstract class Vozilo { private List montiraniDjelovi; Kompozicija public VIN idAutomobila; public String naziv { get; set; } public int status { get; set; } public int tezina { get; set; } public int visina { get; set; } public int duzina { get; set; } public int meduosovinskiRazmak { get; set; } public String gume { get; set; }
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
73
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram klasa –koncepti i razvoj
public int co2 { get; set; } public Vozilo() { montiraniDjelovi = new List(); idAutomobila = new VIN(10,15,54); } public bool addDio(Dio noviDio) { montiraniDjelovi.Add(noviDio); return true; }
Agregacija
public void updateStatus(){ status = status++; } public List getDjelovi() { return montiraniDjelovi; } }
U ovoj smo klasi umjesto klasičnih varijabli koristili svojstva klase kojima ćemo pristupati kao objektima. Prilikom korištenja svojstva, primjerice naziv, koristit ćemo notaciju objekt.svojstvo, dok primjerice kod nekih drugih jezika, primjerice Java, moramo koristiti metodu get, objekt.getSvojstvo. Kod korištenja get, set, Visual Studio sam zaključi da mora pozvati get metodu. Kompoziciju smo implementirali u konstruktoru. Čim konkretne klase kreiraju objekt, automatski će se kreirati i VIN. Time životni ciklus objekta klase VIN ovisi o životnom ciklusu objekta klase Vozilo (odnosno instanca klasa koje nasljeđuju klasu Vozilo).
Pitanje za razmišljanje (1) Zašto životni ciklus objekta klase VIN ovisi o instanci klase koja nasljeđuje klasu Vozilo?
Sada ćemo implementirati sučelje Dio, dodavanjem klase Motor. Na poznati način treba dodati novu klasu u projekt, te nakon toga implementirati sučelje. Kao što prikazuje Slika 46, nakon dodavanja sučelja Dio, prelaskom miša iznad teksta pojavit će se mali znak _.
Slika 46: Implementacija programskog sučelja
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
74
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram klasa –koncepti i razvoj
Klikom na isti ili koristeći prečicu ctrl+. , otvorit će se izbornik koji nam nudi mogućnost implementacije sučelja. Ako ste nestrpljivi s ikonom, možete desnim klikom aktivirati izbornik u kojoj postoji opcija implementacije sučelja. Kôd 40 namespace vjclass.AutomotiveClasses { class Motor : Dio { public int brojCilindara { get; set; } public int ccm { get; set; } public int konjskeSnage { get; set; } public EnumHelper.Gorivo gorivo { get; set; } public Motor(int brojCilindara, int ccm, int konjskeSnage, EnumHelper.Gorivo gorivo) { this.brojCilindara = brojCilindara; this.ccm = ccm; this.konjskeSnage = konjskeSnage; this.gorivo = gorivo; } public string naziv { get; set; } public int sifra { get; set; } public string printAttributes() { String attributes = "Broj cilindara: " + brojCilindara.ToString(); attributes += "CCM: " + ccm.ToString(); attributes += "KS: " + konjskeSnage.ToString(); attributes += "Gorivo: " + gorivo.ToString(); return attributes; } } }
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
75
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram klasa –koncepti i razvoj
Još ćemo napraviti klasu Automobil koja mora naslijediti klasu Vozilo. Kôd 41 namespace vjclass.AutomotiveClasses { class Automobil : Vozilo { public int volumenPrtljaznika { get; set; } public int ncap { get; set; } public int vrata { get; set; } public EnumHelper.Kocnice prednjeKocnice { get; set; } public EnumHelper.Kocnice zadnjeKocnice { get; set; } } }
Sada ćete vi implementirati ostatak UML dijagrama, a nakon toga slijedi primjer korištenja sustava. Napravit ćemo novu formu, na nju ćemo dodati Rich text box kontrolu, koju ćemo koristiti za ispis testnih rezultata. U konstruktor ćemo dodati sljedeći kôd: Kôd 42 // kreiranje novog objekta klase Automobil AutomotiveClasses.Automobil noviAutomobil = new AutomotiveClasses.Automobil(); // popunjavanje svojstva i polja noviAutomobil.naziv = "Jetta"; noviAutomobil.tezina = 1300; noviAutomobil.visina = 1430; noviAutomobil.duzina = 4644; noviAutomobil.meduosovinskiRazmak = 2651; noviAutomobil.gume = "195/65/15"; noviAutomobil.co2 = 119; noviAutomobil.volumenPrtljaznika = 510; noviAutomobil.ncap = 5; noviAutomobil.vrata = 4; noviAutomobil.prednjeKocnice = AutomotiveClasses.EnumHelper.Kocnice.Diskovi; noviAutomobil.zadnjeKocnice = AutomotiveClasses.EnumHelper.Kocnice.DiskIBubanj; // kreiranje novog objekta klase DIO AutomotiveClasses.Dio motor = new AutomotiveClasses.Motor(4, 1600, 105, AutomotiveClasses.EnumHelper.Gorivo.Diesel); motor.naziv = "Motor TDI"; motor.sifra = 17; // dodavanje novog objekta klase dio u automobil - polimorfizam noviAutomobil.addDio(motor); // dodavanje novog objekta klase mjenjač u obliku neimenovanog objekta (u jednoj liniji koda - one liner) noviAutomobil.addDio(new AutomotiveClasses.Mjenjac(6, AutomotiveClasses.EnumHelper.MjenjacTip.Rucni) { naziv = "Mjenjac", sifra = 12 });
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
76
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram klasa –koncepti i razvoj
// prolaz kroz kolekciju djelova i ispis njihovih naziva // primjetite da se neovisno o objektu (klase Motor ili Mjenjac) poziva ista metoda foreach (AutomotiveClasses.Dio d in noviAutomobil.getDjelovi()) { rtbMain.AppendText(d.naziv + " - " + d.printAttributes() + "\n"); } rtbMain.AppendText("VIN: " + noviAutomobil.idAutomobila.getVin());
Pitanje za razmišljanje (1) Dali je dobro koristiti konstruktor na način koji smo ga koristili u prethodnom kôdu?
Malo proučite i komentirajte gore navedeni kôd. Obratite pozornost na polimorfizam, korištenje metoda sučelja te neimenovani objekt. Ovo su koncepti koji će vam koristiti u vašim budućim projektima i znatno vam olakšati razvoj. Dobar program sastoji se od dvije glavne komponente; dobre arhitekture i dobrih algoritama.
5.6. Generiranje klasa Visual Studio omogućuje i kreiranje dijagrama klasa, koji može ponekad olakšati izradu/preglednost, iako ne slijedi UML standard. Klikom na projekt, navigirajte na Add pa na New item. Iz liste, odaberite Class Diagram (Slika 47). Otvorit će se novi prazni prozor zajedno i s lijeve strane alatna traka (eng. toolbox).
Slika 47: Dodavanje dijagrama klasa
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
77
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram klasa –koncepti i razvoj
Slika 48: Dodavanje novog elementa dijagrama klasa i paleta s alatima
Da bismo kreirali klase pritiskom desnog klika miša, dobivamo kontekstni izbornik iz kojeg možemo odabrati što dodajemo, slično kao i iz palete . Da bismo dodali klasu VIN, odabrat ćemo Class (Slika 48).
Slika 49: Svojstva klase
Nakon toga, kao što prikazuje Slika 49, pritiskom desnog klika na klasu VIN pokazuju se njena svojstva (ovaj puta u kontekstu alata). Možemo dodati novu metodu, svojstvo, polje, događaj (o tome nešto kasnije na vježbama), konstruktor, destruktor ili konstantu. Nama trebaju 3 polja, jedna metoda i konstruktor. Kada smo dodali novo svojstvo, možemo kliknuti desni klik i odabrati Properties, nakon čega se otvara prozor s detaljnijim specifikacijama u kojima možemo odabrati tip (int) i vidljivost (private) (Slika 50). Na jednak način možemo dodati metode. Sve ostale detalje možemo mijenjati u programskom kôdu. Slika 50: Detaljna svojstva polja vds
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
78
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram klasa –koncepti i razvoj
Slika 51: Konačan izgled dijagrama
Slika 51 prikazuje generalizaciju i sučelje, no ono što nedostaje su asocijacije. Dijagram klasa Visual Studio-a 2012 nema ekspresivne mogućnosti prikaza agregacije i kompozicije, no ima mogućnost generiranja kôda. U paleti s alatima, odaberite asocijaciju i povucite je od klase Vozilo prema klasi VIN (Slika 52). VS je nacrtao novu vezu i na kraj stavio naziv novog svojstva koje će sadržavati klasa Vozilo.
Slika 52: Novo svojstvo
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
79
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram klasa –koncepti i razvoj
Ako pogledate kôd, vidjet ćete novi dio: Kôd 43 internal VIN VIN { get { throw new System.NotImplementedException(); } set { } }
Do sada smo koristili idAutomobila, koji je veza prema klasi VIN. Kako je idAutomobila sada viška, možemo ga ukloniti, a naziv VIN možemo promijeniti u nešto drugo (primjerice, idAutomobila). Svojstvu VIN Visual Studio generirao je vidljivost internal. Što je vidljivost? – pomoću tog svojstva objektno orijentiranog razvoja možemo od korisnika sakriti pojedine dijelove programa i pokazati samo one koji su važni. Primjerice, ako imamo klasu koja računa hipotenuzu trokuta prema Pitagorinom poučku, trebamo samo naziv metode kojoj dajemo vrijednosti kateta, a vraća nam hipotenuzu. Metodu za izračunavanje korijena i kvadriranje nam nije potrebno, stoga to možemo sakriti. U Visual Studio-u vidljivost određujemo preko modifikatora9:
Public – član klase kojem se može pristupiti iz bilo kojeg kôda
Private – član klase kojem može pristupiti samo klasa unutar koje se nalazi
Protected – član klase kojem se može pristupiti samo iz klase unutar koje se nalazi ili klase
koja je nasljeđuje
Internal – koristi se kod komponentnog pristupa, označava da je član klase vidljiv samo
unutar jednog izvršnog skupa (unutar istog exe, dll)
9
Protected internal – kombinacija protected i internal modifikatora
http://msdn.microsoft.com/en-us/library/ms173121.aspx
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
80
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram klasa –koncepti i razvoj
5.7. Pitanja i zadaci Zadaci 1. Implementirajte dijagram klasa (Slika 53).
Slika 53: Zadatak za vježbu10
2. Dovršiti dijagram klasa (Slika 53). 3. Implementirajte dijagram klasa (Slika 53), koristeći asocijacije u Visual Studio-u. 4. Napravite dijagram klasa za svoj projekt i promislite na dali je na implementacijskoj razini primjenjiv.
10
Preuzeto i prilagođeno iz http://www.uml-diagrams.org/class-diagrams-examples.html
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
81
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram klasa –koncepti i razvoj
Pitanja 1. Što je klasa? 2. Što je objekt? 3. Što je programsko sučelje? 4. Može li apstraktna klasa implementirati metode? 5. Što je svojstvo klase? 6. Kada se pokreće konstruktor i može li primati parametre (ako da, koje; ako ne, zašto ne?)? 7. Što je ugnježđena klasa? 8. Koja je implementacijska razlika kompozicije i agregacije s aspekta stvaranja objekta? 9. Koja je razlika između internal i protected vidljivosti? 10. Objasnite razliku između protected i private vidljivosti. 11. Koja je razlika kompozicije i agregacije s aspekta brisanja objekata? 12. Može li bilo koja klasa naslijediti bilo koju drugu klasu?
5.8. Više informacija o temi 1. MSDN Library o UML dijagramu klasa (http://msdn.microsoft.com/enus/library/vstudio/dd409437.aspx), dostupno: prosinac, 2013. 2. MSDN Library o UML dijagramu klasa II (http://msdn.microsoft.com/enus/library/vstudio/dd409416.aspx), dostupno: prosinac, 2013. 3. Predavanja prof. Brassa, Svučilište Halle (http://users.informatik.unihalle.de/~brass/dd04/c6_umlcl.pdf), dostupno: prosinac, 2013.
5.9. Izvori
[1] OMG Group, »OMG Group,« 2013. [Mrežno]. Available: http://www.omg.org/spec/UML/2.4.1/ .
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
82
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram aktivnosti – koncepti i razvoj
6. UML dijagram aktivnosti – koncepti i razvoj Sažetak U ovom poglavlju obrađen je jedan primjer dijagrama aktivnosti s najčešćim komponentama. Napravljena je implementacija jedne male aplikacije temeljem tog dijagrama, koja uključuje i koncepte višedretvenog rada. Sukladno tome, pokazano je kako se dretve stvaraju te kako se radi sinkronizacija dretvi. Ključne riječi: dijagram aktivnosti, UML, dretve, sinkronizacija
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
83
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram aktivnosti – koncepti i razvoj
6.1. Uvod Dijagram aktivnosti može prikazivati koncepte različite razine apstrakcije, od najviše gdje može pokazivati funkcioniranje i interakciju različitih sustava u nekom složenijem sustavu, do niže razine gdje može pokazivati funkcioniranje i interakciju objekata neke klase ili može pokazivati i programsku logiku nekog programskog rješenja. Ono što je zajedničko jest da dijagram aktivnosti opisuje unutarnje ponašanje nekog sustava. Dijagram aktivnosti se u nekim scenarijima ne mora tako zvati, već drugačije, jer neki standardi propisuju različiti naziv, različito opisivanje te strukturu samog dijagrama, dok u suštini imaju sličnu svrhu. Bitno je zapamtiti da se istim dijagramom mogu opisati koncepti različite razine apstrakcije. Pokušat ćemo zadržati najnižu razinu apstrakcije, ali opet nećemo ići predetaljno i npr. opisivati memorijsko alociranje objekata ili varijabli.
6.2. Dijagram aktivnosti – elementi Domene odgovornosti Na dijagramu aktivnosti mogu biti prikazane horizontalne odnosno vertikalne domene odgovornosti (eng. Swimlanes, partitions), odabir vrste plivačke staze je individualan. Uobičajeno je domene odgovornosti povezivati s učesnicima u sustavu (iz dijagrama slučajeva korištenja). Domene odgovornosti nam u svakom trenutku govore tko je odgovoran za izvršavanje pojedine aktivnosti/akcije u dijagramu aktivnosti odnosno u sustavu. Slika 54 b) prikazuje dvije prazne domene odgovornosti koje se zovu Korisnik i Dio sustava. Slika 54 a) prikazuje domene odgovornosti (particije) prema UML 2.4.1 superstrukturi.
Slika 54: a) domena odgovornosti prema notaciji UML 2.4.1. superstrukture, b) prazne domene odgovornosti
Aktivnost Aktivnost je stanje s minimalno jednom unutrašnjom akcijom i jednim (ili više) izlaznih prijelaza stanja koje se implicitno aktivira završetkom ulazne akcije. Svaka se aktivnost sastoji on niza akcija i predstavlja jedan korak u proceduri. Aktivnosti se mogu izvoditi paralelno, uzastopno ili naizmjenično.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
84
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram aktivnosti – koncepti i razvoj
Aktivnost možemo koristiti kako bismo grupirali akcije koje su izrazito međuovisne i služe kako bi ispunile jednu zajedničku svrhu. Slika 55 a) preuzeta je iz superstrukture UML v.2.4.1, dok je b) prema notaciji iz alata Visual Paradigm.
Slika 55: a) aktivnosti prema notaciji superstrukture u UML 2.4.1., b) aktivnosti prema alatu Visual Paradigm
Akcija Za razliku od aktivnosti, akcija ne može sadržavati druge elemente. Naziv akcije je upisan u sredinu elementa dok je kod aktivnosti upisan u gornji lijevi kut. Akcija predstavlja jedan korak u nekoj aktivnosti: npr. Unos korisničkih podataka. U ovom primjeru se postavlja logično pitanje, ne bi li se ta akcija mogla razdvojiti na još manje akcije (npr. Upiši korisničko ime -> Upiši lozinku -> Ponovno upiši lozinku -> itd.) odgovor je: da, ali je potrebno zadržati se na nekoj razumnoj razini apstrakcije - detaljnosti. Detaljiziranje bi rezultirao visokim troškovima i nepotrebnim raščlanjivanjem akcija koje su ponekad trivijalne. Slika 56 a) je prema notaciji superstrukture UML 2.4.1. Slika 56: a) akcija prema notaciji superstrukture UML 2.4.1., b) akcija prema alatu Visual Paradigm
Aktivnost se prikazuje pravokutnikom zaobljenih kutova, a sadrži naziv ili slobodno formulirani opis. Ovaj element se ne može nalaziti u više od jedne domene odgovornosti. Inicijalni čvor Svaki dijagram aktivnosti može ali i ne mora imati inicijalni čvor (Slika 57 a). Njegova svrha je definiranje početnih uvjeta za početak neke aktivnosti ili skupa interakcija između akcija ili jednostavnije: označava početak dijagrama aktivnosti odnosno početnu akciju/aktivnost u dijagramu.
Slika 57: a) inicijalni čvor, b) završni čvor
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
85
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram aktivnosti – koncepti i razvoj
Završni čvor Svaki dijagram aktivnosti, ako ima inicijalni čvor, mora imati i završni čvor (Slika 57 b). Njegova svrha je definiranje završnih uvjeta koji nastaju završetkom zadnje aktivnosti u dijagramu aktivnosti ili jednostavnije: označava kraj dijagrama aktivnosti odnosno zadnju akciju/aktivnost u dijagramu. Napomena Inicijalni i završni čvorovi se ne moraju koristiti isključivo u dijagramu aktivnosti kao osnovni elementi, njihova primjena je korisna kod definiranja unutarnje logike pojedine aktivnosti, ako ona sadrži nekoliko akcija od koje su neke „ulazne“ a neke „izlazne“. Kako bi se uveo red u ulaze i izlaze korisno je imati jedan inicijalni (ulazni) i jedan završni (izlazni) čvor unutar aktivnosti.
Objekt U dijagramu aktivnosti je moguće definirati element koji se zove objekt i on reprezentira instancu specifičnog objekta u specifičnom stanju. Primjer objekta može biti „Dokument“ gdje su njegova stanja: napisan, ispisan i uništen. Stanja se prikazuju u uglatim zagradama ispod samog naziva objekta. Slika 58 a) je prema notaciji superstrukture UML 2.4.1.
Slika 58: a) objekt prema notaciji superstrukture UML 2.4.1., b) objekt prema alatu Visual Paradigm
Čvor odluke U dijagramu aktivnosti moguće je definirati i odluke o odabiru niza aktivnosti koje će biti izvršene. Čvor odluke se prikazuje rombom. Čvor odluke može imati samo jednu ulaznu vezu i nekoliko izlaznih veza. Poželjno je na izlaznim vezama napisati uvjet pod kojim će se odabrati pojedina veza, tako da se kasnije zna o kojoj odluci se radi u čvoru odluke. Slika 59 a) je prema notaciji superstrukture UML 2.4.1.
Slika 59: a) čvor odluke prema notaciji superstrukture UML 2.4.1., b) čvor odluke prema alatu Visual Paradigm
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
86
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram aktivnosti – koncepti i razvoj
Paralelni rad
Dijeljenje (eng. fork) je posebna vrsta čvora koja ima jednu ulaznu i više izlaznih veza. Prikazuje se u obliku vertikalne zadebljane linije. Svrha dijeljenja je grananje toka aktivnosti na dva ili više paralelno izvođenih nizova. Aktivnosti proizašle iz Fork čvora se izvode paralelno. Za razliku od čvora odluke gdje se izvršava samo jedan niz aktivnosti ovdje se izvršavaju sve i to istovremeno. Kako bi se zatvorio ciklus paralelnog rada potrebno je aktivnosti spojiti u jedan niz aktivnosti i to uz pomoć elementa spajanja Slika 60: a) paralelni rad prema notaciji (eng. join) koji je prikazan na slici. Element spajanja superstrukture UML 2.4.1., b) paralelni rad prema ima više ulaznih nizova aktivnosti i jedan izlazni niz alatu Visual Paradigm aktivnosti. Simbol je sličan (točnije simetričan) simbolu dijeljenja (Fork). Slika 60 a) je prema notaciji superstrukture UML 2.4.1. Veze U dijagramu aktivnosti možemo koristiti sljedeće veze:
Kontrolna veza (tok): (eng. control flow) je veza kojom jedna akcija (ili čvorovi, aktivnosti, odluke, dijeljenja,...) kao element „prepušta“ kontrolu odnosno izvršavanje drugom elementu (Slika 61 a). Prepuštanjem kontrole se podrazumijeva da je element koji je prepustio kontrolu završio svoje aktivnosti ili zadatke. Svaka aktivnost/akcija mora imati barem jednu izlaznu vezu. Ako ih je više od jedne onda one moraju biti rezultat nekog čvora odluke. Objektna veza (tok): (eng. object flow) je posebna vrsta veze koja služi isključivo za povezivanje aktivnosti/akcija i objekata (Slika 61 b). Objektna veza je neovisna o kontrolnoj vezi što znači da jedna aktivnost/akcija može stvoriti neki objekt, a druga aktivnost/akcija ga koristi (ili mu mijenjati stanja) no, te dvije aktivnosti/akcije ne moraju biti povezane s kontrolnom vezom. Neki autori Slika 61: a) kontrolna veza, b) objektna veza podrazumijevaju i prijenos kontrole sa samim prijenosom objekta objektnom vezom.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
87
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram aktivnosti – koncepti i razvoj
6.3. Implementacija Kada je dijagram aktivnosti dovoljno jasan i detaljan, može se implementirati u obliku aplikacije. Za proces implementacije pomažu nam i drugi dijagrami uz UML-a poput dijagrama klasa, dijagrama slijeda itd. Primjer dijagrama aktivnosti
Slika 62: Dijagram aktivnosti (primjer)
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
88
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram aktivnosti – koncepti i razvoj
Slika 62 prikazuje dijagram aktivnosti kojeg ćemo implementirati. Dijagramom je opisana jednostavna aplikacija koja za dva zadana cijela broja računa sumu i umnožak svih brojeva unutar intervala zadanog s ulaznim brojevima. Suma i umnožak se računaju paralelno u dretvama. Prije samog izračuna, potrebno je analizirati početne vrijednosti i eventualno napraviti zamjenu varijabli tako da je prvi broj uvijek manji od drugog broja. Aplikacija koju ćemo implementirati, će biti obična windows forms desktop aplikacija s jednom formom. Dizajn forme Postoji nekoliko smjernica početka izrade aplikacije, jedan od njih je da na početku pokušamo dizajnirati aplikaciju pa potom implementiramo funkcionalnosti. Dizajniramo formu tako da zadovolji potrebe dijagrama aktivnosti ( Slika 63). Inicijalni dizajn ne mora ostati isti do kraja razvoja jer je teško uočiti, a i navesti sve potrebne elemente dizajna. S obzirom na to da je naš primjer relativno jednostavan moguće je u potpunosti napraviti dizajn aplikacije na samom početku. Slika 63: Dizajn forme
Iz dijagrama aktivnosti se može vidjeti da će na nekoliko mjesta trebati ispisivati informacije u TextBox kojeg smo nazvali log. Napravimo si jednu javnu metodu koja će nam služiti za zapisivanje u log: Kôd 44: Implementacija metode PisiULog(string poruka) public void PisiULog(string poruka) {
lock (log) { log.Text = poruka + Environment.NewLine + log.Text; } }
Nakon dizajna i inicijalnih postavki, trebamo ići po dijagramu aktivnost od početnog čvora pa sve do završnog čvora. Prva akcija koju aplikacija radi jest „Prikaži glavnu formu“ – ova akcija se pokreće i završava nakon pokretanja same izvršne datoteke. Nakon toga slijedi prijenos kontrole na sljedeću akciju „Obavijesti korisnika da može koristiti aplikaciju“. Možemo vidjeti da nakon ove akcije slijedi prijenos objekta MessageBox (poruka) korisniku aplikacije. U suštini: nakon što se aplikacija pokrene korisniku u MessageBox prozoru treba korisniku ispisati poruku da može koristiti aplikaciju. © University of Zagreb, Faculty of Organization and Informatics, Varaždin
89
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram aktivnosti – koncepti i razvoj
MessageBox MessageBox je statična klasa koja služi za prikazivanje skočnih prozora s nekom poruku. Možemo mijenjati tekst, naslovni tekst, ikonu, tipke i ostale atribute. MessageBox klasa ima jednu vlastitu metodu: Show(...) koja nam služi za prikazivanje poruka. Aktivnost „Obavijesti korisnika da može koristiti aplikaciju“ implementiramo tako da u konstruktoru glavne forme pozovemo Show(...) metodu (MessageBox klase) s odgovarajućim parametrima: Kôd 45: Konstruktor glavne forme + MessageBox public ActivityThreadsPI() { InitializeComponent(); MessageBox.Show("Aplikacija je spremna za korištenje."); }
Nakon prikaza poruke sada je potez na korisniku, mora unijeti dva broja i pritisnuti na tipku „Unesi vrijednosti“. Nakon toga aktivira se prva aktivnost: „Analiziraj početne vrijednosti“. Ovu aktivnost implementirat ćemo jednom metodom koja će provjeravati unos brojeva. Prije toga potrebno je napraviti deklaracije svih varijabli koje nam trebaju. Kôd 46: Deklaracije globalnih varijabli glavne forme private int prviBroj=0; private int drugiBroj = 0; public Thread dretvaSuma; public Thread dretvaUmnozak;
Provjeri unos Nakon toga možemo implementirati metodu „ProvjeriUnos()“, bitno nam je samo da prvi broj bude manji od drugog, ako nije onda ih treba međusobno zamijeniti. Kôd 47: Implementacija metode ProvjeriUnos() private void ProvjeriUnos() { int zamjena = 0; prviBroj = int.Parse(ulazPrviBroj.Text); drugiBroj = int.Parse(ulazDrugiBroj.Text); if (prviBroj > drugiBroj) { zamjena = drugiBroj; drugiBroj = prviBroj; prviBroj = zamjena; } PisiULog("Prvi broj: " + prviBroj.ToString()); PisiULog("Drugi broj: " + drugiBroj.ToString()); }
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
90
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram aktivnosti – koncepti i razvoj
Ovu metodu je potrebno pozvati nakon što korisnik klikne na tipku „Unesi vrijednosti“. Kôd 48: Poziv metode ProvjeriUnos() private void unesiVrijednosti_Click(object sender, EventArgs e) { ProvjeriUnos(); }
6.4. Dretve Ostaje nam implementacija aktivnosti koje se odvijaju nakon pritiska na tipku „Pokreni izračun“. Osim glavne dretve, ona koja je odgovorna za prikaz sučelja imat ćemo dvije dodatne dretve za naš izračun. Što je dretva? -
Najmanja sekvenca instrukcija koja se može neovisno izvoditi na jednom procesoru.
Zašto dretva? -
Kako je danas brzina samih procesora dosegla svoj neki gornji limit, industrija se orijentirala u širinu odnosno paralelni način rada. Tako da danas na tržištu imamo procesore s N jezgri. Na jednoj jezgri se može izvršavati samo jedna dretva, teoretski- zato što postoje tehnologije koje virtualno omogućavaju izvršavanje više od jedne dretve u isto vrijeme na jednoj jezgri, to je moguće zbog velike brzine procesora. Dakle, više od jedne dretve koristimo kada želimo neki teški (procesorski zahtjevan) zadatak riješiti u što kraćem roku. Taj teški zadatak podijelimo na više manjih, ali isto „teških“ zadataka koji će se izvršavati svaki u svojoj dretvi – paralelno.
Problemi? Naravno, puno: -
Arhitekturni problem govori o tome da ako bi koristili višedretveni rad u našoj aplikaciji moramo na samom početku (modeliranju) predvidjeti takav način rada. Paralelni rad zahtjeva nešto drugačiji pristup od npr. proceduralnog – slijednog kojeg smo koristili do sada.
-
Drugi veliki problem predstavlja sinkronizacija dretvi, kako i kada pojedina dretva započinje i kada završava.
-
Treći veliki problem je pristup podacima: varijablama, datotekama, mreži,… Problem nastaje kada u istu varijablu u isto vrijeme zapisuju dvije dretve. Čitanje vrijednosti ne predstavlja toliki problem, ako se u varijablu ne zapisuje vrijednost za vrijeme čitanja varijable.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
91
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram aktivnosti – koncepti i razvoj
Dretve se, u pravilu, izvršavaju istovremeno. Svakoj dretvi treba definirati metodu koju treba izvršiti. Višedretveni način rada zna biti poprilično kompliciran jer osim zasebne metode koja se izvršava, postoji čitavi zasebni kontekst. Komunikacija dretvi s glavnom UI dretvom je jedan zaseban problem kojeg ćemo privremeno riješiti dodavanjem naredbe koja će .Net-u reći da ignorira greške koje nastaju kada jedna dretva koristi objekte glavne UI dretve. Iz dijagrama aktivnosti vidimo da dretve zapisuju u log, koji je nastao u glavnoj dretvi te tu nastaje problem pristupa objektima iz drugih dretvi. Kôd 49: Onemogućavanje cross thread grešaka CheckForIllegalCrossThreadCalls = false;
Kada nekom elementu (objektu, varijabli,…) pristupa više dretvi postoji problem istovremenog pisanja i čitanja. Jedan od jednostavnijih načina rješavanja tog problema je korištenjem lock ključne riječi (pogledati kôd PisiULog(…) metode). Lock nam definira dva elementa:
(…) Element zaključavanja pristupa, neka vrsta vrata, tko prvi dođe do vrata ima prednost. {…} Blok kôda koji će se izvršiti s isključivim pravom pristupa.
U našem primjeru element zaključavanja pristupa je log textbox, iako bi u ovom kontekstu to mogao biti bilo koji objekt npr. this ili bilo koji objekt (referenca na objekt). Pozivanje metoda između dretvi (ako su onemogućeni cross thread pozivi) nije dozvoljeno. No postoji način: statičke metode, statička metoda (npr. Show(…) u klasi MessageBox) ne pripada niti jednom objektu, a samim time niti jednoj dretvi, dretve ne mogu pristupati objektima koje je neka druga metoda stvorila. Štoviše, statičke klase se ne moraju instancirati već se mogu koristiti bilo gdje, dakle iz bilo koje dretve. Ako trebamo dijeljene podatke/resurse deklarirat ćemo ih kao statične. Statički objekti + mehanizmi zaključavanja rješavaju problem sinkronizacije i komunikacije između dretvi. Pozivanje metoda na glavnoj dretvi (UI) je relativno komplicirano jer uključuje i delegate. Kôd 50: Invoke kôda na UI dretvi elementUI.Invoke((MethodInvoker)delegate { //kôd koji treba izvršiti });
elementUI je bilo koji objekt kojeg je napravila glavna (UI) dretva, npr. to može biti TextBox, Button,… Ako napravimo metodu koja treba izvršiti neki kôd koji radi nešto s elementUI tada to korištenje treba implementirati kroz poziv Invoke metode. Ova metoda kao parametar prima drugu metodu (tj. njen pokazivač tj. njen delegat) koju će izvršiti kada se za to steknu uvjeti: prvi slobodni CPU ciklus. U .Net-u, umjesto delegata metode možemo izravno u pozivu same Invoke metode napisati čitavi kôd, kako je to i prikazano na primjeru iznad. Ovakav pristup se zove definiranje anonimne metode. © University of Zagreb, Faculty of Organization and Informatics, Varaždin
92
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram aktivnosti – koncepti i razvoj
Dretve (dijagram aktivnosti – nastavak) Sukladno naučenom, u našem kôdu prvo trebamo onemogućiti cross thread greške, navedenu liniju kôda napišemo u konstruktoru glavne forme (klase). Prilikom deklariranja varijabli možemo vidjeti da smo napisali i dvije Thread varijable koje još nisu instancirane, dakle nisu napravljeni objekti. Prije nego ih instanciramo trebat će nam metode koje će te dretve i izvršavati: Kôd 51: Metoda dretve za izračun sume public void DretvaSumaWork() { int suma = 0; for (int i = prviBroj; i < drugiBroj; i++) { suma += i; } PisiULog("Suma= "+ suma.ToString() + " kraj:" + DateTime.Now.ToString()); }
Kôd 52: Metoda dretve za izračun umnoška public void DretvaUmnozakWork() { int umnozak = 0; for (int i = prviBroj; i < drugiBroj; i++) { umnozak *= i; } PisiULog("Umnožak= " + umnozak.ToString() + " kraj:" + DateTime.Now.ToString()); }
U ove obje metode problematičan bi bio poziv metode PisiULog(...) da nismo onemogućili cross thread greške jer ta metoda koristi objekt (log TextBox) iz druge dretve, koja predstavlja glavnu dretvu. Sada kada imamo implementirane metode koje će dretve izvršavati možemo i instancirati same dretve. Dretve ćemo instancirati u konstruktoru glavne forme. Kôd 53: Instanciranje objekata dretvi dretvaSuma = new Thread(new ThreadStart(DretvaSumaWork)); dretvaUmnozak = new Thread(new ThreadStart(DretvaUmnozakWork));
Nakon što smo dretve instancirali možemo ih i pokrenuti i to tek nakon što korisnik klikne na tipku „Pokreni izračun“.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
93
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram aktivnosti – koncepti i razvoj
Kôd 54: Pokretanje dretvi private void pokreniIzracun_Click(object sender, EventArgs e) { PisiULog("početak svih izračuna u: " + DateTime.Now.ToString()); dretvaSuma.Start(); dretvaUmnozak.Start(); }
Sukladno s dijagramom aktivnosti u ovoj metodi (za pokretanje izračuna) ispisujemo vrijeme početka izračuna u log. Testiranje aplikacije Tokom razvoja aplikacije bilo bi je dobro testirati. Na kraju, potrebno je napraviti test aplikacije: Testni podaci:
Prvi broj = 10 Drugi broj = 0 Pitanje za razmišljanje (1)
Zašto je rezultat umnoška uvijek 0?
Pitanje za razmišljanje (2) Zašto je rezultat umnoška još uvijek 0?
6.5. Pitanja i zadaci Samostalni zadatak U primjeru s vježbi komentirajte liniju Kôd 55: Onemogućavanje cross thread grešaka CheckForIllegalCrossThreadCalls = false;
te riješite grešku koja se pojavila. Možete se služite izvorima na Internetu.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
94
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram aktivnosti – koncepti i razvoj
Zadaci 1. Proučite Thread klasu, metode i postavke. 2. Proučite kako pokrenuti dretvu s parametrima. 3. Proučite metode sinkronizacije dretvi i razmjene podataka između dretvi. 4. Proučite BackgroundWorker klasu. 5. Proučite ThreadPool klasu. Pitanja 1. Što je to dretva? 2. Koja je razlika između objektnog i kontrolnog toka? 3. Koja je razlika između akcije i aktivnosti? 4. Kada koristimo aktivnost? 5. Kako se implementira čvor odluke 6. Kako implementiramo kontrolni tok? 7. Kako iz jedne dretve koristimo elemente UI dretve? 8. Koji su problemi kod višedretvenog rada? 9. Može li procesor sa jednom jezgrom izvoditi više dretvi? 10. Može li procesor sa jednom jezgrom izvoditi više dretvi istovremeno? 11. Kako koristimo statične objekte kod višedretvenog rada? 12. Koja je svrha početnog čvora u dijagramu aktivnosti? 13. Koja je svrha završnog čvora u dijagramu aktivnosti? 14. Što su to input i output pinovi? 15. Što je to interuptable region? 16. Kada u aktivnosti koristimo početne i završne čvorove? 17. Što je to swimlane (domena odgovornosti)?
6.6. Više informacija o temi 1. P. Grassle, H. Baumann, P. Bauman: UML 2.0 in Action 2. WinForms UI Thread Invokes: An In-Depth Review of Invoke/BeginInvoke/InvokeRequred (http://weblogs.asp.net/justin_rogers/pages/126345.aspx), dostupno: prosinac, 2013. 3. Threading (http://msdn.microsoft.com/en-us/library/aa645740(VS.71).aspx), dostupno: prosinac, 2013. 4. How to synchronize access to a shares resource in a multithreading environment by using Visual C# (http://support.microsoft.com/kb/816161), dostupno: prosinac, 2013.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
95
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram aktivnosti – koncepti i razvoj
6.7. Prilozi 1. (InputAD.zip) Projekt u Visual Paradigm s dijagramom aktivnosti. 2. (ActivityThreads.zip) Projekt u Visual Studio s implementacijom dijagrama aktivnosti 3. (Dretve.zip) Projekt u Visual Studio koji pokazuje jednostavan rad i sinkronizaciju dretvi pomoću statičnih objekata.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
96
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram slijeda – koncepti i razvoj
7. UML dijagram slijeda – koncepti i razvoj Sažetak Definirani su osnovni koncepti vezani uz dijagram slijeda. Na primjeru je prikazano kako se implementiraju neki od tih koncepata. Primjer koji je implementiran koristi mrežni rad, odnosno komponente za implementaciju TCP/UDP protokola koristeći socket objekte. Ključne riječi: sequence diagram, dijagram slijeda, network, TCP, UDP, socket, stream
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
97
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram slijeda – koncepti i razvoj
7.1. Uvod Dijagram slijeda nam omogućava da razradimo detalje oko funkcioniranja nekog sustava. Moderni računalni sustavi, u pravilu imaju barem jedan vid mrežne komunikacije, bilo da je to slanje maila, preuzimanje web stranice ili možda mrežna komunikacija koristeći TCP/UDP protokole. Upravo se na temelju mrežne komunikacije temelji primjer na kojem je prikazano kako prevoditi dijagram slijeda u programski kôd.
7.2. Dijagram slijeda – sequence diagram Dijagram slijeda (eng. Sequence Diagram) prikazuje vremenski uređenu interakciju između instanci koje sudjeluju u njoj. Pod interakcijom se podrazumijeva skup poruka koje se izmjenjuju između instanci objekata koje surađuju radi postizanja nekog učinka. Dijagram slijeda može prikazivati interakciju na više razina, tako na primjer može prikazivati interakciju (komunikaciju) između različitih modula nekog složenog sustava, ali može i prikazivati interakciju između objekata različitih klasa. Generalno korištenje dijagrama slijeda jest da pobliže opiše dijagram aktivnosti odnosno dio sustava koji je opisan dijagramom aktivnosti. Samim time dodatno opisuje pojedini slučaj korištenja (use case), međutim, to nije pravilo, jedan dijagram slijeda može opisivati dio jednog slučaja korištenja, ali i ponašanje više slučajeva korištenja. Dijagram slijeda ima dvije dimenzije: vertikalnu koja predstavlja vremensku skalu kojom se prikazuje vremenski poredak odaslanih i primljenih poruka između instanci i horizontalnu koja prikazuje instance objekata koje sudjeluju u interakciji pri čemu poredak nije bitan, međutim poželjno je napraviti raspored da se minimiziraju preklapanja poruka. Životni tok (Lifeline) Kako bi naznačili da je pojedina instanca aktivna u određenom vremenskom intervalu koristimo se životnom linijom instance (eng. lifeline) koju na dijagramu prikazujemo vertikalnom isprekidanom linijom koja izlazi iz simbola instance objekta nacrtane na horizontalnoj osi (Slika 64). Njome prikazujemo egzistenciju neke instance i trajanje te egzistencije. Kada neka instanca biva uništena tijekom interakcije, njena se životna linija prekida i opcionalno se označava sa simbolom X na kraju linije. Period u kojem instanca izvršava neku akciju naziva se aktivacija (eng. activation), a prikazuje se uskim, izduženim pravokutnikom koji prekriva životnu liniju (plavi pravokutnik Slika 64). Slika 64: Životna linija instance
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
98
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram slijeda – koncepti i razvoj
Poruke Komunikacija između dviju instanci ostvaruje se porukama (Slika 65). Njih prikazujemo punim horizontalnim strelicama koje izlaze iz životne linije jednog objekta, a završavaju u životnoj liniji drugog objekta (ako objekt ne šalje poruku sam sebi). Poruka sadrži naziv te opcionalno i uvjete slanja poruke. Završetak pozvane operacije označavamo isprekidanom linijom koja je usmjerena prema instanci koja ju je pozvala. Slika 65: Komunikacija porukama
Ostale poruke (Slika 66): Create Message - poruka koja stvara novi životni tok (lifeline). U nižim razinama apstrakcije, ta poruka može biti poziv konstruktora neke klase prilikom instanciranja objekta. Duration Message – alternativa običnoj poruci koja se koristi kako bi se naznačilo da je za slanje poruke potrebno rezervirati ili čekati određeno vrijeme. Self Message – jedan sudionik rezultate određene operacije šalje kao poruku samom sebi. Recursive Message – poruka koja se šalje više puta, prije nego što se prijeđe na izvođenje sljedećeg koraka. Lost & Found Message – poruka se može izgubiti ili ponovno pronaći tijekom različite komunikacije. Ako želimo specificirati ovu mogućnost, odabrat ćemo Lost Message te povući crtu od učesnika prema sredini, gdje bi poruka trebala biti izgubljena. S druge strane, za označavanje pronađene poruke povući ćemo crtu od sredine dijagrama prema učesniku. Reentrant Message – poruka koja poziva novu instancu programskog kôda, klase ili metode. Nova instanca se može odvijati paralelno sa već postojećom instancom ili instancama. Slika 66: Ostale vrste poruka
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
99
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram slijeda – koncepti i razvoj
Fragmenti Alt fragment predstavlja grananje poruka, implementacija kojeg je obična selekcija (Slika 67). Alt fragment ima svoje operatore, na slici su prikazana dva, prvi se izvršava kada je uvjet „Ako je uvjet“ istina, dok se drugi izvršava kada je uvjet „Ako nije uvjet“ istina. Alt fragment može imati jedan ili više operanada.
Slika 67: Alt fragment
Drugi značajan fragment je Loop pravokutnik, koji služi za ponavljanje nekog dijela dijagrama slijeda. Trajanje ponavljanja se definira operandom. U konkretnom primjeru se poruke ponavljaju 10 puta (Slika 68). Loop fragment se implementira običnom iteracijom (petlje).
Slika 68: Loop fragment
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
100
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram slijeda – koncepti i razvoj
7.3. Dijagram
slijeda
primjera
sustava
za
mrežnu
komunikaciju
Slika 69: Dijagram slijeda za mrežnu komunikaciju
Temeljem dijagrama slijeda (Slika 69) implementirat ćemo jednostavni sustav prijenosa podataka koristeći mrežnu komunikaciju UDP protokolom.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
101
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram slijeda – koncepti i razvoj
7.4. Implementacija Dijagram slijeda pokazuje sustav s dvije aplikacije od kojih jedna „sluša“, a druga „šalje“ poruke. Za početak napravimo rješenje (eng. Solution) s dva Windows Forms projekta od kojih svaki ima samo po jednu formu: piServer Sastoji se od: 1. TextBox a. Name: ReceiveContainer b. Multiline: True 2. Button a. Name: StartListeningAction b. Text: Start Listening c. Click Event Handler: StartListeningAction_Click(...) Slika 70: piServer
piClient Sastoji se od: 1. Label a. Text: Destination 2. MaskedTextBox11 a. Name: DestinationAddress b. Mask: 990\.990\.990\.990 c. Text: 000000000000 d. InsertKeyMode: Overwrite 3. Button a. Name: DestinationSetAction Slika 71: piClient b. Text: Set c. Click Event Handler: DestinationSetAction_Click(…) 4. TextBox a. Name: ReceiveContainer b. Multiline: True 5. TextBox a. Name: MessageToSend 6. Button a. Name: MessageSendAction 11
Više o ovoj kontroli na: http://msdn.microsoft.com/en-us/library/1fkc1cz6.aspx
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
102
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram slijeda – koncepti i razvoj
b. Text: Send c. Click Event Handler: MessageSendAction_Click (…) Napomena Kada radimo s dva projekta koja je potrebno u isto vrijeme pokrenuti i debugati, potrebno je na razini rješenja definirati što se pokreće. Do sada se uvijek pokretao jedan projekt, kako bi pokrenuli dva potrebno je otvoriti properties od čitavog rješenja. Odaberimo opciju Startup
Project
te
odaberemo
Multiple startup projects gdje za oba
naša
projekta
odaberemo
akciju Start (Slika 72).
Slika 72: Multiple startup projects
piServer (kôd) Iz dijagrama slijeda možemo vidjeti da serverski dio zahtjeva implementaciju nekoliko metoda: 1. private void WriteToLog(string message) 2. Initialize components = set metoda koje uključuju InitializeComponents() i konstruktor 3. private void StartListening()
Prije nego li nastavimo s implementacijama navedenih metoda, nužno je napraviti deklariranja objekata i varijabli koje ćemo koristiti u kôdu: Kôd 56: Deklariranje mrežnih objekata private Socket netSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); private SocketAsyncEventArgs netSocketServerArgs = new SocketAsyncEventArgs(); private byte[] netSocketBuffer = new byte[1024];
Kôd iznad prikazuje deklaraciju svih potrebnih objekata za mrežni rad u klasi glavne forme: Socket – osnovna komponenta za mrežnu komunikaciju, konstruktor prima kao parametre: a. Vrsta mreže, odabran je InterNetwork koji predstavlja IPv4 mrežu © University of Zagreb, Faculty of Organization and Informatics, Varaždin
103
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram slijeda – koncepti i razvoj
b. Vrsta Socketa, odabran je Dgram što definira socket da radi s UDP malim paketima. Kada bi radili s TCP protokolom u ovom slučaju bi odabrali Stream c. Vrsta Protokola, odabran je UDP, definira protokol koji će se koristiti u socket objektu SocketAsyncEventArgs – u .netu je moguće na više načina implementirati mrežnu komunikaciju: asinkrono koristeći delegate ili asinkrono koristeći SocketAsyncEventArgs ( više na : http://msdn.microsoft.com/enus/library/system.net.sockets.socketasynceventargs.aspx) Objekt ove klase se prosljeđuje kao parametar svim asinkronim metodama Socket objekta. Buffer – SocketAsyncEventArgs objekt u sebi ima i buffer koji služi kao spremnik za primljene odnosno poslane podatke. Zbog toga ovdje radimo buffer veličine 1024 elemenata, a tipa je polje byte Kôd 57: 1. WriteToLog(…) private void WriteToLog(string message) { ReceiveContainer.Invoke((MethodInvoker)delegate { ReceiveContainer.Text = message + Environment.NewLine + ReceiveContainer.Text; }); }
WriteToLog (…) metoda nam služi za zapisivanje teksta (poruka) u ReceiveContainer (TextBox). Primijetimo kako ovdje koristimo delegat kako bi napravili Thread Safe izmjenu sadržaja TextBoxa. Pitanje za razmišljanje (1) Zašto u WriteToLog metodi koristimo Invoke tehniku? Napomena: odgovor nije samo: „kako bi napravili Thread Safe izmjenu sadržaja TextBoxa.“
Kôd 58: 2. Initialize components (konstruktor glavne forme) public Form1() { InitializeComponent(); netSocket.ExclusiveAddressUse = false; netSocketServerArgs.Completed += netSocketServerArgs_Completed; netSocketServerArgs.SetBuffer(netSocketBuffer, 0, 1024);//jako bitno }
Kôd iznad prikazuje konstuktor glavne forme. Tu se, standardno poziva InitalizeComponent() metoda. Osim toga, na razini našeg Socket objekta definiramo da, lokalnu adresu na koju će se naš socket povezati (eng. Bind), mogu koristiti i drugi socketi na raznini čitavog operacijskog sustava.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
104
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram slijeda – koncepti i razvoj
Definiramo i metodu koja će obrađivati netSocketServerArgs.Completed događaj. Ovaj događaj se bude dogodio svaki puta kada završi neka operacija u kojoj je sudjelovao SocketAsyncEventArgs objekt, a koju je inicirao Socket objekt. Izuzetno je bitno postaviti buffer koristeći SetBuffer metodu koja prima kao parametre postojeći buffer (kojeg smo ranije deklarirali) i početni byte i završni byte, u konkretnom slučaju postavljamo čitavi buffer. Ovaj korak je bitan jer ako izostane, komunikacija neće biti moguća te neće biti generirana nikakva run-time iznimka. Kôd 59: 3. StartListening() private void StartListening() { netSocket.Bind(new IPEndPoint(IPAddress.Any, 8087)); netSocket.ReceiveAsync(netSocketServerArgs); WriteToLog("listening"); }
Kôd iznad prikazuje implementaciju StartListening() metode. Prva naredba povezuje socket s lokalnom adresom (eng. Bind). Svaki socket se mora povezati na lokalnu adresu i port kako bi operacijski sustav (mrežna kartica) znao kome treba delegirati mrežne pakete. Za to nam služi Bind metoda koja prima jedan IPEndPoint objekt kojeg smo napravili tako da smo konstruktoru proslijedili IPAdresu i port. IP adresa koju ćemo koristiti je Any, što znači bilo koja adresa jer računala mogu imati više od jedne IP adrese. Mi ćemo koristiti sve, što znači da paketi upućeni na bilo koju adresu, bit će obrađeni s našim Socket objektom. Osim adrese nužno je i definirati port. Naime pojedine aplikacije (konzumenti, odnosno Socketi) mrežnih paketa se međusobno razlikuju po portu, mi ćemo koristiti port 8087. Nakon toga se poziva asinkrona metoda ReceiveAsync, što znači da će metoda odmah završiti. Ova metoda kao parametar prima naš jedan SocketAsyncEventArgs objekt kojeg smo deklarirali na početku. Ova metoda će čekati sve dok Socket ne primi podatke, kada su podaci primljeni dogodit će se Completed događaj kojeg smo definirali na početku. Na kraju pozivamo WriteToLog() metodu koja će nam u Log ispisati tekst „listening“. Kôd 60: 3.1. netSocketServerArgs.Completed event handler void netSocketServerArgs_Completed(object sender, SocketAsyncEventArgs e) { if (netSocketServerArgs.LastOperation == SocketAsyncOperation.Receive) { WriteToLog("message: " + UTF8Encoding.UTF8.GetString( netSocketServerArgs.Buffer, 0, e.BytesTransferred)); netSocket.ReceiveAsync(netSocketServerArgs); } }
U StartListening() metodi smo nad netSocket pozvali metodu ReceiveAsync. Kada socket primi podatke s mreže dogodit će se Completed događaj nad netSocketServerArgs objektom. © University of Zagreb, Faculty of Organization and Informatics, Varaždin
105
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram slijeda – koncepti i razvoj
U kôdu iznad je prikazana metoda koja će obrađivati ovaj događaj. Ta ista metoda služi za sve moguće operacije na razini Socketa: http://msdn.microsoft.com/enus/library/system.net.sockets.socketasyncoperation.aspx Kako
trenutno
radimo
na
serveru jedina operacija koju ćemo mi obrađivati jest SocketAsyncOperation.Receive U ovoj metodi ispisujemo sadržaj buffera koji se nalazi u netSocketServerArgs objektu, kako se radi o polju byte-ova potrebno ga je konvertirati u string koristeći UTF8 enkodiranje (metoda GetString()). Napomena Na primjer: server prima dvije poruke jednu iza druge: „DugaPoruka“ i „Poruka“. Ako koristimo jedan buffer tada, nakon primanja druge poruke, sadržaj buffera će biti: „Porukaruka“ te nećemo moći razaznati gdje završava druga poruka.
Zbog navedenog, potrebno je iz buffera pročitati samo početnih N byte-ova, odnosno onoliko koliko ih je poslano; zbog toga GetString(…) kao drugi parametar ima 0 i kao treći parametar e.BytesTransferred Napomena Iako se u konkretnom primjeru koristi UDP, nije potrebno izričito „slušati“ na vezu te se „slušanje“ svodi na bindanje na lokalni port i pozivanje petlje za slušanje. Pravo „slušanje“ je potrebno koristiti u slučaju
da
se
radi
TCP
komunikacija,
vidi:
http://msdn.microsoft.com/en-
us/library/system.net.sockets.tcplistener.aspx
piKlijent (kôd) Iz dijagrama slijeda možemo vidjeti da klijentski dio zahtjeva implementaciju nekoliko metoda: 1. Initialize components = set metoda koje uključuju InitializeComponents() i konstruktor 2. private void DestinationSet(IPEndPoint address) 3. private IPEndPoint GetDestination() 4. private void WriteToLog(string message) 5. private void MessageSend(string message) 6. private void ClearMessageInput()
Prije nego li nastavimo s implementacijama navedenih metoda nužno je napraviti deklariranja objekata i varijabli koje ćemo koristiti u kôdu: Kôd 61: Deklariranje mrežnih objekata private Socket netSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); private SocketAsyncEventArgs netClientServerArgs = new SocketAsyncEventArgs();
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
106
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram slijeda – koncepti i razvoj
private byte[] netSocketBuffer = new byte[1024];
Kôd iznad prikazuje deklaraciju svih potrebnih objekata za mrežni rad u klasi glavne forme, ova deklaracija je identična onoj već opisanoj za serversku aplikaciju. Kôd 62: 1. Initialize components (konstruktor glavne forme) public Form1() { InitializeComponent(); netSocket.ExclusiveAddressUse = false; netSocketClientArgs.Completed += netSocketClientArgs_Completed; netSocketClientArgs.SetBuffer(netSocketBuffer, 0, 1024); }
Kôd iznad prikazuje konstruktor glavne forme klijentske aplikacije, za primijetiti je da je identičan kao i kôd konstruktora serverske aplikacije. U konstruktoru povezujemo događaj Completed (nad netSocketClientArgs objektom) s odgovarajućom metodom za obradu: Kôd 63: 1.1. netSocketServerArgs.Completed event handler void netSocketClientArgs_Completed(object sender, SocketAsyncEventArgs e) { if (netSocketClientArgs.LastOperation == SocketAsyncOperation.SendTo) { WriteToLog("destination: " + netSocketClientArgs.RemoteEndPoint.ToString() + " message: " + UTF8Encoding.UTF8.GetString(netSocketClientArgs.Buffer,0,e.BytesTransferred)); } }
Kôd iznad prikazuje obradu Completed događaja. Za razliku od servera ovdje obrađujemo samo SendTo operaciju, u konkretnom primjeru u log ispisujemo odredište i sadržaj poruke kojeg smo dobili iz buffera (spremnika) na isti način kao što to i server radi. Kôd 64: 2. DestinationSet private void DestinationSet(IPEndPoint address) { netSocketClientArgs.RemoteEndPoint = address; WriteToLog("destination set: " + netSocketClientArgs.RemoteEndPoint.ToString()); }
Metoda DestinationSet služi za postavljanje odredišta za zadani netSocketClientArgs. U trenutku kada je ovaj objekt poslan kao parametar npr. metodi Send(…) Socket objekta, mora imati postavljeno odredište inače Socket neće znati kome treba poslati poruku. Kôd 65: 3. GetDestination private IPEndPoint GetDestination() { return new IPEndPoint(IPAddress.Parse(
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
107
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram slijeda – koncepti i razvoj
DestinationAddress.Text.Replace(" ", "")), 8087); }
Ova metoda iz MaskedTextbox-a dohvaća IP adresu te ju parsira i vraća odgovarajući IPEndPoint s dogovorenim portom 8087. Kôd 66: 4. WriteToLog private void WriteToLog(string message) { ReceiveContainer.Invoke((MethodInvoker)delegate { ReceiveContainer.Text = message+ Environment.NewLine + ReceiveContainer.Text; }); }
WriteToLog metoda nam služi za zapisivanje u log, njena implementacija je ista kao i kod serverske komponente. Kôd 67: 5. MessageSend private void MessageSend(string message) { netSocketBuffer = UTF8Encoding.UTF8.GetBytes(message); netSocketClientArgs.SetBuffer(netSocketBuffer, 0, netSocketBuffer.Length); netSocket.SendToAsync(netSocketClientArgs); }
Ova metoda nam služi za slanje poruka koristeći, na početku definirane Socket objekt i SocketAsyncEventArgs objekt. Metoda kao parametar prima string vrijednost koju pretvara u polje byte-ova koristeći GetBytes(…) metodu. To polje byte-ova pohranjuje u buffer koji je na početku definiran. Taj isti buffer se postavlja kao buffer SocketAsyncEventArgs objekta koristeći SetBuffer(…) metodu. Nakon toga se nad Socket-om poziva SendToAsync(…) metoda koja kao parametar prima SocketAsyncEventArgs objekt. Poziv ove metode je asinkroni što znači da se neće čekati dok se poruka fizički ne pošalje. Kada je poruka poslana dogodit će se Completed događaj kojeg će obrađivati metoda koju smo ranije definirali. Kôd 68: 6. ClearMessageInput private void ClearMessageInput() { MessageToSend.Clear(); }
Ova metoda čisti sadržaj TextBoxa kroz kojeg unosimo tekst poruke koju treba poslati. Sadržaj se čisti pozivom Clear() metode koja se nalazi u TextBox klasi. Sve ove navedene metode potrebno je pozvati na odgovarajućem mjestu. U klijentskoj formi imamo dvije tipke: Set i Send. Pritiskom na tipku Set korisnik postavlja odredište, metoda koja obrađuje ovaj događaj je:
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
108
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram slijeda – koncepti i razvoj
Kôd 69: DestinationSetAction_Click private void DestinationSetAction_Click(object sender, EventArgs e) { DestinationSet(GetDestination()); }
Osim Set Destination događaja imamo i događaj koji se dogodi kada korisnik stisne na tipku Send. Metoda koja obrađuje ovaj događaj je: Kôd 70: DestinationSetAction_Click private void MessageSendAction_Click(object sender, EventArgs e) { MessageSend(MessageToSend.Text); ClearMessageInput(); WriteToLog("message sent"); }
Primijetimo kako implementacija ove metode prati dijagram slijeda, naročito na poruke koje se događaju nakon poziva MessageSent(…) metode.
7.5. Testiranje Aplikacije testiramo na način kako je to i prikazano na dijagramu slijeda, dakle: na serveru prvo pokrenemo „Slušanje“, nakon toga u klijentskoj aplikaciji upišemo adresu odredišta npr. 127.0.0.1 koja predstavlja adresu lokalnog računala. Aplikacija će ignorirati vodeće nule(0) npr. svejedno je ako je adresa 127.000.000.001. U polje za unos poruke upišemo neki tekst i pritisnemo tipku Send. Poruka bi se trebala pojaviti na serverskoj aplikaciji. Komunikacija između dva računala Moguće je napraviti i testiranje koristeći dva računala. U tom slučaju je potrebno na oba računala pokrenuti obje aplikacije, te u polje za unos adrese upisati IP adresu drugog računala. Napomena Moguće je da će Windows Firewall blokirati aplikacije. U tom slučaju potrebno je dozvoliti klijentsku i serversku aplikaciju na privatnim i javnim mrežama.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
109
Odabrana poglavlja programskog inženjerstva – razvoj programa UML dijagram slijeda – koncepti i razvoj
7.6. Pitanja i zadaci Samostalni zadatak Napravite identičan primjer koristeći TCP protokol. Čitavo rješenje mora biti jedna WindowsForms Aplikacija. Pitanja 1. Koja je razlika između UDP i TCP protokola na razini implementacije? 2. Za što nam služi SocketAsyncEventArgs? 3. Za što služi Bind() metoda u Socket klasi? 4. Zašto nije nužno koristiti Bind() na lokalnu adresu samo za slanje UDP poruka? 5. Koja je razlika između lost i found poruke? 6. Koja je razlika između normalne i duration poruke? 7. Za što služi CreateMessage? 8. Koja je razlika između Aktivacije i Lifeline? 9. Čemu je Lifeline najsličniji u dijagramu aktivnosti? 10. Što nam definira operand u Loop fragmentu? 11. Što nam definira operand u Alt fragmentu? 12. Koliko operanada najviše može imati Alt fragment? 13. Što je Alt fragment? 14. Što je Loop fragment? 15. Koja je razlika između Alt i Loop fragmenta?
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
110
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka I - osnove
8. Rad s bazom podataka I – osnove Sažetak Većina današnjih aplikacija zahtjeva pohranu većeg ili manjeg broja podataka. U tu svrhu vrlo često koriste baze podataka kao popularan i moćan način pohrane i pretraživanje strukturiranih podataka. U ovom poglavlju opisane su osnove ADO.NET povezanog (eng. connected-based) načina rada s bazom podataka, koja je pokazana na primjeru jednostavne aplikacije. Ključne riječi: ADO.NET, DataReader, SQLite, Data Provider, SqlCommand
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
111
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka I - osnove
8.1. Uvod Osnovna svrha svih softverskih aplikacija je rad s većim ili manjim brojem podataka. Neke aplikacije su podatkovno intenzivnije i manipuliraju s većim brojem složenih podataka, dok su druge u tom smislu jednostavnije. Za vrijeme izvođenja aplikacije, podaci se nalaze pohranjeni u radnoj memoriji, a završetkom izvođenja aplikacije podaci se gube. Vrlo često je potrebno sačuvati podatke i rezultate i nakon zatvaranja aplikacije, a to radimo spremanjem podataka (perzistencija) iz privremene radne memorije na neki drugi medij koji omogućava trajnu pohranu podataka (tvrdi disk, USB memorija i slično). Ovisno o vrsti podataka, ali i o operacijama koje trebamo vršiti nad podacima, neke od mogućnosti za pohranu podataka su: tekstualne datoteke, binarne datoteke, baze podataka. Tradicionalne aplikacije koje rade uglavnom sa strukturiranim numeričkim i tekstualnim podacima, te zahtijevaju složenije načine pristupa i pretraživanja podataka se oslanjaju na baze podataka. Postoje veliki broj definicija baza podataka, međutim većina ih pod pojmom baze podataka podrazumijeva skup više podataka koji su trajno pohranjeni na računalu, bez ili s kontroliranom redundancijom , koji su organizirani i strukturirani na takav način da se mogu vrlo brzo i jednostavno dohvatiti. Spomenimo još i pojam sustava za upravljanje bazom podatka SUBP (eng. DBMS), koji upravlja svakom interakcijom krajnjeg korisnika s fizički pohranjenom bazom podataka na nekom mediju. Koliko je taj sustav važan, govori i činjenica da danas kada govorimo o bazama podataka vrlo često zapravo mislimo na neki sustav za upravljanje bazom podataka. Postoje različite vrste baza podataka, međutim u području tradicionalnih poslovnih aplikacija već više od 30 godina dominiraju relacijske baze podataka. Primjeri najčešće korištenih relacijskih baza podataka su: Oracle Database, Microsoft SQL Server, MySQL, IBM DB2, IBM Informix, PostgreSQL, SQLite, Microsoft Access, Microsoft SQL Server Compact… Kratka povijest relacijskih baza podataka Relacijske baze podataka svoje korijene vuku iz relacijskog modela podataka, čije je temelje udario matematičar iz IBM-a Edgar Codd još davne 1970. godine u svom članku „A Relational Model of Data for Large Shared Data Banks“. Prema mnogima, uvođenje relacijskog modela nedvojbeno predstavlja najznačajniji trenutak u povijesti računalnih baza podataka. Ime relacijski je dobio po tome što se entiteti u relacijskoj bazi prikazuju u obliku relacija, što je zapravo matematički izraz za tablicu. Zahvaljujući Codd-u, relacijski model je imao snažnu matematičku podlogu temeljenu na formalnim sustavima, relacijskog algebri i relacijskom računu. Upravo zbog toga, relacijske baze podataka su omogućavale kontrolu redundancije, veze jedan-jedan, jedan-više, više-više, te brzo i učinkovito pretraživanje podataka, postigle su veliku popularnost te su i danas vodeći model baza podataka. Danas se relacijske baze podataka koriste u najrazličitije svrhe, počevši od stolnih, pa do mrežnih i web aplikacija. Proizvode ih različiti proizvođači, u različitim oblicima, kao klijentske baze – u obliku datoteke, ili kao serverske baze.
Koristit ćemo SQLite klijentsku bazu podataka u obliku datoteke, na koju ćemo se spajati preko ugrađenih funkcionalnosti ADO.NET, i to connected-based načinom rada. © University of Zagreb, Faculty of Organization and Informatics, Varaždin
112
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka I - osnove
8.2. ADO.NET Microsoft-ov glavni pristup za rad s podacima u bazama podataka je ADO.NET (Slika 73). Preko ADO.NET-a za komunikaciju s bazom podataka možemo koristiti dva pristupa: povezani (eng. connected-based) i nepovezani (eng. disconnected). Nepovezani pristup dohvaća podatke iz baze podataka i sprema ih u memoriju za buduće korištenje u obliku DataSet objekta. Aplikacija manipulira s podacima u DataSet-u te po potrebi sinkronizira DataSet i bazu podataka. Ovaj pristup, kao što i samo ime kaže, ne održava konstantnu vezu s bazom, nego se spaja na bazu samo da bi se izvršila sinkronizacija promjena. Više o ovom pristupu ćemo govoriti u sljedećem poglavlju. Kod povezanog pristupa aplikacija izravno komunicira s bazom podataka, bez DataSet-a ili nekog drugog posrednika. Sve promjene se odmah izvršavaju na bazi podataka.
Slika 73: ADO.NET arhitektura
Dvije osnovne komponente za rad s podacima su pružatelji podataka (eng. Data Providers) i DataSet objekt. S obzirom na to da ćemo se u ovom poglavlju baviti povezanim pristupom rada s bazom podataka, fokusirat ćemo se samo na pružatelje podataka i elemente od kojih se oni sastoje. Pružatelje podataka možemo promatrati kao upravljačke programe za bazu podataka (eng. drivers), te obično svaka baza podataka ima svoje pružatelje podataka. Postoje i generički pružatelji podataka koji bi u teoriji trebali omogućiti pristup različitim bazama podataka, ali se uvijek preporučuje koristiti one pružatelje podataka koji su napisani za točno određenu bazu. Visual Studio nam preko .NET frameworka nudi nekoliko ugrađenih pružatelja podataka za spajanje na MS SQL Server bazu podataka, OLE DB izvore podatka, ODBC izvore podataka, Oracle baze podataka, Access bazu podataka. Ako koristimo neku drugu bazu podataka koja nije ovdje navedena kao što je npr. SQLite, MySQL, PostgreSQL, potrebno je instalirati odgovarajući pružatelj podataka za tu bazu. Obično proizvođači baza podataka na svojim mrežnim stranicama nude i odgovarajuće pružatelje podataka za različite tehnologije, pa tako i za .NET. © University of Zagreb, Faculty of Organization and Informatics, Varaždin
113
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka I - osnove
Osnovni objekti pružatelja podataka prikazani su u Tablica 13. Tablica 13: Objekti pružatelja podataka
Objekti pružatelja podataka Naziv
Opis
Connection
Uspostavlja vezu s određenom bazom podataka, nasljeđuje klasu DbConnection.
Command
Izvršava naredbu (npr. SQL upit) nad bazom podataka, nasljeđuje DbCommand.
DataReader
Čita podatke iz baze podataka redak po redak, bez mogućnosti vraćanja na prethodni redak (eng. Forward-only, read-only), nasljeđuje DbDataReader.
DataAdapter
Puni DataSet s podacima i brine o ažuriranju tih podataka (nasljeđuje DbDataAdapter).
SQLite baza podataka SQLite je kompaktna relacijska baza podataka, cijela smještena u obliku jedne datoteke na disku, bez serverskog procesa. Besplatna je za osobnu i komercijalnu uporabu, otvorenog je kôda, lako se prenosi s jednog računala na drugo jednostavnim kopiranjem datoteke. Iako neke mogućnosti SQL jezika nedostaju, većina SQL-92 standarda je implementirana. Danas je SQLite jako raširena baza podataka, koristi se u stolnim aplikacijama, web aplikacijama, a zbog svoje kompaktnosti je najčešći izbor u mobilnim aplikacijama. Neki od poznatih proizvoda koji koriste SQLite su Mozilla FireFox i Thunderbird, Google Chrome, Skype, Opera i dr. Na mrežnim stranicama SQLite baze podataka možete naći pružatelje podataka za različite tehnologije, između ostalog i za .NET (http://www.sqlite.org/download.html - System.Data.SQLite). Također, postoji i nekoliko alata za kreiranje i editiranje SQLite baza podataka kao štu su: -
Sqlite Manager – dodatak za FireFox (https://addons.mozilla.org/en-us/firefox/addon/sqlitemanager/)
-
SQLite Expert Personal (PREPORUKA) - http://www.sqliteexpert.com/
-
SQLite Studio - http://sqlitestudio.pl/
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
114
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka I - osnove
8.3. Kreiranje aplikacije za evidenciju studenata Kako bismo u praksi pokazali rad s bazom podataka, odnosno objektima pružatelja podataka, izradit ćemo jednu jednostavnu aplikaciju: Aplikacija omogućava vođenje evidencije o studentima na kolegiju programsko inženjerstvo, njihovom timu i odabranom projektu. Funkcionalnosti koje će biti podržane su: dodavanje i brisanje studenata, izmjena podataka o studentu, dodavanje tima i brisanje tima, izmjena podataka o timu i projektu te dodjela studenata u timove. Sve informacije će se pohranjivati u SQLite bazu podataka navedenu na ERA dijagramu (Slika 74). Za potrebu komuniciranja s bazom podataka bit će izgrađena klasa DB, koju će pozivati sve ostale klase koje žele pristupiti bazi podataka. Osim klase DB, bit će izgrađene i klase Student i Tim prema priloženom dijagramu klasa (Slika 75). S obzirom na to da se želimo fokusirati na izradu pristupa bazi podataka, s GitHub sustava ćemo preuzeti projekt (https://github.com/PI2013FOI/Lab_2_4_EvidencijaStudenata.git) koji sadrži gotovu SQLite bazu podataka, definirano korisničko sučelje aplikacije (3 forme: GlavnaForma, NoviTim, NoviStudent), te djelomično definirane klase Tim i Student. Također u projekt je uključen i referenciran pružatelj podataka za SQLite bazu podataka.
Slika 74: ERA model aplikacije
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
115
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka I - osnove
Slika 75: Dijagram klasa
Izrada klase za rad s bazom Radi lakše kasnije nadogradnje aplikacije, lakšeg testiranja, podjele odgovornosti, poželjno je aplikaciju izgraditi u slojevima. U tu svrhu ćemo izraditi klasu DB, koja će jedina direktno komunicirati s bazom podataka. Sve ostale klase koje trebaju izvršiti upit ili dohvatiti podatke iz baze, moraju to napraviti preko klase DB. Singleton Klasa za pristup bazi podataka se u praksi vrlo često implementira kao Singleton uzorak dizajna. Razlog tome je što nam je zapravo dovoljna samo jedna instanca klase za pristup bazi podataka. Singleton uzorak dizajna radi upravo to - onemogućuje kreiranje više od jedne instance objekta. Implementacija Singleton-a je relativno jednostavna, i sastoji se od privatnog konstruktora, statičnog atributa klase koji će predstavljati tu jednu instancu klase te svojstva koje će pri pozivu vratiti instancu klase.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
116
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka I - osnove
Već smo spomenuli da se za spajanje na bazu podataka koristi objekt Connection. S obzirom na to da se spajamo na SQLite bazu podataka, referencirali smo se na SQLite pružatelj podataka za .NET, koji sadrži objekt SQLiteConnection, te ćemo njega koristiti za spajanje na bazu. Naša klasa za pristup bazi sadržavat će sljedeće atribute i svojstva (obratite pažnju na implementaciju Singleton-a preko get metode statičnog svojstva Instance): Kôd 71: Atributi i svojstva klase DB private static DB instance; //Singleton objekt private string connectionString; //Putanja i ostali podaci za spajanje na bazu private SQLiteConnection connection; //Konekcija prema bazi public static DB Instance //Singleton instanca klase za rad za bazom. { get { if (instance == null) { instance = new DB(); } return instance; } } public string ConnectionString //Putanja i ostali podaci za spajanje na bazu { get { return connectionString; } private set { connectionString = value; } } public SQLiteConnection Connection //Konekcija prema bazi { get { return connection; } private set { connection = value; } }
Kreirali smo nekoliko atributa klase DB te njihova pripadna svojstva s get i set metodama. Primijetite da su get metode javne, te da im se može pristupiti izvan klase, a set metode su privatne i dostupne su samo unutar klase DB. Još jedna specifičnost u klasi DB je statičko svojstvo Instance, koje vraća tip upravo klase u kojoj se nalazi, odnosno pokazuje na jednu jedinu moguću instancu klase DB. Svojstvo ima definiranu samo get metodu, unutar koje provjerava je li je klasa već instancirana. Ako jeste, vraća postojeću instancu, a ako nije, kreira instancu. Ovo je dio tipične implementacije Singleton uzorka dizajna. Nakon toga, možemo definirati konstruktor i destruktor klase. U konstruktoru smo definirali podatke za pristup bazi (eng. Connection String), te smo na temelju tih podataka kreirali objekt konekciju prema bazi podataka i otvorili konekciju za naredbom Open.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
117
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka I - osnove
Connection String ovisi o bazi podataka na koju se spajamo, a osim putanje definira i različite parametre za pristup bazi kao što su npr. podaci za autentifikaciju, podaci o pružatelju podataka, podaci o serveru i bazi podataka. U destruktoru smo zatvorili konekciju i oslobodili resurse. Kôd 72: Konstruktor i destruktor klase DB private DB() //Konstruktor klase { ConnectionString = @"Data Source= ..\..\Baza\EvidencijaStudenataPI.db3"; Connection = new SQLiteConnection(ConnectionString); Connection.Open(); } ~DB() //Destruktor klase { Connection.Close(); Connection = null; }
I ovdje postoji jedna specifičnost povezana s implementacijom Singleton-a. Naime, konstruktori klase su u pravilu javni, što naravno omogućava da klase instanciramo. Međutim, s obzirom na to da mi želimo onemogućiti instanciranje klase DB više od jedanput, konstruktor smo definirali kao privatni, te smo omogućili instanciranje klase samo preko već spomenutog svojstva Instance. Da bi završili klasu za pristup bazi podataka preostaje nam još definirati metode za izvršavanje upita i dohvaćanje podataka. Definirat ćemo ukupno tri metode, i to dvije za dohvaćanje podataka (SELECT) i jedna za izvršavanje SQL naredbi (INSERT, UPDATE, DELETE). Primijetite da za izvršavanje bilo kakvog SQL upita ili naredbe koristimo objekt SQLiteCommand. Međutim, potrebno je naglasiti da za vraćanje podataka SELECT upita koristimo metode ExecuteReader ili ExecuteScalar navedenog objekta, koje zapravo vraćaju tražene podatke iz baze. S druge strane, za izvršavanje INSERT, UPDATE, DELETE naredbi koristimo metodu ExecuteNonQuery, koja vraća samo broj redaka u tablici koji su promijenjeni. Metoda DohvatiDataReader prihvaća SQL SELECT upit kao parametar, kreira novi SQLiteCommand objekt uz pomoć kojeg izvršava upit, te vraća rezultate upita u obliku DataReader objekta. Metoda DohvatiVrijednost radi na isti način, osim što vraća samo jednu vrijednost (uz pomoć metoda ExecuteScalar objekta SQLiteCommand). Metoda IzvrsiUpit prima kao parametar INSERT, UPDATE, DELETE naredbu SQL-a, te uz pomoć metode ExecuteNonQuery objekta SQLiteCommand izvršava taj upit i vraća broj izmijenjenih redaka (eng. affected rows).
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
118
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka I - osnove
Kôd 73: Metode za dohvat podataka i izvršavanje upita klase DB /// /// Dohvaća podatke u obliku DataReader objekta na temelju proslijeđenog upita. /// /// SQL upit. /// Rezultati upita. public DbDataReader DohvatiDataReader(string sqlUpit) { SQLiteCommand command = new SQLiteCommand(sqlUpit, Connection); return command.ExecuteReader(); } /// /// Dohvaća skalarnu vrijednost kao rezultat proslijeđenog upita. /// /// SQL upit. /// Skalarna vrijednost kao rezultat upita. public object DohvatiVrijednost(string sqlUpit) { SQLiteCommand command = new SQLiteCommand(sqlUpit, Connection); return command.ExecuteScalar(); } /// /// Izvršava INSERT, UPDATE, DELETE SQL izraz. /// /// INSERT, UPDATE, DELETE SQL izraz. /// Broj redaka u tablici koji su promijenjeni. public int IzvrsiUpit(string sqlUpit) { SQLiteCommand command = new SQLiteCommand(sqlUpit, Connection); return command.ExecuteNonQuery(); }
Izrada klase Tim Sada možemo pristupiti izradi, odnosno nadogradnji klase Tim, koja nam služi da spremimo podatke i opišemo ponašanje tima studenata. Ako pogledamo priloženi dijagram klasa, možemo vidjeti da se klasa Tim sastoji od nekoliko privatnih atributa, i nekoliko metoda. Ono što još postoji u klasi, a nismo prikazali zbog uštede prostora, su javna svojstva klase koja će predstavljati mehanizam dohvaćanja i postavljanja navedenih atributa klase. Atributi i svojstva klase su već napravljeni, tako da se možemo usredotočiti na pisanje metoda i programske logike klase. Krenut ćemo s definiranjem dva konstruktora klase. Prvi prazan konstruktor će nam služiti kada želimo kreirati novi tim. Želimo instancirati prazan objekt klase Tim, koji ćemo poslije popuniti s korisnički unesenim podacima. © University of Zagreb, Faculty of Organization and Informatics, Varaždin
119
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka I - osnove
Drugi konstruktor je nešto složeniji jer kao parametar prima objekt klase DbDataReader, iz njega čita podatke o timu i zapisuje ih u svojstva tima. Ovaj konstruktor ćemo koristiti kada dohvaćamo postojeće timove iz baze podataka. Vrijednosti pojedinog atributa u DataReader objektu možemo pristupiti koristeći indeks ili naziv atributa tablice unutar uglatih zagrada. DataReader vraća vrijednosti kao podatak tipa objekt, te ga je potrebno konvertirati u odgovarajući odredišni tip podataka. Postoji još jedan način dohvaćanja podataka iz DataReader-a, kojim možemo izbjeći eksplicitno konvertiranje vrijednosti iz tipa objekt u odredišni tip podataka. Naime, DataReader objekt sadrži čitav niz Get metoda za svaki tip podataka u .NET-u, kojim onda možemo dohvatiti odgovarajući vrijednost iz DataReadera. Primjer uporabe specifičnih Get metoda DataReadera možemo vidjeti zakomentirane u kôdu ispod za svojstva Id i OznakaTima. Treba napomenuti da je uporabom Get metoda pojedine atribute moguće dohvaćati samo uz pomoć indeksa, a ne i uz pomoć naziva atributa. Kôd 74: Konstruktori klase Tim /// /// Konstruktor za kreiranje novog tima. /// public Tim() { } /// /// Konstruktor koji kreira Tim sa podacima iz DataReader objekta. /// /// DataReader objekt sa podacima za Tim. public Tim(DbDataReader dr) { if (dr != null) { Id = int.Parse(dr["Id"].ToString()); //Id = dr.GetInt32(0); OznakaTima = dr["OznakaTima"].ToString(); //OznakaTima = dr.GetString(1); NazivProjekta = dr["NazivProjekta"].ToString(); OpisProjekta = dr["OpisProjekta"].ToString(); Napomena = dr["Napomena"].ToString(); } }
Nakon što smo definirali konstruktore klase možemo prijeći na ostale metode klase Tim. Metoda Spremi kao što samo ime kaže sprema vrijednosti svojstava objekta u odgovarajuće atribute u tablici Tim. Ovisno o tome radi li se o dodavanju novog tima ili se radi o postojećem timu, generira se INSERT ili UPDATE SQL naredba, koja se zatim izvršava uz pomoć metode IzvrsiUpit klase DB. Metoda Obrisi briše tim po Id-u iz baze podataka i vraća broj obrisanih redaka (0 ili 1).
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
120
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka I - osnove
Kôd 75: Metode Spremi() i Obrisi() klase Tim /// /// Sprema vrijednosti objekta u bazu podataka. /// /// Broj redaka koji su izmijenjeni ili dodani. public int Spremi() { string sqlUpit = ""; if (Id == 0) //Insert { sqlUpit = "INSERT INTO Tim (Id, OznakaTima, NazivProjekta, OpisProjekta, Napomena) VALUES (null, '" + OznakaTima + "','" + NazivProjekta + "','" + OpisProjekta + "','" + Napomena + "')"; } else //Update { sqlUpit = "UPDATE Tim SET OznakaTima = '" + OznakaTima + "', NazivProjekta = '" + NazivProjekta + "', OpisProjekta = '" + OpisProjekta + "', Napomena = '" + Napomena + "' WHERE Id = " + Id; } return DB.Instance.IzvrsiUpit(sqlUpit); } /// /// Briše objekt iz baze podataka. /// /// Broj obrisanih redaka. public int Obrisi() { string sqlDelete = "DELETE FROM Tim WHERE Id = " + Id; return DB.Instance.IzvrsiUpit(sqlDelete); }
Metoda DohvatiTimove dohvaća sve timove iz tablice Tim u bazi podataka i vraća ih u obliku generičke liste. Unutar metode se izvršava SELECT upit uz pomoć metode DohvatiDataReader, koja vraća rezultate upita u obliku DataReader objekta. Primijetite kako se uz pomoć While petlje prolazi kroz pojedine retke tablice, kreira se objekt Tim s podacima iz trenutnog retka, te se novokreirani objekt dodaje u generičku listu. Također, ovdje smo definirali i nadjačanu metodu ToString, koja umjesto općenitih informacija o objektu sada pokazuje oznaku tima.
Upozorenje Nakon uporabe DataReader je potrebno obavezno zatvoriti s naredbom Close().
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
121
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka I - osnove
Kôd 76: Metoda DohvatiTimove /// /// Dohvaća sve timove iz baze i vraća ih u obliku generičke liste. /// /// Lista timova. public static List DohvatiTimove() { List lista = new List(); string sqlUpit = "SELECT * FROM Tim"; DbDataReader dr = DB.Instance.DohvatiDataReader(sqlUpit); while (dr.Read()) { Tim tim = new Tim(dr); lista.Add(tim); } dr.Close(); //DataReader treba obavezno zatvoriti nakon uporabe. return lista; } /// /// Metoda koja nadjačava ToString metodu. /// /// Oznaka tima. public override string ToString() { return OznakaTima; }
Kolekcije Prilikom programiranja se često javlja potreba za pohranom većeg broja istovrsnih objekata u neku strukturu. Najjednostavniji tip podataka koji to omogućava je klasično polje podataka. Velik nedostatak polja je što prilikom instanciranja moramo navesti veličinu, odnosno broj elemenata koje polje može primiti, te ta veličina ostaje fiksna. Međutim, vrlo često mi ne znamo koliki broj elemenata ćemo imati. Tu nam mogu pomoći kolekcije, koje dinamički povećavaju i smanjuju svoju veličinu. Iako su riješile problem s veličinom spremnika elemenata, početne kolekcije su pohranjivale elemente tipa object, što znači da su mogle pohraniti bilo koji tip podataka. Takav način pohrane je implicirao mnogo konverzija između tipa object i stvarnog tipa vrijednosti koja se nalazila u kolekciji. U .NET 2.0. verziji između ostalog uvode se i generičke kolekcije, koje su taj problem riješile tako da eksplicitno definiraju koji tip podatka će se nalaziti u kolekciji. Neke od dostupnih generičkih kolekcija su Collection, List, Queue, Stack i druge. Kolekcija koju ćemo mi najčešće koristiti je lista List. Predložak sintakse za definiranje liste je sljedeći, gdje je tip podataka, odnosno klasa objekata koji su sadržani u listi.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
122
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka I - osnove
Kôd 77: Predložak definiranja generičke liste public List lista = new List(); foreach (T item in lista) { }
Mi smo već koristili generičke kolekcije, a između ostalog primjer generičke liste možemo vidjeti i u statičkoj metodi DohvatiTimove klase Tim. Generička lista implementira nekoliko sučelja, a jedno od njih je IEnumerator koje omogućava da uz pomoć foreach petlje prolazimo kroz elemente liste. Generičke kolekcije sadrže velik broj korisnih svojstava i metoda. Npr. neka od svojstava i metoda koje sadrži generička lista su: Tablica 14: Metode i svojstva generičke liste
Metode i svojstva generičke liste List Naziv
Opis
Add
Dodaje proslijeđeni element u listu. Može se dodati element onog tipa koji smo naveli pod
AddRange
Dodaje proslijeđenu kolekciju elemenata u listu.
Clear
Briše sve elemente iz liste.
Contains
Provjerava nalazi li se proslijeđeni element u listi.
Count
Vraća broj elemenata liste.
Insert
Dodaje proslijeđeni element na definiranu poziciju u listi.
Remove
Briše proslijeđeni element iz liste.
Grafičko sučelje aplikacije U ovom trenutku imamo gotovu SQLite bazu podataka, klasu za komuniciranje s bazom podataka, klasu Tim i grafičko sučelje aplikacije. Ono što nam preostaje je doraditi grafičko sučelje tako da implementira potrebne funkcionalnosti koristeći postojeće klase. Izgled grafičkog sučelja je već definiran i sastoji se od 3 forme. Tablica 15: Grafičko sučelje
Grafičko sučelje Naziv forme
Opis
GlavnaForma
Sadrži dvije DataGridView kontrole u kojima je prikazan popis timova i studenata u tim timovima. Također sadrži gumbe za dodavanje, brisanje i izmjenu timova i studenata.
NoviStudent
Omogućava unos podataka prilikom dodavanja novog ili izmjene postojećeg studenta.
NoviTim
Omogućava unos podataka prilikom dodavanja novog ili izmjene
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
123
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka I - osnove
postojećeg tima.
Započet ćemo s formom GlavnaForma i to s funkcionalnostima za prikaz, dodavanje, brisanje i izmjenu timova. Na formi se prikazuju postojeći timovi unutar DataGridView kontrole, moguće je dodavati nove timove te brisati i mijenjati postojeće. U tu svrhu je potrebno kreirati metodu OsvjeziTimove i 4 rukovatelja događajima. Upis podataka za dodavanje novog odnosno izmjenu postojećeg tima se odvija u formi NoviStudent. Kôd 78: Funkcionalnosti za prikaz, dodavanje, brisanje i izmjenu timova /// /// Dohvaća listu timova uz pomoć statičke metode DohvatiTimove, /// te ih prikazuje u DataGridView kontroli. /// private void OsvjeziTimove() { List listaTimova = Tim.DohvatiTimove(); dgvTimovi.DataSource = listaTimova; } /// /// Rukuje događajem pokretanja forme, kada ćemo i osvježiti prikaz timova. /// private void GlavnaForma_Load(object sender, EventArgs e) { OsvjeziTimove(); } /// /// Rukuje događajem klika na gumb btnDodajTim. Otvara formu frmNoviTim /// u kojoj se upisuju podaci, a nakon zatvaranja forme osvježava prikaz timova. /// private void btnDodajTim_Click(object sender, EventArgs e) { NoviTim frmNoviTim = new NoviTim (); frmNoviTim.ShowDialog(); OsvjeziTimove(); } /// /// Rukuje događajem klika na gumb btnIzmijeniTim. Dohvaća selektirani tim iz /// DataGridView kontrole, proslijeđuje ga formi frmNoviTim i ostvara formu. /// Nakon zatvaranja forme osvježava popis timova. /// private void btnIzmijeniTim_Click(object sender, EventArgs e) { if (dgvTimovi.SelectedRows.Count > 0) { Tim odabraniTim = dgvTimovi.SelectedRows[0].DataBoundItem as Tim; NoviTim frmNoviTim = new NoviTim (odabraniTim); frmNoviTim.ShowDialog();
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
124
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka I - osnove
OsvjeziTimove(); } } /// /// Rukuje događajem klika na gumb btnObrisiTim. Dohvaća selektirani tim/timove iz /// DataGridView kontrole, te ih briše. /// private void btnObrisiTim_Click(object sender, EventArgs e) { if (dgvTimovi.SelectedRows.Count > 0) { foreach (DataGridViewRow row in dgvTimovi.SelectedRows) { Tim odabraniTim = row.DataBoundItem as Tim; odabraniTim.Obrisi(); } } OsvjeziTimove(); }
Da bi upotpunili funkcionalnosti za rad s timovima potrebno je doraditi i formu NoviTim, koja nam omogućuje unos podataka o timu. Ova forma mora razlikovati, radi li se o dodavanju novog tima ili izmjeni postojećeg. U tu svrhu ćemo definirati atribut Tim koji će sadržavati null vrijednost ako dodajemo novi tim, odnosno sadržavat će referencu na postojeći tim, ako mijenjamo postojeći. Za svaki od ova dva način imat ćemo i poseban konstruktor. Nakon što unesemo podatke na formi, možemo ih spremiti klikom na gumb Spremi. Kôd 79: Unos podataka za novi i postojeći tim //Referenca na postojeći tim. private Tim tim = null; /// /// Konstruktor forme koji pozivam kada kreiram novi tim. /// public NoviTim() { InitializeComponent(); } /// /// Konstruktor forme koji pozivam kada mijenjam postojeći tim. /// /// Postojeći tim čije podatke mijenjam. public NoviTim(Tim odabraniTim) { InitializeComponent(); tim = odabraniTim;
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
125
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka I - osnove
} /// /// Rukuje događajem pokretanja forme. Ako se radi o izmjeni postojećeg /// tima, tada se kontrole popunjavaju s postojećim podacima. /// private void NoviTim_Load(object sender, EventArgs e) { txtOznakaTima.Focus(); if (tim != null) { txtId.Text = tim.Id.ToString(); txtOznakaTima.Text = tim.OznakaTima; txtNazivProjekta.Text = tim.NazivProjekta; txtOpisProjekta.Text = tim.OpisProjekta; txtNapomena.Text = tim.Napomena; } } /// /// Rukuje događajem klika na gumb btnSpremi. Mijenja podatke /// tima u skladu sa unesenim podacima na formi, te ih zapisuje u bazu. /// private void btnSpremi_Click(object sender, EventArgs e) { if (tim == null) { tim = new Tim(); } tim.OznakaTima = txtOznakaTima.Text; tim.NazivProjekta = txtNazivProjekta.Text; tim.OpisProjekta = txtOpisProjekta.Text; tim.Napomena = txtNapomena.Text; tim.Spremi(); this.Close(); } /// /// Rukuje klikom na gumb btnOdustani. Zatvara formu bez spremanja podataka. /// private void btnOdustani_Click(object sender, EventArgs e) { this.Close(); }
Iako sada naša aplikacija nije do kraja gotova možemo isprobati dio funkcionalnosti koje se odnose na rad s timovima. Pokrenite aplikaciju i isprobajte dodavanje timova, brisanje timova te njihovu izmjenu podataka.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
126
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka I - osnove
8.4. Pitanja i zadaci Samostalni zadatak Dovršite započetu aplikaciju tako da implementirate funkcionalnosti za dodavanje novih te brisanje i izmjenu postojećih studenata po uzoru na funkcionalnost za Tim. Pitanja 1. Što je relacijska baza podataka? 2. Čemu relacijske baze podataka duguju svoju uspješnost? 3. Koje su prednosti relacijskih baza podataka u odnosu na tekstualne datoteke? 4. Koja je razlika između priloženog ERA modela i dijagrama klasa? 5. Što je ADO.NET? 6. Koje su osnovne komponente ADO.NET-a? 7. Koja je razlika između povezanog (eng. Connected-based) i nepovezanog (eng. Disconnected) pristupa bazi podataka u ADO.NET-u? 8. Nabrojite i objasnite osnovne objekte ADO.NET pružatelja podataka (eng. Data Provider). 9. Što je to Singleton uzorak dizajna i kako se implementira? 10. U čemu je prednost implementiranja pristupa bazi podataka kao posebnog sloja u aplikaciji? Na koji način to možemo ostvariti? 11. Što su to kolekcije i koja je njihova prednost u odnosu na polja? 12. Koja je prednost generičkih kolekcija u odnosu na standardne kolekcije?
8.5. Više informacija o temi 1. Pregled ADO.NET tehnologije na MSDN-u (http://msdn.microsoft.com/enus/library/h43ks021.aspx), dostupno: prosinac, 2013. 2. The C# Station ADO.NET Tutorial (http://csharp-station.com/Tutorial/AdoDotNet/Lesson01), dostupno: prosinac, 2013. 3. DataReader in ADO.NET (http://www.c-sharpcorner.com/uploadfile/mahesh/datareader-in-adonet/ ), dostupno: prosinac, 2013. 4. ADO.NET Objects (http://www.c-sharpcorner.com/uploadfile/puranindia/ado-net-objects-part-i/ ), (http://www.c-sharpcorner.com/uploadfile/puranindia/ado-net-objects-part-ii/ ), dostupno: prosinac, 2013. 5. ADO.NET Interview Questions (http://www.codeproject.com/Articles/28819/ADO-NETInterview-Questions-Part-1), dostupno: prosinac, 2013. 6. ADO.NET - Connected For Beginners (http://www.youtube.com/watch?v=koYk3zHQnG8 ), (http://www.youtube.com/watch?v=Rwdedptaou0), (http://www.youtube.com/watch?v=QkpNVNyYFoM ), dostupno: prosinac, 2013. © University of Zagreb, Faculty of Organization and Informatics, Varaždin
127
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka I - osnove
7. Implementing Singleton in C# (http://msdn.microsoft.com/en-us/library/ff650316.aspx), dostupno: prosinac, 2013. 8. Singleton Pattern (http://www.oodesign.com/singleton-pattern.html ), dostupno: prosinac, 2013. 9. Three Layer Architecture in C# .NET (http://www.codeproject.com/Articles/36847/Three-LayerArchitecture-in-C-NET), dostupno: prosinac, 2013. 10. An Elegant C# Data Access Layer using the Template Pattern and Generics (http://www.csharpcorner.com/UploadFile/rmcochran/elegant_dal05212006130957PM/elegant_dal.aspx), dostupno: prosinac, 2013. 11. Introduction to Generic Collections (http://csharp-station.com/Tutorial/CSharp/Lesson20), dostupno: prosinac, 2013.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
128
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka II – dataset, procedura, okidači
9. Rad s bazom podataka II – dataset, procedure, okidači Sažetak U ovom poglavlju opisan je gotovo najstariji način korištenja baze podataka koju podržava .NET, odnosno korištenje DataSet-ova. Uz DataSet-ove ukratko je opisan i SQL Server 2012. Objašnjene su pohranjene procedure, okidače (eng. trigger) i vezanje podataka s kontrolama (eng. data binding) Ključne riječi: ADO.NET, DataSet, SQL Server 2012
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
129
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka II – dataset, procedura, okidači
9.1. Uvod Koristit ćemo sustav za upravljanje bazom podataka, kako bismo kreirali bazu podatka i isprobali dohvaćanje, pohranu, ažuriranje i brisanje podataka (CRUD). Nakon toga ćemo napraviti jednu aplikaciju koja samostalno pristupa podacima i omogućuje CRUD operacije.
9.2. SQL Server 2012 Microsoft SQL Server je sustav za upravljanje relacijskom bazom podataka. Iako postoji više definicija baze podataka mi ćemo koristiti najjednostavniju, prema kojoj je baza podataka (kolokvijalno nazivana samo baza) organizirani skup podataka. Organiziran u ovom slučaju znači da postoje stroga formalno definirana pravila koja se primjenjuju za pohranu podataka. Sustav za upravljanje bazom podataka omogućuje kreiranje, čitanje, ažuriranje i brisanje baza podataka, dok baza podataka sadrži tablice, koje su preslika relacijskog modela podataka i u njima pohranjuje strukturirane zapise. Uz Microsoft SQL Server 2012 kojeg ćemo koristiti na ovom kolegiju, postoji još mnogo drugih, primjerice: MySQL, PostgreSQL, Oracle, Sybase, dBase, IBM DB2 itd. Kreiranje baze podataka i tablica Pokrenuti ćemo SQL Server Management Studio koji će nam omogućiti pregled baza podataka. Ovisno o instalaciji sustav nas traži lozinku i korisničko ime. Nakon uspješne prijave, prikazuje se prozor aplikacije vrlo slične strukture kao Visual Studio. Prema uputama u Tablica 16, kreirat ćemo bazu podataka. U stablastom ispisu na Databases ćemo desnim klikom miša odabrati New Database, nakon toga navodimo ime baze podataka i završeno je kreiranje. Bazu ćemo nazvati PIDatabase. Postoje još i napredne mogućnosti kreiranja baze podataka, no kako to nije temelj ovog kolegija i potrebna su određena predznanja, nećemo ići u daljnje razmatranje kreiranje baze podataka.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
130
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka II – dataset, procedura, okidači
Tablica 16: Postupak kreiranja baze podataka, dodavanja nove tablice i otvaranja prozora za upite
Kreiranje baze podataka
Dodavanje nove tablice
Postavljanje upita nad bazom
Nakon uspješno kreirane baze podataka, unutar datoteke Databases pojavit će se upravo kreirana PIDatabase. Na nju ćemo desnim klikom miša odabrati „New Table“. Nakon toga otvara se tablica (Slika 76). Dodat ćemo te atribute i za svakog odabrati tip podataka iz padajućeg izbornika „Data Type“. Polje ID će nam biti primarni ključ, to ćemo postaviti tako da desnim klikom aktiviramo kontekstni izbornik i odaberemo opciju „Set primary key“.
Slika 76: Kreiranje strukture tablice
S obzirom na to da je primarni ključ prirodni broj i da iza njega ne stoji poslovna logika, možemo koristiti samo-povećavajuće polje. Da bismo to napravili u donjem prozoru sa svojstvima proširit ćemo opciju“Identity specification“, kao na Error! Reference source not found. Polje „Is Identity“ ćemo postaviti na Yes, polje „Identity Increment“ ćemo postaviti na 1 (vrijednost za koju se svaki puta kod dodavanja novog zapisa u bazu, primarni ključ poveća) i polje „Identity Seed“ ćemo postaviti na 1 (vrijednost od kojeg se primarni ključ počinje brojat). Slika 77: Automatsko povećavanje primarnog ključa © University of Zagreb, Faculty of Organization and Informatics, Varaždin
131
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka II – dataset, procedura, okidači
Da bismo okončali proces kreiranja tablice, kliknut ćemo na Save. To će nam otvoriti novi prozor u kojem biramo naziv tablice, napisat ćemo Customer. Postavljanje upita Prije postavljanja upita, moramo otvoriti prozor u kojeg ćemo ih pisati. Da bismo to napravili, na bazu podataka koju želimo koristiti kliknemo desnom tipkom miša i odaberemo opciju „New Query“. Sada možemo unijeti kôd kojim ćemo dodati prvi zapis u bazu podataka: Kôd 80 insert into Customer (CompanyName, ContactName, ContactTitle, Email, Active) values ('Microsoft', 'Joe Willson', 'MR', '[email protected]', 0);
Konačno, da bismo upit izvršili, označit ćemo upit koji želimo izvršiti i odabrati opciju Execute. Moguće je napisati više upita, a SQL Server će izvršiti samo one označene. Pohranjene procedure Pohranjena procedura je blok SQL upita koji omogućuje funkcionalnost sličnu kao metoda u objektno orijentiranom programiranju. Primjerice, niz upita koji rade zapis, validaciju, zamjenu podataka itd., pohranimo kao proceduru koju možemo iznova pozivati. Narudžba i ponuda u bazi podataka mogu biti tablice vrlo slične (često i jednake) strukture. U poslovnom procesu klijent dobiva ponudu koju može potvrditi. U tom trenutku, da ne bismo ponovno zapisivali podatke kao narudžbu (ako se ponuda ne promjeni), možemo napraviti proceduru koja će ponudu pretvoriti u narudžbu. Sljedeći kôd prikazuje kako kreirati pohranjenu proceduru koja aktivira klijenta u bazi podataka. Neka je svaki klijent u bazi neaktivan, sve dok ne naruči jedan proizvod. Kada ga naruči, korisnik će pokrenuti ActivitySet proceduru (npr. u aplikaciji odabere opciju Aktiviraj) Kôd 81 create procedure ActivitySet @CustomerID integer AS update Customer set Active = 1 where ID = @CustomerID; GO
Ponovno označimo kôd (upit) procedure i odaberemo Execute. Kao rezultat trebali bismo dobiti poruku Command(s) completed successfully. Time je procedura pohranjena, a da bismo je pokrenuli moramo izvršiti sljedeći upit: execute ActivitySet 1; Ulazi parametar je 1 jer je to primarni ključ retka koji mijenjamo. Ako pogledamo proceduru onda se vidi da koristimo parametar @CustomerID.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
132
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka II – dataset, procedura, okidači
Okidač Okidač je pohranjena procedura ili upit koji poziva pohranjenu proceduru koja se automatski izvršava kao odgovor na neki događaj u bazi podataka. Sljedeći upit prikazuje okidač koji svaki puta nakon dodavanja klijenta u tablicu Customer automatski radi aktivaciju. Kôd 82 create trigger UpdateActivityTrigger on Customer after insert as declare @varijabla AS int; set @varijabla = (select id from inserted); execute dbo.ActivitySet @varijabla; print 'Uspjesno sam upisao aktivnost za ' + convert(varchar, @varijabla); go Command(s) completed successfully.
Nakon izvršavanja upita koji kreira ovaj okidač, svaki puta kada dodamo novi redak dobiti ćemo poruku o uspješnom zapisu u bazu podataka i poruku od okidača. Sintaksa okidača je sljedeća: Kôd 83 Trigger on an INSERT, UPDATE, or DELETE statement to a table or view (DML Trigger) CREATE TRIGGER [ schema_name . ]trigger_name ON { table | view } [ WITH [ ,...n ] ] { FOR | AFTER | INSTEAD OF } { [ INSERT ] [ , ] [ UPDATE ] [ , ] [ DELETE ] } [ NOT FOR REPLICATION ] AS { sql_statement [ ; ] [ ,...n ] | EXTERNAL NAME } ::= [ ENCRYPTION ] [ EXECUTE AS Clause ] ::= assembly_name.class_name.method_name
Detach / Attach database Podaci baze podataka mogu se „otkačiti“ i ponovno „zakačiti“ na istu ili neku drugu instancu SQL servera. Taj je postupak vrlo koristan zbog migracije baze podataka, tim više što korisnik koji kasnije koristi bazu podataka u aplikaciji ne mora nužno imati instalaciju SQL Server-a.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
133
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka II – dataset, procedura, okidači
Baza podataka koja se otkači zapisana je u .mdf formatu koji je portabilna baza podataka, odnosno baza podataka u jednoj datoteci. U skladu s time, imamo dvije mogućnosti korištenja baze podataka; a) pristupanje preko DBMS-a (u našem slušaju SQL Server), b) pristupanje kao datoteci (u našem slučaju .mdf)12. Da bismo otkačili bazu podataka (eng. detach), najprije je moramo ugasiti (Take offline), a nakon toga pokrenemo postupak kojim se ona otkači od poslužitelja baze podataka. Možemo to zamisliti kao sljedeće, poslužitelj baze podataka, u našem slučaju SQL Server 2012 je niz utičnica koje sadrže odgovarajući napon i frekvenciju napona. Svaki uređaj spojen na utičnicu je u ovom slučaju jedna baza podataka. Bazu podataka je u ovom slučaju, kao i neki električni uređaj, moguće isključiti, prenijeti na drugi poslužitelj jednakih karakteristika te ponovno uključiti. Da bismo to napravili, desnim klikom na bazu podataka u izborniku Tasks odaberemo opciju Take offline, a nakon toga, ponovno desni klik, izbornik Tasks, opcija Detach database. Time dobijemo dvije datoteke (.mdf – podaci i .ldf – log podaci). Northwind baza podataka Kako ne bismo radili posebnu bazu podataka (to je gradivo ranijih kolegija), koristit ćemo pokaznu bazu podataka koja dolazi s Visual Studio-m, Northwind database. Northwind baza podataka implementira model podataka manje organizacije i prati proces dobavljanja, naručivanja, prodaje, praćenje klijenata, zaposlenika i dostavljača (Slika 78). Northwind također sadrži procedure i okidače. Na kraju ovog poglavlja u prilogu dolazi otkačena datoteka Northwind koju ćemo koristiti u Visual Studio-u da bismo pokazali proces kreiranja, prikazivanja, ažuriranja i brisanja podataka putem aplikacije.
12
http://msdn.microsoft.com/en-us/library/ms190794.aspx
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
134
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka II – dataset, procedura, okidači
Slika 78: Struktura Northwind baze podataka - nije u pravilnoj ERA notaciji, već u MS SQL Server notaciji
9.3. Pristupanje bazi podataka korištenjem DataSet-ova. DataSet je klasa koja omogućuje offline pristup bazi podataka i njeni objekti predstavljaju memorijske reprezentacije dohvaćenih podataka iz nekog izvora podataka (ne mora biti baza podataka iako najčešće jest). DataSet predstavlja sve podatke uključujući tablice, ograničenja i veze između tablica.
Slika 79: DataSet13
Promotrimo prethodnu sliku. Na lijevom rubu slike nalazi se neki izvor podataka (XML datoteka ili baza podataka), a na desnom rubu slike nalazi se korisnik koji pregledava podatke pomoću DataGrid kontrole. Korištenjem DataSeta procedura za dobavljanje podataka je sljedeća. Korisnik traži neke podatke za ispis na DataGrid kontrolu. 13
http://msdn.microsoft.com/en-us/library/ms180730(v=vs.90).aspx
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
135
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka II – dataset, procedura, okidači
DataGrid kontrola pomoću BindingSource klase povezana je s podacima. BindingSource ponaša se kao izvor podataka za neku kontrolu (primjerice TextBox, ComboBox ili DataGridView). Svaku promjenu korisnika propagira na sljedeći sloj prema bazi podataka, a svaku promjenu baze podataka propagira dalje prema korisniku. Ukratko, omogućuje „vezanje“ kontrole za podatak. Te podatke dobiva iz DataSet kontrole koja je memorijska preslika baze podataka, a ta memorijska preslika baze podataka puni se pomoću TableAdapter klase. Dakle, korisnik zatraži podatke, BindingSource potraži te podatke u DataSet-u, kojeg je prethodno napunio TableAdapter. TableAdapter sadrži upite prema kojima ima mogućnost filtrirati DataSet. Ako korisnik mijenja podatke, oni će se putem BindingSource-a mijenjati samo do DataSet-a. Tek nakon što korisnik odabere „Save“ (ili programer tako isprogramira), podaci iz DataSet-a preslikavaju se u bazu podataka. Više o tome na http://blogs.msdn.com/b/bethmassi/archive/2007/09/19/binding-multiple-comboboxes-to-thesame-datasource.aspx.
Kreiranje nove aplikacije s pristupom podacima Kreiranje aplikacije s modalnim prozorima Nakon što smo kreirali Windows Forms projekt, Form1 ćemo preimenovati u frmMain i urediti svojstva: Text StartPosition IsMdiContainer
ACME d.o.o. CenterScreen True
Ovo zadnje svojstvo nam omogućuje kreiranje forme koja može ugnijezditi druge forme. Na frmMain ćemo još dodati MenuStrip kontrolu koja će biti glavni izbornik aplikacije. Strukturu izbornika ćemo napraviti tako da File sadrži opciju Exit, a Data sadrži opcije za prikaz tablica iz baze podataka (Slika 80). Name svojstvo izbornika ćemo postaviti msMain, a svaki element izbornika ćemo početi Slika 80: Glavni izbornik aplikacije prefiksom „mi“ (kao menu item) i nastaviti sa strukturom izbornika. Orders opcija će se zvati miDataBrowseOrders, a opcija Browse će se zvati miDataBrowse.
Spajanje na bazu podataka Kao što smo najavili, koristit ćemo pokaznu bazu podataka Northwind koja je u obliku Microsoft SQL Database datoteke. Prvi korak je dodavanje izvora podataka (Data Source).
Slika 81: Dodavanje izvora podataka
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
136
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka II – dataset, procedura, okidači
U izborniku DataSources (ako nije vidljiv, unutar izbornika View, Other Windows, Data Sources) odaberemo Add new datasource. Nakon pokretanja „čarobnjaka“ za dodavanje novog izvora podataka, odabrat ćemo bazu podataka (Database), nakon toga DataSet (Slika 81). Iz sljedećeg izbornika kliknut ćemo na gumb New Connection. Na novom prozoru ima nekoliko zanimljivih opcija. Gumbom Change se otvara novi prozor na kojem biramo tip baze podataka na koju se spajamo. U ovom prozoru možemo postaviti korisničko ime, lozinku, kreirati novu bazu podataka itd. Mi ćemo odabrati Change… i odabrati Microsoft SQL Server Database File. Tablica 17
Lociranje datoteke
Testiranje veze prema bazi podataka
Nakon toga će se dialog s kojeg smo krenuli promijeniti i tražit će nas lokaciju .mdf datoteke. Kada smo je pronašli, testirat ćemo vezu prema bazi podataka pritiskom gumba „Test Connection“. Ako je rezultat „Test connection succeded“ tada je baza podataka spremna za korištenje i možemo kliknuti na OK.
Slika 82: Želimo li presnimati datoteku baze podataka u projekt?
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
137
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka II – dataset, procedura, okidači
Nakon tog koraka otvara se prozor koji nas pita želimo li presnimiti bazu podataka u projekt, mi ćemo odabrati NE (Slika 82). U posljednjem koraku moramo odabrati koje ćemo sve tablice, poglede, procedure i funkcije iz baze podataka koristiti. Odaberemo Categories, Customers, Employees, Order Details, Orders, Products i Suppliers (Slika 83). Nećemo koristiti sve tablice, već ćemo pokazati samo jedan CRUD primjer, a ostatak ostavljamo čitateljima za implementaciju.
Slika 83: Tablice koje ćemo koristiti
Prikaz podataka – READ Podatke ćemo prikazivati na prozoru kojeg ćemo aktivirati iz izbornika. Napravit ćemo novi direktorij u strukturi projekta i nazvati ga DataBrowseForms. Unutar tog direktorija ćemo dodati novu formu i nazvati je frmBrowseOrders. Unutar izbornika Data, Browse, Orders odabrati ćemo novu metodu za rukovanje događajem MouseUp i tu ćemo pokretati formu za prikaz narudžbi: Kôd 84 private void miDataBrowseOrders_MouseUp(object sender, MouseEventArgs e) { DataBrowseForms.frmBrowseOrders browseOrdersFrom = new DataBrowseForms.frmBrowseOrders(); browseOrdersFrom.MdiParent = this; //<- postavlja prozor kao dijete od frmMain browseOrdersFrom.WindowState = FormWindowState.Maximized; browseOrdersFrom.Show(); }
Ona će nam poslužiti za prikaz narudžbi i stavki narudžbi. Za to moramo napraviti sljedeće: 1. Dodati DataGridView kontrole za narudžbe (Orders) i stavke narudžbi (Order Details). To ćemo napraviti tako da iz DataSources prozora kliknemo na tablicu i dovučemo je na prozor. To napravimo za svaku tablicu. 2. Sada ćemo promijeniti svojstva forme (Text = Browse orders, StartPosition= CenterParent) 3. Za DataGridView kontrole koje smo dodali promijenimo svojstvo Anchor (Slika 84).
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
138
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka II – dataset, procedura, okidači
Slika 84: Pripremljene DataGridView kontrole
4. Za DataGridView kontrole svojstvo SelectionMode ćemo postaviti na FullRowSelect, svojstvo AllowUsersToAddRows = false. Možemo pokrenuti aplikaciju i primijetiti da se ispisuju podaci, no tablica s narudžbama nije povezana s tablicom stavki. Korisnik želi takav prikaz da svaki puta kada označi neku narudžbu, vidi njene detalje, odnosno stavke. Pogledajmo programski kôd koji se nalazi ispod ove forme: Kôd 85 private void ordersBindingNavigatorSaveItem_Click(object sender, EventArgs e) { this.Validate(); this.ordersBindingSource.EndEdit(); this.tableAdapterManager.UpdateAll(this.northwndDataSet); } private void frmBrowseCategories_Load(object sender, EventArgs e) { // TODO: This line of code loads data into the 'northwndDataSet.Order_Details' table. You can move, or remove it, as needed. this.order_DetailsTableAdapter.Fill(this.northwndDataSet.Order_Details); // TODO: This line of code loads data into the 'northwndDataSet.Orders' table. You can move, or remove it, as needed. this.ordersTableAdapter.Fill(this.northwndDataSet.Orders); }
Prva metoda izvršava se kada korisnik u „Binding Navigator“ kontroli pritisne na ikonu za spremanje. Pokrene se postupak validacije podataka, zatvara se BindingSource i TableAdapter preslikava sadržaj našeg DataSet-a u bazu podataka (vidi uvodni tekst). Druga metoda, prilikom učitavanja forme pročita podatke iz baze u tablice unutar DataSet-a. DataGridView kontrole povezane preko BindingSource-a na northwindDataSet. Za svaki Slika 85: Dodavanje novog upita na DataGridView to je moguće vidjeti u svojstvu DataSource. TableAdapter © University of Zagreb, Faculty of Organization and Informatics, Varaždin
139
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka II – dataset, procedura, okidači
Ono što želimo jest OrderDetails tablicu puniti ovisno o odabranom primarnom ključu iz Orders tablice. Za to moramo napraviti novi upit u bazi podataka. 5. Da bismo kreirali novi upit iz Solution Explorer prozora odabrat ćemo northwindDataSet.xsd, pronaći tablicu Order Details i u njezin TableAdapter kliknuti desnom tipkom miša. Odabiremo opciju Add Query (Slika 85). U sljedećem prozoru koji se otvori, odabrat ćemo „Use SQL statements“, nakon toga „SELECT which returns rows“ i zatim odaberemo Query Builder (Slika 86). Podesimo upit tako da vraća samo one retke od određene narudžbe. Napravimo novu varijablu i nazovemo je OrderID
Slika 86: Query Builder
Možemo testirati upit da vidimo dobivamo li dobre rezultate. Nakon toga, odabiremo „Next“ i promijenimo nazive metoda. Ono što se događa u pozadini jest da će Visual Studio generirati metode na TableAdapter-u za Order Details tablicu, pa ćemo umjesto dosadašnje metode Fill (vidi raniji kôd), pozivati novu metodu (Slika 87).
Slika 87: Odabir naziva metoda za punjenje DataTable-a
Sada tablica izgleda nešto drugačije, odnosno vidljiv nam je novi, upravo dodani upit (Slika 88).
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
140
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka II – dataset, procedura, okidači
Slika 88: Konačan izgled TableAdapter-a nakon dodavanja novog upita
Vraćamo se u frmBrowseOrders dizajner, gdje ćemo dodati novu metodu za rukovanje događajem SelectionChanged nad kontrolom ordersDataGridView, te promijeniti raniju metodu za rukovanje događajem Load: Kôd 86 private void frmBrowseOrders_Load(object sender, EventArgs e) { this.ordersTableAdapter.Fill(this.northwndDataSet.Orders); } private void ordersDataGridView_SelectionChanged(object sender, EventArgs e) { if (ordersDataGridView.RowCount > 0) { int orderID = int.Parse(ordersDataGridView.CurrentRow.Cells[0].Value.ToString()); this.order_DetailsTableAdapter.FillByOrderID(this.northwndDataSet.Order_Details, orderID); } }
Ako sada pokrenemo aplikaciju i navigiramo kontrolom za prikaz narudžbi, stavke će se mijenjati i prikazivati samo za onu narudžbu koju promatramo. Fino podešavanje prikaza DataGridView kontrole Kao što možete uočiti neki atributi tablice Orders i Order Details su nepotrebni ili nejasni. Primjerice, CustomerID, EmployeeID ili RequiredDate nam ne znače mnogo isto kao iz ProductID nismo sigurni o kojem se proizvodu radi, tako dugo dok ne znamo šifru. Najprije ćemo neke atribute sakriti, a ProductID ćemo izmijeniti da prikazuje naziv, a ne šifru. Odabrat ćemo orderDataGridView i u njenom gornjem desnom kutu kliknuti na malu ikonu trokuta (Slika 89).
Slika 89: Izbornik za fino podešavanje DataGridView kontrole
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
141
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka II – dataset, procedura, okidači
Pojavit će se izbornik na kojem trebamo odabrati Edit Columns (Slika 90). Time dobivamo detaljan prikaz svih redaka te možemo mijenjati njihov tip, naziv, prikaz itd. Za Kolone CustomerID, EmployeeID i RequiredDate ćemo postaviti na Visible = false.
Slika 90: Uređivanje stupaca
Slično ćemo napraviti i za order_DetailsDataGridView. Umjesto da vidimo šifre ProductID želimo korisniku prikazati naziv proizvoda. Da bismo to omogućili za ColumnType ćemo odabrati DataGridViewComboBoxColumn (Slika 91). Odabrat ćemo novi DataSource, tablicu Products iz northwindDataSet.
Slika 91: Uređivanje izvora podataka, podataka za prikaz i podatka za selekciju
Nakon toga imamo mogućnost napraviti razliku između toga što korisniku prikazujemo, a što će Visual Studio u pozadini koristiti i to preko DisplayMember opcije koju ćemo postaviti na ProductName, a ValueMember na ProductID. Time prikazujemo naziv, a koristimo šifru. Da bi tablica i dalje izgledala kao tablica (bez kolone s ComboBox kontrolom) unutar kategorije Apperence, za DisplayStyle ćemo odabrati Nothing i kao naziv kolone (Header text) ćemo staviti Product. Pokrenemo aplikaciju. © University of Zagreb, Faculty of Organization and Informatics, Varaždin
142
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka II – dataset, procedura, okidači
Slika 92: Aplikacija - promjenjeni izgled stupca vanjskih ključeva
Primijetite da sada kako pretražujemo elemente, više se ne prikazuje šifra, već naziv artikla i time je završen Read (Slika 92).
Brisanje podataka - Delete Ako ništa ne diramo, aplikacija koju smo do sada napravili omogućuje brisanje. Kada smo prvi puta dodali ordersDataGridView kontrolu, Visual Studio je generirao i BindingNavigator kontrolu s prikazom trenutne pozicije, gumbima za pomicanje po tablici, gumbima za spremanje, brisanje i dodavanje novih zapisa. Možemo probati koristiti tipku brisanja. Primijetite da ako ne odaberemo opciju Save, podaci se neće obrisati. To je upravo zbog DataSet-a. Nama se ne sviđa ova generirana metoda za brisanje, već želimo vlastitu i to takvu, da korisnika pitamo je li siguran da želi obrisati zapis. Za to, na dnu dizajnera odaberemo ordersBindingNavigator i svojstvo DeleteItem postavljamo na (none). Dva puta ćemo kliknuti na opciju za brisanje kako bi nam se generirala metoda za rukovanje klikom. S obzirom na to da su nam narudžbe vezane uz detalje narudžbi, odnosno stavke, ne možemo narudžbu toliko dugo obrisati dok postoji stavka. Stoga, moramo najprije obrisati sve stavke, a onda tek narudžbu. To možemo napraviti na nekoliko načina, no mi ćemo napisati jednostavan kôd koji će to napraviti. Kliknut ćemo na ikonu za brisanje i izmijeniti kod metoda. Kôd 87 private void ordersBindingNavigatorSaveItem_Click(object sender, EventArgs e) { this.Validate(); this.order_DetailsBindingSource.EndEdit(); this.ordersBindingSource.EndEdit(); this.tableAdapterManager.UpdateAll(this.northwndDataSet); }
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
143
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka II – dataset, procedura, okidači
private void bindingNavigatorDeleteItem_Click(object sender, EventArgs e) { if (MessageBox.Show("Do you whish to delete this order?", "Question", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == System.Windows.Forms.DialogResult.Yes) { //delete order details for (int i = 0; i < order_DetailsDataGridView.Rows.Count; i++) { order_DetailsDataGridView.Rows.RemoveAt(i); } ordersDataGridView.Rows.RemoveAt(ordersDataGridView.CurrentRow.Index); } }
Dodavanje novih redaka – Create Ono što sada želimo napraviti je dodavanje novih narudžbi. Za to moramo imati novu formu koja će prihvaćati podatke i zapisivati ih u bazu. No svaka narudžba ima neke stavke, stoga moramo napraviti i formu koja će omogućiti dodavanje stavki. Kada korisnik dobije novi prozor s mogućnošću kreiranja narudžbe, postavit ćemo gumb kojim će se narudžba spremiti (kako bismo dobili primarni ključ iz baze podataka) te proslijediti ključ putem konstruktora forme za dodavanje stavki i vezati nove stavke uz ključ upravo kreirane narudžbe. Formu za dodavanje narudžbe kreirat ćemo unutar novog foldera našeg Solution Explorera, DataCreateForms i nazvati ju frmCreateOrder. Da bismo je prikazali, u orderBindingNavigatoru ćemo opciju AddNewItem postaviti na (none) i dodati metodu za rukovanje klikom. Kôd 88 private void bindingNavigatorAddNewItem_Click(object sender, EventArgs e) { DataCreateForms.frmCreateOrder frmOrder = new DataCreateForms.frmCreateOrder(); frmOrder.ShowDialog(); //<- dialog ne dozvoljava fokus drugih kontroli }
Na novu formu, ovaj put nećemo dovlačiti tablicu iz izbornika DataSources, već element po element. Visual Studio će nam generirati labelu i kontrole za odabrane tipove podataka, koje su već povezane na bazu podataka (eng. Binding). Da nisu povezane, morali bismo imati kôd koji svaku vrijednost čita iz određene kontrole (npr. TextBox) i stavlja ih u upit za kreiranje zapisa. Prije nego što počnemo dovlačiti atribute na kontrole, za CustomerID i EmployeeID za vrstu kontrole odabrat ćemo ComboBox. Napravit ćemo kontrolu kao na sljedećoj slici.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
144
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka II – dataset, procedura, okidači
Slika 93: Uređivanje forme za dodavanje nove narudžbe - ComboBox zamjena
Kao što smo ranije kod DataGridView kontrole odabirali ValueMember i DisplayMember, ovdje ćemo također slično napraviti. Klikom trokuta na customerIDComboBox, za DataSource ćemo odabrati Customers, za DisplayMember CustomerName, za ValueMember CustomerID i za SelectedValue CustomerID. SelectedValue se u ovom slučaju odnosi na vezu prema trenutnoj Narudžbi. S obzirom na to da smo za DataSource odabrali Customers tablicu (Slika 94), kontrolu smo razdvojili od Orders tablice, pa pomoću SelectedValue odabiremo da nam unutar Slika 94: Customers tablica konteksta orderBindingSource SelectedValue bude ValueMember. Visual Studio je generirao novu kontrolu BindingNavigator koju nećemo koristiti, pa je možemo obrisati. Umjesto toga dodat ćemo tri nova gumba: „…“ (btnAddDetails) koji će nam služiti za otvaranje stavki, „Create“ kojim ćemo napraviti narudžbu i „Clear“ pomoću kojeg zatvaramo prozor. Sada ćemo dodati kôd za MouseUp događaje na te gumbe: Kôd 89: Konstruktor kod frmCreateDetails private void btnCreate_MouseUp(object sender, MouseEventArgs e) { createNewEntry(); } private void createNewEntry() { this.Validate(); this.ordersBindingSource.EndEdit(); this.tableAdapterManager.UpdateAll(this.northwndDataSet); }
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
145
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka II – dataset, procedura, okidači
private void btnAddDetails_MouseUp(object sender, MouseEventArgs e) { createNewEntry(); int OrderId = (int)northwndDataSet.Orders.Rows[0]["OrderID"]; // frmCreateDetails ćemo kreirati nešto kasnije, no ovdje prosljedimo ID frmCreateDetails frmCreateDetails = new frmCreateDetails(OrderId); if (Editing) frmCreateDetails.Editing = true; frmCreateDetails.ShowDialog(); } private void btnClear_MouseUp(object sender, MouseEventArgs e) { this.Close(); }
Također, želimo da čim se otvori ovaj prozor, korisnik već uređuje novu narudžbu. U skladu s time, moramo promijeniti metodu Load. Kôd 90: Konstruktor kod frmCreateDetails public frmCreateOrder() { InitializeComponent();} private void frmCreateOrder_Load(object sender, EventArgs e) { this.employeesTableAdapter.Fill(this.northwndDataSet.Employees); this.customersTableAdapter.Fill(this.northwndDataSet.Customers); ordersBindingSource.AddNew(); }
Sada imamo čitav kôd koji nam je potreban da bismo kreirali i spremali nove narudžbe. (OPREZ, neka su polja obavezna, pa ovdje treba još finog podešavanja i interakcije s korisnikom).
Dodavanje stavki Kreirat ćemo novu formu unutar direktorija DataCreateForms i nazvati je frmCreateDetails. Promijenit ćemo joj svojstvo Text (Add order details) i na sličan način kao ranije, povući tekstualna polja iz DataSources izbornika, no ovaj put ćemo povući i čitavu tablicu u obliku DataGridView kontrole kako bismo vidjeli stavke koje smo dodali do sada. Uz to, pored productIDTextBox kontrole ćemo dodati gumb „…“, koji će nam kasnije prikazati listu svih proizvoda i u taj TextBox zapisati šifru odabranog proizvoda.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
146
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka II – dataset, procedura, okidači
Slika 95: Forma za dodavanje stavki narudžbe
Kao i za dodavanje narudžbi, moramo podesiti formu tako da kad je korisnik otvori, automatski se dodaje novi zapis, a kada klikne na „…“, otvara se forma s proizvodima i ona ima mogućnost vratiti ProductID i UnitPrice (Slika 95). Da bismo mogli vratiti šifru proizvoda i cijenu, trebamo tablicu s proizvodima. Idemo u direktorij DataBrowseForms i dodajemo novu formu frmBrowseProducts. U nju ćemo iz DataSource kontrole povući tablicu Products i napraviti sve promjene (postavke svojstva, selekcije, dodavanje novog elementa u glavni izbornik, otvaranje forme na klik, sve kao i za frmBrowseOrders). Kako želimo da nam ta tablica vraća vrijednosti, trebamo napraviti nova svojstva koja ćemo čitati i gumbe koji će upravljati formom (Slika 96).
Slika 96: Forma za pretraživanje proizvoda koja vraća vrijednost odabira - Lookup
Otvorit ćemo formu frmBrowseProducts i dodat ćemo svojstva; ProductID i UnitPrice te njihove gettere i sttere. Kada korisnik klikne na dataGridView kontrolu ta će se svojstva postaviti, a njih ćemo moći dohvatiti i zapisati vrijednosti u polja koja nedostaju. Stoga, morat ćemo obraditi događaj selekcije i iz tablice čitati vrijednosti. Da bismo to lakše napravili moramo si unutar productsDataGridView kontrole postaviti imena. © University of Zagreb, Faculty of Organization and Informatics, Varaždin
147
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka II – dataset, procedura, okidači
Slika 97 prikazuje odabiremo opciju najprije ProductID pa UnitPrice i mijenjamo svojstvo DataPropertyName u ProductIDColumn i UnitPriceColumn.
Slika 97: Imenovanje stupaca
Sada možemo napisati kôd kojim postavljamo vrijednosti svojstava. Dodajemo novu metodu za obradu događaja SelectionChanged nad kontrolom productsDataGridView. Kôd 91 public int ProductID { get; set; } public float UnitPrice { get; set; } private void productsDataGridView_SelectionChanged(object sender, EventArgs e) { this.ProductID = int.Parse(productsDataGridView["ProductIDColumn", productsDataGridView.CurrentRow.Index].Value.ToString()); this.UnitPrice = float.Parse(productsDataGridView["UnitPriceColumn", productsDataGridView.CurrentRow.Index].Value.ToString()); }
Na gumb OK ćemo zatvarati formu (kasnije se to može izmijeniti po potrebi). Sada se možemo vratiti na frmCreateDetails i možemo obraditi MouseUp događaj za gumb „…“. Kôd 92 private void btnListProducts_Click(object sender, EventArgs e) { DataBrowseForms.frmBrowseProducts frmProducts = new DataBrowseForms.frmBrowseProducts(); frmProducts.ShowDialog(); productIDTextBox.Text = frmProducts.ProductID.ToString(); unitPriceTextBox.Text = frmProducts.UnitPrice.ToString(); }
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
148
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka II – dataset, procedura, okidači
Pomoću tog kôda dohvatili smo vrijednosti i zapisali ih unutar productIDTextbox kontrole i unitPriceTextBox kontrole koje su već vezane za bazu podataka. Time je postupak dodavanja stavki skoro završen, no ono što još moramo napraviti jest prosljeđivanje šifre narudžbe na koju vežemo stavke, stoga promjenimo konstruktor (čime i raniji kôd na frmCreateOrder za btnAddDetails_MouseUp). Kôd 93: Konstruktor kod frmCreateDetails public int OrderId { get; set; } public frmCreateDetails(int orderId) { InitializeComponent(); this.OrderId = orderId; }
Također ćemo izmijeniti kôd za spremanje i dodavanje elementa. Najprije za order_DetailsBindingNavigator treba promijeniti svojstvo AddNewItem na none. A nakon toga ćemo dodati metodu koja obrađuje događaj klik za bindingNavigatorAddNewItem i za order_DetailsBindingNavigatorSaveItem. Kôd će izgledati ovako: Kôd 94 private void frmCreateDetails_Load(object sender, EventArgs e) { this.productsTableAdapter.Fill(this.northwndDataSet.Products); this.order_DetailsTableAdapter.FillByOrderID(this.northwndDataSet.Order_Details, OrderId); // moramo napraviti filtriranje za samo one stavke koje dodajemo } private void order_DetailsBindingNavigatorSaveItem_Click(object sender, EventArgs e) { SaveCurrent(); }
private void bindingNavigatorAddNewItem_Click(object sender, EventArgs e) { addNewEntry(); } private void addNewEntry() { try { this.order_DetailsBindingSource.AddNew(); orderIDTextBox.Text = OrderId.ToString(); } catch (Exception ex) {
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
149
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka II – dataset, procedura, okidači
MessageBox.Show("Exception: " + ex.Message); } } private void SaveCurrent() { this.Validate(); this.order_DetailsBindingSource.EndEdit(); this.tableAdapterManager.UpdateAll(this.northwndDataSet); }
Time smo završili kôd za dodavanje zapisa u tablicu Order i OrderDetails.
Ažuriranje zapisa - Update Da bismo omogućili ažuriranje trebamo napraviti minimalne promjene na postojećim formama. Naime, kako koristimo kontrole koje su vezane za podatke (eng. data bound controls) puno nam je lakše napraviti ažuriranje jer podatke koje prikazujemo možemo mijenjati i spremati. Ono što nam preostaje je eliminirati automatsko dodavanje redaka prilikom otvaranja prozora za dodavanje novih redaka. Iste ćemo koristiti i za ažuriranje. Stoga, najprije ćemo promijeniti formu za narudžbe, odnosno frmCreateOrder. Kôd 95 private bool Editing { get; set; } public int OrderID { get; set; } public frmCreateOrder() { InitializeComponent(); OrderID = -10; //<- mora biti postavljena pa koristimo neutralnu vrijednost 10 } // preopterećenje konstruktora (ovaj ćemo konstruktor koristiti kada korisnik odabere ažuriranje public frmCreateOrder(int OrderID) { InitializeComponent(); this.OrderID = OrderID; this.Editing = true; } private void frmCreateOrder_Load(object sender, EventArgs e) { this.employeesTableAdapter.Fill(this.northwndDataSet.Employees);
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
150
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka II – dataset, procedura, okidači
this.customersTableAdapter.Fill(this.northwndDataSet.Customers); if (this.OrderID != -10) { this.ordersTableAdapter.FillByOrderID(this.northwndDataSet.Orders, this.OrderID); // ako je ažuriranje popuni prema ID-u } else { ordersBindingSource.AddNew(); // ako nije ažuriranje } }
Sada znamo koju narudžbu uređujemo, pa te iste podatke možemo proslijediti i formi za dodavanje stavki: Kôd 96 private void btnAddDetails_MouseUp(object sender, MouseEventArgs e) { createNewEntry(); int OrderId = (int)northwndDataSet.Orders.Rows[0]["OrderID"]; frmCreateDetails frmCreateDetails = new frmCreateDetails(OrderId); if (Editing) frmCreateDetails.Editing = true; frmCreateDetails.ShowDialog(); }
A u formi za dodavanje stavki moramo osigurati da se ne dodaje novi redak čim se forma otvori, pa ćemo i njezin kod izmijeniti: Kôd 97 public int OrderId { get; set; } public bool Editing { get; set; } public frmCreateDetails(int orderId) { InitializeComponent(); this.OrderId = orderId; Editing = false; } private void frmCreateDetails_Load(object sender, EventArgs e) { this.productsTableAdapter.Fill(this.northwndDataSet.Products); this.order_DetailsTableAdapter.FillByOrderID(this.northwndDataSet.Order_Details, OrderId); if (!Editing)
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
151
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka II – dataset, procedura, okidači
{ addNewEntry(); } }
I sada možemo dodati kontekstni izbornik, koji će se pojaviti kada unutar tablice frmBrowseOrders kliknemo desni gumb miša, a sadržava opciju ažuriranja. Za to, iz izbornika s kontrolama dodajemo novi ContextStripMenu i nazovemo ga ordersContextStripMenu. Unutar njega slično kao i za glavni izbornik dodajemo novu opciju Edit i na nju metodu za rukovanje događajem MouseUp. Prije toga, moramo obraditi klik desnog gumba miša na ordersDataGridView kontrolu koja će prikazati izbornik i označiti redak (jer tako možemo dohvatiti šifru). Moramo simulirati klik miša i označavanje retka. Konačan kôd izgleda: Kôd 98 private void ordersDataGridView_MouseDown(object sender, MouseEventArgs e) { if (e.Button == System.Windows.Forms.MouseButtons.Right) { DataGridView.HitTestInfo hitTest = ordersDataGridView.HitTest(e.X, e.Y); ordersDataGridView.ClearSelection(); ordersDataGridView.Rows[hitTest.RowIndex].Selected = true; ordersContextStripMenu.Show(ordersDataGridView,e.Location); } } private void editToolStripMenuItem_MouseUp(object sender, MouseEventArgs e) { int OrderID = int.Parse(ordersDataGridView.SelectedRows[0].Cells["OrderIDColumn"].Value.ToString ()); DataCreateForms.frmCreateOrder order = new DataCreateForms.frmCreateOrder(OrderID); order.ShowDialog(); }
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
152
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka II – dataset, procedura, okidači
9.4. Pitanja 1. Što je DataSet? 2. Što je okidač (trigger) u bazi podataka? 3. Navedite primjer okidača (triggera) i scenarij korištenja. 4. Što je pohranjena procedura? 5. Da li se podaci u DataSetu automatski ažuriraju u bazi podataka? 6. Što je TableAdapter? 7. Što je Identity polje? 8. Čemu služi attach/detach baze podataka? 9. Što je MDI container? 10. Što je data binding?
9.5. Više informacija o temi 1. DataSets, DataTables and DataViews (http://msdn.microsoft.com/en-us/library/ss7fbaez.aspx), dostupno: prosinac, 2013. 2. ADO.NET Overview (http://msdn.microsoft.com/en-us/library/h43ks021.aspx), dostupno: prosinac, 2013. 3. Create trigger (http://msdn.microsoft.com/en-us/library/ms189799.aspx), dostupno: prosinac, 2013.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
153
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka III - ORM
10. Rad s bazom podataka III – ORM Sažetak U ovom poglavlju su obrađeni koncepti ORM alata kroz Entity Framework, koji postepeno postaje glavna .NET tehnologija za rad s podacima. Kao i ostali ORM alati omogućava apstrahiranje baze podataka, tako da korisnik radi s konceptualnim modelom. Korisnik više ne mora sam preslikavati strukturu baze podataka u klase aplikacije, nego to za njega radi ORM. Na taj način se generira velik dio infrastrukturalnog kôda kojeg je prije morao pisati sam programer, te se ubrzava proces izrade aplikacija. Ključne riječi: ADO.NET, ORM, Entity Framework
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
154
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka III - ORM
10.1. Uvod Podaci se u relacijskim bazama podataka nalaze u obliku relacija sa zapisima, strukturiranim i prilagođenim tako da zadovoljavaju pravila oblikovanja baza podataka (normalizacija). Ta pravila osiguravaju bazu podataka od pojave redundancije, nekonzistentnosti podataka, različitih anomalija, te omogućuju brzo pretraživanje. Međutim, istovremeno uzrokuju da podaci nisu uvijek spremljeni na najprirodniji način za razumijevanje. S druge strane, većina današnjih aplikacija, koje koriste podatke iz relacijskih baza podataka, su napisane u objektno-orijentiranih programskim jezicima. Suprotno od relacijskih baza podataka, objektni pristup pokušava prikazati entitete na prirodan način kao objekte definirane njihovim svojstvima i ponašanjima. Iako struktura klase vrlo često odgovara strukturi tablice u relacijskoj bazi podataka, njihove strukture nisu nužno jednake. Ta različitost uzrokuje problem pretvorbe podataka iz jednog pristupa u drugi. U literaturi je poznat pod imenom objektno-relacijska neusklađenost (eng. object-relational impedance mismatch).
10.2. ADO.NET Entity Framework Jedan od ORM (eng. Object-Relational Mapping) alata je i Entity Framework, razvijen od strane Microsofta 2008. godine u okviru .NET Framework-a 3.5. Iako je u početku bio dočekan s izvjesnom dozom skepticizma i nije bio najbolje prihvaćen, zahvaljujući brojnim poboljšanjima, sada je prerastao u glavnu .NET tehnologiju za pristup bazi podataka, što je potvrdio i Microsoft. Arhitekturalno Entity Framework se i dalje temelji na ADO.NET tehnologiji, konkretnije DataReader i Command objektima, ali omogućava korisniku da radi s podacima na višoj razini apstrakcije. Zapravo, glavna korist Entity Framework-a je da oslobađa programera brige o strukturi baze podataka, tako da se radi s konceptualnim modelom podataka koji reflektira poslovne objekte naše aplikacije. Nije više potrebno pisati i izvršavati SQL upite za dohvaćanje podataka iz baze i unos podataka u bazu. Sada Entity Framework omogućava da se s podacima radi u obliku specifičnih domenskih objekata, bez da se moramo brinuti o tome u kojoj su konkretno tablici i atributu pohranjeni podaci.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
155
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka III - ORM
Arhitektura Entity Framework-a Slika 98 prikazuje pojednostavljenu arhitekturu Entity Framework-a.
Slika 98: Arhitektura Entity Framework-a
Arhitekturu Entity Framework-a se sastoji od sljedećih elemenata: Entitetni podatkovni model (eng. Entity Data Model - EDM) – srž Entity Framework-a, definira poveznicu između konceptualnog modela nad kojim programer radi te baze podataka. Sastoji se od 3 XML datoteke (ovisno o verziji), od kojih svaka ima posebnu funkciju: 1. Konceptualni model (eng. Conceptual Model) – opisuje domenske klase i veze između njih. Definira se uz pomoć CSDL jezika (eng. Conceptual Schema Definition Language). Sadrži popis svih entiteta i veza između njih te detaljan opis strukture svakog od entiteta. 2. Model pohrane (eng. Storage Model) – predstavlja ekvivalent konceptualnom modelu, ali s razlikom da opisuje strukturu baze podataka, njenih tablica, pogleda, pohranjenih procedura, ključeva i veza. Definira se uz pomoć SSDL jezika (eng. Store Schema Definition Language). 3. Model preslikavanja (eng. Mapping Model) – služi za neutraliziranje razlika između konceptualnog modela i modela pohrane. On specificira pravila preslikavanja iz jednog modela u drugi, i obrnuto, uz pomoć MSL jezika (eng. Mapping Specification Language). © University of Zagreb, Faculty of Organization and Informatics, Varaždin
156
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka III - ORM
Objektne usluge (eng. Object Services) – sloj zadužen za upravljanje objektima u Entity Framework-u. Na temelju proslijeđenog upita (Entity SQL ili LINQ to SQL) i konceptualnog modela, sloj objektnih usluga kreira komandno stablo (eng. Command Tree) koje prosljeđuje sloju entitetnog klijenta (eng. Entity Client). S druge strane, kada od entitetnog klijenta zaprimi podatke u formatu definiranom konceptualnim modelom, uz pomoć tih podataka i konceptualnog modela rekonstruira domenske objekte. Entitetni klijent (eng. Entity Client Data Provider) – sloj zadužen za komunikaciju s bazom podataka. Ne komunicira izravno s bazom podataka, nego preko ADO.NET pružatelja podataka. Koristi entitetni podatkovni model (sloj mapiranja i sloj pohrane) kako bi komandno stablo dobiveno od sloja objektnih usluga pretvorio u SQL naredbe. Također, kada od ADO.NET sloja dobije podatke u tabličnom obliku DataReader-a, koristi konceptualni model kako bi podatke oblikovao da budu razumljivi sloju objektnih usluga. LINQ to Entities – je LINQ dijalekt i glavni upitni jezik u Entity Framework-u. Omogućava pisanje tipiziranih upita nad konceptualnim modelom, koji vraćaju gotove objekte. Iako LINQ to Entities radi isključivo nad objektima i dalje se on mora prevesti u standardni SQL kako bi se mogao izvršiti nad bazom. Entity SQL – koristio se prije nego je nastao LINQ to Entities jezik, međutim koristi se u određenim slučajevima i danas. Vrlo je složen, ali ima i neke prednosti u odnosu na L2E, kao što su: zadaje se u tekstualnom obliku, pa ga je lakše dinamički generirati, sadrži više funkcije, može se izvršavati izravno u sloju entitetnog klijenta. Tokovi rada u Entity Framework-u Entity Framework trenutno nudi 4 načina rada, nazvana tokovima rada (eng. Workflows). Da bi odlučili koji od njih koristiti moramo odgovoriti na sljedeća pitanja: 1. Radimo li s postojećom bazom podataka ili baza podataka još ne postoji? 2. Želimo li objektni model kreirati uz pomoć grafičkog alata – dizajnera ili pisanjem kôda.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
157
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka III - ORM
Slika 99: Tokovi rada u Entity Framework-u
Slika 99 prikazuje koji tok rada bi bio prikladan u ovisnosti o odgovorima na postavljena pitanja, te je moguće odabrati neki od sljedećih pristupa: 1. Prvo model (eng. Model First) – prikladan kada nemamo postojeću bazu podataka, već je potrebno kreirati novu te kada objektni model želimo kreirati uz pomoć grafičkog alata. U dizajneru prvo kreiramo objektni model, a onda se na temelju njega generira baza podataka i kôd za klase. 2. Prvo baza podataka (eng. Database First) – prikladan kada već imamo izgrađenu bazu podataka, iz koje tada u dizajneru reverznim inženjeringom generiramo model, a zatim iz modela generiramo kôd za klase. 3. Prvo kôd – nova baza (eng. Code First – New Database) – prikladan kada nemamo postojeću bazu podataka nego treba kreirati novu te kada objektni model želimo kreirati pisanjem kôda. Tada objektni model kreiramo u kôdu pisanjem klasa i definiranjem pravila mapiranja, te iz tako kreiranog modela generiramo bazu podataka. 4. Prvo kôd – postojeća baza (eng. Code First – Existing Database) – prikladan kada već imamo izgrađenu bazu podataka, a model želimo kreirati u kôdu pisanjem klasa i definiranjem pravila mapiranja.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
158
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka III - ORM
Bottom-up i Top-down dizajn Izbor gore navedenih pristupa ovisi i o osobnim preferencijama i navikama. Neki preferiraju tzv. bottom-up pristup koji najveću važnost pridaje podacima, odnosno bazi podataka. Zato taj pristup uvijek kreće od definiranja baze podataka. Nasuprot tome, top-down pristup prednost daje procesima kojima se bavi aplikacija, pa zbog toga zagovara prvo kreiranje modela, a tek onda podataka.
10.3. Kreiranje
aplikacije
pristupom
„Prvo
baza
podataka“ Obradit ćemo pristup koji pretpostavlja postojanje gotove baze podataka, na temelju koje ćemo generirati objektni model (eng. Database First). Ako vas zanimaju ostali pristupi možete pogledati dodatnu literaturu navedenu na kraju ovog poglavlja. Praktičan rad s Entity Framework-om ćemo pokazati na sljedećoj aplikaciji: Aplikacija omogućava vođenje evidencije o studentima na kolegiju programsko inženjerstvo, njihovom timu, odabranom projektu te aktivnostima koje izvršavaju. Funkcionalnosti koje aplikacija mora imati implementirane su: 1. Dodavanje, izmjena i brisanje timova, dodjela studenata u timove. 2. Dodavanje, izmjena i brisanje studenata, te praćenje aktivnosti studenata. 3. Dodavanje, izmjena i brisanje aktivnosti. 4. Dodavanje, izmjena i brisanje tipova aktivnosti. Sve informacije će se pohranjivati u SQL Server Compact bazu podataka navedenu na ERA dijagramu (Slika 100). S obzirom na to da se želimo fokusirati na izradu pristupa bazi podataka, s GitHub sustava ćemo preuzeti projekt (https://github.com/PI2013FOI/Lab_3_1_EvidencijaStudenata.git) koji sadrži gotovu bazu podataka i definirano korisničko sučelje aplikacije. Prikazan je dio aplikacije, a ostatak možete uraditi sami.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
159
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka III - ORM
Slika 100: ERA model baze podataka
Spajanje na bazu i generiranje modela S obzirom na to da naša aplikacija ima već izgrađeno grafičko sučelje, možemo odmah prijeći na sljedeći korak, a to je spajanje na bazu podataka i generiranje entitetnog podatkovnog modela. To možemo napraviti na sljedeći način: 1. U Solution Explorer-u kliknite desnom tipkom miša na projekt te odaberite Add -> New Item. 2. U listi elemenata odaberite ADO. NET Entity Data „EvidencijaStudenataModel“ te kliknite na Add (Slika 101).
Model,
pod
Name
upišite
Slika 101: ADO.NET Entity Data Model
3. Nakon toga nas Visual Studio pita što novokreirani model treba sadržavati. Imamo dvije ponuđene opcije: generiranje iz postojeće baze ili prazan model. S obzirom na to da mi imamo gotovu bazu, generirat ćemo model iz nje, i to tako da odaberemo opciju Generate from database. © University of Zagreb, Faculty of Organization and Informatics, Varaždin
160
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka III - ORM
4. Otvara nam se prozor u kojem trebamo odabrati konekciju na bazu podataka. 5. Odaberite New Connection. 6. U prozoru za novu konekciju postavimo vrijednost Data Source-a na Microsoft SQL Server Compact (gumb Change). 7. Pod Database pronađite bazu pod nazivom EvidencijaStudenata.sdf (gumb Browse). 8. Testirajte konekciju na bazu s opcijom Test Connection te ako je sve uredu kliknite OK. 9. Kliknite Next kako bi spremili ConnectionString. 10. Stavite kvačicu na opciju Tables kako bi uključili sve tablice u model. Također opcija Include foreign key columns in model treba biti uključena. 11. Kliknite Finish.
Konceptualni model Visual Studio nam je sada generirao entitetni model na temelju baze podataka. Slika 102 Prikazuje kako izgleda konceptualni model.
Slika 102: Konceptualni model
Možemo primijetiti da je prikazani konceptualni model zapravo jako sličan ERA modelu naše baze. Ipak, u određenim slučajevima razlike će postojati. S obzirom na to da konceptualni model prikazuje generirane klase, tu možemo iskoristiti koncepte objektno-orijentiranog programiranja, kao što je nasljeđivanje. U bazi podataka nije moguće definirati nasljeđivanje između tablica. Osim toga, u bazi podataka nužno moramo razbiti vezu više-više, dok u konceptualnom modelu nije potrebno.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
161
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka III - ORM
Ako pogledamo svojstva klasa u konceptualnom modelu, možemo vidjeti da postoje dvije vrste: skalarna svojstva i navigacijska svojstva. Skalarna svojstva preslikavaju uobičajene atribute tablice iz baze, kao što su atributi tipa integer, float, char. S druge strane, navigacijska svojstva preslikavaju veze između entiteta. Da bi razumjeli navigacijska svojstva moramo promotriti tipove veza između klasa na konceptualnom modelu. Postoje 3 vrste veza: Veza 1:1 – implementira se tako da svaka klasa sadrži navigacijsko svojstvo koje zapravo predstavlja referencu na objekt klase s kojom je povezana. Veza 1:N – implementira se tako da klasa na strani 1 sadrži navigacijsko svojstvo koje predstavlja kolekciju objekata klase s kojom je povezana, a klasa na strani N sadrži referencu na jedan objekt klase s kojom je povezana. Veza N:M – implementira se tako da obje klase sadrže navigacijsko svojstvo kolekciju objekata one druge klase. Slika 102 prikazuje konceptualni model koji sadrži samo veze tipa 1:N. Npr. klasa Tim je povezana vezom 1:N s klasom Student. To znači da klasa Tim sadrži navigacijsko svojstvo Student koje je kolekcija objekata tipa Student. S druge strane, klasa Student sadrži navigacijsko svojstvo Tim, koje predstavlja referencu na objekt tipa Tim.
Entitetne klase i kontekst Klase koje možemo vidjeti na konceptualnom modelu je Visual Studio i generirao (Aktivnost, AktivnostStudenata, Student, Model). Možemo ih pronaći u Solution Exploreru u okviru edmx skupine datoteka (Slika 103). Osim njih na tom mjestu možemo pronaći i klasu konteksta EvidencijaStudenataEntities. Slika 104 prikazuje primjer generirane klase Student. S obzirom na to da je klasa generirana na temelju tablice iz baze podataka, u njoj su generirana Slika 103: Datoteke EDM-a skalarna svojstva koja preslikavaju strukturu tablice te navigacijska svojstva koja implementiraju vezu s drugim tablicama. Osim konstruktora nikakve druge metode i logika nisu generirane. Ovakve klase, koje sadrže samo atribute i svojstva, često se nazivaju entitetnim klasama. Ipak primijetite da je klasa definirana kao parcijalna klasa (eng. partial), što ostavlja mogućnost definiranja logike klase, ali i ostalih dodatnih elemenata klase u drugoj datoteci. To je ujedno i jedna od velikih prednosti ORM pristupa u odnosu na npr. nepovezani način rada s bazom podataka (DataSet).
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
162
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka III - ORM
Slika 104: Generirana klasa Student
Jedna od najbitnijih generiranih klasa je i klasa EvidencijaStudenataEntities koju nazivamo kontekst. Kontekstna klasa nasljeđuje klasu DbContext i predstavlja primarni način za dohvaćanje podataka iz baze (u obliku objekata) te spremanje napravljenih promjena u bazu. Kontekstna klasa sadrži DbSet kolekcije objekata za svaku tablicu iz baze podataka, preko kojih zapravo radimo s podacima. Ako želimo npr. dohvatiti jednog ili više studenata, izmijeniti, obrisati ili dodati studenta, tada to nećemo raditi izravno nad tablicom Studenti u bazi podataka, nego sa DbSet kolekcijom Studenti. Detalje komunikacije s bazom ostavljamo Entity Framework-u. Također, kroz konstruktor klase je proslijeđen naziv konekcije na bazu na koju se kontekst spaja (podaci o konekciju su dostupni kroz App.config datoteku u Solution Exploreru).
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
163
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka III - ORM
Slika 105: Kontekstna klasa
Čitanje podataka uz pomoć EF-a S obzirom na to da već imamo definirano grafičko sučelje te da smo se uz pomoć Entity Framework-a spojili na bazu podataka, možemo pročitati podatke i prikazati ih na odgovarajućim formama. Započet ćemo s prikazom studenata po timovima na formi FrmPopisStudenata i to kroz dvije DataGridView kontrole. Da bi DataGridView kontrole znale kakvi će podaci biti smješteni u njih, postavit ćemo im izvor podataka (eng. Data Source) na sljedeći način: 1. Označimo DataGridView dgvTimovi te kliknimo na trokutić koji se pojavio u gornjem desnom kutu kontrole. 2. U izborniku otvorimo opciju Chose Data Source te odaberimo opciju Add Project Data Source. 3. Odaberimo Object kao izvor podataka i kliknimo Next. 4. Pronađite klasu Tim, označite je i kliknite Finish. Primijetite kako je Visual Studio automatski generirao BindingSource objekt i da je svako svojstvo klase Tim sada prikazano u obliku stupca u DataGridView kontroli. Ipak, ne trebaju nam svi stupci. Naime, klasa tim sadrži navigacijsko svojstvo Studenti, koje zapravo predstavlja kolekciju studenata koji se nalaze u timu. DataGridView kontrola ne zna kako da u jednoj ćeliji prikaže kolekciju objekata, te bi u ovom slučaju prilikom prikaza DataGridView-a dogodila iznimka. Zbog toga ćemo stupac Studenti sakriti preko opcije Edit Columns (svojstvo Visible = False). Na isti način potrebno je definirati izvor podataka za DataGridView kontrolu za prikaz studenata (dgvStudenti) te sakriti stupce TimId, AktivnostiStudenta, Tim. © University of Zagreb, Faculty of Organization and Informatics, Varaždin
164
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka III - ORM
Kada smo definirali strukturu prikaza u DataGridView kontrolama možemo izraditi metode koje će zapravo ažurirati prikaz timova i studenata. Metode ćemo dodati u formi FrmPopisStudenata, te ćemo ih za početak pozvati u Load događaju forme. Također kako bi se studenti prikazivali ovisno o selektiranom timu, metodu PrikaziStudente ćemo pozvati i prilikom promjene selektiranog retka u dgvTimovi. Kôd 99: Metode za prikaz studenata po timovima /// /// Dohvaća listu svih timova iz kolekcije timova u kontekstu, te ih prikazuje /// u DataGridView-u. /// private void PrikaziTimove() { BindingList listaTimova = null; using (var db = new EvidencijaStudenataEntities()) { listaTimova = new BindingList(db.Tim.ToList()); } timBindingSource.DataSource = listaTimova; } /// /// Dohvaća listu studenata proslijeđenog tima te ih prikazuje u DataGridView-u. /// /// Tim čije studente želimo prikazati. private void PrikaziStudente(Tim tim) { BindingList listaStudenata = null; using (var db = new EvidencijaStudenataEntities()) { db.Tim.Attach(tim); listaStudenata = new BindingList(tim.Student.ToList()); } studentBindingSource.DataSource = listaStudenata; } /// /// Rukuje događajem pokretanja forme. /// private void FrmPopisStudenata_Load(object sender, EventArgs e) { PrikaziTimove(); PrikaziStudente(timBindingSource.Current as Tim); } /// /// Rukuje događajem promjene selektiranog retka u tablici. ///
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
165
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka III - ORM
private void dgvTimovi_SelectionChanged(object sender, EventArgs e) { Tim selektiraniTim = timBindingSource.Current as Tim; if (selektiraniTim != null) { PrikaziStudente(selektiraniTim); } }
Primijetite kako za pristup podacima koristimo instancu kontekstne klase (EvidencijaStudenataEntities), koju smo kreirali unutar using bloka kako ne bi morali brinuti o oslobađanju resursa. Za dohvaćanje svih timova koristimo DbSet kolekciju Timovi koja se nalazi unutar konteksta. S druge strane, s obzirom na to da želimo prikazati samo studente trenutno označenog tima, kolekciju tih studenata možemo dobiti preko navigacijskog svojstva Studenti koje sadrži klasa Tim. Naime, navigacijsko svojstvo Studenti klase Tim zapravo predstavlja kolekciju studenata koji se nalaze u tom timu. Povezani (eng. Connected) i nepovezani (eng. Disconnected) scenarij Jedna od stvari na koju treba obratiti pažnju prilikom rada s Entity Framework-om je postojanje dva scenarija rada s entitetnim objektima. Povezani način rada podrazumijeva da entitetne objekte koristimo unutar konteksta u kojem smo ih i dohvatili. S druge strane, nepovezani scenarij podrazumijeva da unutar jednog konteksta koristimo entitetni objekt koji smo kreirali u nekom drugom kontekstu i proslijedili trenutnom kontekstu. U nepovezanom scenariju kontekst ne može prepoznati proslijeđeni objekt, te ga je potrebno eksplicitno „registrirati“ uz pomoć naredbe Attach. Primjer povezanog i nepovezanog scenarija su sljedeći: private void PovezaniScenarij() { using (var db = new EvidencijaStudenataEntities4()) { //Tim dohvaćamo preko trenutnog konteksta Tim prviTim = (from t in db.Timovi where t.Id == 1 select t).First(); BindingList lista = new BindingList(prviTim.Studenti.ToList()); studentBindingSource.DataSource = lista; } } private void NepovezaniScenarij(Tim prviTim) { using (var db = new EvidencijaStudenataEntities4()) { //Proslijedili smo Tim trenutnom kontekstu i zato ga je potrebno "registrirati".
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
166
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka III - ORM
db.Timovi.Attach(prviTim); BindingList lista = new BindingList(prviTim.Studenti.ToList()); studentBindingSource.DataSource = lista; } }
Ako sada pokrenemo projekt i otvorimo formu s popisom studenata, rezultat bi trebao biti kao na slici ispod (Slika 106).
Slika 106: Popis studenata po timovima
Kreiranje novog entitetnog objekta Na primjeru dodavanja novog tima i novog studenta, pokazat ćemo kako se dodaju novi entitetni objekti u kontekst te kako se iz konteksta spremaju u bazu podataka. Na formi FrmNoviTim moguće je upisati podatke za novi tim te klikom na gumb Uredu kreirati novi objekt i spremiti ga u bazu. Kôd 100: Kreiranje novog tima /// /// Rukuje događajem klika na gumb Uredu. /// private void btnUredu_Click(object sender, EventArgs e) { using(var db = new EvidencijaStudenataEntities()) { //Kreiramo novi objekt klase Tim i popunjavamo ga podacima sa forme. Tim tim = new Tim
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
167
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka III - ORM
{ OznakaTima = txtOznakaTima.Text, NazivProjekta = txtNazivProjekta.Text, OpisProjekta = txtOpisProjekta.Text, Napomena = txtNapomena.Text }; db.Tim.Add(tim); //Dodajemo tim u odgovarajuću kolekciju u kontekstu. db.SaveChanges(); //Spremamo napravljene promjene u bazu podataka. } Close(); }
Obratite pažnju na dodavanje novog objekta u odgovarajuću kolekciju u kontekstu te pozivanje metode konteksta SaveChanges, koja sve promjene koje su napravljene u kontekstu sprema u bazu podataka. Kako bi novokreirani tim bio prikazan u tablici timovi na formi FrmPopisStudenata potrebno je osvježiti prikaz ponovnim pozivanjem metode PrikaziTimove i to u metodi btnNoviTim_Click forme FrmPopisStudenata: Kôd 101: Ažuriranje popisa timova nakon dodavanja novog tima /// /// Rukuje događajem klika na gumb za dodavanje novog tima. /// private void btnNoviTim_Click(object sender, EventArgs e) { FrmNoviTim forma = new FrmNoviTim(); forma.ShowDialog(); PrikaziTimove(); }
Na sličan način možemo napraviti i dodavanje novog studenta na formi FrmNoviStudent (Slika 107). Klikom na gumb Uredu kreirat će se novi objekt klase Student, popuniti podacima s forme, dodati u kolekciju Studenti u kontekst te na kraju spremiti u bazu podataka. Da bi znali kojem timu pripada student, formi FrmNoviStudent ćemo proslijediti selektirani tim s forme FrmPopisStudenata.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
168
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka III - ORM
Slika 107: Dodavanje novog studenta
Kôd 102: Kreiranje novog studenta private Tim selektiraniTim;
//Atribut u koji ćemo pohraniti selektirani tim.
public FrmNoviStudent(Tim tim) { InitializeComponent(); selektiraniTim = tim; }
//Formi prosljeđujemo selektirani tim.
private void btnUredu_Click(object sender, EventArgs e) { using (var db = new EvidencijaStudenataEntities()) { //S obzirom da objekt selektiraniTim nije kreiran u //tekućem kontekstu, moramo ga "registrirati". db.Tim.Attach(selektiraniTim); Student student = new Student //Kreiramo novog studenta { Ime = txtIme.Text, Prezime = txtPrezime.Text, Status = txtStatus.Text, OdabraniModel = txtOdabraniModel.Text, Napomena = txtNapomena.Text, Email = txtEmail.Text, Tim = selektiraniTim };
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
169
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka III - ORM
db.Student.Add(student); db.SaveChanges();
//Dodajemo studenta u kolekciju. //Spremamo promjene u bazu podataka.
} Close(); }
Da bi novokreirani student bio prikazan u popisu studenata potrebno je ažurirati tablicu studenti na formi FrmPopisStudenata. Također, prilikom pozivanja forme za dodavanje novog studenta, potrebno je proslijediti selektirani tim. Sve to ćemo napraviti u metodi btnNoviStudent_Click forme FrmPopisStudenata. Kôd 103: Ažuriranje popisa studenata nakon dodavanja novog studenta /// /// Rukuje događajem klika na gumb za dodavanje novog studenta. /// private void btnNoviStudent_Click(object sender, EventArgs e) { FrmNoviStudent forma = new FrmNoviStudent(timBindingSource.Current as Tim); forma.ShowDialog(); PrikaziStudente(timBindingSource.Current as Tim); }
Izmjena postojećeg entitetnog objekta Da bi pokazali način izmjene postojećeg entitetnog objekta u Entity Framework-u napravit ćemo mogućnost izmjene postojećeg. Kada selektiramo tim iz popisa timova na formi FrmPopisStudenata i kliknemo na gumb izmijeni, otvorit će nam se forma za izmjenu podataka o timu. Ovdje ćemo koristiti istu formu kao i za dodavanje timova (FrmNoviTim), ali dodatno prilagođenu za funkcionalnost izmjene tima. Da bi forma FrmNoviTim znala koji tim želimo izmijeniti, potrebno je proslijediti tim selektiran na formi FrmPopisStudenata. Za to će nam biti potreban još jedan konstruktor i jedan atribut za formu FrmNoviTim. Kada se pokrene forma za izmjenu tima, želimo popuniti TextBox kontrole na formi s podacima proslijeđenog tima, pa ćemo doraditi metodu Load. Na kraju, klikom na gumb Uredu korisnik želi spremiti promjene na postojećem timu te ćemo doraditi i metodu btnUredu_Click. Kôd 104: Dorada forme FrmNoviTim za funkcionalnost izmjene podataka o timu private Tim timZaIzmjenu; /// /// Konstruktor forme koji koristimo za izmjenu postojećeg tima. /// /// Tim koji treba izmijeniti. public FrmNoviTim(Tim tim) {
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
170
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka III - ORM
InitializeComponent(); timZaIzmjenu = tim; } /// /// Rukuje događajem Load pokretanja forme. /// private void FrmNoviTim_Load(object sender, EventArgs e) { txtOznakaTima.Focus(); if (timZaIzmjenu != null) { txtId.Text = timZaIzmjenu.Id.ToString(); txtOznakaTima.Text = timZaIzmjenu.OznakaTima; txtNazivProjekta.Text = timZaIzmjenu.NazivProjekta; txtOpisProjekta.Text = timZaIzmjenu.OpisProjekta; txtNapomena.Text = timZaIzmjenu.Napomena; } } /// /// Rukuje događajem klika na gumb Uredu. /// private void btnUredu_Click(object sender, EventArgs e) { using (var db = new EvidencijaStudenataEntities()) { if (timZaIzmjenu == null) { //Kreiramo novi objekt klase Tim i popunjavamo ga //podacima sa forme. Tim tim = new Tim { OznakaTima = txtOznakaTima.Text, NazivProjekta = txtNazivProjekta.Text, OpisProjekta = txtOpisProjekta.Text, Napomena = txtNapomena.Text }; db.Tim.Add(tim); //Dodajemo tim u odgovarajuću kolekciju db.SaveChanges(); //Spremamo napravljene promjene u bazu podataka. } else //Mijenjamo postojeći tim { db.Tim.Attach(timZaIzmjenu); //registriramo prosljeđeni tim. timZaIzmjenu.OznakaTima = txtOznakaTima.Text; timZaIzmjenu.NazivProjekta = txtNazivProjekta.Text; timZaIzmjenu.OpisProjekta = txtOpisProjekta.Text; timZaIzmjenu.Napomena = txtNapomena.Text; db.SaveChanges();
//Spremamo promjene u bazu.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
171
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka III - ORM
} } Close(); }
Osim dorade forme FrmNoviTim potrebno je dodati metodu za rukovanje klikom na gumb Izmijeni na formi FrmPopisStudenata. Unutar te metode ćemo dohvatiti selektirani tim te pozvati formu za izmjenu tog tima. Kôd 105: Pozivanje forme za izmjenu tima /// /// Rukuje događajem klika na gumb Izmijeni. /// private void btnIzmijeniTim_Click(object sender, EventArgs e) { Tim selektiraniTim = timBindingSource.Current as Tim; if (selektiraniTim != null) { FrmNoviTim forma = new FrmNoviTim(selektiraniTim); forma.ShowDialog(); PrikaziTimove(); } }
Brisanje postojećeg entitetnog objekta Brisanje postojećeg entitetnog objekta je jednostavna operacija, a svodi se na uklanjanje objekta iz konteksta, odnosno njegove kolekcije u kontekstu te pozivanje metode SaveChanges za spremanje promjena u bazu. To ćemo pokazati na primjeru brisanja tima i studenta. Da bi korisnik izbrisao tim mora ga selektirati u popisu timova na formi FrmPopisStudenata, te kliknuti na gumb Obriši. S obzirom na to da tim može sadržavati studente u kolekciji studenata, prije brisanja tima moramo provjeriti nalaze li se u njemu studenti. Ako tim ima studente, obavijestit ćemo korisnika da nije moguće obrisati tim. Kôd 106: Brisanje tima na formi FrmPopisStudenata /// /// Rukuje događajem klika na gumb za brisanje tima. /// private void btnObrisiTim_Click(object sender, EventArgs e) { Tim selektiraniTim = timBindingSource.Current as Tim; if (selektiraniTim != null) { if (MessageBox.Show("Da li ste sigurni?", "Upozorenje!", MessageBoxButtons.YesNo) == System.Windows.Forms.DialogResult.Yes) { using (var db = new EvidencijaStudenataEntities())
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
172
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka III - ORM
{ db.Tim.Attach(selektiraniTim); //Registriramo tim. //Provjeravamo da li tim sadrži studente. if (selektiraniTim.Student.Count == 0) { db.Tim.Remove(selektiraniTim); //Brišemo tim.. db.SaveChanges(); //Spremamo promjene u bazu. } else { MessageBox.Show("Nije moguće obrisati tim koji sadrži studente!"); } } PrikaziTimove();
//Ažuriramo popis timova.
} } }
10.3. Pitanja i zadaci Samostalni zadatak Napravite mogućnost izmjene podataka za studenta te mogućnost brisanja studenta. Napravite ostale opcije aplikacije koje su definirane u korisničkim zahtjevima. Pitanja 1. Objasnite koncept ORM-a. Koje su njegove prednosti? 2. Što je to entitetni model podataka (eng. Entity Data Model – EDM), od čega se on sastoji? 3. Objasnite konceptualni model u EF-u. Od čega se sastoji i gdje ga u projektu možemo naći? 4. Objasnite model pohrane u EF-u. Od čega se sastoji i gdje ga u projektu možemo naći? 5. Objasnite model preslikavanja. Od čega se sastoji i gdje ga u projektu možemo naći? 6. Koja je uloga sloja objektnih usluga u EF-u? 7. Koja je uloga sloja entitetnog klijenta u EF-u? 8. Objasnite ulogu ADO.NET tehnologije u EF-u. 9. Koji je glavni upitni jezik u Entity Framework-u? 10. Koji tokovi rada (eng. Workflows) postoje u Entity Framework-u i kada ih koristimo? 11. Objasnite razliku između Top-Down i Bottom-Up pristupa. 12. Koje vrste svojstava sadrži klasa u Entity Framework-u? 13. Što je to kontekstna klasa i čemu služi? 14. Objasnite povezani i nepovezani scenarij rada s kontekstnom klasom. © University of Zagreb, Faculty of Organization and Informatics, Varaždin
173
Odabrana poglavlja programskog inženjerstva – razvoj programa Rad s bazom podataka III - ORM
10.4. Više informacija o temi 1. MSDN Get started with Entity Framework (EF) (http://msdn.microsoft.com/enus/data/ee712907), dostupno: prosinac, 2013. 2. MSDN Data Development Technical Articles (http://msdn.microsoft.com/en-us/data/aa937699), dostupno: prosinac, 2013. 3. Entity Framework Tutorial (http://entityframeworktutorial.net/), dostupno: prosinac, 2013. 4. Entity Framework in WinForms (http://www.codeproject.com/Articles/221931/EntityFramework-in-WinForms), dostupno: prosinac, 2013. 5. Youtube Entity Framework tutorial Part 1 (http://www.youtube.com/watch?v=BS6IKdUd2V8), Part 2 (http://www.youtube.com/watch?v=5RgL5O28B58), Part 3 (http://www.youtube.com/watch?v=iQqzTr-1waE), Part 4 (http://www.youtube.com/watch?v=OTasIwcXNyE), Part 5 (http://www.youtube.com/watch?v=tAJCwY-sClc), Part 6 (http://www.youtube.com/watch?v=tAJCwY-sClc), dostupno: prosinac, 2013.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
174
Odabrana poglavlja programskog inženjerstva – razvoj programa Testiranje programa
11. Testiranje programa Sažetak U ovom poglavlju opisana je tema testiranja, njezina uloga i važnost u cjelokupnom životnom ciklusu softvera. Na praktičnim primjerima prikazane su neke od integriranih mogućnosti Visual Studio-a. Spominju se jedinični testovi kao vrsta testiranja podržana u fazi razvoja softvera, te kodirani funkcionalni testovi koji spadaju u testiranja u fazi izdavanja programskog rješenja. Ključne riječi: Testing, Unit Tests, Coded UI Tests, Test Driven Development
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
175
Odabrana poglavlja programskog inženjerstva – razvoj programa Testiranje programa
11.1. Uvod Testiranje se provodi u cilju procjene i unaprjeđenja kvalitete proizvoda, tako da identificiramo probleme i greške. Testiranje programa je proces kojim želimo pokazati da program radi ono što bi trebao raditi. U skladu s time, možemo reći da testiranje ima dva cilja: -
Validacijsko testiranje (eng. validation testing): demonstrirati programerima ali i korisnicima da program ispunjava definirane funkcionalne i nefunkcionalne zahtjeve. Ovdje definiramo skup testnih slučajeva (npr. jedan test za svaki zahtjev) koji reflektiraju očekivano ponašanje programa.
-
Testiranje grešaka (eng. defect testing): pronaći situacije i scenarije u kojima se program ponaša netočno, nepoželjno ili ne ispunjava neki od zahtjeva. Za razliku od validacijskog testiranja ovdje namjerno pokušavamo iscenirati greške, često koristeći program i na neuobičajen način.
Prirodu testiranja vrlo dobro ocrtava čuvena izjava Edsgera Dijskstre: „Testing can only show the presence of errors, not their absence“. Što znači da koliko god testirali nikad nećemo moći dokazati da sustav nema greške, jedino što možemo testiranjem dokazati je da sustav ima greške. Naravno to nije izgovor da se testiranje ne provodi. Verifikacija i validacija Testiranje je dio jednog šireg procesa u razvoju softvera koji se naziva verifikacija i validacija. Pod verifikacijom podrazumijevamo utvrđivanje da program zadovoljava funkcionalne i nefunkcionalne zahtjeve. S druge strane, validacija pokazuje zadovoljava li program očekivanja korisnika (ovdje glavnu riječ ima korisnik). Ili kako je to jedan od pionira programskog inženjerstva Barry Boehm izrekao: -
„Validation: Are we building the right product?“,
-
„Verification: Are we building the product right?“.
Danas se testiranje programa vrlo često zanemaruje iako o važnosti testiranja svjedoče mnogi, nažalost, negativni primjeri. Između ostalog o tome koliko je testiranje važno govori i činjenica da je ono na jedan ili drugi način uključeno u sve metodike razvoja softvera. Npr. u klasičnom vodopadnom modelu, testiranju je posvećena cijela jedna faza razvoja. U inkrementalnom/iterativnom razvoju, testiranje se provodi i dokumentira na kraju svake iteracije. U agilnim metodikama se vrlo često primjenjuje radikalan pristup – razvoj upravljan testovima (eng. Test Driven Development). Naravno, opsežnost testiranja programa ovisi o mnogim faktorima, pa tako ovisi o: -
složenosti programa (npr. jednostavan CRUD program nasuprot algoritamski zahtjevnog programa),
-
veličini programa (adresar nasuprot informacijskog sustava proizvodnje),
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
176
Odabrana poglavlja programskog inženjerstva – razvoj programa Testiranje programa
-
vrsti aplikacije (aplikacija za upravljanje osobnim kontaktima nasuprot medicinskih aplikacija).
-
... TDD – Test Driven Development
Kao što je već rečeno razvoj upravljan testovima se vrlo često koristi u agilnim metodikama razvoja softvera. U ovom pristupu radimo obrnuto od tradicionalnog razvojnog procesa. Naime, u TDD-u prvo definiramo testove, a zatim pišemo kôd koji te testove zadovoljava. Funkcionalnost je gotova kada su svi testovi zadovoljeni.
11.2. Faze u testiranju Programi koje razvijamo često su vrlo složeni, pa tako i proces testiranja programa može biti vrlo složen i izvoditi se u nekoliko faza. Tipičan softver bi trebao/mogao proći kroz 3 faze testiranja: -
Testiranje u razvoju (eng. development testing) – testiraju se dijelovi kôda od strane programera prilikom razvoja, kako bi se otkrile greške.
-
Testiranje pri izdavanju (eng. release testing), često se zove i funkcionalno testiranje – različiti timovi testiraju gotovi sustav prije nego se izda korisnicima. Cilj je provjeriti zadovoljava li sustav sve definirane korisničke zahtjeve.
-
Korisničko testiranje (eng. user testing) – korisnici ili potencijalni korisnici testiraju sustav u svom okruženju.
Testiranje u razvoju Testiranje u razvoju obuhvaća sve one aktivnosti testiranja provedene od strane tima koji razvija softver. Ovisno o ustroju tima to može biti programer ili specijalizirani tester. U ovoj fazi možemo govoriti o 3 razine granularnosti: -
Jedinično testiranje (eng. unit testing) objekata i metoda objekata,
-
Testiranje komponenata (eng. component testing) – nekoliko jedinica je integrirano u veću cjelinu – komponentu te je potrebno testirati sučelja te komponente.
-
Testiranje sustava (eng. system testing) – komponente se integriraju u cjeloviti sustav, pa je potrebno testirati sustav u smislu testiranja međudjelovanja komponenata.
usredotočuje se na testiranje funkcionalnosti
Testiranje pri izdavanju Ova faza podrazumijeva testiranje pojedine verzije aplikacije prije nego će biti isporučena nekome izvan razvojnog tima. Taj netko može biti krajnji korisnik ili neki drugi razvojni tim. Glavni cilj testiranja prilikom izdavanja verzije je uvjeriti korisnika da je aplikacija dovoljno dobra za korištenje, da zadovoljava definirane zahtjeve i da ispravno radi. © University of Zagreb, Faculty of Organization and Informatics, Varaždin
177
Odabrana poglavlja programskog inženjerstva – razvoj programa Testiranje programa
S obzirom na to da se ovdje aplikacija promatra kao crna kutija, kod koje su nam samo bitne funkcionalnosti, ova vrsta testiranja se naziva i funkcionalno testiranje. U nastavku ćemo vidjeti da se testiranje funkcionalnih zahtjeva može u određenoj mjeri i automatizirati. Korisničko testiranje Korisničko testiranje je zadnja faza testiranja u kojoj korisnici u svom okruženju testiraju gotovu aplikaciju. To testiranje može biti formalno specificirano ili se može odvijati implicitno, tako da korisnici eksperimentiraju s aplikacijom i daju povratnu informaciju o dojmu, novim zahtjevima i eventualnim greškama. U ovoj fazi imamo 3 tipa korisničkog testiranja: -
Alfa testiranje (eng. alpha testing) – testiranje obično vrše članovi razvojnog tima ili krajnji korisnici, ali usko surađujući s razvojnim timom,
-
Beta testiranje (eng. beta testing) – aplikacija se daje na korištenje krajnjim korisnicima, koji onda eksperimentiraju s aplikacijom i prijavljuju pronađene greške razvojnom timu,
-
Testiranje prihvaćanja (eng. acceptance testing) – aplikaciju testiraju krajnji korisnici i daju sud o tome je li aplikacija spremna za isporuku.
11.3. Izrada jediničnih testova U nastavku ćemo pokazati primjer testiranja aplikacije na jednostavnoj aplikaciji koja računa svojstva različitih geometrijskih objekata (površina, opseg, dijagonala i sl.). U fazi razvoja ćemo to napraviti preko jediničnih testova, a u fazi testiranja pri izdavanju preko tzv. kodiranih UI testova (eng. coded UI tests). Logika aplikacije je gotova, te je sada potrebno napraviti testove koji će provjeriti ispravnost funkcioniranja aplikacije.
Izrada testnog projekta Visual Studio podržava testiranje stanja i ponašanja objekata preko jediničnih testova. Jedinični testovi se kreiraju u posebnom projektu, te ćemo stoga u našem rješenju kreirati novi projekt (Slika 108): -
U Solution Exploreru kliknite desnom tipkom miša na rješenje „Lab_3_2_Testiranje“,
-
odaberite Add -> New Project,
-
U ponuđenim predlošcima odaberite Visual C# -> Test -> Unit test project,
-
Projekt nazovite JedinicnoTestiranje i kliknite OK.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
178
Odabrana poglavlja programskog inženjerstva – razvoj programa Testiranje programa
Slika 108: Dodavanje testnog projekta
Visual Studio je sada kreirao novi testni projekt u koji ćemo dodavati jedinične testove. S obzirom na to da s jediničnim testovima provjeravamo ispravnost stanja i ponašanja objekata, za svaku klasu koju budemo htjeli testirati, kreirat ćemo testnu klasu. Analogno tome, za svaku metodu klase koju testiramo, kreirat ćemo testnu metodu u testnoj klasi. Da bi u testnim klasama mogli pristupiti klasama koje ćemo kreirati, moramo dodati referencu projekta „Lab_3_2_Testiranje“ u testni projekt. To ćemo napraviti na sljedeći način (Slika 109): -
U Solution Exploreru kliknite desnom tipkom miša na projekt JedinicnoTestiranje te odaberite opciju Add Reference,
-
Pod opcijom Solution->Projects stavite kvačicu na projekt Lab_3_2_Testiranje,
-
Kliknite Ok.
Slika 109: Dodavanje reference na glavni project
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
179
Odabrana poglavlja programskog inženjerstva – razvoj programa Testiranje programa
Izrada testne klase i testnih metoda U glavnom projektu se nalazi klasa Pravokutnik u kojoj su implementirane metode za izračunavanje dijagonale, površine i opsega pravokutnika. Da bi testirali tu klasu, u projektu JedinicnoTestiranje ćemo dodati njenu testnu klasu: -
U Solution Exploreru kliknite desnom tipkom miša na projekt JedinicnoTestiranje,
-
Odaberite Add->Unit Test,
-
U Solution Exploreru preimenujte novokreiranu klasu u „PravokutnikTest.cs“,
-
U dijalogu koji se pojavio odaberite Yes.
Klasa PravokutnikTest koju smo sada kreirali će nam služiti za testiranje klase Pravokutnik. Klasa pravokutnik ima 3 metode koje ćemo testirati: IzracunajPovrsinu, IzracunajOpseg i IzracunajDijagonalu. Zato ćemo i u klasi PravokutnikTest dodati odgovarajuće metode za testiranje metoda klase Pravokutnik. Krenut ćemo s metodom za testiranje izračuna dijagonale pravokutnika. Kôd 107: Metoda za testiranje izračuna dijagonale /// /// Testna metoda za testiranje izračuna dijagonale pravokutnika. /// [TestMethod] public void IzracunajDijagonaluTest() { Pravokutnik p1 = new Pravokutnik(0, 0); float d1 = p1.IzracunajDijagonalu(); Assert.AreEqual(0, d1, "Izračun dijagonale nije točan!"); Pravokutnik p2 = new Pravokutnik(-5, 1); float d2 = p2.IzracunajDijagonalu(); Assert.IsTrue(d2 >= 0, "Dijagonala mora biti veća od nule!"); Pravokutnik p3 = new Pravokutnik(4, 3); float d3 = p3.IzracunajDijagonalu(); Assert.AreEqual(5, d3, "Izračun dijagonale nije točan!"); }
Primijetite da metoda ima definiran TestMethod atribut koji zapravo predstavlja metapodatak o metodi koji Visual Studio-u govori da se radi o testnoj metodi i da je treba uzeti u obzir prilikom testiranja. Također, i sama klasa PravokutnikTest ima definiran sličan atribut (TestClass). Ako bi te atribute maknuli, klasa/metoda ne bi bile uzete u obzir prilikom testiranja. Naravno, u testne klase možemo staviti metode koji nisu testne, nego nam predstavljaju pomoćne metode koje se npr. pokreću u testnim metodama. Osim toga potrebno je napomenuti da su testne metode uvijek tipa void te da nemaju parametara.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
180
Odabrana poglavlja programskog inženjerstva – razvoj programa Testiranje programa
Ako pogledamo implementaciju metode IzracunajDijagonaluTest možemo prepoznati tri operacije: -
Instanciranje klase Pravokutnik i prosljeđivanje stranica pravokutnika konstruktoru,
-
Izračunavanje dijagonale pravokutnika,
-
Uspoređivanje (uz pomoć klase Assert) očekivane vrijednosti i stvarne izračunate vrijednosti dijagonale. Klasa Assert
Klasa Assert je statička klasa koja služi za provjeru uvjeta u jediničnim testovima. Ona sadrži veliki broj statičkih metoda koje na određene načine provjeravaju valjanost rezultata. Neke od metoda koje klasa Assert sadrži su: AreEqual, AreNotEqual, AreSame, AreNotSame, IsFalse, IsTrue, IsNull, IsInstanceOfType...
Primijetite da smo u metodi IzracunajDijagonaluTest zapravo testirali metodu IzracunajTest u više scenarija, odnosno za više ulaznih vrijednosti. Kod jediničnih testova nije moguće napisati test za svaki ulazni parametar, zato je vrlo bitno odabrati one ulazne parametre metode koji će pokriti što veći broj scenarija (npr. posebno obratiti pažnju na „granične vrijednosti“, negativne vrijednosti, null vrijednosti i sl.). Pogledajmo sada kako izgledaju i ostale dvije testne metode za klasu Pravokutnik: Kôd 108: Metoda za testiranje izračuna površine i opsega /// /// Testna metoda za testiranje izračuna površine pravokutnika. /// [TestMethod] public void IzracunajPovrsinuTest() { Pravokutnik p1 = new Pravokutnik(0, 0); float povrsina1 = p1.IzracunajPovrsinu(); Assert.AreEqual(0, povrsina1, "Izračun površine nije točan!"); Pravokutnik p2 = new Pravokutnik(-1, 5); float povrsina2 = p2.IzracunajPovrsinu(); Assert.IsTrue(povrsina2 >= 0, "Površina mora biti veća od nule!"); Pravokutnik p3 = new Pravokutnik(5, 6.5f); float povrsina3 = p3.IzracunajPovrsinu(); Assert.AreEqual(32.5f, povrsina3, "Izračun površine nije točan!"); } /// /// Testna metoda za testiranje izračuna opsega pravokutnika. /// [TestMethod] public void IzracunajOpsegTest()
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
181
Odabrana poglavlja programskog inženjerstva – razvoj programa Testiranje programa
{ Pravokutnik p1 = new Pravokutnik(0, 0); float opseg1 = p1.IzracunajOpseg(); Assert.AreEqual(0, opseg1, "Izračun opsega nije točan!"); Pravokutnik p2 = new Pravokutnik(-1, 5); float opseg2 = p2.IzracunajOpseg(); Assert.IsTrue(opseg2 >= 0, "Opseg mora biti veći od nule!"); Pravokutnik p3 = new Pravokutnik(5, 6.5f); float opseg3 = p3.IzracunajOpseg(); Assert.AreEqual(23, opseg3, "Izračun opsega nije točan!"); }
Pravila imenovanja Dobra praksa imenovanja testnih klasa i metoda nalaže da se testna klasa nazove isto kao i klasa koja se testira, ali uz dodatak sufiksa Test. Na isti način se imenuju i testne metode. Tako u našem primjeru imamo klasu Pravokutnik za koju smo definirali testnu klasu PravokutnikTest te npr. metodu IzracunajDijagonalu za koju smo definirali testnu metodu IzracunajDijagonaluTest.
Izvršavanje jediničnih testova Sada kada smo definirali jedinične testove za klasu Pravokutnik, možemo ih pokrenuti i provjeriti prolaze li metode klase testove. To možemo učiniti preko izbornika Test tako da odaberemo opciju Run -> All Tests (Slika 110).
Slika 110: Pokretanje testova
Pokretanje testova u Run i Debug modu Naravno, kao što je vidljivo iz izbornika nije nužno pokrenuti sve testove, već je moguće pokrenuti samo selektirane testove (samo testove koji su u prethodnom pokretanju pronašli grešku, samo testove koji do sad još nikad nisu pokretani i sl.). Osim u Run modu testove je moguće pokrenuti i u Debug modu, što znači da testove možemo debuggirati kao i sav drugi kôd, zaustaviti izvršavanje testa, izvoditi liniju po liniju kôda i sl.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
182
Odabrana poglavlja programskog inženjerstva – razvoj programa Testiranje programa
Nakon što se testovi izvrše Visual Studio će rezultate testiranja prikazati u panelu Test Explorer (ako se ne prikaže automatski, moguće ga je otvoriti kroz Test>Windows->Test Explorer). Ako test nije zadovoljen sustav će generirati iznimku AssertFailedException, te prikazati taj test u skupini testova koji nisu prošli (eng. failed tests). S druge strane, testovi koji su prošli, svrstavaju se u skupinu uspješnih testova (eng. passed tests). Ako pogledamo kakva je situacija s naše tri testne metode možemo vidjeti da su sve tri pale na testu (Slika 111). Pokrenite ponovno testove, ali ovaj put u Debug modu, istražite razloge zbog kojih testovi nisu zadovoljili, te ispravite greške u klasi Pravokutnik tako da svi testovi zadovolje.
Slika 111: Rezultati izvršavanja testova
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
183
Odabrana poglavlja programskog inženjerstva – razvoj programa Testiranje programa
11.4. Izrada kodiranih UI testova Uz pomoć kodiranih UI testova u Visual Studio-u možemo automatizirati funkcionalno testiranje aplikacije, tj. testiranje scenarija korištenja aplikacija. Ovu vrstu testova možemo demonstrirati tako da testiramo scenarij unosa podataka o pravokutniku (unos stranica pravokutnika) i izračunavanja geometrijskih svojstava pravokutnika (dijagonala, površina, opseg). Kodirani UI testovi će nam zapravo omogućiti snimanje scenarija korištenja aplikacije u obliku tzv. makroa, i kasnije automatsko reproduciranje tih makroa u svrhu testiranja. Za početak potrebno je dodati kodirani UI test. Iako za kodirane UI testove možemo kreirati i poseban testni projekt, iskoristit ćemo postojeći testni projekt kreiran za potrebe jediničnih testova. Kodirani UI test ćemo dodati na sljedeći način: -
U Solution Exploreru kliknite desnom tipkom miša na testni projekt „JedinicniTestovi“ te u izborniku odaberite Add->Coded UI Test,
-
U dijalogu koji se pojavi ostavite zadanu opciju (Record Actions, edit UI map or add assertions) i kliknite OK (Slika 112),
Slika 112: Odabir načina izrade kodiranih UI testova
-
Visual Studio kreira potrebnu infrastrukturu za kodirane UI testove te nam prikazuje traku alata za snimanje testova,
-
U Visual Studio pokrenite aplikaciju (Debug>Start Without Debugging) i kada se prikaže početna forma kliknite na gumb Start Recording u traci s alatima za snimanje,
-
Kliknite na gumb Pravokutnik,
-
Unesite podatke o dužini stranica A i B u formi koja se otvorila (stranica A = 3, Slika 113: a) UIMap –Coded UI Test Builder, b) Start stranica B = 4), i kliknite Izračunaj, Recording, c) Pause Recording, d) Generate Code
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
184
Odabrana poglavlja programskog inženjerstva – razvoj programa Testiranje programa
-
U alatnoj traci za snimanje kliknite na gumb Pause Recording (Slika 113 c),
-
U alatnoj traci za snimanje kliknite na gumb Generate Code (Slika 113 d) ,
-
Upišite naziv i Add and Generate
opis
metode
koja
će
se
generirati
te
kliknite
na
gumb
Slika 114: Generiranje metode za testiranje izračuna
Visual Studio je sada generirao metodu testiranja za snimljeni scenarij korištenja aplikacije (Slika 114). Međutim, trenutno ta metoda ne provjerava jesu li vrijednosti dobivene izračunom točne. Zato ćemo prije nego pokrenemo test dodati još jednu metodu koja će provjeriti ispravnost samih vrijednosti izračuna: -
U alatnoj traci za snimanje testova kliknite na gumb Add Assertions i povucite ga na textbox za prikaz dijagonale,
-
Pojavit će se panel sa svojstvima, pronađite svojstvo Text, napravite desni klik mišem na svojstvo te odaberite opciju Add Assertion,
-
Definirajte da u textboxu treba pisati vrijednost „5“ (Slika 115 b) te kliknite OK,
-
Na isti način definirajte odgovarajuće testne vrijednosti i za texboxove za površinu i opseg pravokutnika,
-
U alatnoj traci za snimanje testova odaberite opciju Generate Code, definirajte ime metodi (PravokutnikIzracunAssert) i opis te kliknite Add and Generate.
-
Zatvorite aplikaciju.
Visual Studio je sada generirao i metodu za provjeru vrijednosti izračuna te ju je zajedno s metodom PravkutnikIzracun, pozvao unutar metode CodedUITestMethod1 klase CodedUITest1.
Slika 115: a) Add Assertions, b) Add assertion for: Text
Prije nego možemo pokrenuti ove testove, moramo kompilirati aplikaciju. Kada smo to napravili u Test Exploreru možemo vidjeti novu testnu metodu CodedUITestMethod1. Da bi je pokrenuli trebamo napraviti sljedeće korake: © University of Zagreb, Faculty of Organization and Informatics, Varaždin
185
Odabrana poglavlja programskog inženjerstva – razvoj programa Testiranje programa
-
Pokrenuti aplikaciju kroz Debug-> Start Without Debuggin (Ctrl+F5) opciju,
-
U Test Exploreru napravimo desni klik na CodedUITestMethod1, te odaberimo opciju Run Selected Tests,
-
Visual Studio će pokrenuti test, i scenarij korištenja koji smo snimili će se odvijati bez naše intervencije.
Sada možemo vidjeti kako Visual Studio sam ponavlja one iste korake u korištenju aplikacije koje smo mi napravili: pokreće formu FrmPravokutnik, unosi podatke, izračunava podatke, zatvara formu FrmPravokutnik te zatvara aplikaciju. Nakon zatvaranja aplikacije u Test Exploreru bi ovaj test trebao biti u kategoriji testova koji su zadovoljeni (Slika 116).
Slika 116: Aplikacija je prošla test
Prilikom izrade stvarnih aplikacija, vrlo često se rade izmjene ili nadogradnje funkcionalnosti aplikacije. Ponekad se desi da izmjena jedne funkcionalnosti ili ispravka jedne greške, uzrokuje grešku u funkcioniranju drugog dijela aplikacije. Teoretski bi trebalo nakon svake izmjene aplikacije napraviti detaljno testiranje. Kod većih aplikacija je to vrlo zahtjevan posao i zahtijeva mnogo vremena. U takvim slučajevima je posjedovanje testova koje pokrivaju cijelu funkcionalnost aplikacije vrlo korisno. Simulirajte jednu takvu potencijalnu situaciju, npr. otkačite Event Handler metodu s gumba za pokretanje forme FrmPravokutnik ili s gumba Izračunaj. Ili, napravite namjernu pogrešku u metodama za računanje geometrijskih svojstava pravokutnika. Nakon toga ponovno pokrenite test i analizirajte rezultate testa. Opcija Recorded Steps Prilikom snimanja CodedUI testova dostupna vam je opcija Recorded Steps, preko koje možete pregledati korake scenarija korištenja koji su do tada zabilježeni (Slika 117). Osim toga u klasi UIMap.cs možete vidjeti konkretan kôd metoda koje opisuju korake snimljenih scenarija i naravno taj kôd po potrebi doraditi. Slika 117: Recorded Steps
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
186
Odabrana poglavlja programskog inženjerstva – razvoj programa Testiranje programa
11.5. Pitanja i zadaci Samostalni zadatak Implementirajte jedinične testove za klase Krug, Kvadrat, i Trokut. Ako pronađete greške, ispravite ih tako da klase zadovolje testove. Implementirajte kodirane UI testove za ostale scenarije korištenja aplikacije – izračunavanje geometrijskih svojstava za Krug, Kvadrat, Trokut. Pitanja 1. Što je to testiranje i zašto se provodi? 2. Koja je razlika između validacijskog testiranja i testiranja grešaka? 3. Što podrazumijevamo pod verifikacijom, a što pod validacijom? 4. Što je to razvoj upravljan testovima (eng. Test Driven Development)? 5. Kako je testiranje zastupljeno u metodikama razvoja softvera? 6. Nabrojite faze u testiranju? 7. Objasnite fazu testiranja u razvoju (eng. development testing). Koje razine granularnosti postoje? 8. Što su to jedinični testovi i što testiramo pomoću njih? 9. Koja je razlika između testiranja komponenata i testiranja sustava? 10. Objasnite fazu testiranja pri izdavanju (eng. release testing). 11. Objasnite fazu korisničkog testiranja (eng. user testing). 12. Koja je razlika između alfa testiranja i beta testiranja? 13. Što je to testiranje prihvaćanja (eng. acceptance testing)? 14. Objasnite ulogu klase Assert u jediničnom testiranju. Nabrojite neke od njenih metoda. 15. Za što nam koriste kodirani UI testovi, što s njima testiramo?
11.6. Više informacija o temi 1. Ian Sommerville, Software Engineering, 9. izdanje, str. 205 – 233. 2. SWEBOK (Software Engineering Body of Knowledge), Software Testing (http://www.computer.org/portal/web/swebok), dostupno: prosinac, 2013. 3. Testing .NET Application Blocks, Microsoft (http://www.microsoft.com/enus/download/details.aspx?id=20988), dostupno: prosinac, 2013. 4. Get Started With Coded UI Tests (http://msdn.microsoft.com/en-us/vstudio/ee957688.aspx), dostupno: prosinac, 2013. 5. Verifying Code by Using Unit Tests (http://msdn.microsoft.com/en-us/library/dd264975.aspx), dostupno: prosinac, 2013.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
187
Odabrana poglavlja programskog inženjerstva – razvoj programa Vizualizacija podataka – rad s izvještajima i vlastitim kontrolama
12. Vizualizacija podataka – rad s izvještajima i vlastitim kontrolama Sažetak U ovom poglavlju opisuju se izvještaji, grafovi i vlastite kontrole. Izvještaji su posebna komponenta Visual Studio-a i softverskom inženjeru omogućuju kreiranje obrazaca koje puni podacima. Ti podaci najčešće su potrebni vodstvu organizacije ili su zakonski regulirani (primjerice račun i fiskalizacija). Također,
opisan je i Google Chart API za vizualizaciju
podataka. Na kraju se spominje napredniji razvoj te je prikazan postupak razvoja vlastite kontrole zajedno s Timer-om. Ključne riječi: Report viewer, rdlc, Google Chart API, UserControl
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
188
Odabrana poglavlja programskog inženjerstva – razvoj programa Vizualizacija podataka – rad s izvještajima i vlastitim kontrolama
12.1. Uvod Jedna od najjednostavnijih definicija informacijskog sustava je, da je to sustav koji prikuplja, pohranjuje, obrađuje i distribuira informacije. Na ovim vježbama bavit ćemo se distribucijom informacija odnosno poslovnim izvještavanjem.
12.2. Report viewer Izvještaji sadrže sažetke informacija potrebnih za donošenje odluka o daljnjem smjeru djelovanja organizacije, poslovnih timova ili pojedinaca. Osim toga, neki izvještaji zakonski su regulirani i svaka organizacija ih mora izdavati po propisanom obrascu. Komponenta Visual Studio Report Designer omogućuje kreiranje izvještaja prema podacima iz nekog izvora podataka. Izvještaji su zapisani u obliku .rdlc datoteka, a prikazuju se na kontroli Report viewer. Napravit ćemo novi Windows Forms projekt te ćemo na glavnu formu dodati kontrolu Report viewer.
Slika 118: Report Viewer kontrola
Kontrolu možemo podesiti prema vlastitom dizajnerskom odabiru, a nakon toga u gornjem desnom kutu kliknemo na trokut (Slika 118).
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
189
Odabrana poglavlja programskog inženjerstva – razvoj programa Vizualizacija podataka – rad s izvještajima i vlastitim kontrolama
Nakon toga u kontekstnom izborniku odaberemo „Design a new report“, pa odaberemo Database i na kraju DataSet. Kao što smo se do sada spajali na bazu podataka, tako ćemo se i ovaj put spojiti na već poznatu Northwind bazu podataka. Odabrat ćemo SQL File i pronaći je u direktoriju gdje se nalazi.
Slika 119: Pogledi u Northwind bazi podataka
Odabrat ćemo sve poglede koji su dostupni unutar Views, a od tablica Order i Order Details (Slika 119). Nakon uspješnog dodavanja baze podataka, pojavit će se čarobnjak za izradu izvještaja. U slučaju da se ne pojavi, možemo ga aktivirati ponovnim klikom na ranije spominjani trokut te odabrati opciju „Design a new report“. Slika 120 prikazuje kako ćemo urediti Izvještaj. Naziv ćemo staviti „dsReport“, a dataset će biti „Customer and Suppliers by City“.
Slika 120: Čarobnjak za izradu izvještaja
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
190
Odabrana poglavlja programskog inženjerstva – razvoj programa Vizualizacija podataka – rad s izvještajima i vlastitim kontrolama
U sljedećem prozoru imamo mogućnost odabira koje podatke želimo prikazati te na koji način (Slika 121). Označimo sve i odvučemo ih do prozora „Values“. Pa nakon toga odaberemo Next.
Slika 121: Odabir podataka za prikaz na izvještaju
Posljednji prozor nam nudi odabir izgleda izvještaja. Nakon što smo odabrali onaj koji nam vizualno najviše odgovara, odaberemo Next. Slika 122 prikazuje željeni izgled izvještaja.
Slika 122: Izgled izvještaja
Stoga najprije moramo dodati nekoliko fiksnih tekstualnih polja, sliku, zaglavlje i podnožje. Možemo primijetiti da je srednji dio, odnosno tablica već pripremljena za prikaz. Taj dio će se ovisno o količini stranica proširiti, dok će zaglavlje i podnožje uvijek biti fiksni. Najprije ćemo dodati zaglavlje i © University of Zagreb, Faculty of Organization and Informatics, Varaždin
191
Odabrana poglavlja programskog inženjerstva – razvoj programa Vizualizacija podataka – rad s izvještajima i vlastitim kontrolama
podnožje. To ćemo učiniti tako da u kontekstnom izborniku kojeg prikaže desni klik na bijelu površnu izvještaja, odaberemo Insert > Page Header, a nakon toga još jednom Insert > Page Footer. Sljedeći korak je dodavanje slike. Ponovno napravimo desni klik i ovaj puta odaberemo Insert > Image. Nakon toga otvara nam se novi prozor pomoću kojeg ćemo odrediti naziv slike i samu sliku pomoću opcije Import.
Slika 123: Dodavanje slike na izvještaj
Konačno, podešavanjem izvještaja, odnosno dodavanjem elemenata poput Line i TextBox posložit ćemo izvještaj (Slika 123). Unutar Report data pogleda moguće je odabrati i neka predefinirana polja poput broja stranica, no primijetite da je ta polja moguće koristiti samo na zaglavlju ili podnožju stranice. Time je izvještaj završen i možete pokrenuti aplikaciju da biste dobili konačan izgled.
12.3. Izrada složenijeg izvještaja Sada ćemo kreirati složeniji izvještaj koji će u zaglavlju prikazivati podatke iz jedne tablice baze podataka, dok će stavke biti podaci iz druge tablice baze podataka. Tipičan primjer takvog izvještaja je račun, narudžba, otpremnica, primka itd. Mi ćemo koristiti narudžbe. Napravit ćemo novi izvještaj, no ovaj put ćemo odabrati desni klik na projekt, a nakon toga Add > New Item, Reporting, Report (Slika 124). Taj ćemo izvještaj nazvati rptOrders.
Slika 124: Dodavanje izvještaja (drugi način)
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
192
Odabrana poglavlja programskog inženjerstva – razvoj programa Vizualizacija podataka – rad s izvještajima i vlastitim kontrolama
U polje DataSets ovaj put ćemo dodati dva nova izvora podataka, Orders i OrderDetails. S obzirom na to da ćemo primarni ključ narudžbe koristiti za dohvat svih stavki iste, najprije moramo kreirati novi filter, odnosno upit na TableAdapterima (Slika 125). To je tema koja je već obrađena, stoga samostalno kreirajte dva nova upita, za Order Details i za Orders koji će vraćati narudžbu i stavke prema proslijeđenoj šifri narudžbe.
Slika 125: Prikaz DataAdaptera
Nakon što su upiti dodani DataSet bi trebao prikazivati ovakve tablice. Najbolje je da nove metode nazovemo FillByOrderId. Sada se vraćamo na izvještaj i Report Data prozoru možemo dodati dva nova DataSeta-a (Slika 126) jedan za Order(dsOrders), a drugi za Order Details (dsOrderDetails).
Slika 126: Dodavanje DataSet-ova na izvještaj
Izgled izvještaja Najprije ćemo dodati novu tablicu (iz izbornika Toolbox odaberemo Table) i dodati joj vrijednosti koje su sadržane u DataSet-u dsOrderDetails. Ako nam ponestane kolona uvijek možemo dodati nove, tako da iz kontekstnog izbornika koji se pojavi prilikom desnog klika miša na tablicu odaberemo opciju Insert > Column, Right (Slika 127).
Slika 127: Dodavanje novih stupaca
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
193
Odabrana poglavlja programskog inženjerstva – razvoj programa Vizualizacija podataka – rad s izvještajima i vlastitim kontrolama
Nakon toga, iz DataSet-a jednostavno dovučemo vrijednosti koje će se prikazivati u tablici. Obratite pozornost na drugi redak tablice i tri linije ispred tog retka. Ta oznaka označava da će se unutar tog polja vrijednosti multiplicirati, odnosno puniti iz baze podataka. Zadnja kolona tablice obično sadrži sume (količina robe, cijena itd.). Kako te podatke ne spremamo u bazi podataka, već su to izvedene vrijednosti, moramo ih izračunati na izvještaju. Iznos (eng. amout), koji se dobije umnoškom jedinične cijene (eng. unit price), količine (eng. quantity) i popusta (eng. discount) (1-popust, jer imamo iznos u postocima). Da bismo to napravili dodat ćemo novu kolonu i nazvati je Amount, a kao njenu vrijednost ćemo napraviti izraz (Desni klik na polje, Expression). Vrijednosti možemo dohvaćati iz DataSet-a, no ostale matematičke izraze pišemo ručno.
Slika 128: Unos formule za izračun
Formatiranje redaka
Slika 129: Uređivanje vrijednosti
S obzirom na to da je UnitPrice izražen u novčanom iznosu, možemo napraviti formatiranje kojim će Visual Studio automatski urediti tekst kao iznos i dodati mu valutu (Slika 129). Da bismo to napravili, odaberemo Placeholder, desni klik, Placeholder Properties, a nakon toga odaberemo Number i kao tip Currency. Na isti način odaberemo za Discount (postotke) i za Amount (valutu). Sada ćemo dodati još jedan redak, ali izvan liste (lista je onaj dio koji će se puniti). Mi želimo da novi redak sadrži konačne vrijednosti total (Slika 130). © University of Zagreb, Faculty of Organization and Informatics, Varaždin
194
Odabrana poglavlja programskog inženjerstva – razvoj programa Vizualizacija podataka – rad s izvještajima i vlastitim kontrolama
Slika 130: Dodavanje redaka van grupe podataka koji se ponavljaju. Ako je unutar grupe tada će se ponavljati.
Nakon dodavanja novog retka, možemo se pozabaviti uređivanjem izgleda (Slika 131). U novi zadnji redak ćemo dodati TextBox u kojemu piše TOTAL, te novi Placeholder u kojem ćemo zapisati izraz za izračunavanje vrijednosti svih stavki (možemo odmah urediti izgled i formatiranje).
Slika 131: Konačan izgled tablice
Vrijednost za izraz na polju total (Slika 132) će izgledati identično kao za kolonu Amount, no ispred svega će imati operaciju „sum“ (s pripadajućim zagradama).
Slika 132: Izraz za računanje sume
Nakon toga dodamo zaglavlje i podnožje (Slika 133). Podatke iz zaglavlja ovaj put punimo iz DataSet-a koji se odnosi na Orders tablicu (a ne OrderDetails kao do sada).
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
195
Odabrana poglavlja programskog inženjerstva – razvoj programa Vizualizacija podataka – rad s izvještajima i vlastitim kontrolama
Slika 133: Konačan dizajn izvještaja
Prikaz izvještaja Na glavnu formu iznad do sad dodanog ReportView-a, dodati ćemo jedan ComboBox koji ćemo popuniti sa šiframa narudžbi iz tablice Orders. Nazovimo ga cmbOrderID, a na način koji je već poznati povežemo ga s bazom (Slika 134).
Slika 134: Povezivanje ComboBox kontrole na bazu podataka
U kôdu iza forme, kreirat će se događaj Load i popuniti DataSet za narudžbe. Kôd 109 private void frmReport_Load(object sender, EventArgs e) { this.ordersTableAdapter.Fill(this.northwndDataSet.Orders); }
Taj isti DataSet ne možemo koristiti za prikaz podataka na izvještaju jer će biti popunjen drugim podacima. DataSet za cmbOrderID će sadržavati sve šifre narudžbi, dok nam novi DataSet za izvještaj mora prikazati podatke samo za jednu određenu narudžbu. Da bismo najjednostavnije to riješili, napravit ćemo kopije DataSet-a i DataAdaptera koji trenutno postoje u projektu (Slika 135). Obično se nalaze na Design prikazu forme, pri dnu. Dakle, označimo northwindDataSet i ordersTableAdapter i kopiramo ih.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
196
Odabrana poglavlja programskog inženjerstva – razvoj programa Vizualizacija podataka – rad s izvještajima i vlastitim kontrolama
Slika 135: Kopiranje DataSet-a i TableAdapter-a
Sada ćemo za izvještaj podesiti drugi BindingSource tako da kliknemo na mali trokut u desnom kutu ReportView kontrole i odaberemo opciju Choose Data Sources, a nakon toga za DataSet narudžbi odaberemo još jednom tablicu Orders. Visual Studio će sam kreirati ordersBindingSource1 (Slika 136).
Slika 136: Izbor izvora podataka za izvještaj
Za taj novi ordersBindingSource, kao DataSource odabrat ćemo northwindDataSet1, koji se nalazi unutar prozora Properties (Slika 137).
Slika 137: Promjena DataSource-a za ordersBindingSource1
Sada samo moramo povezati čitavi kôd i napuniti DataSet-ove podacima. Kôd 110 private void frmReport_Load(object sender, EventArgs e) { this.ordersTableAdapter.Fill(this.northwndDataSet.Orders);
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
197
Odabrana poglavlja programskog inženjerstva – razvoj programa Vizualizacija podataka – rad s izvještajima i vlastitim kontrolama
} // na promjenu odabrane vrijednosti unutar ComboBox-a moraju se popuniti DataSetovi da prikažu nove podatke. Koristimo FillByOrderId, a za Izvještaje koristimo drugačji DataSet i drugačiji TableAdapter, nego onaj za ComboBox private void comboBox1_SelectedValueChanged(object sender, EventArgs e) { if (cmbOrderId.SelectedValue != null) { int orderID = (int)cmbOrderId.SelectedValue; this.Order_DetailsTableAdapter.FillByOrderId(this.northwndDataSet.Order_Details, orderID); this.ordersTableAdapter1.FillByOrderId(this.northwndDataSet1.Orders, orderID); } this.rptReport.RefreshReport(); }
Slika 138: Konačan prikaz izvještaja
12.4. Google Chart API Google Chart API je biblioteka koja nam omogućuje vizualizaciju podataka u različitim oblicima poput, linijskog grafa, pita-grafa, grafa raspršenosti itd. To je lijepi dodatak na analitiku stoga ćemo ovdje pokazati kako možemo jednostavno primijeniti Google Chart API na naše podatke. Prvi korak je preuzimanje biblioteke treće strane (treća strana, nismo je razvili mi ni Microsoft) i dodati referencu u naš projekt. DOWNLOAD: https://code.google.com/p/googlechartsharp/ © University of Zagreb, Faculty of Organization and Informatics, Varaždin
198
Odabrana poglavlja programskog inženjerstva – razvoj programa Vizualizacija podataka – rad s izvještajima i vlastitim kontrolama
S obzirom na to da je ovo web orijentirani projekt, da bismo grafove pokazali na formama moramo koristi Web Browser kontrolu koja će nam omogućiti da prikažemo graf. Načelno, API koji smo preuzeli će nam u HTML obliku zapakirati podatke, koje ćemo učitavati na web stranicu prikazanu na kontroli Web Browser unutar kontrole. Najjednostavniji primjer: Kôd 111 private void frmReport_Load(object sender, EventArgs e) { int[] data = new int[] { 0, 10, 20, 30, 40 }; LineChart chart = new LineChart(150, 150); chart.SetData(data); wsCharts.Navigate(chart.GetUrl()); }
Slika 139: Google Chart na Windows Forms kontroli
I ne zaboraviti: using GoogleChartSharp;
12.5. Vlastite kontrole U ovom dijelu ćemo proći kroz malo složeniji koncept, odnosno kreiranje vlastitih kontrola. Ideja je napraviti kontrolu koja će u grafičkom obliku prikazivati 80 vrijednosti iz baze podataka, ako postoji više od 80 vrijednosti tada se svakih 100ms, vrijednosti pomiču za jedan prema kraju, pa time dobivamo dojam animiranosti podataka. Za izradu vlastite kontrole postoji više pristupa koje nam pruža .NET. Možemo jednostavno naslijediti klasu Control ili možemo koristiti klasu UserControl koja je namijenjena proširenju za potrebe programera. Tu kontrolu ćemo mi koristiti. Stoga, prvi korak, desni klik na projekt, Add, User Control (Slika 140). © University of Zagreb, Faculty of Organization and Informatics, Varaždin
199
Odabrana poglavlja programskog inženjerstva – razvoj programa Vizualizacija podataka – rad s izvještajima i vlastitim kontrolama
Slika 140: Dodavanje nove User Control
Novoj kontroli (Slika 141) ćemo dati naziv ucGraph (Slika 142) i pokrenuti projekt kako bi nam se kreirala kontrola koju ćemo moći dovući na formu.
Slika 141: User Control
Slika 142: ucGraph uvršten u Toolbox
Nakon kompajliranja projekta u Toolbox izborniku moguće je pronaći ucGraph kontrolu koju sada možemo koristiti kao i bilo koju drugu. Uzeti ćemo je i dodati na prozor našeg projekta.
Svojstvo Data će se koristiti za zapis podataka koje trenutno kontrola mora prikazati Metoda onPaint se mora pregaziti vlastitom onPaint metodom koja će nacrtati rub, X i Y osi, te podatke. Metoda DrawFrame crta okvir kontrole Metoda DrawAxis crta apscisu i ordinatu Metoda DrawData crta okomitih linija čije visine su zapisane unutar Data
NAPOMENA: 0, 0 koordinate kod crtanja nalaze se u gornjem lijevom kutu. Kôd 112 public partial class ucGraph : UserControl { public int[] Data { get; set; } public ucGraph() { InitializeComponent(); } protected override void OnPaint(PaintEventArgs e) { //base.OnPaint(e); Graphics g = e.Graphics; int offset = 2; // border offset
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
200
Odabrana poglavlja programskog inženjerstva – razvoj programa Vizualizacija podataka – rad s izvještajima i vlastitim kontrolama
int offset2 = 15; // axis offset int offset3 = 5; // data offset x int offset4 = 2; // data offset y int max = 30; DrawFrame(g, offset); DrawAxis(g, offset, offset2); DrawData(g, offset, offset2, offset3, offset4, max); } private void DrawData(Graphics g, int offset, int offset2, int offset3, int offset4, int max) { if (Data != null) { for (int i = 0; i < 100; i++) { g.DrawLine(new Pen(Brushes.Blue, 3), new Point(4 * i + offset2 + offset3, this.Height - offset2 - offset - offset4), new Point(i * 4 + offset2 + offset3, (this.Height - offset2 - offset) - (Data[i] / max))); } } } private void DrawAxis(Graphics g, int offset, int offset2) { g.DrawLine(new Pen(Brushes.Green, 2), new Point(offset + offset2, this.Height - offset - offset2), new Point(offset + offset2, offset + offset2)); g.DrawLine(new Pen(Brushes.Green, 2), new Point(offset + offset2, this.Height - offset - offset2), new Point(this.Width - offset - offset2, this.Height - offset - offset2)); } private void DrawFrame(Graphics g, int offset) { g.DrawLine(new Pen(Brushes.Gray), new Point(offset, offset), new Point(this.Width - offset, offset)); g.DrawLine(new Pen(Brushes.Gray), new Point(offset, this.Height - offset), new Point(this.Width - offset, this.Height - offset)); g.DrawLine(new Pen(Brushes.Gray), new Point(offset, offset), new Point(offset, this.Height - offset)); g.DrawLine(new Pen(Brushes.Gray), new Point(this.Width - offset, offset), new Point(this.Width - offset, this.Height - offset)); } }
Sada moramo podesiti formu kako bismo čitali podatke iz baze podataka, proslijediti ih našoj kontroli i pokrenuti postupak prikaza. © University of Zagreb, Faculty of Organization and Informatics, Varaždin
201
Odabrana poglavlja programskog inženjerstva – razvoj programa Vizualizacija podataka – rad s izvještajima i vlastitim kontrolama
Slika 143: Konačan izgled forme
Na formu također moramo dodati Timer kontrolu koja nam omogućuje da svakih N milisekundi izvršimo neku metodu. Mi ćemo pročitati sve podatke iz baze podataka, a kada korisnik klikne na gumb Play data, ćemo pokrenuti metodu timer_Thick (Pokrećemo li metodu ili se dešava nešto drugo?). Ta metoda će znati koje je podatke već prikazala, te će svaki put Data podatke za našu kontrolu popuniti jednom novom vrijednošću. Primjerice, to će u prvom prolazu biti podaci od 0 do 100, nakon toga od 1 do 101 itd. do kraja.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
202
Odabrana poglavlja programskog inženjerstva – razvoj programa Vizualizacija podataka – rad s izvještajima i vlastitim kontrolama
Kôd 113 private int[] Data { get; set; } private int PreviousState { get; set; } public frmCharts() { InitializeComponent(); } private void frmReport_Load(object sender, EventArgs e) { northwndDataSetTableAdapters.Summary_of_Sales_by_QuarterTableAdapter taQuarter = new northwndDataSetTableAdapters.Summary_of_Sales_by_QuarterTableAdapter(); northwndDataSet.Summary_of_Sales_by_QuarterDataTable table = new northwndDataSet.Summary_of_Sales_by_QuarterDataTable(); taQuarter.Fill(table); Data = new int[table.Count]; // dohvatili smo SVE podatke iz baze for (int i = 0; i < table.Count; i++) { Data[i] = (int)table[i].Subtotal; } ucGraph1.Data = Data; PreviousState = 0; // gdje počinjemo čitati podatke koje prosljeđujemo? } private void btnPlay_Click(object sender, EventArgs e) { timer.Interval = 50; timer.Start(); // pokrećemo timer koji će pozivati metodu timer_Thick } private void timer_Tick(object sender, EventArgs e) { int[] data = new int[100]; for (int i = 0; i < 100; i++) { data[i] = Data[i + PreviousState]; } if (PreviousState >= Data.Length - 100) timer.Stop(); PreviousState++; // sljedeći put ne čitamo od početka nego za 1 više ucGraph1.Data = data; ucGraph1.Invalidate(); }
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
203
Odabrana poglavlja programskog inženjerstva – razvoj programa Vizualizacija podataka – rad s izvještajima i vlastitim kontrolama
Slika 144: Forma s pripadajućim ispisom podataka
S obzirom na to da kontrola ima podrhtavanje kod iscrtavanja, preporučujemo da za ucGraph unutar prozora Properties uključite opciju Double Buffering. To će promijeniti način iscrtavanja forme tako da se novi podaci iscrtavanju u „sliku“ u memoriji, a kada je crtanje gotovo, tada se jednostavno prikaže ta slika, a u međuvremenu počinje crtanje druge. Dakle, uvijek je stara slika aktivna sve dok nova nije završena. Za više informacija preporučujemo kolegij Računalna grafika.
12.6. Pitanja i zadaci Samostalni zadatak Više o uputama kako kreirati graf: https://code.google.com/p/googlechartsharp/w/list Povežite se na bazu podataka i napravite graf koji prikazuje
Slika 145: Konačan izgled samostalnog zadatka
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
204
Odabrana poglavlja programskog inženjerstva – razvoj programa Vizualizacija podataka – rad s izvještajima i vlastitim kontrolama
Pitanja 1. Što je rdlc? 2. Je li na izvještaju moguće računati vrijednost? 3. Koja kontrola se koristi za prikaz izvještaja? 4. Na kojoj kontroli se mogu prikazati Google Chart izvještaji? 5. Koja se kontrola koristi za kreiranje vlastitih kontrola? 6. Čemu služi override ključna riječ? 7. Što omogućuje Timer kontrola? 8. Čemu služi DoubleBuffering?
12.7. Više informacija o temi 1. Adding Printable Reports to Visual Studio Applications (http://msdn.microsoft.com/enus/library/ms233804(v=vs.80).aspx), dostupno: prosinac, 2013. 2. UserControl Class (http://msdn.microsoft.com/enus/library/system.windows.forms.usercontrol.aspx), dostupno: prosinac, 2013.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
205
Odabrana poglavlja programskog inženjerstva – razvoj programa Uvođenje programa u rad
13. Uvođenje programa u rad Sažetak U ovom poglavlju se završava priča oko razvoja programskog proizvoda s različitim tehnikama distribucije aplikacija. Ključne riječi: objava, deployment, setup, instalacija
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
206
Odabrana poglavlja programskog inženjerstva – razvoj programa Uvođenje programa u rad
13.1. Uvod Postoji nekoliko načina distribucije programskog proizvoda:
Copy & Paste distribucija
Publish
Setup
Koji god način odaberemo potrebno je programski proizvod (aplikaciju) prethodno pripremiti za distribuciju. Do sada samo uvijek radili na samom razvoj aplikacija, koji podrazumijeva kontrolirano izvršavanje aplikacije pogodno za debug. Takva aplikacija je fizički veća od aplikacije koja je pripremljena za distribuciju. Osim toga debug verzija je i sporija. Svi ti nedostaci su neprimjetni, ali ih ne treba zanemariti.
13.2. Priprema aplikacije za distribuciju Uz pretpostavku da je aplikacija logički završena potrebno je paziti na nekoliko čestih situacija: -
Iz dizajna aplikacije ukloniti svaku referencu da se radi o razvojnoj verziji (logo, beta, test simboli i sl.)
-
Podesiti aplikaciju da radi s produkcijskim poslužiteljem i produkcijskom bazom podataka.
-
Postaviti ikone aplikacije i izvršnih datoteka.
-
Počistiti i ukloniti sve reference koje se ne koriste.
-
Počistiti strukturu direktorija; idealni scenarij bi bio imati samo izvršnu datoteku i jedan direktorij u kojem se nalaze svi potrebni resursi za aplikaciju (npr. AppData direktorij)
-
Ukloniti sve hard coded vrijednosti poput connection stringa, korisničkih imena, lozinki i sl.
-
Razriješiti moguće sigurnosne probleme; ukloniti testni korisnički račun i sl.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
207
Odabrana poglavlja programskog inženjerstva – razvoj programa Uvođenje programa u rad
Slika 146: Postavke aplikacije
Slika 146 prikazuje uobičajene postavke aplikacije (panel Application) do kojih se dolazi odabirom Properties nad projektom. Na ovom mjestu se mijenja naziv izvršne datoteke (Assembly). Također, na slici se vide i uobičajene postavke izvršne datoteke (Assembly) do kojih se dolazi pritiskom na tipku Assembly Information. U panelu Build potrebno je odabrati Release kao opciju u Configuration combo boxu. Pritiskom na tipku Advanced otvara se dijalog u kojem je potrebno Debug Info: postaviti u None. Trenutno je postavljenu u pdb-only; s takvom konfiguracijom uz svaku izvršnu datoteku ili library dolazi jedna .pdb (Program database) datoteka koja sadrži informacije za debug i informacije o stanju projekta, a stvara se svaki puta kada se kompilira projekt. U panelu Debug potrebno je ugasiti opciju Enable the Visual Studio hosting process kako nam Visual Studio ne bi generirao .vshost.* datoteke koje služe za brže pokretanje debug-a jer Visual Studio ima uvijek pripravan proces u kojem će se izvršavati naša aplikacija.
Slika 147: Release build projekta
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
208
Odabrana poglavlja programskog inženjerstva – razvoj programa Uvođenje programa u rad
Nakon postavki potrebno je napraviti Release build projekta (Slika 147), tako da trenutnu konfiguraciju postavite kao Release. Tek tada normalno Buildamo projekt.
Slika 148: Release direktorij
Slika 149: Debug direktorij
Slika 148 i Slika 149 prikazuje izgled strukture Bin direktorija s poddirektorijima Debug i Release. Primijetimo da u Release direktoriju nema .pdb niti .vshost.* datoteka.
13.3. Copy &Paste distribucija Korisniku se dostavljaju samo izvršne datoteke aplikacije i svi ostali potrebni resursi. Ovakav način distribucije je jednostavan jer nema instalacije niti deinstalacije. Taj oblik aplikacije je prijenosan i može se pokretati izravno s prijenosnih medija. Kako bi se aplikacija distribuirala na ovakav način, prilikom same izrade aplikacije je potrebno voditi računa o ovom načinu distribucije jer aplikacija neće imati uninstall opciju, prečace (eng. Shortcuts), startup opcije (start with Windows) i sl.
13.4. Publish Publish je integrirana funkcionalnost .Net frameworka kojom se omogućava jednostavna distribucija aplikacija u obliku nekoliko datoteka za instalaciju (instalacijskog paketa). Mogućnosti konfiguriranja instalacijskog paketa su ograničene. Prednost ovakvog distribuiranja je automatsko dodavanja prečaca u Start izbornik kao i deinstalacija aplikacije kroz Control Panel.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
209
Odabrana poglavlja programskog inženjerstva – razvoj programa Uvođenje programa u rad
Postupak Otvoriti Project Properties panel Publish 1. Kliknuti na tipku Options… 2. Popuniti Description s informacijama o proizvođaču itd. 3. U Manifests staviti kvačicu na Create desktop shortcut kako bi nam instalacija automatski stvorila prečicu na korisnički desktop. 4. Zatvoriti dijalog s opcijama 5. Kliknuti na tipku Publish Now Kada završi objava aplikacije sve potrebne datoteke bit će pohranjene u publish direktoriju (direktoriju samog projekta).
Slika 150: Lokacija publish datoteka
Slika 150 prikazuje datoteke koje su nastale objavom (Publish) projekta. Čitavi sadržaj ovog direktorija treba distribuirati korisniku kako bi mogao instalirati aplikaciju. Prednosti Publish pristupa su: -
Jednostavnost izrade
-
Podrška za nadogradnje
-
Mogućnost web instalacije
-
Integracija s operacijskim sustavom (file type)
-
Generiranje uninstall opcije u Control Panelu
Nedostaci Publish pristupa su: -
Ograničene funkcionalnosti
-
Nemogućnost uređivanja dijaloga koraka instalacije
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
210
Odabrana poglavlja programskog inženjerstva – razvoj programa Uvođenje programa u rad
13.5. Setup Stvaranje instalacije zahtjeva posebnu tehnologiju. Na tržištu postoji nekoliko alata za izradu instalacijskih paketa. Donedavno je u Visual Studio-u bio integrirani Install shield predložak projekta. Nažalost, više nije moguće raditi instalacijske pakete iz Visual Studio-a. Koristit ćemo besplatnu aplikaciju Inno Setup (http://en.wikipedia.org/wiki/Inno_Setup) Postupak 1. Napravimo Release Build našeg testnog projekta. 2. Pokrenimo Inno Setup Compiler. 3. Odaberimo opciju Create a new script file using the Script Wizard.
Slika 151: New script file using the Script Wizard
4. Pritisnimo OK te nam se otvara Wizard za izradu nove instalacije. 5. Pritisnimo Next na uvodnom koraku. 6. Popunimo osnovne informacije o aplikaciji.
Slika 152: Osnove informacije o aplikaciji
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
211
Odabrana poglavlja programskog inženjerstva – razvoj programa Uvođenje programa u rad
7. Na sljedećem koraku definiramo gdje želimo da se naša aplikacija instalira (Slika 153). Ostavimo postavke kakve jesu. 8. Na ovom koraku definiramo potrebne datoteke za našu aplikaciju.
Slika 153: Određivanje mjesta instaliravanja aplikacije
Obavezno pod Other application files treba dodati SampleApplication.exe.config datoteku. 9. Na sljedećem koraku definiramo lokacije ikona naše aplikacije.
Slika 154: Lokacije ikona aplikacije
10. Definiramo dokumente odnosno informacije koje treba korisniku prikazati: a. Licenca. b. Informacija prije instalacije. © University of Zagreb, Faculty of Organization and Informatics, Varaždin
212
Odabrana poglavlja programskog inženjerstva – razvoj programa Uvođenje programa u rad
c. Informacija poslije instalacije. Za sada nije nužno odabrati ove datoteke. 11. Na sljedećem koraku definiramo jezike koje podržava naša aplikacija. Ostavimo odabran samo engleski jezik. 12. Definiramo postavke kompajlera poput ikone, naziva same instalacije.
Slika 155: Definiranje postavki kompajlera
13. Na sljedećem koraku ostavimo postavke kako jesu. 14. Kliknemo Finish kako bi završili definiranje našeg instalacijskog paketa. 15. Po završetku alat nas pita želimo li izvršiti novu instalacijsku skriptu. Odgovorimo potvrdno. 16. Na pitanje želimo li spremiti Skriptu odgovorimo potvrdno. Po završetku izvršavanja skripte u output direktoriju se nalazi jedna .exe datoteka koja se zove SampleApplicationSetup.exe. Ovu datoteku distribuiramo našim korisnicima.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
213
Odabrana poglavlja programskog inženjerstva – razvoj programa Uvođenje programa u rad
13.6. Pitanja i zadaci Samostalni zadatak Analizirajte setup skriptu koja je generirana. U skripti promijeniti verziju aplikacije iz 1.5 u 1.1. te ponovo generirajte instalacijsku datoteku. Pitanja 1. Što je .pdb datoteka i čemu služi 2. Navedite prednosti Publish metode distribucije. 3. Navedite nedostatke Publish metode distribucije. 4. Koja je osnovna prednost Copy&Paste distribucije? 5. Navedite metode distribucije.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
214
Odabrana poglavlja programskog inženjerstva – razvoj programa Zbirka zadataka
14. Zbirka zadataka Sažetak Na kraju ove skripte donosimo kratki skup programskih zadataka koji služe za vježbu, teme zadataka pokrivaju područja obrađena ovom skriptom.
Potrebno vrijeme za rješavanje
pojedinog zadatka, u prosjeku, iznosi deset minuta.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
215
Odabrana poglavlja programskog inženjerstva – razvoj programa Zbirka zadataka
1. Napravite GUI aplikaciju koja u svojoj glavnom formi sadrži jednu tipku. Pritiskom na tipku prikazuje se nova instanca iste glavne forme. Pritiskom na tipku novo prikazane forme opet se prikazuje nova instanca glavne forme, itd. U natpisu tipke (Text) neka piše broj instanci glavnih formi koje su prikazane. (ovu vrijednost je potrebno ažurirati svaki puta kada korisnik klikne na tipku u bilo kojoj od instanci glavne forme). 2. Napravite GUI aplikaciju koja služi kao brojač klikova (sadrži tipku, textbox i label), klikom na tipku broji se broj klikova, nakon X klikova se vrijednost resetira. X vrijednost korisnik unosi preko textboxa. Broj klikova se zapisuje u label. 3. Napravite GUI aplikaciju koja se sastoji od forme i tipke, kada pređe mišem preko tipke (odnosno na onMouseOver događaj) potrebno je premjesti tipku na neku drugu lokaciju (slučajnu) na formi. 4. Napravite GUI aplikaciju koja sadrži izbornik, s opcijama open i exit. Na exit se zatvara aplikacija, a na open se otvara nova instanca prozora glavne forme kao MDI dijete postojeće glavne forme. 5. Napravite GUI aplikaciju koja čim ju korisnik pokrene, otvara prozor s pozdravnom porukom i slikom, koju kad korisnik klikne zatvara aplikaciju. 6. Implementirajte smisleni primjer agregacije. 7. Implementirajte smisleni primjer kompozicije. 8. Napravite GUI aplikaciju koja ima 3 texboxa. U prvom textboxu korisnik unosi tekst A. U drugi textbox korisnik unosi tekst B. U treći textbox unosi bilo koji tekst, prilikom unosa teksta u treći textbox svaka pojava teksta A se mora zamijeniti tekstom B. (npr. korisniku treći textbox piše tekst i svaki puta kada napiše ZG automatski će mu se umjesto ZG napisati Zagreb). 9. Napravite GUI aplikaciju koja učitava web stranicu (Pomoć: webbrowser kontrola) temeljem URLa kojeg korisnik zadaje upisom u textbox, stranica se učitava pritiskom na tipku u formi. Kada je stranica uspješno učitana promijeniti boju pozadine forme u zeleno. 10. Napraviti GUI aplikaciju koja ima samo jedan label u kojem se u realnom vremenu ispisuju koordinate miša kada se kreće po površini glavne forme. Oblik zapisa neka bude "x=vrijednostX, y=vrijednostY" gdje su vrijednostX i vrijednostY stvarne koordinate miša.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
216
Odabrana poglavlja programskog inženjerstva – razvoj programa Zbirka zadataka
11. Napravite GUI aplikaciju koja ima 2 textboxa za unos lozinke (sadržaj neka vizualno bude zamijenjen znakom @), Korisnik mora unesti dva puta lozinku, pritiskom na tipku aplikacija moram provjeriti ispravnost lozinke. Lozinke moraju biti jednake u dva textboxa, moraju biti duže od 5 znakova, moraju sadržavati minimalno 3 velika slova, i minimalno jedan znak za svaki element iz skupa: #, $, % 12. Napravite konzolnu aplikaciju koja ispisuje sadržaj upisanog direktorija. Korisnik prilikom pokretanja aplikacije mora upisati putanju nekog direktorija. Ako je unos neispravan treba ispisati poruku da je direktorij neispravan inače treba ispisati nazive svih datoteka u direktoriju 13. Napravite GUI aplikaciju koja ima textbox i tipku. U textbox korisnik unosi proizvoljan broj elemenata(brojeva) odvojenih zarezom. npr. 2,6,1,2,5 Pritiskom na tipku u isti textbox aplikacija mora ispisati iste brojeve ali sortirano uzlazno i odvojeno zarezom. 14. Napravite GUI aplikaciju koja ima jedan label u kojem je ispisan broj sekundi do početka utakmice (20:45). Aplikacija u realnom vremenu (svake sekunde) osvježava stanje labela (broja sekundi do početka). 15. Napravite GUI aplikaciju koja ima jedan textbox i jednu tipku, korisnik u textbox unosi godinu. pritiskom na tipku aplikacija za zadanu godinu treba u isti textbox ispisati koliko petaka 13 se nalazi u zadanoj godini. 16. Napravite GUI aplikaciju koja prilikom pokretanja pokazuje (u label kontroli) koliko dana treba proći do sljedećeg petka. 17. Kreirati klasu Artikl s atributima (int idArtikla, string nazivArtikla, float jedinicnaCijena). Kreirati klasu StavkaRacuna s atributima (int idStavke, Artikl , float količina). Kreirati klasu Račun koja ima atribute (int brojRacuna, datetime datumKreiranja, string prodavač, string nacinPlacanja, List stavke). U klasi StavkaRacuna napišite metodu koja će računati ukupan iznos stavke. U klasi Račun napišite metodu koja će računati ukupan iznos računa. 18. Kreirajte klasu Mjesec (kalendarski mjesec) koja ima atribute (int redniBroj, string nazivMjeseca, int brojDana). Kreirajte klasu Semestar koja ima atribute (Mjesec pocetakSemestra, Mjesec zavrsetakSemestra) i ima metodu koja vraća listu mjeseca koji pripadaju tom semestru.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
217
Odabrana poglavlja programskog inženjerstva – razvoj programa Zbirka zadataka
19. Napravite GUI aplikaciju u kojoj korisnik unosi dva broja (2 textboxa) i pritiskom na tipku korisniku se u rezultatu ispisuje rezultat dijeljena ta dva broja. Napravite provjeru jesu li su uneseni brojevi a ne slova te provjeru je li drugi broj različit od nule. 20. Napravite GUI aplikaciju koja za zadani datum (koristeći DateTimePicker) u Label ispisuje u kojem godišnjem dobu je zadani datum. Određivanje godišnjeg doba treba napraviti svaki puta prilikom odabira datuma iz DTP. 21. Napravite GUI aplikaciju koja ima jedan textbox, korisnik unosi proizvoljan tekst u textbox, prilikom unosa svaka pojava slova "a" se zamjenjuje slovom "b" 22. Napravite GUI aplikaciju koja služi kako preglednik slika. Forma sadrži jedan pictureBox objekt u kojem se učitava slika te jednu tipku, Tipka otvara dijalog za odabir datoteka (OpenFileDialog objekt) nakon što je datoteka odabrana (FileOK event) treba ju prikazati u pictureBox kontroli (Load metoda). 23. Napravite GUI aplikaciju koja u sebi sadrži jednu tipku. Pozadina forme neka bude zelena. Tekst te tipke jednak je broj klikova na tipku. Ako je broj klikova djeljiv s 10 tada pozadina forme treba biti crvena, inače pozadina neka bude zelena. 24. Napravite GUI aplikaciju koja ima tri textboxa i jednu tipku. Korisnik u prva dva textboxa unosi dva skupa brojeva odvojenih zarezom. Pritiskom na tipku u treći textbox se trebaju ispisati samo zajednički elementi dva skupa (iz dva textboxa) odvojeni zarezom. 25. Napravite GUI aplikaciju koja ima 3 textboxa (R,G i B) i jednu tipku. Korisnik u svaki textbox unosi jedan broj od 0-255 (napravite provjeru unosa). Pritiskom na tipku potrebno je promijeniti pozadinu forme u boju temeljem vrijednosti iz textboxa (R,G,B). Pomoć:Color.FromArgb(R,G,B); 26. Napravite GUI aplikaciju koja sadrži jednu tipku i textbox. Korisnik u textbox unosi broj. Nakon pritiska na tipku aplikacija se mora ugasiti za uneseni broj sekundi. Broj preostalih sekundi do gašenja aplikacije treba ispisivati u sadržaju tipke. (u realnom vremenu, svake sekunde). 27. Napravite GUI aplikaciju koja sadrži labelu i tipku. Klikom na tipku, labela broji broj klikova, odnosno ispisuje koliko puta je korisnik kliknuo. Ako je broj klikova paran tada je tekst crveni, inače je crni. Kada je broj klikova veći od 10, veličina prozora forme se maximizira. (Pomoć: Modulo, FormWindowState)
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
218
Odabrana poglavlja programskog inženjerstva – razvoj programa Zbirka zadataka
28. Napravi novu GUI aplikaciju koja sadrži jednu tipku, veličina forme je 500x500. Kada korisnik dođe mišem iznad tipke, lokacija (svojstvo) se postavlja na slučajne vrijednosti X i Y. (Pomoć: Random r = new Random(); 29. Napravite GUI aplikaciju koja slučajnim odabirom izbacuje loto brojeve za 7/39 slučajnim odabirom. Brojeve treba prikazati u 7 label kontrola. Brojevi se odabiru pritiskom na tipku. Ako je broj između manji od 20 boja teksta neka bude zelena inače neka bude crvena. Napomena: brojevi se ne smiju ponavljati, npr. ne mogu biti dva ista odabrana broja. Pomoć (Random klasa). 30. Napravite GUI aplikaciju koja ima jedan ComboBox i jedan MonthCalendar objekt. ComboBox objekt je potrebno napuniti brojevima od 1 do 12 (programski). Odabirom nekog broja iz ComboBoxa potrebno je promijeniti trenutno prikazani mjesec u MonthCalendar objektu temeljem odabira iz ComboBoxa. 31. Napravite aplikaciju kojoj se svaku sekundu promjeni pozicija. 32. Napravite GUI aplikaciju koja ima 3 TrackBar kontrole. Prilikom svakog pomicanja track bara treba promijeniti boju pozadine forme sukladno vrijednostima svakih od trackbar kontrola. Napomena: format zapisa boje je R,G,B vrijednost od 0 do 255. Color.FromArgb(...) 33. Napravite GUI aplikaciju koja ima tri textboxa (A,B,C) i jednu tipku. Pritiskom na tipku se korisniku računa suma brojeva od A do B. Prilikom računanja sume treba preskočiti brojeve djeljive s C. Rezultat treba ispisati umjesto teksta tipke. 34. Napravite GUI aplikaciju koja je virtualno podijeljena na 4 kvadranta. Ovisno o tome u koji kvadrant se klikne, korisniku se u label (u sredini forme) ispisuje u koji kvadrant je kliknuto(SZ,SI,JZ,JI). Forma ne smije biti fiksne veličine. Neka se promjeni pozadine forme ovisno u koji kvadrant je kliknuto: SZ=Zelena, SI =Plava, JZ = Crvena, JI = Žuta. 35. Napravite GUI aplikaciju koja ima 2 ProgressBar. ProgressBar kontrole će grafički prikazivati koordinate miša na glavnoj formi u obliku popunjenosti progressbarova. Prvi progressbar je za X vrijednost, drugi je za Y vrijednost. Ako je miš u gornjem lijevom kutu ProgressBar kontrole imaju vrijednost 0%, ukoliko je miš u donjem desnom kutu ProgressBar kontrole imaju vijednost 100% itd. 36. Napravite GUI aplikaciju koja sadrži dvije NumericUpDown kontrole, jedan label i jednu tipku. Korisnik u NUD kontrolama odabire mjesece u godini, pritiskom na tipku korisniku © University of Zagreb, Faculty of Organization and Informatics, Varaždin
219
Odabrana poglavlja programskog inženjerstva – razvoj programa Zbirka zadataka
se u label treba ispisati popis mjeseca između dvije NUD vrijednosti. Mjeseci (brojevi) trebaju biti odvojeni zarezom. 37. Napravite GUI aplikaciju koja prilikom klika na svaki puta kada netko upiše slovo T u textbox, promjeni lokaciju textboxa slučajnim odabirom. 38. Napravite GUI aplikaciju koja sadrži 5 labela i tipku. Svake sekunde labele prikazuju slučajne brojeve i nalaze se na slučajnim pozicijama unutar okvira glavne forme. Pritiskom na tipku se pokreće/zaustavlja slučajni razmještaj (i slučajni sadržaj labela). 39. Napravite GUI aplikaciju koja korisniku omogućuje provjeru vida, klikom na tipke povećaj ili smanji, povećava se ili smanjuje font centralne labele, čiji se sadržaj mijenja slučajnim odabirom nakon klika na labelu. 40. Napravite GUI aplikaciju koja dinamički na zaslonu generira 10 progress barova, kojima će se svaku sekundu vrijednosti postaviti na slučajan broj. 41. Napravite aplikaciju koja će ispisivati fibbonacijev niz do proizvoljnog reda. 42. Napravite aplikaciju koja dijeli 2 broja i omogućuje dijeljenje s 0 (x/= inf). 43. Napravite GUI aplikaciju koja implementira kalkulator s 2 broja (textbox) i 2 operacije ( tipke + i -) koji ispisuje rezultat u messagebox. 44. Napravite GUI aplikaciju koja ima textbox i tipku. Korisnik kroz textbox unosi broj X, pritiskom na tipku potrebno je generirati X label objekata te ih smjestiti u glavnu formu (controls kolekcija). pozicija svakog labela neka bude slučajna unutar dimenzija glavne forme. Sadržaj labela je slučajan broj. 45. Napravite GUI aplikaciju koja ima 2 ListBox kontrole. Prvu kontrolu programski popunite s rednim brojevima mjeseca (od 1 do 12). Kada korisnik odabere neki mjesec, druga ListBox kontrola se popuni s rednim brojevima dana tog mjeseca. Napomena: Koristiti DateTime.DaysInMonth metodu za dohvaćanje broja dana u mjesecu. 46. Napravite GUI aplikaciju s jednom formom i jednom klasom Pravokutnik. Na formi se nalazi 5 texboxova i jedna tipka. U prva 2 textboxa korisnik unosi visinu i širinu pravokutnika, a zatim klikne na tipku Izračunaj. U ostala 3 textboxa se tada prikažu vrijednosti površine pravokutnika, opsega pravokutnika i dijagonale pravokutnika. Sva funkcionalnost koja se odnosi na pravokutnik se nalazi u klasi Pravokutnik. Klasa © University of Zagreb, Faculty of Organization and Informatics, Varaždin
220
Odabrana poglavlja programskog inženjerstva – razvoj programa Zbirka zadataka
pravokutnik ima privatne atribute: širina, visina; konstruktor koji prima parametre: širina, visina; javne metode: IzracunajPovrsinu, IzracunajOpseg, IzracunajDijagonalu. 47. Napravite GUI aplikaciju koja sadrži samo jednu tipku. Pritiskom na tipku, na formi se generira 100 labela na sljedeći način. Svaka labela u sebi sadrži slučajan broj od 1 do 1000. U gornjoj polovici forme ispisuju se brojevi veći od 500, a u donjoj manji od 500. U lijevoj polovici forme ispisuju se neparni brojevi, a u desnoj polovici forme se ispisuju parni brojevi. 48. Napravite GUI aplikaciju koja sadrži jedan textbox i jednu tipku. Korisnik u textbox unosi riječ. Pritiskom na tipku aplikacija provjerava da li je unesena riječ palindrom ili ne. Rezultat provjere se ispisuje u messageboxu (DA ili NE). 49. Napravite GUI aplikaciju koja sadrži jedan textbox i jednu tipku. Korisnik u textbox unosi tekst. Pritiskom na tipku aplikaciju ispisuje statistiku (broj ponavljanja) za svako slovo u tekstu. Rezultat se ispisuje u messageboxu. Primjer: unos: "ovo je test" rezultat: o=2 v=1 j=1 t=2 e=2 s=1 50. Napravite GUI aplikaciju koja ima textbox i tipku. Korisnik u textbox unosi broj X, pritiskom na tipku potrebno je generirati X button objekata te ih smjestiti u glavnu formu (controls kolekcija). pozicija svakog buttona neka bude slučajna unutar dimenzija glavne forme. Tekstualni sadržaj buttona je slučajan broj. Pritiskom na bilo koji od X buttona korisniku se prikazuje messagebox s informacijom o lokaciji buttona. 51. Napravite windows forma aplikaciju koja sadrži 4 textboxa (T1,T2,T3,T4) u koje korisnik unosi brojeve (potrebna je provjera unosa broja). pritiskom na tipku korisniku se ispisuje presjek dva intervala ( [T1, T2] i [T3, T4]) u obliku messageboxa, ispisuju se vrijednosti odvojene zarezom. 52. Napravite GUI aplikaciju koja sluzi kao brojač klikova (sadrži tipku, textbox), klikom na tipku broji se broj klikova, nakon X klikova se vrijednost resetira. X vrijednost korisnik © University of Zagreb, Faculty of Organization and Informatics, Varaždin
221
Odabrana poglavlja programskog inženjerstva – razvoj programa Zbirka zadataka
unosi preko textboxa. Prilikom pritiska na tipku potrebno je promijeniti boju pozadine tipke, u početku (i nakon resetiranja) boja pozadine je siva, što se broj klikova približava vrijednosti X boja pozadine tipke postaje sve crvenija. u Text tipke treba ispisati trenutno stanje klikova u obliku: broj_klikova/X. 53. Napravite konzolnu aplikaciju. U njoj kreirajte novu klasu "Mjesec", koja predstavlja kalendarski mjesec i sadrži sljedeće elemente: privatne atribute (int _id, string _naziv, int _brojDana), odgovarajuća javna svojstva za navedene atribute te konstruktor koji prima ova tri parametra i dodjeljuje ih atributima klase. U Main metodi kreirajte 12 instanci klase Mjesec (jedna instanca za svaki kalendarski mjesec), popunite odgovarajuće podatke, i dodajte ih u listu (List). Nakon toga napravite statičku metodu IzracunSemestra, koja vraća listu mjeseca koji ulaze u semestar, a prima kao parametre listu svih 12 mjeseca, početni mjesec, i završni mjesec semestra (na primjeru metode IzracunSemestra pokažite princip preopterećivanja metoda - overloading, na način da prva metoda IzracunSemestra početni i završni mjesec prima kao id-ove mjeseca, a druga kao cijele objekte klase Mjesec). Na kraju u metodi Main uz pomoć metode IzracunSemestra ispišite posebno mjesece zimskog semestra a posebno mjesece ljetnog semestra. Također ispišite ukupan broj dana pojedinog semestra. 54. Napravite konzolnu aplikaciju. U njoj kreirajte novu klasu Zaposlenik koja predstavlja zaposlenika u nekom poduzeću. Klasa zaposlenik sadrži sljedeće elemente: javne atribute (int _id, string _imePrezime, float[] _mjesecnePlace), konstruktor koji prima parametre (id, imePrezime), te javne metode IzracunGodisnjeZarade, IzracunProsjecneZarade, koje vraćaju odgovarajuće vrijednosti. U izračun prosječe zarade ulaze samo plaće veće od nule. U Main metodi dodajte jednog zaposlenika, ispunite podatke o plaći, te ispišite njegove podatke, zajedno sa mjesečnim zaradama,te ukupnom i prosječnom zaradom. 55. Napravite GUI aplikaciju koja implementira operacije unije, presjeka i razlike dvaju skupova cijelih brojeva. Na formi se nalaze 2 Textbox-a (txtUnos1, txtUnos2), 3 Listbox-a (lstUnos1, lstUnos2, lstRezultat), i 3 tipke (btnUnija, btnPresjek, btnRazlika). Korisnik unosi brojeve u listu brojeva na sljedeći način. Za unos prve liste brojeva korisnik unosi broj u txtUnos1 pritisne ENTER na tipkovnici, uneseni broj se doda u Listbox lstUnos1 i obriše iz txtUnos1. Zatim u txtUnos1 unosi drugi broj + ENTER itd. Na isti način se dodaju brojevi i u drugu listu lstUnos2 s razlikom da za unos ovaj put koristimo txtUnos2. S obzirom na to da se radi o skupovima, u listi brojeva se ne smiju pojaviti dvije iste vrijednosti. Također, potrebno je validirati korisnički unos, tako da budu dozvoljeni samo cijeli brojevi. Nakon što korisnik unese brojeve u obje liste, klikom na odgovarajuću tipku u lstRezultat se prikažu brojevi dobiveni unijom, presjekom ili razlikom prve dvije liste.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
222
Odabrana poglavlja programskog inženjerstva – razvoj programa Zbirka zadataka
56. Napravite GUI aplikaciju koja će množiti brojeve od N do M(koje unosi korisnik kroz textbox odvojeno zarezom Pomoć string.Split(...). Pritiskom na tipku započinje računanje. U drugi textbox treba zapisati vrijeme početka izračuna i vrijeme kraja izračuna kao i rezultat izračuna. (Pomoć: DateTime.Now). 57. Napravite GUI aplikaciju s jednom formom i jednom klasom Trokut. Na formi se nalazi 5 texboxova i jedna tipka. U prva 3 textboxa korisnik unosi stranice a b i c, a zatim klikne na tipku Izračunaj. U ostala 2 textboxa se tada prikažu vrijednosti opsega trokuta. Sva funkcionalnost koja se odnosi na trokut se nalazi u klasi Trokut. Klasa trokut ima privatne atribute: strA, strB, strC; konstruktor koji prima parametre: a, b, c; javne metode: IzracunajOpseg. 58. Napravite GUI aplikaciju s jednom formom i jednom klasom Krug. Na formi se nalazi 3 texboxova i jedna tipka. U prvi textbox korisnik unosi polumjer kruga, a zatim klikne na tipku Izračunaj. U ostala 2 textboxa se tada prikažu vrijednosti površine kruga i opsega kruga. Sva funkcionalnost koja se odnosi na krug se nalazi u klasi krug. Klasa krug ima privatne atribute: polumjer; konstruktor koji prima parametre: UlazPolumjer; javne metode: IzracunajPovrsinu, IzracunajOpseg. 59. Napravite GUI aplikaciju koja će zbrajati dva broja 0-9. Korisnik unosi u textbox izraz tipa 2+3 pritiskom na tipku izračunaj u isti textbox se zapisuje rezultat (nalijepljeno na postojeći tekst i između stoji znak=) npr. 2+3=5. (Pomoć S: string.Split(...), ili Substring(...)) 60. Napravite GUI aplikaciju koja će sadržavati jednu tipku. Pritiskom na tipku korisnik pokreće dretvu koja će samo spavati (Pomoć: Thread.Sleep(...)) slučajan broj sekundi do max 10 sekundi. Dretva će u messagebox ispisati vrijeme početka zatim će spavati i na kraju u message box ispisati vrijeme kraja spavanja (Pomoć: DateTime.Now) 61. Napravite konzolnu aplikaciju koja pokreće dvije dretve (A i B). Dretva A u konzolu svake 2 sekunde ispisuje slovo "A". Dretva B u konzolu svake 3 sekunde ispisuje slovo "B". Pomoć: Console.WriteLine(...); 62. Napravite GUI aplikaciju koja ima jedan textbox (Multiline = true), u textbox korisnik upisuje tekst. u slučaju da je korisnik 4 puta za redom stisnuo tipku Home tada se na postojeći tekst zalijepi izraz "Home run!" 63. Napravite GUI aplikaciju koja ima jedan textbox i jednu tipku. U text box se upisuje naziv tekstualne datoteke (npr. "test.txt", datoteka se treba nalaziti na C: disku), klikom na tipku
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
223
Odabrana poglavlja programskog inženjerstva – razvoj programa Zbirka zadataka
započinje pisanje u navedenu datoteku. Kad se započne pisanje u datoteku svake 4 sekunde u novi red u datoteci se zapisuje trenutni datum i vrijeme. Pomoć: za pisanje datoteke možete instancirati objekt klase StreamWriter (System.IO namespace), odnosno metodu Write. Za mjerenje sekundi postavite interval Timer-a na 4 sekunde. 64. Napravite GUI aplikaciju koja sadrži samo jedan Label objekt (Text = Programsko inženjerstvo). Prilikom pokretanja aplikacije od korisnika treba tražiti da odabere boju (ColorDialog) i da odabere font (FontDialog). Odabrana boja neka bude pozadina forme. Odabrani font neka bude font Labele. 65. Napravite GUI aplikaciju koja temeljem parametara: Traženi iznos kredita, broj mjeseci otplate i kamate računa otplatni plan kredita. Način unosa nije bitan. Izgled jednog reda otplatne tablice: rbr. mjeseca, mj. rata, preostalo duga. Način prikaza otplatne tablice nije bitan (npr. jedan red u ListBox, TextBox, DataGridView,...) 66. Napravite GUI aplikaciju koja ima jednu tipku (width:50px, height: 50px, lokacija:negdje u sredini forme), prelaskom miša preko te tipke ona nestaje te umjesto nje nastaju dvije nove (koje imaju isto ponašanje). Lokacija nove dvije tipke je relativno blizu: u radijusu od max 100px od lokacije početne tipke. 67. Napravite GUI aplikaciju koja ima jedan textbox. Korisnik u njega unosi izraz tipa: "jedan+tri". Moguća operacija je samo zbrajanje. Aplikacija pritiskom na tipku "Izračunaj" u nastavku teksta kojeg je korisnik upisao ispisuje rezultat. npr. "jedan+tri=četiri". Mogući je unos samo brojeva od nula do pet. 68. Napravite GUI aplikaciju koja služi kao preglednik tekstualnih datoteka. Pritiskom na tipku "Otvori" otvara se dijalog (FileOpenDialog) za odabir datoteke, po uspješnom odabiru (FileOK) treba sadržaj datoteke ispisati u textboxu. Pomoć: statična klasa File. 69. Napravite GUI aplikaciju koja na google maps karti prikazuje putanju između dva grada. Popis gradova je: Zagreb, Varazdin, Split, Rijeka, Dubrovnik, Vinkovci, Bjelovar. Korisnik odabire gradove iz dvije combobox liste (Polazište i Odredište), nakon odabira odredišta na google karti se prikazuje putanja. Sintaksa URL putanje je: http://maps.google.com/maps?saddr=polaziste&daddr=odrediste Potrebno je korisnika upozoriti ako je odabran isti grad za polazište i odredište
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
224
Odabrana poglavlja programskog inženjerstva – razvoj programa Zbirka zadataka
70. Napravite GUI aplikaciju koja prikazuje trenutno vrijeme u tekstualnom obliku (Label objekt) s preciznošću od 15 min. primjeri: 14:34 = dva i pola sata 15:21 = tri i četvrt sata 15:47 = tri i tri četvrt sata 16:05 = četiri sata Nije potrebno obraćati pozornost na padeže. 71. Napravite GUI aplikaciju koja služi kao preglednik slika. Korisnik u početku
odabire
direktorij te se iz direktorija prikazuju samo .jpg slike i to tako da je uvijek prikazana samo jedna slika. Desnim klikom miša na sliku se prikazuje sljedeća slika iz odabranog direktorija. Lijevim klikom miša na sliku se prikazuje prethodna slika iz odabranog direktorija.
© University of Zagreb, Faculty of Organization and Informatics, Varaždin
225
Bilješke
226
227