Knihovny vs. frameworky

Termíny knihovna a framework jsou ve světě programování často používané, ale málokdo by asi dokázal říct, jak přesně spolu souvisí a čím se vlastně od sebe knihovna a framework liší. Framework by se možná na první pokus dal definovat jako ucelený soubor tematicky zaměřených knihoven, ale vystihuje tato definice opravdu vše? Pojďme se na věc podrobněji podívat.

Knihovny

Intuitivní představu o významu pojmu knihovna mají asi všichni programátoři. Velmi vágně řečeno jde o soubor funkcí a tříd poskytující nějaký okruh služeb.

Knihovnu mohou využívat různé aplikace, které ji typicky nebudou využívat samostatně, ale v kombinaci s dalšími knihovnami. Knihovny se také můžou využívat navzájem. Na nejnižší úrovni pak knihovny či samotná aplikace komunikují s vrstvami kódu pod sebou či přímo s hardwarem.

Situace v aplikaci tedy typicky vypadá zhruba takto:

Schéma aplikace využívající několik knihoven

Pro knihovnu z popsaného způsobu využití plynou dva důležité požadavky:

  1. Musí si rozumět s prakticky libovolnou aplikací.
  2. Musí být schopna koexistence s dalšími knihovnami.

Tyto dvě vlastnosti každý kus kódu poskytující nějaký okruh služeb rozhodně nemá. Aby jimi knihovna oplývala, nesmí především nijak ovlivňovat globální stav programu – všechen její vliv musí být lokalizovaný. Součástí toho je, že nesmí provádět policy decisions – říkat aplikaci, co se jak bude dělat.

Několik příkladů v různých programovacích jazycích:

PHP

Knihovna by neměla nastavovat úroveň reportování chyb, vlastní error handler, či jinak ovlivňovat zpracování chyb – to je typické policy decision. Naopak by měla pracovat s jakýmkoliv nastavením zpracování chyb, které si autor programu vymyslí.

Podobnými příklady v PHP jsou nastavení register_globals, magic_quotes_gpc apod.

C++

Knihovna by měla fungovat jak v programech kompilovaných s podporou výjimek, tak v programech kompilovaných bez nich. Byť C++ výjimky má už dlouho, řada projektů je nepoužívá (policy decision) a jejich podporu má při kompilaci vypnutou. Pochopitelně to znamená, že knihovna, která chce být univerzálně použitelná, interně výjimky vůbec nesmí používat (což může být pro jejího autora frustrující).

Ruby

Knihovna v Ruby by neměla rozšiřovat globální třídy jako Object, String, Array atd. Rozšiřování objektů není úplně policy decision, ale je to něco, co podstatně ovlivňuje globální stav programu.

JavaScript

Knihovna by neměla své součásti deklarovat v globálním jmenném prostoru a neměla by měnit prototypy globálních objektů (což je pro všechny praktické účely stejné jako rozšiřování globálních tříd v Ruby).

Myslím, že z příkladů je jasné, jak se má slušná knihovna chovat. Nyní se podívejme na frameworky.

Frameworky

Framework bych definoval jako ucelený soubor tematicky zaměřených knihoven + policy decisions. Situace vypadá takto:

Schéma aplikace využívající framework

Pod frameworkem si v obrázku můžete představit například Rails, Nette, Prototype, ale třeba i Qt nebo GTK+.

Pokud bychom hráli hru "najděte 10 rozdílů" s obrázkem na začátku článku, brzy přijdeme na dvě důležité věci:

  1. Knihoven bylo víc, ale framework je jen jeden.
  2. Aplikace řeší pomocí frameworku všechny požadavky, zatímco knihovny používala jen na specifické úkoly.

Z bodu 1 plyne, že framework nemusí nutně dodržovat pravidla slušného chování pro knihovny a může ovlivňovat globální chování aplikace. Bod 2 pak naznačuje, že framework může (a někdy dokonce musí) být místem, kde je vhodné udělat různé policy decisions.

Není tedy v principu chybou, když například Ruby on Rails předefinovává v globálních objektech kde co a některé aplikační frameworky v C++ požadují, aby aplikace byla kompilována se zapnutou podporou výjimek nebo naopak bez nich.

Právě možnost frameworku definovat policy decisions a přimět programátora aplikace k jejich dodržování považuju za klíčovou vlastnost, která frameworky odlišuje od knihoven.

Závěr

Místo dlouhého povídání by se dal rozdíl situací u knihoven a frameworků vyjádřit i stručněji: Aplikace využívá knihovny, ale je psána ve frameworku. Z tohoto rozdílu plynou i odlišná pravidla, která musí tvůrci knihoven a frameworků dodržovat. Knihovny musí být týmoví hráči, zatímco frameworky si mohou dovolit být občas diktátory. Až tedy příště budete psát kus znovupoužitelného kódu, dobře zvažte, do které kategorie spadá.

Poznámky:

  1. V textu pomíjím možnost existence více frameworků v aplikaci na více úrovních. Myslím, že takto stavěných aplikací není mnoho a při bližším zkoumání by se u nich dost možná přišlo na to, že některý framework je "pouze" knihovna.
  2. Diagramy v článku byly vytvořeny pomocí nástroje ditaa (viz zdrojový kód) a mírně ručně upraveny.
Oct 14, 2009 – 12:14

Comments

Botanicus
"Knihovna v Ruby by neměla rozšiřovat globální třídy jako Object, String, Array atd. Rozšiřování objektů není úplně policy decision, ale je to něco, co podstatně ovlivňuje globální stav programu."

Nebral bych to tak striktne, konecne obrovske procento z Ruby knihoven to dela a nevadi to. Samozrejme pokud se nekdo silene zhuli a napise RubyGems, ktere se serou do Kernel#require, tak to je pak blby, ale obecne setrne pridavani metod do core classes neni zas takove issue. V nekterych pripadech mi nevadi ani prepis core method, kdyz se zachova kompatibilita a treba se jen neco drobneho prida nebo se metoda prepise aby lepe performovala. Ale tohle uz jsou diskutabilni techniky.

"Není tedy v principu chybou, když například Ruby on Rails předefinovává v globálních objektech kde co a některé aplikační frameworky v C++ požadují, aby aplikace byla kompilována se zapnutou podporou výjimek nebo naopak bez nich."

S tema Rails je to prinejmensim diskutabilni. Problem je ze predefinovavaji spoustu veci nekompatibilnim zpusobem (nedavno mi po nacteni nake verze ActiveSupportu nefungovalo ani Kernel#rand, to jsem rozdychaval dost spatne)

Hezke zamysleni Davide, diky.
David Grudl
Ahoj Davide, vidím to prakticky stejně jako ty, až na jednu věc, kvůli které bych dospěl k trošku jiným závěrům. Jde o to, že aplikace napsaná *ve frameworku* běžně *využívá knihovny*.

Framework tedy může (a často musí) ovlivňovat globální chování aplikace, ale neměl by ovlivňovat globální prostředí jazyka, které využívají knihovny.

Tedy souhlasím s výjimkou toho, že není v principu(!) chybou, když Ruby on Rails předefinovává v globálních objektech kde co :-)
David Majda
[1] Jo, tyhle debaty se už vedly mockrát :-)

[2] Máš pravdu, že globální prostředí jazyka a globální chování aplikace jsou dvě různé věci. Otázka je, jestli mezi nimi lze vést jasnou hranici a pokud ano, tak podle čeho - viz třeba ty výjimky v C++, které jsou jazyková záležitost, ale chovají se jinak než rozšiřování/předefinovávání.

Předpokládám správně, že s tím, že framework může *rozšiřovat*, ale nesmí *předefinovávát* souhlasíš? (Knihovna nesmí ani to první - co kdyby dvě rozšířily stejným směrem ale s jinou implementací.) Měl jsem tady být přesnější a ty dvě věci nemíchat do sebe.

Napadá mě ještě jeden pohled na framework - jako rozšíření jazyka. V tu chvíli nebudeš řešit, zda knihovna běhá v jazyce X, ale v jazyce X s frameworkem Y - jsou to jakoby dva různé dialekty. Pak má framework zelenou si s jazykem dělat co chce. Mám pocit, že rubysti takhle vnímají Rails (resp. jeho komponentu ActiveSupport, která je odpovědná za rozšiřování a předefinovávání standardních tříd).
Borek
Toto rozdělení na knihovny a frameworky s podobným vysvětlením je poměrně populární a nelze mu upřít eleganci, je ale také praktické? Schválně,

- je PHP jako takové framework?
- je .NET Framework framework?
- je Nette Framework framework?

A co Flex framework, frameworky postavené nad Flexem, Java Swing, ASP.NET Web Forms, ASP.NET MVC, Delphi a tak dále?

Má dnes smysl vůbec řešit, co přesně framework je? Např. "komponentový framework" (Java Swing, Flex, Web Forms, PRADO) je ustálené slovní spojení, a přitom vůbec nemusí znamenat, že dochází k nějakým "policy decisions" (BTW, tenhle termín se mi moc líbí). Ve světě Flexu zase existuje několik "frameworků", které v podstatě jen kombinují IoC kontejner a Event Bus - aplikace postavená na těchto frameworcích vykazuje jasné známky závislosti na daném frameworku a policy decisions jsou jasně patrné, na druhou stranu lze použít jen tohle nebo támhleto, což je naopak charakteristika knihovny.

Takže nevím. Řada balíčku vykazuje znaky obou, takže je otázka, zda je striktní oddělení na frameworky a knihovny vůbec možné.
David Majda
[4] Prakticky každá nematematická definice se pod dostatečným tlakem rozpadne, dají se najít patologické příklady apod. To platí i pro pokusy o definici knihovny a frameworku. Z jistého pohledu jsou tyto pokusy jen sémantická hra, ale na druhou stranu mi přišlo zajímavé se pokusit rozdíl mezi nimi vystihnout. Že si pod těmi pojmy různí lidé představují různé věci je mi jasné.

Celý článek je v podstatě o tom, že nejlepší charakteristika frameworku se mi jeví to, že je v apliakci jediný, tvoří pod ní ucelenou vrstvu a obvykle dělá policy decisions. Je mi jasné, že ne vždy vše z toho platí a dají se najít různé výjimky, ale praktické mi to přijde dost.

K tvým příkladům:

* PHP - není framework, to je černý box na obrázcích
* .NET - opět černý box a spolu s ním několik knihoven; zda je jeho součástí i nějaký framework, nevím, natolik ho neznám
* Nette - je framework, tvořený několika knihovnami, z nichž některé dělají policy decisions (např. laděnka, Object)
* Swing, Web Forms, MVC - jsou frameworky
* Delphi - totéž jako .NET
* zbytek neznám dost na posouzení
estonto
A není to taky tak, že zatímco knihovnu můžeme snadno zakrýt nějakým rozhraním a pak není obtížné vyměnit jednu za jinou, framework s aplikací srůstá a výměna jednoho za jiný je o mnoho řádů obtížnější?

Jinak definice pomocí policy decisions a neměnění globálního stavu se mi líbí, ale nechtěl bych ji vysvětlovat např. managerům bez technických znalostí.

Add comment

It is not possible to add comments to posts older than one month.