Jarda Jirava

Vývojář a architekt řešení postavených na technologii .net framework. Zabývám se jak vývojem webových aplikací za pomoci asp.net, tak také desktopových aplikací winform. Při návrhu řešení a samotném vývoji pak využívám dlouholetých zkušeností se zpracováním obchodní logiky a pravidel aplikací získaných z vývoje komerčních aplikací pro finanční a bankovní instituce.

Microsoft MVP

Microsoft MVP - Client App dev

Poslední příspěvky

analysis/sw architecture

8
V

Názor k použití objektů podle Ronnieho

Minulý týden jsem si přečetl příspěvek Ronnieho "Proč používat objekty - pokračování" a nedalo mi to, abych nereagoval v komentářích. Přeci jen mám trochu jiný pohled na danou problematiku vycházející z mých zkušenosti a dosavadních znalostí.

Pod článkem se rozjela fajn debata, ze které jsem poznal pohled i jiných vývojářů na používání objektů. Pokud jsem správně pochopil jejich výroky, nelíbí se jim možnost používat protected viditelnost u objektů. Konzistence a zapouzdření je pro objekty požadováno, avšak můj názor je takový, že tyto požadavky nejsou porušeny tím, že objekt zpřístupní svůj rozšířený interface svým potomkům. Znamená to, že objekt má vesměs dva (v případě .net tři) přístupné interface.

Public interface

První interface je tvořen public viditelností vlastností a metod. Tento interface je přístupný všem okolním objektům, které chtějí s naší instancí třídy komunikovat.

Rozšířený interface za pomoci protected viditelnosti

Public interface však není jediný, který může architekt zpřístupnit. Dalším a neméně důležitým je i tento interface - byť se to někomu nemusí zdát. Samozřejmě v tomto případě hodně záleží na samotném tvůrci, aby zajistil, že tento interface neporuší konzistenci objektu. To znamená, zpřístupnil pouze takový interface, který konzistenci neporuší. Nemusí se však jednat o public interface, ale pouze takový, který uvidí třídní potomci. V .net frameworku, a vesměs asi v každém propracovaném a dobře navrženém frameworku, nalezneme spoustu tříd, který zpřístupňují svůj rozšířený interface svým potomkům.

K čemu rozšířený interface použít

Napadá mě hned několik možností, při kterých návrhář třídy bude využívat tohoto rozšířeného interface k zajištění konzistence a přitom nedojde k porušení zapouzdření. Pro lepší názornost bude vhodnější uvést příklad takové třídy a jejího potomka.

Dejme tomu, že máme třídu Osoba, která má několik vlastností (atributů) a metod pro manipulaci s touto třídou. Jednou z veřejných metod je i metoda pro uložení objektu, tato metoda by měla zkontrolovat, zda je při splnění komplexnějších podmínek umožněno uložit objekt Osoba. Tyto podmínky platí i pro potomky.

V případě, že bych Osoba poskytovala pouze veřejný interface, musel by každý potomek zajistit kontrolu těchto podmínek, neboť ukládání je pro každého potomka malinko odlišné. Jak si ale v takovém případě může být autor třídy Osoba v tomto jistý? Řešení je v tomto případě zajištěno implementací návrhového vzoru Template method a tedy rozšířením interface pomocí protected metody. V takovém případě nebudeme public metodu k uložení označovat jako virtuální a po zkontrolování podmínek budeme volat protected metodu.

Taková třída tedy může vypadat následovně:

public abstract class Osoba {
public void Save() {
  if (podminkySplneny()) {
    saveInternal();
  } else {
    // zalogování informací a případné vyvolání výjimky
  }
}
protected abstract void saveInternal();
}

Tímto způsobem máme zajištěno, že objekt zůstane zapouzdřený a viditelný zůstane pouze požadovaný veřejný interface, neboť je celkem oprávněný požadavek, aby okolí nemohlo volat žádost o uložení objektu, bez kontroly splnění podmínek.

Je to jen jeden z mnoha případů, proč si myslím, že není nebezpečné používat také protected viditelnost u objektů. Ale jak jsem se již někde vyjadřoval, vše je to o konkrétním člověku. Pokud navrhnu veřejný interface špatně, může to být více nebezpečné než pokud správně navrhnu celou třídu i s rozšířeným interfacem.

Samozřejmě je to jen můj pohled na danou problematiku a nemusí to být ten nejlepší pohled, vychází však z mých současných znalostí a zkušeností. Určitě se nebudu bránit jakékoli konzultaci nebo připomínkám, neboť jenom tak je možné si utřídit poznatky a porovnat je s okolím.

10
X

Přístup k datům v .net 2.0

V předchozích příspěvcích, kde jsem v praktických ukázkách používal přístup k datům, jsem se zmiňoval o správné konfiguraci datového spojení. V této oblasti došlo k poměrně významným změnám oproti předchozím verzím .net frameworku.

Obecný přístup k datům

Pro obecný přístup k datům v předchozích verzích .net frameworku bylo nutné používat interface. Vznikla proto řada obecných řešení, jedním z nich byla i komplexní Enterprise library, případně několik dalších, většinou uvedených na CodeProject.com, která se snažila odstínit konkrétní implementaci a přístup k datové vrstvě a rozhodování ponechat na konfiguraci. Taková řešení byla dobrá, převážně založená na design pattern a to především konkrétně na vzoru Abstract factory, případně Factory metod. Jak už to však bývá, bylo zde jedno ale a to konkrétně generování výjimek. Tento neduh nespočíval ani tak v samotné implementaci, ale již v .net frameworku. A pokud se nepoužilo některé více komplexní řešení, které by jednotlivé výjimky zapouzdřilo, bylo velice obtížné takové Exception obecně odchytit. Jednotlivé výjimky totiž byly závislé na implementaci a tak nebylo možné odchytit např. DbException, která neexistovala, ale musela se zachytávat přímo SqlException, OleDbException atd.

S příchodem .net 2.0 se mnohé změnilo a to k dobrému.

Je dobře známé, že architektura .net frameworku využívá a je postavena na návrhových vzorech. A i pro práci s datovými zdroji bylo těchto vzorů hojně využito. Tím prvním, se kterým se setkáte je tak Provider pattern, který byl použit pro získání příslušné Abstract factory třídy, konkrétně potomka DbProviderFactory.

Získání DbProviderFactory

Abychom mohli přistupovat k datům v abstraktní rovině, musíme také používat abstraktní objekty jejichž konkrétní implementaci získáme až za běhu programu pomocí konfigurace. Tímto "tvůrcem", který nám zprostředkuje potřebné objekty je právě DbProviderFactory třída, kterou dostaneme při volání statické metody DbProviderFactories.GetFactory(). Po získání instance tak můžeme od této instance žádat voláním příslušných metod o vytvoření potomka pro DbConnection, DbCommand, DbCommandBuilder, DbDataAdapter nebo DbParameter.

Třída DbProviderFactories má pak ještě druhou statickou metodu nazvanou GetFactoryClasses() o které se ještě zmíním.

DbConnection, DbCommand a také DbException

Nejen zmíněné objekty tvoří abstrakci - Bridge - pro přístup k datům. I další objekty umožňující obecný přístup k datům, které je možné získat pomocí volání metod nad získanou instancí objektu DbProviderFactory, a jenž jsem zde neuvedl, jsou umístěny v System.Data.Common namespace. V tomto namespace jsou vedeny také výčty, jenž se mohou s těmito objekty použít, např. výčet datových typů.

Určitě bych neměl opomenout zmínit, že existuje také obecná databázová výjimka DbException a tak se přístup k datům může stát téměř univerzálním.

Dříve než se podívám na to, jak to celé tedy správně nakonfigurovat, neopomenu ještě jedno malé upozornění. Týká se parametrů, které se předávají do příkazů. Při vytváření parametrů se totiž nepřidává žádný prefix a tak tato operace je na vývojáři, který musí vědět, jaký symbol prefixu pro daný databázový stroj použít. Tímto se tak úroveň abstrakce malinko snižuje a musí být zvoleno vhodné řešení pro tvorbu parametrů.

Použití ConnectionStringSettings a .config soubor

K vytvoření správné instance DbProviderFactory potřebujeme znát provider name. Jeho přesný název je uveden u každé definice příslušné Factory v souboru machine.config a to v atributu invariant. Tento název poté použijeme v konfiguraci connectionstringu v atributu providerName, abychom jej následně předaly do volání metody GetFactory().

Sekce connectionStrings tak může vypadat následovně:


 

Samotný prováděcí kód pro získání příslušné faktory a vytvoření connection bude následující:

ConnectionStringSettings settings = ConfigurationManager.ConnectionStrings["MainConn"];
DbProviderFactory factory = DbProviderFactories.GetFactory(settings.ProviderName);
using(DbConnection connection = factory.CreateConnection()) {
}

na základě výše uvedeného nastavení vytvoří spojení na SQL Server databázi

K jakým datovým zdrojům tedy máme přístup ihned po instalaci .net frameworku? Jsou to následující implementace:

  • Sql server - System.Data.SqlClient
  • Oracle - System.Data.OracleClient
  • OleDb - System.Data.OleDb
  • Odbc - System.Data.Odbc
  • Sql server CE - Microsoft.SqlServerCe.Client

jež jsou definovány v souboru machine.config. Samozřejmě nám nic nebrání v tom, napsat si vlastní Factory, která bude zprostředkovávat - tvořit - objekty pro přístup i k jiným databázím, např. FireBird nebo MySQL. Seznam všech dostupných implementací je k dispozici též za běhu programu po volání statické metody DbProviderFactories.GetFactoryClasses().

Měníme datové úložiště

Chceme-li tedy následně změnit datové úložiště, jediným zásehem tak může být záměna připojovacího řetězce a změna hodnoty v atributu providerName, která bude reflektovat nově používanou databázi a s tím nově používanou implementaci k tvorbě objektů zajišťujících přístup k danému datovému zdroji.

Na samotný závěr je třeba uvést, že z důvodu použití abstrakce se přichází o specifické možnosti, které jsou dostupné pro daný databázový stroj. I zde tedy platí, že každé plus má své minus.