• No results found

Pokročilé metody návrhu informačního systému

N/A
N/A
Protected

Academic year: 2022

Share "Pokročilé metody návrhu informačního systému "

Copied!
51
0
0

Loading.... (view fulltext now)

Full text

(1)

TECHNICKÁ UNIVERZITA V LIBERCI

Fakulta mechatroniky, informatiky a mezioborových studií

Studijní program: N2612 Elektrotechnika a informatika Studijní obor: Informační technologie

Pokročilé metody návrhu informačního systému

Diplomová práce

Autor: Bc. Michal Pavlík

Vedoucí práce: Doc. RNDr. Pavel Satrapa, Ph.D.

V Liberci 18.5.2010

(2)

3

Prohlášení

Byl jsem seznámen s tím, že na mou diplomovou práci se plně vztahuje zákon č.

121/2000 o právu autorském, zejména §60 (školní dílo).

Beru na vědomí, že TUL má právo na uzavření licenční smlouvy o užití mé DP a prohlašuji, že s o u h l a s í m s případným užitím mé diplomové práce (prodej, zapůjčení apod.)

Jsem si vědom, že užití své diplomové práce či poskytnout licenci k jejímu využití mohu jen se souhlasem TUL, která má právo ode mne požadovat přiměřený příspěvek na úhradu nákladů, vynaložených univerzitou na vytvoření díla (až do jejich skutečné výše).

Diplomovou práci jsem vypracoval samostatně s použitím uvedené literatury a na základě konzultací s vedoucím diplomové práce a konzultantem.

Datum

Podpis

(3)

4

Abstract

This thesis aims to describe the main symptoms of imperfect software design, and methods (or patterns) which can be used to remove these symptoms. Based on this knowledge will be then designed architecture of information system, which will be implemented in C# language on .NET platform as a web application. After all information system will be used in real university department environment.

Keywords: Information system, software architecture, .NET, code quality

(4)

5

Abstrakt

Cílem diplomové práce je popsat hlavní symptomy nekvalitního softwarového návrhu a metody (resp. vzory), pomocí kterých lze tyto symptomy odstranit. Na základě těchto znalostí bude následně navrhnuta architektura informačního systému, který bude implementován v jazyce C# na platformě .NET jako webová aplikace. Informační systém bude nakonec nasazen v reálném prostředí univerzitní katedry.

Klíčová slova: Informační systém, softwarová architektura, .NET, kvalita kódu

(5)

6

Obsah

ABSTRACT... 4

ABSTRAKT ... 5

OBSAH ... 6

SEZNAM OBRÁZKŮ ... 7

SEZNAM ZKRATEK ... 8

ÚVOD ... 9

1 NÁVRH KVALITNÍ ARCHITEKTURY ... 10

1.1 SEPARATION OF CONCERNS ... 11

1.1.1 Horizontální separace ... 11

1.1.2 Vertikální separace ... 12

1.1.3 Model-View-Controller ... 13

1.1.4 Model-View-Presenter ... 14

1.2 ASPEKTOVĚ ORIENTOVANÉ PROGRAMOVÁNÍ ... 15

1.2.1 Crosscutting concerns ... 16

1.2.2 Aspekty ... 16

1.3 INVERSION OF CONTROL ... 17

1.4 KONTRAKTY... 20

1.4.1 Design by Contract ... 20

1.4.2 Možnosti deklarace ... 22

1.5 TEST-DRIVEN DEVELOPMENT ... 24

1.6 MODELOVÁNÍ DOMÉNY ... 25

1.6.1 Domain-Specific Language ... 26

1.7 OBJEKTOVĚ-RELAČNÍ MAPOVÁNÍ ... 27

1.7.1 Teorie objektově-relačního mapování ... 27

1.7.2 Mapování dědičnosti ... 28

1.7.3 Asociace ... 31

1.7.4 Identita ... 32

1.7.5 Objektově-relační návrhové vzory ... 32

2 IMPLEMENTACE WEBOVÉ APLIKACE... 35

2.1 POUŽITÉ TECHNOLOGIE ... 35

2.2 ARCHITEKTURA ... 35

2.3 IMPLEMENTACE MODULŮ ... 37

2.3.1 Modul Data ... 37

2.3.2 Modul Security ... 38

2.3.3 Modul Infrastructure ... 41

2.3.4 Modul Shell ... 43

2.3.5 Modul Administration ... 44

2.4 TRANSPARENTNÍ LOKALIZACE ... 45

3 STATICKÁ ANALÝZA ... 47

3.1 ANALÝZA ZDROJOVÉHO KÓDU ... 47

ZÁVĚR ... 49

LITERATURA ... 51

(6)

7

Seznam obrázků

Obrázek 1.1: Třívrstvá architektura ... 12

Obrázek 1.2: Vertikální separace ... 12

Obrázek 1.3: Kombinace horizontální a vertikální separace ... 13

Obrázek 1.4: Struktura MVC ... 14

Obrázek 1.5: MVP Passive View ... 15

Obrázek 1.6: Vizuální znázornění průřezových částí ... 16

Obrázek 1.7: Princip proplétání ... 17

Obrázek 1.8: Přímá asociace ... 18

Obrázek 1.9: Dependency Injection ... 19

Obrázek 1.10: Service Locator ... 20

Obrázek 1.11: Diagram vývoje řízeného testy ... 25

Obrázek 1.12: Technika Table per Hierarchy ... 29

Obrázek 1.13: Technika Table per Concrete Class ... 29

Obrázek 1.14: Technika Table per Class ... 30

Obrázek 1.15: Mapování pomocí generického schématu ... 31

Obrázek 2.1: Dependency diagram informačního systému ... 37

Obrázek 2.2: UML diagram rozhraní modulu Data ... 38

Obrázek 2.3: UML diagram rozhraní modulu Security ... 40

Obrázek 2.4: Dialog pro nastavení oprávnění ... 41

Obrázek 2.5: UML diagram rozhraní modulu Infrastructure ... 42

Obrázek 2.6: Stránka s WYSIWYG editorem ... 44

Obrázek 2.7: Stránka pro modifikaci navigačního stromu ... 45

(7)

8

Seznam zkratek

ACL Access Control List

AOP Aspektově orientované programování

DbC Design by Contract

DI Dependency Injection

DSL Domain-Specific Language FTP File Transfer Protocol

GS Generic Schema

HTML Hypertext Markup Language HTTP Hypertext Transfer Protocol IoC Inversion of Control

MVC Model-View-Controller MVP Model-View-Presenter

OOP Objektově orientované programování ORM Object-Relational Mapping

SoC Separation of Concerns SQL Structured Query Language TDD Test-Driven Development

TPC Table per Class

TPCC Table per Concrete Class TPH Table per Hierarchy URL Uniform Resource Locator WCSF Web Client Software Factory WYSIWYG What you see is what you get XML Extensible Markup Language

(8)

9

Úvod

Svět se nyní nachází v takzvaném informačním věku, který je charakteristický tím, že většina lidské populace má volný přístup k ohromnému množství informací a dat téměř odkudkoliv. Software pro perzistenci, manipulaci a prezentaci těchto dat se pak nazývá informační systém, bez kterého se dnes neobejde žádná větší organizace a výrazně usnadňuje práci i v těch menších jako je univerzitní katedra. Díky informačnímu systému mohou členové katedry efektivně spravovat data nebo komunikovat se svými studenty a to z jakéhokoliv místa kde je dostupné připojení k internetu. Cílem této práce ovšem není pouze implementovat na základě požadavků vhodný informační systém, ale popsat také techniky pro návrh kvalitní architektury.

V sedmdesátých letech 20. století nastala softwarová krize, kdy se téměř všechny projekty protahovaly, byly nesmírně drahé a v neposlední řadě obsahovaly velké množství chyb. V roce 1968 byla tato krize diskutována na konferenci NATO a výsledkem je softwarové inženýrství, které se zabývá metodikami tvorby kvalitnějšího a levnějšího softwaru. Od té doby vzniklo mnoho vzorů, principů a metodik, pomocí kterých se dá navrhnout komplexní software, který se po čase nezačne rozpadat vinou špatné architektury nebo implementace.

Práce je rozdělena do tří kapitol. První kapitola popisuje převážně techniky a návrhové vzory pro vytvoření kvalitní architektury. Druhá kapitola je pak věnována samotné implementaci webové aplikace na základě většiny dříve popsaných technik.

Nakonec jsou ve třetí kapitole popsané základy statické analýzy a její aplikace na vytvořený kód.

(9)

10

1 Návrh kvalitní architektury

S postupem doby jsou softwarové aplikace stále větší a složitější, což přináší potřebu měnit způsob, jakým jsou tyto aplikace navrhovány a vyvíjeny. Jen těžko si lze představit, že by se dnešní velké softwarové projekty programovaly v jazyce symbolických adres. Změna paradigmatu vývoje softwaru, kde v čele stojí zavedení jazyků vyšší úrovně, by se dala shrnout do jednoho slova – abstrakce. Softwarová architektura se pak dá zjednodušeně chápat jako vysoký stupeň abstrakce, definující rozložení a vztahy jednotlivých komponent v určitém softwarovém systému.

Před samotnou tvorbou architektury je vhodné znát základní symptomy jejího nekvalitního návrhu a způsoby jak tyto symptomy do jisté míry eliminovat. Robert C.

Martin ve své knize [Martin] identifikoval několik symptomů nekvalitního návrhu:

 Rigidita

Jednoduchá změna v jedné části projektu vede k vynuceným změnám v dalších částech projektu. Tento symptom brání vývojáři dělat větší zásahy v kódu.

 Fragilita

Fragilita (křehkost) úzce souvisí s rigiditou a kód trpící tímto symptomem se vyznačuje tím, že změna v jedné části způsobí nefunkčnost jiných částí, které s tou první nemají žádný logický vztah.

 Imobilita

Imobilní kód nelze jednoduše integrovat do jiného projektu a není tak dodržen jeden ze základních principů – znovupoužitelnost.

 Viskozita

Pokud chce vývojář udělat změnu, může ji provést několika způsoby. Obecně lze tyto způsoby rozdělit podle toho, zda respektují původní návrh nebo ho porušují (tzv. hack). V případě, že je daleko jednodušší provést změnu porušující návrh, trpí kód viskozitou návrhu. Existuje ovšem ještě viskozita prostředí, které souvisí s kvalitou vývojových nástrojů. Pokud například vývojář vytváří větší počet menších souborů pouze kvůli velmi pomalému kompilátoru, jde o viskozitu prostředí.

(10)

11

 Komplexita

Podle [McConnell] je jedním z měřítek složitosti počet objektů, na které musí vývojář myslet, aby byl schopen programu porozumět. Existuje několik exaktních metod jak složitost měřit, ovšem nadměrná složitost je kombinací jiných symptomů, které dělají kód méně pochopitelný.

 Duplicita

Duplicitní kód je charakteristický opakováním některých úseků, což při nutnosti zásahu do tohoto úseku vede k úpravám všude tam, kde se tento úsek opakuje.

V podstatě jde o jistou formu rigidity.

 Opacita

Podle Martina Fowlera umí každý napsat kód, kterému rozumí počítač, ale pouze dobrý programátor píše kód, kterému rozumí člověk. Neprůhledný kód se vyznačuje použitím konstrukcí, kterým rozumí jen autor a nikdo jiný není schopen definovat účel metody podle kódu. Důležitou roli hraje i dodržování jisté kultury při vybírání vhodných názvů proměnných, tříd, metod a jiných elementů.

1.1 Separation of Concerns

Analýzou některých symptomů lze dojít k závěru, že příčinou jejich výskytu je existence skrytých, nelogických nebo nesmyslných vazeb v kódu. Logickým řešením tedy bude odstranění těchto vazeb a tím se zabývá technika zvaná Separation of Concerns (SoC). Tento termín poprvé použila jedna z největších osob informačního věku – Edsger W. Dijkstra a podle [Greer2008] je SoC definování a vytyčování logických hranic, které zlepší údržbu systému. Aplikace tvořené z malých, znovupoužitelných a správně nazvaných jednotek se vyznačují dobře pochopitelným a elegantním návrhem. Takové aplikace jsou navíc jednoduše rozšiřitelné a méně náchylné k chybám.

1.1.1 Horizontální separace

Horizontální separace je rozdělení aplikace na logické vrstvy, kde každá vrstva seskupuje třídy specializující se na určitý aspekt aplikace. Typická reprezentace horizontální separace je třívrstvá architektura. Prezentační vrstva obsahuje komponenty související s uživatelským rozhraním. Vrstva business logiky obsahuje převážně skupinu tříd reprezentující reálné objekty (model) a definuje závislosti mezi těmito

(11)

12 objekty. Perzistenční vrstva se nejčastěji stará o

odstínění datového úložiště respektive libovolného datového zdroje. Jedním ze znaků vrstvené architektury je i přesně daná závislost mezi vrstvami. Platí, že volání metod mezi vrstvami probíhá pouze k podřízené vrstvě.

Prezentační vrstva tak může volat pouze metody ve vrstvě business logiky a ta může volat pouze

metody datové vrstvy. To značně zjednodušuje nejen strom závislosti, ale také výměnu celé vrstvy například při změně uživatelského rozhraní.

1.1.2 Vertikální separace

Vertikální separace je proces rozdělení aplikace na moduly, které poskytují v systému jistou funkcionalitu. Hranice mezi moduly mohou být logické nebo fyzické. Logické pomáhají hlavně v organizování kódu, zatímco fyzické jsou dobré pro týmový vývoj, kde každý tým může nezávisle pracovat na jednom modulu. Fyzické moduly jsou implementovány v separátních knihovnách a

v některých případech lze doslova za chodu moduly do systému přidávat případně je ze systému odebírat nebo nahrazovat.

K modulům neodmyslitelně patří také pojmy koheze (soudržnost) a vazba.

Koheze vyjadřuje, za jakým účelem jsou třídy seskupeny v jednom modulu a vazba pak míru závislosti na jiných modulech.1 Steve McConnell identifikoval několik úrovní (respektive druhů) soudržnosti, kde nejlepší je funkční soudržnost [McConnell]. Tento druh soudržnosti v kontextu vertikální separace nastává, pokud jsou třídy seskupeny za účelem vykonání jasně definovaného úkolu. Za zmínku dále stojí komunikační koheze, které je dosáhnuto v případě, že jsou třídy seskupeny za účelem operace nad stejnými daty. Nejhorším typem je pak náhodná koheze, u které neexistuje žádný zřejmý důvod seskupení.

1 Tuto myšlenku lze stejně aplikovat i na třídy a metody.

Prezentační vrstva

Business logika

Perzistenční vrstva

Modul 1

Modul 2

Obrázek 1.1: Třívrstvá architektura

Modul 3

Obrázek 1.2: Vertikální separace

(12)

13 Dobrou známkou návrhu modulů je tedy v konečné fázi vysoký stupeň funkční koheze a nízký počet vazeb. Pokud je do modulu umístěna třída, která je více závislá na třídách z jiného modulu, měla by být přesunuta. Z pohledu vazeb je tedy teoreticky nejlepší modul, který nemá žádné vazby na jiné moduly. V praxi toho ovšem nelze vždy dosáhnout, a tak je snaha alespoň tyto vazby oslabit například pomocí návrhového vzoru Separated Interface [Fowler2002]. Podle tohoto vzoru by měl být modul rozdělen na dvě knihovny, kde jedna obsahuje rozhraní a druhá implementaci.

V případě závislosti by pak závislý modul měl referenci pouze na knihovnu s rozhraním. Je ovšem zapotřebí mechanizmus, který bude vytvářet instance implementující rozhraní a tyto instance pak předávat závislému modulu. Technika, která se tímto problémem zabývá, se jmenuje

Inversion of Control a je popsána v části 1.3.

Horizontální a vertikální separace se navzájem nevylučují. Lze tedy při návrhu aplikace použít obě strategie a využít jejich výhod. V případě modularizace není řešení složité, protože stačí agregovat související kód do oddělených projektů. Složitější je dosáhnout vrstvené architektury v těchto modulech.

1.1.3 Model-View-Controller

V dnešní době se často vytváří uživatelské rozhraní přetahováním ovládacích prvků na formulář a obsluhou událostí přímo ve třídě formuláře. Tuto architekturu Martin Fowler nazval „Forms and Controls“ [Fowler2006]. Jde o jednoduchý a rychlý způsob vhodný pro malé aplikace. Má ovšem několik zásadních negativních vlastností.

Takto napsaná aplikace se velmi obtížně testuje, protože aplikační logika je převážně implementována v obsluze událostí ovládacích prvků. Dále není aplikační logika přenositelná – nelze z klasické aplikace jednoduše vytvořit webovou aplikaci nebo použít jen jiný prezentační framework. V neposlední řadě aplikace není modulární, protože se v ní uplatňují silné vazby a kód se také obtížně udržuje.

Uvedené nedostatky se snaží řešit jiné architektury respektive architektonické vzory (architectural patterns), které vývojáři pomohou odstranit uvedené problémy při návrhu aplikace. Jedním z nich je Model-View-Controller (MVC), jehož hlavním cílem

Prezentační vrstva

Business logika

Perzistenční vrstva

Modul 3 Modul 2

Modul 1

Obrázek 1.3: Kombinace horizontální a vertikální separace

(13)

14 je separovat business logiku od uživatelského rozhraní. Schéma vzoru tvoří komponenty Model, View a Controller jejichž asociace jsou znázorněny na následujícím obrázku.

Model je často reprezentován doménovým modelem, kde jednotlivé třídy zastupují objekty reálného světa. Model neobsahuje žádnou přímou asociaci na View nebo Controller, ale může být těmito komponentami modifikován. View je vizuální reprezentace modelu. Controller má na starosti hlavně přijímat vstup od uživatele a na jeho základě provádět změny v modelu.

View a Controller pracují společně, aby umožnily uživatelovi pracovat s modelem. Zatímco Controller se stará o vstup uživatele, View má na starosti výstup.

Každý Controller je asociován právě s jednou komponentou View a ta je asociována vždy právě s jedním Controllerem. View i Controller mají asociaci na Model, ovšem Controller jej má za účelem modifikace, zatímco View ji má za účelem získání dat, které má zobrazit. Nepřímá asociace mezi Modelem a View je důsledkem implementace návrhového vzoru Observer. View se u Modelu zaregistruje za účelem upozorňování na změny. Dohromady to tedy funguje tak, že na základě akce uživatele Controller modifikuje model, ten upozorní View že došlo ke změně a View je promítne na výstupu. Stejně tak může Controller modifikovat pouze View, například pokud má být výsledkem uživatelské akce zobrazení jiné záložky na formuláři. Nevýhodou tohoto vzoru může být fakt, že pro každý prvek, který může vyvolat nějakou akci (například tlačítko), musí existovat Controller. Tvorba sofistikovaných uživatelských rozhraní se tak stává komplikovanější.

1.1.4 Model-View-Presenter

Model-View-Presenter je variace MVC a jeho kořeny sahají do let 1995-96.

Samotný vzor je založen na programovacím modelu společnosti Taligent, což byl společný podnik IBM a Apple (poté se přidal i Hewlett Packard). Tento model byl poté

Obrázek 1.4: Struktura MVC

Controller

View Model

Přímá asociace Nepřímá asociace

(14)

15 formálně zjednodušen týmem vyvíjející jazyk Dolphin Smalltalk. Schéma vzoru MVP je téměř identické se schématem MVC. Jediná změna je v nahrazení bloku Controller blokem Presenter, takže hlavní rozdíl MVP vzoru spočívá ve

zpracování uživatelských akcí. Kliknutí na tlačítko nyní registruje View a tuto akci pouze deleguje Presenterovi, který modifikuje Model. V případě MVP už není zapotřebí mít pro každý prvek Controller a navíc několik komponent View může pracovat s jedním Presenterem. Přestože může v tomto uspořádání View přímo modifikovat Model, není tato možnost příliš využívána.

V roce 2006 Martin Fowler rozdělil MVP na dvě hlavní varianty – Supervising Controller a Passive View (Presenter a Controller jsou často brány jako ekvivalentní komponenty).

Supervising Controller má stejné schéma, ale nově i definované úlohy. View se stará o jednoduchou prezentační logiku, zatímco

Presenter/Controller modifikuje model nebo provádí jiné komplexní procesy. Na rozdíl od Supervising Controller, varianta Passive View se již svým schématem liší, protože je zrušena jakákoliv vazba mezi View a Modelem. Často je vhodné navíc abstrahovat View od Presenteru pomocí rozhraní, což umožní jednoduchou výměnu celé View komponenty. Příkladem může být více reprezentací jedné webové stránky, kde je zvlášť View pro klasické zobrazení, pro tisk, pro mobilní zařízení s malým displejem nebo speciální verze pro nevidomé.

1.2 Aspektově orientované programování

Objektově orientované programování (OOP) je v současné době bezesporu nejrozšířenější paradigma vývoje softwaru. OOP má kořeny v 70. letech 20. století, kdy bylo oblíbené modulární programování, které se snažilo zjednodušit vývoj a údržbu softwaru oddělením jednotlivých částí do modulů. S vyspělejším hardwarem ovšem rostla také složitost softwaru a výzkum se začal zabývat hledáním metodiky, která by této složitosti dokázala čelit. Výsledkem byl diametrálně jiný pohled na vývoj softwaru.

Zatímco modulární programování oddělovalo kód a data, OOP je do určité míry spojilo v objektech, které mezi sebou komunikují. Pojem OOP byl poprvé použit v roce 1970 kdy Xerox PARC (Pao Alto Research Center) uvedl jazyk Smalltalk.

OOP zcela jistě pomáhá architektům jednoduše a efektivně modelovat doménu a vytvářet tak kvalitní software, ovšem některé problémy se pomocí tohoto paradigmatu

Presenter View

Model

IView

Obrázek 1.5: MVP Passive View

(15)

16 řešit nedají. Jedním z těchto problémů jsou takzvané Crosscutting concerns, kterými se také zabývalo vývojové centrum PARC a odpovědí na tento problém je pak rozšíření OOP paradigmatu s názvem Aspektově orientované programování (AOP).

1.2.1 Crosscutting concerns

V části 1.1 byl popsán princip Separation of Concerns jako jeden ze stavebních kamenů kvalitního návrhu. Existují ovšem části kódu, které jednoduše separovat nelze, a prorůstají celým projektem. Takové části, mezi které patří například logování nebo trasováním se nazývají průřezové (crosscuting) a OOP si s nimi nedokáže efektivně poradit

Na obrázku 1.6 je vidět, jak průřezové části zasahují do jinak dobře navrhnuté třívrstvé architektury. Již zmíněné logování nebo trasování totiž probíhá na začátcích (nebo i koncích) většiny metod, takže zasahují do všech vrstev, tříd a metod – v podstatě „prořezávají“ celou aplikaci. Negativním důsledkem je např. duplicita kódu a z toho vyplývající komplikace při modifikaci těchto částí. Samotná metoda pak navíc obsahuje kód, který nemá nic společného s jejím účelem a paradoxně může být tento kód delší než původní logika metody.

1.2.2 Aspekty

Cílem AOP je zapouzdřit průřezové části do konstruktů, které se nazývají aspekty. Tyto aspekty implementují tzv. advice (dodatečné chování), které se aplikují na místa určená přípojnými body (join points). Jednoduše řečeno, jde o injekci kódu do určitých míst vybraných metod. Tato injekce kódu se nazývá proplétání (weaving) a může být realizována několika způsoby (v závislosti na implementaci AOP).

Prezentační vrstva

Business logika

Perzistenční vrstva

Obrázek 1.6: Vizuální znázornění průřezových částí

(16)

17

 Preprocesor

Proplétání proběhne ještě před samotnou kompilací. Preprocesor se používá v případech, kdy kompilátor AOP nepodporuje.

 Postprocesor

Proplétání proběhne až po kompilaci injektováním do výsledného modulu.

Stejně jako u preprocesoru nemusí kompilátor podporu AOP implementovat.

AOP kompilátor

Proplétání proběhne při samotné kompilaci. Jde o lepší řešení, protože není potřeba žádný další software a dá se očekávat podpora vývojového prostředí.

Propletení při zavedení

Proplétání provede běhové prostředí při zavádění modulu do paměti.

Propletení za běhu

V tomto případě běhové prostředí podle přípojných bodů vykonává advice a k samotnému propletení kódu tedy fyzicky nedojde.

1.3 Inversion of Control

Inversion of Control (IoC) je technika, která vývojářům pomáhá v odstranění silných vazeb mezi třídami. U malých projektů není problém vytvářet asociace na jiné konkrétní třídy, ovšem u větších projektů to přináší řadu komplikací. Mezi hlavní patří například nutný zásah do několika částí kódu při úpravě jedné třídy (tento problém může částečně řešit vývojové prostředí) nebo pevné svázání s jiným modulem, což brání jeho jednoduché výměně v systému. Princip IoC tedy spočívá v tom, že třída si sama nevytváří asociace na jiné konkrétní třídy, ale reference na instance jsou předány třetí stranou. Velmi jednoduchým příkladem může být model o dvou třídách. První třída (FilesFinder) má za úkol vrátit seznam souborů, jejichž název odpovídá zadanému

Obrázek 1.7: Princip proplétání Prezentační vrstva

Business logika

Perzistenční vrstva Aspekty

Prezentační vrstva

Business logika

Perzistenční vrstva

(17)

18 kritériu. Druhá třída (XmlFileSource) poskytuje seznam všech souborů uložených v XML souboru. Nejjednodušší řešení je znázorněno diagramem na obrázku.

Obrázek 1.8: Přímá asociace

Prvním krokem při optimalizaci bude nahrazení asociace na konkrétní třídu.

Konkrétní typ bude nahrazen rozhraním (IFileSource) a XmlFileSource jej bude implementovat. Tato úprava vede na logičtější strukturu, protože třída FilesFinder

konzumuje službu a ignoruje implementaci této služby. FilesFinder nyní tedy může získávat seznam i z jiných zdrojů implementující IFileSource, ovšem díky přímému vytváření instance by si změna poskytovatele vyžádala zásah v kódu. Druhým krokem tak bude implementace komponenty, která vytvoří instanci zdroje a referenci předá objektu FilesFinder, takže výsledkem je pak úplné zrušení vazby mezi FilesFinder

a XmlFilesSource (popřípadě jinými zdroji). Popsaná implementace IoC principu se nazývá Dependency Injection (DI) a existují tři základní způsoby, jak předat referenci konzumentovi služby.

1. Injekce konstruktorem

Konzument má parametrizovaný konstruktor, ve kterém přijímá referenci na objekt implementující rozhraní služby.

public class FilesFinder {

private IFileSource source;

public FilesFinder(IFileSource source) { this.source = source; }

public string[] Search(string name) {/* implementace */ } }

2. Injekce setterem

Konzument má metodu (případně vlastnost), přijímající jako parametr referenci na objekt implementující rozhraní služby.

(18)

19

public class FilesFinder {

private IFileSource source;

public IFileSource Source

{

set { this.source = value; }

}

public string[] Search(string name) {/* implementace */ } }

3. Injekce rozhraním

Tento způsob je v podstatě identický s injekcí setterem, ale setter je definován rozhraním, které konzument implementuje.

Z uvedených způsobů jsou nejrozšířenější injekce konstruktorem a setterem.

Komponenta provádějící injekci se nazývá builder (někdy také assembler) a k jeho správné funkci je důležitá konfigurace, pomocí které builder pozná, jakým konzumentům má předávat konkrétní instance. Konfigurace nejčastěji probíhá v kódu pomocí metod builderu nebo může být uložena v separátním XML souboru. Výhoda konfigurace pomocí kódu je v tom, že přiřazení služby může proběhnout na základě stavu jiné části systému (konfiguraci lze parametrizovat). Naproti tomu konfigurace pomocí XML se dá upravovat bez rekompilace. Výjimkou však nejsou ani hybridní přístupy pomocí kterých si vývojář může způsob konfigurace vybrat.

Kromě DI existuje ještě další způsob jak realizovat IoC. Nazývá se Service Locator a jeho logika je jednodušší než v případě DI. Základem je komponenta Locator, která má za úkol uchovávat seznam služeb a na vyžádání konzumenta vracet

Obrázek 1.9: Dependency Injection

Konzument Builder

Rozhraní Služba

3. použití služby

1. vytvoření služby 2. vytvoření konzumenta a

předání reference na službu

(19)

20 reference na jejich instance. V podstatě se pouze nahrazuje injekce za explicitní vyžádání ze strany konzumenta. Nevýhodou ale může být fakt, že konzument musí vědět o existenci Locatoru.

1.4 Kontrakty

Cílem každého vývojáře je bezesporu tvořit kvalitní a bezchybný kód. Vytvořit komplexní bezchybnou aplikaci je prakticky nemožné, ovšem lze se k tomuto stavu pomocí známých metodik přiblížit. Jednou z metodik zabývajících se spolehlivostí je Design by Contract (DbC), jejíž cílem je explicitně deklarovat chování softwarových entit (v objektově orientovaných jazycích jsou to metody a objekty).

1.4.1 Design by Contract

Termín Design by Contract poprvé použil Bertrand Meyer v roce 1986 a v roce 2004 se stal registrovanou obchodní značkou. Tato metodika uvažuje softwarový systém jako skupinu komunikujících entit, kde se interakce řídí přesně danými pravidly – mají mezi sebou kontrakt [Meyer]. Tento kontrakt je definován třemi základními typy podmínek:

Preconditions je sada podmínek, které musí být splněny před tím, než se začne vykonávat metoda. Tato kontrola zahrnuje obecně vstupní argumenty a stav systému respektive instance. Preconditions tedy stanoví podmínky, které musí entita splnit před samotným zavoláním příslušné metody.

Postconditions je naopak sada podmínek, která musí být splněna po ukončení metody. Podmínky mohou zahrnovat stav systému před a po vykonání metody, argumenty metody a návratovou hodnotu. Postconditions zaručují volající entitě, že bylo provedeno to, co bylo žádáno.

Invariants jsou podmínky, které musí být dodrženy kdykoliv. Tyto podmínky mohou být chápány jako specifikace objektu a zaručují, že tyto specifikace budou

Obrázek 1.10: Service Locator

Konzument Locator

Služba A

Služba B

(20)

21 dodrženy. Prakticky jsou podmínky kontrolovány na začátku a na konci veřejných metod.

Bertrand Meyer metodiku DbC implementoval v jazyce Eiffel, a přestože ho velké množství předních hráčů na poli jazyků/kompilátorů nenásleduje, existuje pro každou rozšířenou vývojovou platformu několik frameworků a nástrojů pomáhající vývojáři DbC aplikovat. Následující metoda demonstruje, jakým způsobem jsou v jazyce Eiffel definovány preconditions a postconditions:

-- Vložení "x" do kolekce tak, aby byl přístupný pomocí klíče "key".

put (x: ELEMENT; key: STRING) is require

count < capacity not key.empty do

-- implementace vložení ensure

has(x)

item(key) = x

count = old count + 1 end

Jak je patrné, precondition jsou definovány v bloku require a postconditions v bloku ensure uvnitř metody. Vstupní podmínky kontrolují, zda kolekce nepřekročila kapacitu a zda klíč není prázdný. Výstupní podmínky pak kontrolují, zda je x v kolekci, zda se dá získat pomocí klíče a zda se inkrementoval počet položek. Invariants se implementují analogicky pomocí klauzule invariant v těle třídy:

invariant

0 <= count

count <= capacity

Jazyk Eiffel a celá metodika DbC je samozřejmě komplexnější, ovšem zmíněné principy jsou klíčové.

(21)

22

1.4.2 Možnosti deklarace

Nativní podpora jazyka

Eiffel není v současné době jediný jazyk, který implementuje metodiku DbC, ovšem nejpopulárnější jazyky jako C++, Object Pascal / Delphi, C#, Visual Basic nebo Java syntakticky DbC nepodporují. Za zmínku stojí jazyk Spec# z dílny Microsoft Research. Tento jazyk je však experimentální a není jisté kdy a jestli vůbec bude podpora DbC přenesena i do některé budoucí verze jazyka C#. Následující kód ukazuje, že syntaxe je podobná jako v jazyce Eiffel.

class ArrayList {

public virtual void Insert(int index, object value) requires 0 <= index && index <= Count;

requires !IsReadOnly && !IsFixedSize;

{ ... }

Jde o jistě nejlepší řešení, ovšem znamenalo by to napsat vlastní kompilátor respektive preprocesor a zajistit podporu vývojového prostředí.

Deklarace v komentářích

Jestliže jazyk implicitně DbC nepodporuje, musí se podmínky deklarovat jiným způsobem. Jedním z nich je i zápis v komentářích jako například v případě frameworku iContract pro platformu Java.

/**

* @pre f >= 0.0

* @post Math.abs((return * return) - f) < 0.001

*/

public float sqrt(float f) { ... }

Řešení pomocí komentářů je komplikované, protože je nutné explicitně kód v komentářích zkompilovat (popřípadě provést preprocesing) a vložit na patřičná místa.

Z pohledu vývojáře je pak nepříjemná hlavně ztráta podpory vývojového prostředí.

(22)

23 Externí třída

Nejméně komplikovaná cesta je vytvořit třídu se statickými metodami Require, Ensure a Invariant, které přijímají jeden booleovský parametr. Pokud parametr bude nabývat hodnoty false, volaná metoda učiní patřičné kroky (nejčastěji vytvoření výjimky).

public int Foo(int x) {

Check.Require(1 < x);

// implementace metody Check.Ensure(result < 15);

return result;

}

Výhodou externí třídy je jednoduchost, ale na druhou stranu velice snadno může dojít k duplicitě a navíc metoda obsahuje kód, který nemá nic společného s jejím hlavním účelem.

Dekorace atributy

Většina moderních jazyků podporuje dekorování tříd, metod, parametrů a jiných konstrukcí takzvanými atributy (anotace v jazyce Java), které slouží převážně k uchování metadat dekorované entity. Pomocí atributů se tak může zjednodušit získávání podmínek, které např. iContract uchovává v komentářích.

[Precondition("!Full", Label="Stack Overflow")]

[Postcondition("!Empty")]

[Postcondition("Total == Total.Old + 1")]

public void Push(object x) {...}

Problém s podporou vývojového prostředí však přetrvává, protože kód je stále uchován v řetězci. Daleko systematičtější je pomocí atributů deklarovat určitá předdefinovaná omezení:

[NotNullValidator][StringLengthValidator(1,200)]

public string Name {

get { return name; } set { name = value; } }

(23)

24 Uvedený způsob deklarace sice nemá takové možnosti jako předchozí řešení, ale je daleko čistější.

Zbývá se tedy ještě rozhodnout, jakým způsobem bude docházet k validaci. Ke kontrole může docházet explicitně voláním metody validační třídy, která pomocí reflexe porovná omezení s aktuální hodnotou. Další možností je využít dynamických proxy tříd a nechat v době běhu vytvořit zástupný objekt, který před zavoláním metody původního objektu provede kontrolu omezení. První jmenovaná možnost vyžaduje ale další kód a dynamické proxy třídy zase mohou způsobit snížení výkonu aplikace. Naštěstí existuje ještě jeden způsob – po kompilaci nechat na příslušná místa injektovat kód na základě atributů.

1.5 Test-Driven Development

Žádný vývojový tým ani programátor, není schopen vytvořit bezchybnou aplikaci, a proto se nedílnou součástí vývoje jakéhokoliv softwaru stalo i jeho testování. U tradičních metod vývoje softwaru se rozsáhlejší testování provádělo v době, kdy byla aplikace téměř hotová. V roce 1999 ovšem Kent Beck představil metodikou extrémního programování zahrnující nové postupy vývoje softwaru. Jednou z částí extrémního programování byl i základ pro současný Test-Driven Development (TDD) modifikující paradigma testování a vývojový cyklus. Podle TDD se totiž má začít testovat ještě dříve, než se začne psát produkční kód. Tento postup se nazývá Red/Green/Refactor a jeho filozofie spočívá v tom, že se nejprve napíše test, poté se spustí a samozřejmě selže (Red). Následně se implementuje minimální část zajišťující úspěšné proběhnutí testu (Green) a poté se mohou aplikovat úpravy jako například odstranění duplicitního kódu (Refactoring). Postup Red/Green/Refactor zajistí, že produkční kód bude funkční, minimální a díky krátkým intervalům mezi testy je při selhání některého testu okamžitě známa příčina. Následující zjednodušený obrázek ilustruje základní myšlenku pomocí diagramu.

(24)

25

Obrázek 1.11: Diagram vývoje řízeného testy

Ačkoliv se může zdát, že TDD nemá mnoho společného s návrhem architektury, opak je pravdou. Není pochyb, že nejlépe se testují naprosto izolované třídy, které nemají vazby na jiné třídy. Vývoj řízený testy tedy vývojáře nepřímo nutí k co největší separaci, protože jinak se tvorba testů stane velice obtížnou disciplínou.

1.6 Modelování domény

Účelem počítačových aplikací je pomoci svým uživatelům řešit problémy respektive automatizovat operace k dosažení určitého výsledku. Problémová oblast, ve které uživatel aplikaci používá, se pak nazývá doména, která může být zvláště ve firemní sféře velmi komplexní. Před samotným vývojem či návrhem aplikace je tedy potřeba provést analýzu a vytvořit abstrakci domény – model. Model obsahuje koncepty, vztahy, případně pravidla, která jsou důležitá pro funkci aplikace a čím přesněji je model v kódu zachycen, tím srozumitelnější pak kód je.

Eric Evans ve své knize [Evans] popisuje návrh aplikace řízený doménou (Domain-Driven Design), kde vše začíná komunikací s odborníkem na doménu. Během této komunikace se analytik snaží zachytit podstatné pojmy, které by měly být součástí modelu. Důležité ale je, že tyto pojmy jsou následně striktně použity v samotném kódu

(25)

26 jako názvy tříd, metod nebo vlastností. Mimo přehledného a dobře udržovatelného kódu navíc odborník na doménu, analytik a vývojáři používají společný jazyk (označovaný jako ubiqitous languge), což zabraňuje nedorozuměním a usnadňuje komunikaci především u větších vývojových týmů.

Martin Fowler pak ve [Fowler2003] poukazuje na rozšiřující se praktiku vytváření modelu, který se sice skládá ze správně nazvaných a dobře propojených tříd, ale na rozdíl od správného doménového modelu tyto třídy neimplementují metody. Veškeré chování je přesunuto do služeb a objekty v modelu slouží pouze jako nositelé dat.

Fowler takový model nazval anemický (Anemic Domain Model) a nejedná se o správnou techniku v kontextu Domain-Driven Design. Je ovšem důležité zdůraznit, že třídy by měli implementovat pouze chování spadající do vrstvy business logiky, takže zanesení prezentační nebo perzistenční logiky do modelu je porušení principu Separation of Concerns.

1.6.1 Domain-Specific Language

Zatímco se model obvykle implementuje pomocí univerzálních (general puprose) jazyků jako je C# nebo Java, Martin Ward ve své prácí [Ward] popisuje zcela odlišný způsob vycházející ze skutečnosti, že jedno z největších zvýšení produktivity v historii vývoje softwaru bylo dosaženo použitím jazyků vyšší úrovně. Kód ve vysokoúrovňovém jazyce je bezesporu menší, čitelnější a lépe pochopitelný než například v jazyce symbolických adres. Stejný princip lze aplikovat i na samotnou doménu, pro kterou lze navrhnout speciální jazyk, který se označuje jako Domain- Specific Language (DSL) a způsob vývoje se pak nazývá Language Oriented Programming.

Hlavním rysem doménového jazyka je jeho jednoduchost, protože na rozdíl od univerzálních jazyků je navrhnut pro řešení konkrétního problému. V případě složitých domén lze pomocí DSL dosáhnout implementace s menším objemem kódu a DSL navíc zachycuje i celou znalost o doméně. Jednou z nezanedbatelných výhod je ovšem rozšiřitelnost systému samotnými uživateli. Jelikož jsou DSL velmi jednoduché, mohou sami uživatelé pomocí vhodných nástrojů snadno zasahovat do systému a zásadně tak ovlivňovat celkové chování. Vliv na to má i fakt, že DSL jsou abstraktní vzhledem ke způsobu vykonání, takže uživatel nemusí řešit například práci s pamětí, což je typické při vývoji v univerzálním jazyce.

(26)

27 Stejně jako každá technologie, i použití DSL má své nevýhody. Navrhnout dobrý jazyk je těžké a navíc je nutné pro něj vytvořit i kompilátor popřípadě ještě vývojové prostředí. Naštěstí jsou pro různé platformy k dispozici nástroje, které tuto hlavní nevýhodu do jisté míry redukují.

1.7 Objektově-relační mapování

Dá se konstatovat, že téměř všechny aplikace pracují s daty, kterých je stále více a mají převážně trvalý charakter. Každý vývojář tak musí řešit problém perzistence, což je způsob, jak uchovat data i po ukončení aplikace, která je vytvořila. V osmdesátých letech 20. století se začaly uplatňovat relační databáze postavené na modelu tabulek, jejich sloupců, řádků a relací mezi nimi. Relační databáze jsou nesmírně flexibilní a robustní co se týče spravování uložených dat, ovšem zatímco se programátorský svět přeorientoval na objektový a aspektový přístup, databáze jsou víceméně stejné. Stále se data získávají pomocí dotazů v jazyce SQL, který každý databázový systém implementuje trochu jinak – každá databáze má svůj SQL dialekt. Tato skutečnost je při návrhu aplikace nepříjemná, protože vnáší do systému složitost reprezentovanou hlavně použitím neobjektového přístupu. Při přechodu na jinou databázi navíc hrozí, že se budou muset přepsat SQL dotazy do jiného dialektu.

Jelikož relační databáze těm objektovým v blízké budoucnosti zřejmě razantně neustoupí, je jediným řešením abstrakce relačního modelu. Mechanismus, který odstiňuje vývojáře od relačního úložiště, se nazývá objektově-relační mapování (ORM).

Mapování je postup explicitního vyjádření toho, jakým způsobem se mají objekty ukládat do relační databáze a na základě takového mapovaní pak mohou perzistenci objektů respektive jejich získávaní z databáze za vývojáře provádět ORM frameworky.

1.7.1 Teorie objektově-relačního mapování

Podle [Mehta] je ORM způsob, jakým se dá automatizovat propojení objektového (doménového) modelu a relační databáze pomocí metadat. Bez frameworků vývojáři museli často toto propojení (logiku přístupu k datům) řešit pomocí metod, které na základě parametrů sestavily SQL výraz, a případně z typově slabého výsledku dotazu materializovali příslušné instance.

Jak plyne z výše uvedené definice, ORM frameworky se snaží tuto práci automatizovat na základě metadat, která mapují třídy na tabulky relační databáze.

Kvalitní frameworky ovšem nabízí další funkce jako například kontrolu konkurentních

(27)

28 modifikací, víceúrovňový caching, automatické verzování, lazy loading nebo sledování změn. Ať už ale vývojář použije jakýkoliv framework, bude muset provést mapování1.

Přestože se objektový svět od toho relačního značně liší, lze si všimnout podobnosti některých základních konstrukcí. Následující tabulka tuto intuitivní podobnost zachycuje vzájemným přiřazením jednotlivých konstrukcí, a jelikož jde o zažitý model, je možné tento způsob mapování očekávat od naprosté většiny frameworků.

OOP Relační databáze

Třída Tabulka

Atribut Sloupec

Instance třídy Řádek

Reference na objekt Relace one-to-one

Reference na kolekci objektů Relace one-to-many

U velmi jednoduchých doménových modelů si lze vystačit s uvedenou tabulkou, ovšem ve skutečnosti se v modelu používají konstrukce, pro které nemají relační databáze žádnou náhradu. Jednou z takových konstrukcí je například dědičnost.

1.7.2 Mapování dědičnosti

Dědičnost je jedním ze základních pilířů OOP, a zatímco některé tzv. post-relační databáze jako PostgreSQL ji nativně podporují, stále existuje více databází, ve kterých se musí dědičnost zachytit jiným způsobem. Obecně existují tři základní techniky jak mapovat dědičnost v relační databázi.

Table per Hierarchy (TPH)

Z obrázku je patrné, že celá hierarchie je mapována do jedné tabulky, která obsahuje sloupce pro atributy všech tříd. Navíc je ovšem potřeba uložit, jakému typu odpovídají jednotlivé řádky (instance). K tomuto účelu obsahuje tabulka navíc sloupec zvaný diskriminátor, který typ instance na příslušném řádku jednoznačně identifikuje.

1 Některé frameworky podporují i implicitní mapování založené na konvencích.

(28)

29 Výhodou této techniky je jednoznačně rychlost. Při získávání objektů konkrétnějších typů není potřeba provádět spojování s jinými tabulkami, které uchovávají atributy předků. Nevýhodou pak je nutnost nastavit všechny sloupce konkrétnějších typů jako nullable. Toto omezení je dáno tím, že atributy které nadřazený typ nedefinuje, jsou frameworky často nastaveny na null. Dále v tabulce může snadno dojít k závislosti mezi neklíčovými sloupci, takže nemusí tabulka vždy vyhovovat 3. normální formě.

Table per Concrete Class (TPCC)

Při mapování technikou TPCC obsahuje databáze tabulku pro každou třídu, která v hierarchii nemá žádného potomka. Tyto tabulky pak obsahují atributy nejen konkrétní třídy, ale také všech předků.

Na rozdíl od TPH je databázové schéma v případě složitějších hierarchií daleko přehlednější a zároveň získávání instancí na rychlosti téměř neztrácí. Velkou nevýhodou TPCC jsou však polymorfní asociace. Pokud by nějaká třída držela referenci na třídu

Obrázek 1.13: Technika Table per Concrete Class Sportovec

Hokejista Atlet

atlet

Hokejista Atlet

Sportovec

Hokejista Atlet

atlet

Sportovec

Obrázek 1.12: Technika Table per Hierarchy

(29)

30 Sportovec (obecně na jakoukoliv nadřazenou), není možné tuto referenci v databázi řešit pouze jedním cizím klíčem, protože třída je „rozprostřena“ ve více tabulkách. Další nevýhodou pramenící z rozprostření nadřazených tříd je redundance. Při modifikaci nadřazené třídy se musí modifikovat schéma všech tabulek příslušných podřízených tříd.

Table per Class (TPC)

Technika TPC je podobná TPCC, ale svoji tabulku mají všechny třídy v hierarchii dědičnosti (včetně abstraktních). Každá tabulka obsahuje atributy pouze pro danou třídu, takže schéma již nemusí být redundantní a všechny tabulky mohou být alespoň ve 3. normální formě. Vztah mezi předkem a potomkem je pak zachycen pomocí cizího klíče, který zpravidla bývá i primární.

TPC řeší většinu problémů popsaných v předchozích technikách, ovšem potřeba spojování tabulek při získávání instancí konkrétnějších typů implikuje nižší výkon.

Pokud mapování dědičnosti provádí vývojář manuálně, rozhodnutí o technice závisí vždy na prioritách a konkrétní hierarchii. Celá aplikace však nemusí využívat pouze jednu techniku, takže různé techniky lze aplikovat na jednotlivé hierarchie v rámci jedné aplikace.

Generic Schema (GS)

V úvodu této části bylo uvedeno, že existují tři základní techniky mapování dědičnosti. Podle [Ambler] ve skutečnosti existuje ještě jedna specifická technika, která je založena na generickém schématu. Zatímco klasicky v databázi reprezentuje tabulka nějakou třídu (respektive hierarchii), v tomto případě tabulka reprezentuje samotné

Obrázek 1.14: Technika Table per Class Sportovec

Hokejista Atlet

atlet

Sportovec

Hokejista Atlet

atlet 1

1 1

1

(30)

31 konstrukce OOP. Obrázek níže demonstruje, jak by mohlo takové schéma být realizováno v databázi. Obrázek také naznačuje, že generické schéma není svázané přímo s dědičností a dá se používat i v aplikacích bez hierarchií tříd prostým odstraněním nebo jen ignorováním tabulky Inheritance.

Výhodou generických schémat je obrovská míra flexibility plynoucí z jednoduché rozšiřitelnosti schématu. Větší počet relací ovšem znamená, že mapování založené na generických schématech podobně jako TPC nebude poskytovat výkon srovnatelný s jednoduššími technikami. Bohužel flexibilita a výkon jdou v případě ORM proti sobě.

1.7.3 Asociace

Z pohledu multiplicity existují tři základní druhy asociací – one-to-one, one-to- many (many-to-one1) a many-to-many, které dále mohou být buď jednosměrné (unidirectional) nebo obousměrné (bidirectional). Naneštěstí implementace je v objektovém a relačním paradigmatu velmi odlišná.

Zatímco OOP implementuje všech 6 kombinací pomocí referencí a kolekcí, relační databáze implementují asociace pomocí cizích klíčů, které však mají omezené možnosti, protože samotný cizí klíč v podstatě vytváří asociaci one-to-many. Asociace one-to-one se dá vynutit například tím, že cizí klíč bude unikátní nebo přímo primární.

Asociace many-to-many se pak obvykle simuluje vytvořením tzv. asociační tabulky, která spojuje obecně více řádků z více tabulek. Naštěstí pro tuto asociační tabulku není nutné vytvářet speciální třídu, protože jde o velmi zažitý vzor a ORM frameworky jej podporují.

1 Many-to-one a one-to-many budou dále zaměnitelné.

Class

Attribute Value

atlet

Inheritance

2 0..*

1..*

1

1..* 1

AttributeType

0..* 1

Obrázek 1.15: Mapování pomocí generického schématu

(31)

32 Další důležitý rozdíl mezi referencí a cizím klíčem spočívá v tom, že reference je vždy jednosměrná, zatímco cizí klíč vždy obousměrný. Obousměrná asociace v doménovém modelu se dá realizovat pomocí dvou referencí, které jsou ale rozdílné1. Jednoduché řešení pomocí dvou cizích klíčů nepřipadá v úvahu, protože by vznikla cyklická závislost. Naštěstí kvalitní ORM frameworky nastaví automaticky oba konce reference, pokud jsou explicitně definovány reference tvořící obousměrnou asociaci.

1.7.4 Identita

Jedním z rozdílů mezi OOP a relačními databázemi je definice identity. Objekty v paměti jsou identické pouze tehdy, mají-li v této paměti stejnou adresu. Perzistentní objekty (reprezentované řádkem v tabulce) jsou identické jen tehdy, pokud mají stejnou hodnotu primárního klíče. Platformy jako .NET nebo Java navíc definují ještě ekvivalenci, která je reprezentována metodou Equals. Tato metoda se dá překrýt (override), takže si může vývojář implementovat kontrolu ekvivalence podle svého uvážení. Obvykle jsou ale dva objekty ekvivalentní, pokud nabývají stejné hodnoty (nemusí tedy být nutně identické).

Otázka identity je v ORM velmi důležitá, protože kdyby framework po každém dotazu na konkrétní objekt vytvářel úplně novou instanci, existovalo by v paměti mnoho neidentických instancí reprezentující jeden perzistentní objekt. Řešení tohoto problému nabízí [Fowler2002] v podobě vzoru Identity Map, který bude popsaný později.

1.7.5 Objektově-relační návrhové vzory

ORM a přístup k datům obecně je velmi komplexní oblast, která zahrnuje velké množstvím složitých problémů. Není tedy divu, že vznikly návrhové vzory zabývající se právě touto problematikou. Následující vzory detailně popsané v [Fowler2002] jsou základem pro mnoho kvalitních frameworků.

Unit of Work

V případě okamžitého ukládání změn modifikovaných objektů může docházet k velkému počtu malých volání databáze. Daleko efektivnější by ale bylo tyto změny sledovat a poté provést jedno velké volání. Unit of work je v tomto případě objekt, který sleduje jednotlivé modifikace a volání databáze co nejdéle odkládá.

1 Reference na objekt B v objektu A není identická s referencí na objekt A v objektu B.

(32)

33 Identity Map

Jak bylo zmíněno dříve, vzor Identity Map má za úkol zabránit vytváření více instancí jednoho perzistentního objektu. Implementace je velmi jednoduchá, protože stačí ukládat reference na získané objekty a v případě dalšího požadavku jen kontrolovat, zda je objekt již v paměti. Tento vzor tak v podstatě implementuje i cache a zvyšuje rychlost při vícenásobném požadování stejných dat.

Lazy Loading

Jeden z problémů provázející získávání objektů z databáze spočívá v tom, že není vždy předem jasné, zda bude zapotřebí načíst i objekty na které má dotazem získaný objekt referenci. Nejjednodušší řešení je bezesporu načtení celého grafu objektů, ale čím větší takový graf je, tím je tato operace náročnější a zároveň je menší šance, že budou zapotřebí všechny instance. Vzor Lazy Loading předchází plýtvání systémových prostředků tím, že související objekty se získávají z databáze, až když se k nim pokusí někdo přistoupit.

Optimistic Offline Lock

V aplikacích, kde je možné konkurenčně modifikovat data, může velice snadno dojít ke konfliktům. Tyto konflikty často nastávají v případě, kdy si uživatel vyžádá objekt, modifikuje ho a následně ukládá do databáze, ovšem mezi načtením objektu a jeho uložením došlo k modifikaci jiným uživatelem. V takovém případě by se dřívější změny nemusely promítnout, což je často nežádoucí efekt.

V podstatě existují dva základní přístupy, jak řešit tyto konflikty - pessimistic concurrency a optimistic concurrency. Pesimistický přístup pracuje (zjednodušeně řečeno) na principu uzamčení řádků, takže pokud si uživatel vyžádá jistý objekt, příslušný řádek (nebo více) se uzamknou a nikdo jiný nemůže provádět změny dokud nebude zámek uvolněn. Tento přístup je velmi účinný a splehlivý, ovšem za velkou cenu – je nutné udržovat spojení s databází a je možnost vzniku tzv. deadlocku, kdy uživatelé vyžadují navzájem zamknutá data.

Optimistický přístup zámky nepoužívá, takže je v první řadě nutné umět kolizi detekovat a následně ji ošetřit. Existuje několik možností implementace, ale vzor Optimistic Offline Lock využívá techniku verzování řádků. U této techniky mají tabulky navíc sloupec, do kterého se ukládají verze jednotlivých řádků (nejčastěji jako celé číslo). Při modifikaci nebo odstranění objektu pak SQL příkazy neidentifikují

(33)

34 příslušný řádek pouze primárním klíčem, ale i verzí. Pokud je modifikace úspěšná, dojde zároveň i k inkrementaci verze. Z uvedených skutečností pak plyne, že není možné modifikovat řádek, který mezitím upravil někdo jiný, protože verze budou rozdílné.

(34)

35

2 Implementace webové aplikace

Zatímco v předchozí kapitole byly představeny techniky a vzory sloužící k redukci symptomů nekvalitního návrhu, tato kapitola se bude zabývat především jejich použitím při vývoji webové aplikace. Jak naznačí část o použitých technologiích, aplikace není napsána kompletně od začátku, ale je realizována kombinací vhodných frameworků, takže nejde o příslovečné znovuobjevování kola.

2.1 Použité technologie

Přestože se dnes k vývoji webových stránek ve velké míře využívá preprocesor PHP, k implementaci informačního systému je využita platforma .NET. Mezi hlavní výhody oproti PHP patří především typová bezpečnost, možnost vyvíjet ve více jazycích, konzistentní základní knihovna nebo vyšší výkon daný kompilací zdrojových kódů.

Součástí .NET Frameworku je pak sada knihoven ASP.NET, která na serveru poskytuje prostředí pro dynamické vytváření dokumentů, které se na žádost posílají klientovi protokolem HTTP. Nejčastěji jsou to dokumenty HTML (webové stránky) a XML (webové služby). Hlavním rysem ASP.NET je snaha co nejvíce eliminovat rozdíl mezi vývojem klasické desktopové a webové aplikace. Bohužel způsob vývoje kopíruje princip „Forms and Controls“, tedy přetahování prvků na formulář a obsluhu událostí přímo v těle třídy formuláře. V předchozí kapitole bylo uvedeno, že tento způsob je nevhodný pro větší aplikace a do značné míry porušuje princip Separation of Concerns.

K vývoji tedy byl použit navíc framework Web Client Software Factory (WCSF), který zapouzdřuje zejména podporu Model-View-Presenter (ve formě Passive View) a Inversion of Control.

Dále byl využit objektově-relační framework NHibernate, který reprezentuje v podstatě celou perzistenční vrstvu a implementuje všechny základní objektově-relační návrhové vzory popsané v předchozí kapitole.

2.2 Architektura

Lze si všimnout, že základní symptomy jako rigidita, fragilita nebo imobilita jsou ve velké míře důsledkem existence skrytých nebo nelogických závislostí v kódu.

(35)

36 Eliminace těchto závislostí pomocí principu Separation of Concerns byla tedy jedním z hlavních cílů při implementaci.

V první fázi byly navrhnuty moduly, které jsou reprezentovány samostatnými knihovnami. Moduly pak obsahují třídy, které dohromady plní jasně definovanou funkci, takže jde o funkční kohezi. WCSF dále rozlišuje takzvané foundational a business moduly. Hlavním rozdílem mezi nimi je ten, že foundational moduly obsahují pouze služby, zatímco business moduly i řízení uživatelského rozhraní (presentery).

Foundational moduly tedy v konečném důsledku poskytují služby business modulům a jsou často jádrem systému, což vede k velkému počtu závislostí na foundational moduly. Z tohoto důvodu jsou tedy foundational moduly podle vzoru separated interface rozděleny na dvě knihovny, kde jedna obsahuje rozhraní a druhá implementaci.

Implementovaný informační systém obsahuje celkem tři foundational moduly.

Modul Data poskytuje abstrakci přístupu k datům, modul Security má na starosti autentifikaci, členství a autorizaci. Nakonec modul Infrastructure implementuje převážně služby poskytující informace o navigačním stromu a samozřejmě možnost jeho modifikace.

Základní funkce aplikace dále zajišťují dva nezbytné business moduly. Modul

Shell poskytuje základní prvky uživatelského rozhraní jako například šablonu, do které se umisťuje obsah stránek z ostatních modulů (master page). Druhý modul s názvem

Administration obsahuje uživatelské rozhraní pro správu navigačního stromu, uživatelských účtů a skupin.

Diagram na obrázku 2.1 zachycuje závislosti mezi jednotlivými moduly a navíc zobrazuje i zařazení modulů do jednotlivých vrstev. Blok Website reprezentující celou prezentační vrstvu není z pohledu WCSF modul jako zbylé knihovny, ale jde o koncentraci kódu závislého na způsobu vykreslení uživatelského rozhraní. Aplikace tak může být konvertována na desktopovou nebo mobilní jednoduše výměnou této knihovny. Z diagramu je také patrné, že neexistuje závislost mezi jednotlivými moduly (je nahrazena závislostí na jejich rozhraní), dále závislosti překračující hranice vrstvy jdou vždy o vrstvu níže a nakonec neexistuje závislost překračující několik vrstev (tedy prezentační vrstva není závislá na perzistenční).

(36)

37

2.3 Implementace modulů

2.3.1 Modul Data

Přestože ORM framework NHibernate je již v podstatě implementací perzistenční vrstvy, je nutné vytvořit nadstavbu ve formě služby, kterou lze zaregistrovat v kompozičním kontejneru a podle principu Inversion of Control injektovat instanci této služby příslušným konzumentům. Modul Data ovšem nefunguje pouze jako adaptér, ale také jako další vrstva abstrakce nad samotným frameworkem. V případě, že bude nutné vyměnit ORM framework, místo rušení závislosti na knihovnu NHibernate a přepisování kódu ve všech modulech, stačí vytvořit nový adaptér implementující

Data.Interface.

Obrázek 2.1: Dependency diagram informačního systému Data

Data Interface Infrastructure

Security Security

Interface

Website

Shell Administration Infrastructure

Interface Prezentační vrstva Business logikaPerzistenční vrstva

References

Related documents

Převažují spojky souřadicí konkrétně slučovací, ale velké zastoupení mají spojky podřadicí – příčinné (protože), časové (když). Ze záznamového archu je patrné,

Téma „Implementace informačního systému z pohledu integrátora“ jsem si vybral, jelikož jsem svoji praxi vykonával ve společnosti KTK Software, s.r.o., která se zabývá

Z výše uvedených definic vyplývá, že podnikové informační systémy typu ERP jsou popisovány z mnoha různých pohledů a každá definice akcentuje jiné

2 Spolupráce systému MATLAB s externím hardwarem 14 2.1 Návrh metodou Model in the

Vzhledem k neustále se rozvíjející společnosti a rychlosti technického pokroku je pro fungování podniku informační systém jedním z nejdůležitějších

Název baka!ářské práce: Integrace aplikace Business Intelligence do informačního systému firmy TIBERINA AUToMoTIvE BěIá spol.. s

cíli bylo navrhnout a implementovat datový sklad, ETL procesy a vytvořit sadu multidimenzionálních kostek OLAP pro datovou analýzu finančního a výrobního

Hlavním cílem této diplomové práce byla optimalizace stávajícího IS kvality, která spočívá v nasazení nového řešení IS pro podporu řízení s využitím BI řešení,