Používáme návrhové vzory v .NET – Abstract Factory
V tomto článku se budeme zabývat návrhovým vzorem zvaným „abstract factory“ (abstraktní továrna). Vzor je použit pro flexibilní výběr typu databázového serveru, se kterým aplikace pracuje. Při návrhu systémů bývá téměř pravidlem, že se jednotlivé návrhové vzory doplňují, a ani v tomto příkladu tomu není jinak. Abstraktní továrnu zde doplňuje známý a často používaný vzor „singleton“ (jedináček).
Řešené zadání
Zadání pro ukázkový příklad zní: „Vytvořit databázovou aplikaci, která bude moci pro ukládání dat používat různé databázové servery.“ Základní implementace bude provedena pro servery MS SQL a Oracle, nicméně při požadavku na přenesení na jiný databázový stroj má být toto co nejsnazší a bez nutnosti rekompilace nebo dokonce přepisování aplikace.
V této ukázce se také nebudeme zabývat řešením střední a prezentační vrstvy. Klient navrženého řešení bude jednoduchá WinForm aplikace, na které bude předveden způsob použití výsledného navrženého řešení.
Návrh řešení
Rozdělení aplikace na vrstvy
Mnoho soudobých databázových aplikací používá vícevrstvý návrh. Asi nejznámější vícevrstvou architekturou je architektura třívrstvá, ve které jsou odděleny vrstvy prezentační, vrstva aplikační logiky (někdy nazývaná jen střední vrstva) a datová vrstva. Výhody plynoucí z rozdělení aplikací na více vrstev je několik, hlavní pro použití v našem návrhu je možnost výměny implementace některé vrstvy bez ovlivnění zbývajících částí aplikace.
Příklad možného rozvrstvení aplikace je uveden na následujícím obrázku:
Aplikace nemusí vždy striktně dodržovat princip rozdělení na tři vrstvy, v některých případech lze každou vrstvu ještě dále rozčlenit na další vrstvy, případně na oddělené sady komponent. V rámci datové vrstvy aplikace bývá zvykem vyčlenit takzvanou vrstvu logiky přístupu k datům (Data Access Logic, DAL). Třídy v této vrstvě se zkráceně nazývají DALC (Data Access Logic Components). Tuto vrstvu potřebujeme implementovat tak, aby bylo možné změnou konfigurace aplikace určit, jaký typ databázového serveru bude aplikace používat.
Základní popis vzoru Abstract Factory
Návrhový vzor abstract factory je takzvaně tvořivý objektový vzor. Z tohoto zařazení vyplývá, že je určen pro vytváření instancí a že pro změnu procesu tvorby se používá změna instancí továrny.
Hlavním důvodem použití abstract factory pro naše řešení je ten, že vzor umožňuje konfigurovat aplikaci pro použití sady tříd, které jsou spolu svázány nějakou společnou vlastností (jsou nějakým způsobem příbuzné). V našem případě je touto sadou tříd již zmíněná sada DAL komponent a společnou vlastností, která je předurčuje ke společnému použití, je typ databázového serveru. Abstract factory navíc ve svém důsledku přímo podporuje snadnou výměnu této sady tříd pouhou výměnou instance použité továrny.
Součásti vzoru jsou:
- Abstraktní továrna
- Popisuje rozhraní pro vytváření produktů.
- Konkrétní továrny
- Implementují rozhraní a vytvářejí konkrétní produkty.
- Abstraktní produkty
- Popis rozhraní produktů vytvářených abstraktní továrnou.
- Konkrétní produkty
- Konkrétní implementace těchto produktů, tyto implementace vytváří příslušná konkrétní továrna.
- Klient
- Třída používající produkty továrny.
V našem případě je abstraktní továrnou třída DatabaseFactory
, konkrétními továrnami poté třídy MsSqlFactory
a OracleFactory
.
Abstraktními produkty jsou zde rozhraní komponent logiky pro přístup k datům – pro příklad zde používáme rozhraní pro přístup k objednávkám IOrderDALC
a pro přístup k produktům IProductDALC
. Tyto jsou implementovány komponentami MsSqlOrderDALC
a MsSqlProductDALC
, případně OracleOrderDALC
a OracleProductDALC
.
Struktura je znázorněna na následujícím třídním modelu:
Abstract Factory – třídní model řešení (plná velikost, cca 10 kB)
Další informace o tomto vzoru je možné nalézt primárně on-line například na Design Patterns: Abstract Factory.
Způsob použití
Jakmile klient (obvykle třída střední vrstvy) potřebuje načítat nebo ukládat data do databáze, musí k tomu získat instanci DAL komponenty. Komponentu získává voláním příslušné metody továrny. Z toho je zřejmé, že pro naši implementaci je nutné zajistit načtení konfiguračních údajů určujících použitý typ konkrétní továrny a následné vytvoření instance této konkrétní továrny.
Konfiguraci a vytvoření instance továrny již samotný vzor neřeší. V příkladu proto k tomu využijeme návrhový vzor singleton, který nám umožní odstínit klienta od vytvoření instance továrny (za výběr nakonfigurované instance by neměl být zodpovědný klient). V konfiguraci aplikace uvedeme název typu použité třídy a pomocí techniky reflexe z tohoto typu vytvoříme instanci továrny.
Vytvoření instance továrny
Pro čtení konfigurace máme navrženu třídu Configuration
, která implementuje rozhraní IConfigurationSectionHandler
(public class Configuration : IConfigurationSectionHandler
). Třída Configuration
načte název typu třídy konkrétní továrny a connection string…
private string _type;
private string _connectionString;
public object Create(object parent, object configContext, System.Xml.XmlNode section)
{
_type = section.Attributes[„type“].Value;
_connectionString = section.Attributes[„connectionString“].Value;
if (_type == null || _type.Length == 0)
throw new ConfigurationException(„Není nakonfigurován typ továrny.“);
if (_connectionString == null || _connectionString.Length == 0)
throw new ConfigurationException(„Není nakonfigurován připojovací řetězec.“);
return this;
}
…a zpřístupní je pomocí svých vlastností:
public string Type
{
get
{
if (_type != null)
return _type;
else
return string.Empty;
}
}
public string ConnectionString
{
get
{
if (_connectionString != null)
return _connectionString;
else
return string.Empty;
}
}
Vytvoření instance je poté zapouzdřeno v metodě GetFactory()
, přičemž ke čtení konfigurace a vytváření instance dochází díky použití vzoru singleton pouze při prvním volání této metody. Implementace jedináčka v abstraktní továrně:
private static volatile DatabaseFactory _instance;
private static object _lock = new object();
public static DatabaseFactory GetFactory()
{
if (_instance == null)
{
lock (_lock)
{
if (_instance == null)
{
Configuration configuration = GetConfiguration();
try
{
_instance = Activator.CreateInstance(Type.GetType(configuration.Type), true) as DatabaseFactory;
}
catch (Exception exc)
{
throw new ApplicationException(„Chyba při vytváření instance továrny.“, exc);
}
}
}
}
return _instance;
}
Pro vytváření instance z nakonfigurovaného typu je využita třída Activator
a její metoda CreateInstance
. Mimochodem, z hlediska vícevláknového programování je vytvoření sdílené instance kritickou sekcí, musíme se tedy postarat o zajištění synchronizace vláken. V příkladu je použit princip „double-checked locking“, který je blíže popisován společně s implementací jedináčka například v článku Implementing Singleton in C#.
Kompletní příklad řešení je vám k dispozici.
Konfigurace aplikace
Konfigurační sekce pro DAL vrstvu se skládá z jediného elementu:
<DAL
type=“Samples.AbstractFactory.MsSqlDAL.MsSqlFactory, Samples.AbstractFactory.MsSqlDAL“
connectionString=“Persist Security Info=False;Integrated Security=SSPI;database=northwind;server=mySQLServer“
/>
Atributem type
volíme třídu implementující konkrétní továrnu (zde Samples.AbstractFactory.MsSqlDAL.MsSqlFactory
) a atributem connectionString
určíme připojovací řetězec k databázi, který budou DAL komponenty využívat. Tvar připojovacího řetězce musí odpovídat databázovému provideru, který implementace DAL komponent využívá.
Pro použití s DB serverem Oracle postačí změnit atribut type
na hodnotu Samples.AbstractFactory.OracleDAL.OracleFactory
a při příštím spuštění již bude aplikace používat tuto implementaci. Podporu dalších databázových serverů lze dodat implementací DatabaseFactory
a komponent IProductDALC
a IOrderDALC
pro tento další typ serveru.
Doporučená literatura
- Gamma E., Helm R., Johnson R., Vlissides J.: Návrh programů pomocí vzorů, Grada Publishing, 2003.
Starší komentáře ke článku
Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.
Mohlo by vás také zajímat
-
AI v programování: Jak používat GitHub Copilot (část 2)
19. února 2024 -
Členská sekce: 4 důvody proč ji mít na svém webu
12. března 2024 -
Globální výpadek IT systémů: Může za to jediná aktualizace
19. července 2024
Nejnovější
-
Jak rozšířit úložiště Macu za pětinovou cenu?
16. prosince 2024 -
Nové trendy v doménách pro osobní projekty – DIY, LIVING a LIFESTYLE
9. prosince 2024 -
Jak chránit webové stránky před Web/AI Scrapingem
27. listopadu 2024 -
Jaký monitor je nejlepší k novému Macu Mini?
25. listopadu 2024