Prototyyppipohjainen ohjelmointi - Prototype-based programming

Prototyyppipohjainen ohjelmointi on tyyli olio-ohjelmointi , joissa käyttäytymisen uudelleenkäyttö (tunnetaan perintö ) suoritetaan kautta prosessin uudelleen vanhaa esineitä , jotka toimivat prototyyppejä . Tätä mallia voidaan kutsua myös prototyyppiseksi , prototyyppisuuntautuneeksi, luokittomaksi tai esiintymäpohjaiseksi ohjelmointiksi.

Prototyyppipohjaisessa ohjelmoinnissa käytetään yleistettyjä objekteja, jotka voidaan sitten kloonata ja laajentaa. Käyttämällä hedelmää esimerkkinä "hedelmä" -esitys edustaa hedelmien ominaisuuksia ja toiminnallisuutta yleensä. "Banaani" -kohde kloonattaisiin "hedelmä" -objektista ja siihen lisättäisiin banaaneille ominaiset yleiset ominaisuudet. Jokainen yksittäinen "banaaniobjekti" kloonattaisiin geneerisestä "banaani" -objektista. Verrata luokkapohjainen paradigma, jossa "hedelmä" luokka olisi pidennettävä "banaani" luokka .

Ensimmäinen prototyyppilähtöinen ohjelmointikieli oli Self , jonka David Ungar ja Randall Smith kehittivät 1980-luvun puolivälissä tutkiakseen objektipohjaisen kielisuunnittelun aiheita. 1990 -luvun lopulta lähtien luokkaton paradigma on tullut yhä suositummaksi. Jotkut nykyiset prototyyppikeskeiset kielet ovat JavaScript (ja muut ECMAScript- toteutukset, kuten JScript ja Flash 's ActionScript 1.0), Lua , Cecil , NewtonScript , Io , Ioke , MOO , REBOL ja AHK .

Suunnittelu ja toteutus

Douglas Crockford kuvailee prototyyppistä perintöä JavaScriptissä seuraavasti:

Teet prototyyppiobjekteja ja sitten… luot uusia esiintymiä. Objektit ovat muuttuvia JavaScriptissä, joten voimme lisätä uusia esiintymiä antamalla niille uusia kenttiä ja menetelmiä. Ne voivat sitten toimia prototyypeinä jopa uudemmille kohteille. Emme tarvitse luokkia tehdäksemme paljon samankaltaisia ​​objekteja… Objektit perivät esineistä. Mikä voisi olla objektiivisempaa kuin tämä?

Prototyyppipohjaisen ohjelmoinnin kannattajat väittävät, että se rohkaisee ohjelmoijaa keskittymään joidenkin esimerkkien käyttäytymiseen ja vasta myöhemmin huolehtimaan siitä, että nämä objektit luokitellaan arkkityyppisiksi kohteiksi, joita käytetään myöhemmin luokkien kaltaisella tavalla . Monet prototyyppipohjaiset järjestelmät kannustavat prototyyppien muuttamiseen ajon aikana , kun taas vain hyvin harvat luokkapohjaiset olio-järjestelmät (kuten dynaaminen olio-järjestelmä, Common Lisp , Dylan , Objective-C , Perl , Python , Ruby) tai Smalltalk ) sallivat luokkien muuttamisen ohjelman suorittamisen aikana.

Lähes kaikki prototyyppipohjaiset järjestelmät perustuvat tulkittuihin ja dynaamisesti kirjoitettuihin kieliin. Staattisesti kirjoitettuihin kieliin perustuvat järjestelmät ovat kuitenkin teknisesti mahdollisia. Prototyyppipohjaisessa ohjelmoinnissa käsitelty Omega-kieli on esimerkki tällaisesta järjestelmästä, vaikka Omegan verkkosivuston mukaan jopa Omega ei ole yksinomaan staattinen, vaan pikemminkin sen "kääntäjä voi halutessaan käyttää staattista sitomista mahdollisuuksien mukaan ja saattaa parantaa ohjelma."

Kohteen rakentaminen

Prototyyppipohjaisilla kielillä ei ole nimenomaisia ​​luokkia. Objektit periytyvät suoraan muista objekteista prototyypin kautta. Prototyyppi ominaisuutta kutsutaan prototypein Self ja JavaScript , tai protovuonna Io . Uusia objekteja voidaan rakentaa kahdella tavalla: ex nihilo ("tyhjästä") -objektin luominen tai olemassa olevan objektin kloonaus . Entinen tuetaan jonkinlaista kohteen kirjaimellisesti , ilmoitusten, joissa esineitä voidaan määritellä suorituksen kautta merkintätapa kuten {...}ja johdetaan suoraan muuttujaan. Vaikka useimmat järjestelmät tukevat erilaisia ​​kloonauksia, ex nihilo -objektin luominen ei ole niin näkyvää.

Luokkapohjaisissa kielissä uusi esiintymä rakennetaan luokan konstruktoritoiminnon avulla . Tämä on erityisfunktio, joka varaa muistin lohkon objektin jäsenille (ominaisuudet ja menetelmät) ja palauttaa viittauksen kyseiseen lohkoon. Valinnainen joukko rakentaja -argumentteja voidaan välittää funktiolle ja niitä säilytetään yleensä ominaisuuksissa. Tuloksena oleva esiintymä perii kaikki menetelmät ja ominaisuudet, jotka määriteltiin luokassa, joka toimii eräänlaisena mallina, josta voidaan rakentaa samantyyppisiä objekteja.

Järjestelmät, jotka tukevat ex nihilo -objektien luomista, mahdollistavat uusien objektien luomisen tyhjästä ilman kloonausta olemassa olevasta prototyypistä. Tällaiset järjestelmät tarjoavat erityisen syntaksin uusien objektien ominaisuuksien ja käyttäytymisen määrittämiseen viittaamatta olemassa oleviin kohteisiin. Monilla prototyyppikielillä on juuriobjekti, jota usein kutsutaan objektiksi , joka on asetettu oletusprototyypiksi kaikille muille ajon aikana luotuille objekteille ja joka sisältää yleisesti tarvittavia menetelmiä, kuten toString()funktion, joka palauttaa objektin kuvauksen merkkijonona . Yksi hyödyllinen näkökohta ex nihilo -objektin luomisessa on varmistaa, että uuden objektin paikan (ominaisuudet ja menetelmät) nimet eivät ole nimitilan ristiriidassa ylimmän tason Objektiobjektin kanssa . ( JavaScript -kielellä tämä voidaan tehdä käyttämällä nolla -prototyyppiä Object.create(null).)

Kloonaus viittaa prosessiin, jossa uusi objekti rakennetaan kopioimalla olemassa olevan objektin käyttäytyminen (sen prototyyppi). Uudessa esineessä on sitten kaikki alkuperäisen ominaisuudet. Tästä lähtien uutta objektia voidaan muokata. Joissakin järjestelmissä syntyvä aliobjekti ylläpitää selkeän linkin ( delegoinnin tai samankaltaisuuden kautta ) prototyyppiinsä, ja muutokset prototyypissä saavat vastaavat muutokset näkymään sen kloonissa. Muut järjestelmät, kuten Forthin kaltainen ohjelmointikieli Kevo , eivät levitä muutosta prototyypistä tällä tavalla ja noudattavat sen sijaan ketjuttelevampaa mallia, jossa kloonattujen objektien muutokset eivät etene automaattisesti jälkeläisten välillä.

// 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"

Toinen esimerkki:

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

Valtuuskunta

Prototyyppipohjaisilla kielillä, jotka käyttävät delegointia , kielen suoritusaika pystyy lähettämään oikean menetelmän tai löytämään oikean datan yksinkertaisesti seuraamalla delegointiosoittimien sarjaa (objektista sen prototyyppiin), kunnes vastaavuus löytyy. Tämän käyttäytymisen jakamisen luomiseksi objektien välillä tarvitaan vain siirto-osoitin. Toisin kuin luokan ja instanssin välinen suhde luokkapohjaisissa objektiorientoiduissa kielissä, prototyypin ja sen sivujälkien välinen suhde ei edellytä, että aliobjektilla on muisti tai rakenteellinen samankaltaisuus prototyypin kanssa tämän linkin ulkopuolella. Sellaisenaan aliobjektia voidaan edelleen muokata ja muokata ajan mittaan ilman, että siihen liittyvän prototyypin rakennetta järjestetään uudelleen kuten luokkapohjaisissa järjestelmissä. On myös tärkeää huomata, että tietojen lisäksi menetelmiä voidaan lisätä tai muuttaa. Tästä syystä jotkut prototyyppipohjaiset kielet viittaavat sekä tietoihin että menetelmiin "aikaväleiksi" tai "jäseniksi".

Ketjutus

Vuonna concatenative prototyyppien - lähestymistapa toteutetaan Kevon ohjelmointikieli - ei ole näkyviä osoittimia tai linkit alkuperäisen prototyypin, josta kohde on kloonattu. Prototyyppi (pää) objekti kopioidaan eikä linkitetä siihen eikä delegointia ole. Tämän seurauksena prototyypin muutokset eivät näy kloonatuissa kohteissa.

Tämän järjestelyn suurin käsitteellinen ero on se, että prototyyppiobjektiin tehtyjä muutoksia ei levitetä automaattisesti klooneiksi. Tämä voidaan nähdä eduna tai haittana. (Kevo tarjoaa kuitenkin lisäprimitiivejä muutosten julkaisemiseen eri objektisarjoissa niiden samankaltaisuuden perusteella-ns. Perheen samankaltaisuudet tai klooniperhemekanismi- pikemminkin kuin taksonomisen alkuperän vuoksi, kuten delegointimallissa on tyypillistä.) delegointipohjaisella prototyypillä on lisähaitta, koska aliobjektin muutokset voivat vaikuttaa vanhemman myöhempään toimintaan. Tämä ongelma ei kuitenkaan ole luontainen delegointipohjaiselle mallille, eikä sitä ole olemassa delegointipohjaisilla kielillä, kuten JavaScript, jotka varmistavat, että aliobjektin muutokset kirjataan aina itse aliobjektiin eikä koskaan vanhempiin (esim. arvo varjostaa vanhemman arvon sen sijaan, että muuttaisi vanhemman arvoa).

Yksinkertaistetuissa toteutuksissa yhdistetyllä prototyypillä on nopeampi jäsenten haku kuin delegointipohjaisella prototyypillä (koska ei ole tarvetta seurata pääobjektien ketjua), mutta päinvastoin käyttää enemmän muistia (koska kaikki paikat kopioidaan, eikä yhtä ainoaa) paikka, joka osoittaa pääobjektia). Kehittyneemmät toteutukset voivat kuitenkin välttää tämän ongelman, vaikka nopeuden ja muistin välillä tarvitaan kompromisseja. Esimerkiksi järjestelmät, joissa on konkatenatiivinen prototyyppi, voivat käyttää kopioi-kirjoita- toteutusta, joka mahdollistaa kulissien takana tapahtuvan tietojen jakamisen-ja tällaista lähestymistapaa Kevo todellakin noudattaa. Päinvastoin, järjestelmät, joissa on delegointipohjaisia ​​prototyyppejä, voivat käyttää välimuistia tiedonhaun nopeuttamiseen.

Kritiikki

Luokkapohjaisten objektimallien kannattajat, jotka arvostavat prototyyppipohjaisia ​​järjestelmiä, ovat usein huolissaan samanlaisista huolenaiheista kuin ohjelmointikielten staattisten tyyppijärjestelmien kannattajilla on dynaamiset tyyppiset järjestelmät (katso tietotyyppi ). Yleensä tällaisia ​​huolenaiheita ovat: oikeellisuus , turvallisuus , ennustettavuus , tehokkuus ja ohjelmoijan tuntemattomuus.

Kolme ensimmäistä kohtaa luokat nähdään usein vastaavina tyypeille (useimmissa staattisesti kirjoitetuissa objektilähtöisissä kielissä ne palvelevat tätä roolia), ja niiden ehdotetaan tarjoavan sopimukselliset takuut esiintymilleen ja esiintymiensä käyttäjille, että he käyttäytyvät jollain tavalla.

Tehokkuuden osalta luokkien ilmoittaminen yksinkertaistaa monia kääntäjien optimointeja, jotka mahdollistavat tehokkaan menetelmän ja esiintymämuuttujan haun kehittämisen. Sillä Self kieli, paljon kehitystä aikaa kului kehittämistä, muokkaamista ja tulkkaus tekniikoita, jotka parantavat suorituskykyä prototyyppi-pohjaisten järjestelmien vs. luokka-pohjaisissa järjestelmissä.

Yleinen kritiikki prototyyppipohjaisia ​​kieliä vastaan ​​on, että ohjelmistokehittäjien yhteisö ei tunne niitä JavaScriptin suosiosta ja markkinoiden läpäisevyydestä huolimatta . Tämä prototyyppipohjaisten järjestelmien tietotaso näyttää kasvavan JavaScript-kehysten leviämisen ja JavaScriptin monimutkaisen käytön myötä, kun verkko kypsyy. ECMAScript 6 esitteli luokat syntaktiseksi sokeriksi JavaScriptin nykyisen prototyyppipohjaisen perinnön päälle, mikä tarjoaa vaihtoehtoisen tavan luoda objekteja ja käsitellä perintöä.

Kielet, jotka tukevat prototyyppipohjaista ohjelmointia

Katso myös

Viitteet

Lue lisää