Prototypové programování - Prototype-based programming

Prototypové programování je styl objektově orientovaného programování, ve kterém se opětovné použití chování (známé jako dědičnost ) provádí prostřednictvím procesu opětovného použití stávajících objektů, které slouží jako prototypy . Tento model může být také známý jako prototypové , prototypově orientované, beztřídní nebo instance založené na programování.

Programování založené na prototypech používá generalizované objekty, které lze poté klonovat a rozšiřovat. Použitím ovoce jako příkladu by předmět „ovoce“ představoval vlastnosti a funkčnost ovoce obecně. Objekt „banán“ by byl klonován z objektu „ovoce“ a byly by připojeny obecné vlastnosti specifické pro banány. Každý jednotlivý objekt „banán“ by byl klonován z obecného objektu „banán“. Srovnejte s paradigmatem založeným na třídách , kde by třída „ovoce“ byla rozšířena o třídu „banánů“ .

První prototypově orientovaný programovací jazyk byl Self , vyvinutý Davidem Ungarem a Randallem Smithem v polovině 80. let za účelem výzkumu témat v objektově orientovaném jazykovém designu. Od konce devadesátých let je beztřídní paradigma stále oblíbenější. Některé současné prototyp orientované jazyky jsou JavaScript (a další ECMAScript implementace, jako je JScript a Flash je ActionScript 1.0), Lua , Cecil , NewtonScript , Io , Ioke , MOO , REBOLu a AHK .

Návrh a implementace

Prototypová dědičnost v JavaScriptu je popsána Douglasem Crockfordem jako:

Vytváříte prototypové objekty a poté ... vytváříte nové instance. Objekty jsou v JavaScriptu měnitelné, takže můžeme nové instance rozšířit a dát jim nová pole a metody. Ty pak mohou fungovat jako prototypy pro ještě novější objekty. K výrobě spousty podobných objektů nepotřebujeme třídy ... Objekty dědí z objektů. Co by mohlo být více objektově orientované než to?

Zastánci programování založeného na prototypech tvrdí, že programátora vybízí, aby se zaměřil na chování nějaké sady příkladů a až později si dělal starosti se zařazováním těchto objektů do archetypálních objektů, které jsou později použity způsobem podobným třídám . Mnoho systémů založených na prototypech podporuje změnu prototypů během běhu , zatímco jen velmi málo třídně orientovaných objektově orientovaných systémů (jako je dynamický objektově orientovaný systém, Common Lisp , Dylan , Objective-C , Perl , Python , Ruby nebo Smalltalk ) umožňují měnit třídy během provádění programu.

Téměř všechny systémy založené na prototypech jsou založeny na tlumočených a dynamicky psaných jazycích. Systémy založené na staticky napsaných jazycích jsou však technicky proveditelné. Jazyk Omega, o kterém pojednává Prototype-Based Programming, je příkladem takového systému, ačkoli podle webových stránek Omega není ani Omega výlučně statická, ale spíše se její „kompilátor může rozhodnout použít statickou vazbu tam, kde je to možné, a může zlepšit účinnost program."

Objektová konstrukce

V jazycích založených na prototypech neexistují žádné explicitní třídy. Objekty dědí přímo z jiných objektů prostřednictvím vlastnosti prototypu. Vlastnost prototype je nazýván prototypev Self a JavaScript , nebo protov Io . Existují dva způsoby konstrukce nových objektů: vytváření objektů ex nihilo („z ničeho“) nebo klonování existujícího objektu. První z nich je podporována prostřednictvím nějaké formy objektových literálů , deklarací, kde lze objekty definovat za běhu pomocí speciální syntaxe, například {...}a předávány přímo do proměnné. Přestože většina systémů podporuje různé klonování, vytváření objektů ex nihilo není tak výrazné.

V jazycích založených na třídách je nová instance vytvořena prostřednictvím funkce konstruktoru třídy , speciální funkce, která rezervuje blok paměti pro členy objektu (vlastnosti a metody) a vrací odkaz na tento blok. Volitelné sadě argumentů konstruktoru lze předat funkci a jsou obvykle uchovávány ve vlastnostech. Výsledná instance zdědí všechny metody a vlastnosti, které byly definovány ve třídě, která funguje jako druh šablony, ze které lze konstruovat podobně napsané objekty.

Systémy, které podporují vytváření objektů ex nihilo, umožňují vytvářet nové objekty od nuly bez klonování ze stávajícího prototypu. Takové systémy poskytují speciální syntaxi pro určování vlastností a chování nových objektů bez odkazování na existující objekty. V mnoha jazycích prototypů existuje kořenový objekt, často nazývaný Object , který je nastaven jako výchozí prototyp pro všechny ostatní objekty vytvořené za běhu a který nese běžně potřebné metody, jako je toString()funkce pro vrácení popisu objektu jako řetězce . Jedním důležitým aspektem ex nihilo vytvoření objektu je zajistit, aby výherní nového objektu (vlastnosti a metody) názvy nemají namespace konflikty s nejvyšší úrovně objektu objekt. (V jazyce JavaScript to lze provést pomocí nulového prototypu, tj Object.create(null).)

Klonování se týká procesu, při kterém je nový objekt konstruován kopírováním chování stávajícího objektu (jeho prototypu). Nový objekt pak nese všechny kvality originálu. Od tohoto okamžiku lze nový objekt upravovat. V některých systémech si výsledný podřízený objekt zachovává explicitní odkaz (prostřednictvím delegování nebo podobnosti ) na jeho prototyp a změny v prototypu způsobují, že v jeho klonu jsou patrné odpovídající změny. Jiné systémy, jako například programovací jazyk Kevo podobný Forthu , nešíří změnu z prototypu tímto způsobem a místo toho se řídí více zřetězeným modelem, kde se změny v klonovaných objektech automaticky nešíří mezi potomky.

// Example of true prototypal inheritance style 
// in JavaScript.

// object creation using the literal 
// object notation {}.
const foo = { name: "foo", one: 1, two: 2 };

// Another object.
const bar = { two: "two", three: 3 };

// Object.setPrototypeOf() is a method introduced in ECMAScript 2015.
// For the sake of simplicity, let us pretend 
// that the following line works regardless of the 
// engine used:
Object.setPrototypeOf(bar, foo); // foo is now the prototype of bar.

// If we try to access foo's properties from bar 
// from now on, we'll succeed. 
bar.one; // Resolves to 1.

// The child object's properties are also accessible.
bar.three; // Resolves to 3.

// Own properties shadow prototype properties
bar.two; // Resolves to "two"
bar.name; // unaffected, resolves to "foo"
foo.name; // Resolves to "foo"

Pro další příklad:

const foo = { one: 1, two: 2 };

// bar.[[prototype]] = foo
const bar = Object.create(foo);

bar.three = 3;

bar.one; // 1
bar.two; // 2
bar.three; // 3

Delegace

V jazycích založených na prototypech, které používají delegování , je jazykový modul runtime schopen odeslat správnou metodu nebo najít správnou část dat jednoduše sledováním řady ukazatelů delegování (od objektu k prototypu), dokud není nalezena shoda. Vše, co je nutné k vytvoření tohoto sdílení chování mezi objekty, je ukazatel delegování. Na rozdíl od vztahu mezi třídou a instancí v objektově orientovaných jazycích založených na třídách vztah mezi prototypem a jeho odnoži nevyžaduje, aby měl podřízený objekt paměť nebo strukturální podobnost s prototypem za tímto odkazem. Jako takový může být podřízený objekt v průběhu času nadále upravován a doplňován, aniž by došlo k přeskupení struktury jeho přidruženého prototypu jako v systémech založených na třídách. Je také důležité si uvědomit, že lze přidávat nebo měnit nejen data, ale také metody. Z tohoto důvodu některé jazyky založené na prototypech označují data i metody jako „sloty“ nebo „členy“.

Zřetězení

Při zřetězeném prototypování - přístupu implementovaném programovacím jazykem Kevo - neexistují žádné viditelné odkazy ani odkazy na původní prototyp, ze kterého je objekt klonován. Prototyp (nadřazený) objekt je zkopírován a nikoli propojen a neexistuje žádné delegování. V důsledku toho se změny prototypu nepromítnou do klonovaných objektů.

Hlavní koncepční rozdíl v tomto uspořádání spočívá v tom, že změny provedené na prototypovém objektu nejsou automaticky šířeny do klonů. To lze považovat za výhodu nebo nevýhodu. (Kevo však poskytuje další primitiva pro publikování změn napříč sadami objektů na základě jejich podobnosti-takzvané rodinné podobnosti nebo mechanismus klonové rodiny -spíše než prostřednictvím taxonomického původu, jak je typické v modelu delegování.) Někdy se také tvrdí že prototypování založené na delegování má další nevýhodu v tom, že změny podřízeného objektu mohou ovlivnit pozdější provoz nadřazeného objektu. Tento problém však není vlastní modelu založenému na delegování a neexistuje v jazycích založených na delegování, jako je JavaScript, které zajišťují, že změny podřízeného objektu jsou vždy zaznamenány v samotném podřízeném objektu a nikdy v rodičích (tj. value shadows the parent's value rather than changed the parent's value).

V zjednodušujících implementacích bude mít zřetězení prototypů rychlejší vyhledávání členů než prototypování založené na delegování (protože není nutné sledovat řetězec nadřazených objektů), ale naopak bude využívat více paměti (protože se zkopírují všechny sloty, než aby existoval jeden slot směřující k nadřazenému objektu). Sofistikovanější implementace se tomuto problému mohou vyhnout, přestože jsou nutné kompromisy mezi rychlostí a pamětí. Například systémy se zřetězeným prototypováním mohou využívat implementaci kopírování při zápisu, aby bylo možné sdílet data ze zákulisí-a takový přístup Kevo skutečně dodržuje. Naopak systémy s prototypováním založeným na delegování mohou pomocí ukládání do mezipaměti urychlit vyhledávání dat.

Kritika

Zastánci objektových modelů založených na třídách, kteří kritizují systémy založené na prototypech, mají často obavy podobné obavám, které mají zastánci systémů se statickým typem pro programovací jazyky u systémů dynamického typu (viz datový typ ). Obvykle takové starosti zahrnují: správnost , bezpečnost , předvídatelnost , efektivitu a neznalost programátora.

V prvních třech bodech jsou třídy často považovány za analogické s typy (ve většině staticky typovaných objektově orientovaných jazycích slouží této roli) a jsou navrženy tak, aby poskytovaly smluvní záruky svým instancím a uživatelům jejich instancí, že se budou chovat nějakým daným způsobem.

Pokud jde o účinnost, deklarace tříd zjednodušuje mnoho optimalizací kompilátoru, které umožňují vývoj efektivního vyhledávání metod a proměnných instancí. Pro jazyk Self bylo mnoho času na vývoj věnováno vývoji, kompilaci a interpretaci technik ke zlepšení výkonu systémů založených na prototypech oproti systémům založeným na třídách.

Běžnou kritikou proti jazykům založeným na prototypech je, že komunita vývojářů softwaru je s nimi obeznámena, a to navzdory popularitě a pronikání JavaScriptu na trh . Zdá se, že tato úroveň znalostí systémů založených na prototypech roste s množstvím rámců JavaScriptu a komplexním používáním JavaScriptu, jak web zraje. ECMAScript 6 zavedl třídy jako syntaktický cukr nad stávající dědičnost založenou na JavaScriptu, což poskytuje alternativní způsob vytváření objektů a řešení dědičnosti.

Jazyky podporující programování založené na prototypech

Viz také

Reference

Další čtení