Programação baseada em protótipo - Prototype-based programming

A programação baseada em protótipo é um estilo de programação orientada a objetos em que a reutilização de comportamento (conhecida como herança ) é realizada por meio de um processo de reutilização de objetos existentes que servem como protótipos . Esse modelo também pode ser conhecido como programação prototípica , orientada a protótipos, sem classes ou baseada em instância .

A programação baseada em protótipo usa objetos generalizados, que podem então ser clonados e estendidos. Usando frutas como exemplo, um objeto "fruta" representaria as propriedades e a funcionalidade das frutas em geral. Um objeto "banana" seria clonado do objeto "fruta" e propriedades gerais específicas para bananas seriam anexadas. Cada objeto "banana" individual seria clonado do objeto "banana" genérico. Compare com o paradigma baseado em classe , onde uma classe "fruta" seria estendida por uma classe "banana" .

A primeira linguagem de programação orientada a protótipos foi Self , desenvolvida por David Ungar e Randall Smith em meados da década de 1980 para pesquisar tópicos de design de linguagem orientada a objetos. Desde o final da década de 1990, o paradigma sem classes tem se tornado cada vez mais popular. Algumas linguagens atuais orientada a protótipo são JavaScript (e outros ECMAScript implementações, como JScript e flash 's ActionScript 1.0), Lua , Cecil , NewtonScript , Io , Ioke , MOO , REBOL e AHK .

Design e implementação

A herança prototípica em JavaScript é descrita por Douglas Crockford como:

Você faz objetos de protótipo e então… cria novas instâncias. Os objetos são mutáveis ​​em JavaScript, portanto, podemos aumentar as novas instâncias, dando-lhes novos campos e métodos. Estes podem atuar como protótipos para objetos ainda mais recentes. Não precisamos de classes para fazer muitos objetos semelhantes ... Objetos herdam de objetos. O que poderia ser mais orientado a objetos do que isso?

Os defensores da programação baseada em protótipo argumentam que ela encoraja o programador a se concentrar no comportamento de algum conjunto de exemplos e só mais tarde se preocupar em classificar esses objetos em objetos arquetípicos que mais tarde serão usados ​​de maneira semelhante às classes . Muitos sistemas baseados em protótipos encorajam a alteração de protótipos durante o tempo de execução , enquanto poucos sistemas orientados a objetos baseados em classes (como o sistema orientado a objetos dinâmico, Common Lisp , Dylan , Objective-C , Perl , Python , Ruby ou Smalltalk ) permitem que as classes sejam alteradas durante a execução de um programa.

Quase todos os sistemas baseados em protótipos são baseados em linguagens interpretadas e digitadas dinamicamente . Sistemas baseados em linguagens estaticamente tipadas são tecnicamente viáveis, entretanto. A linguagem Omega discutida na Programação Baseada em Protótipo é um exemplo de tal sistema, embora de acordo com o site da Omega mesmo Omega não seja exclusivamente estático, mas sim seu "compilador pode escolher usar ligação estática onde for possível e pode melhorar a eficiência de um programa."

Construção de objeto

Em linguagens baseadas em protótipo, não existem classes explícitas. Os objetos herdam diretamente de outros objetos por meio de uma propriedade de protótipo. A propriedade prototype é chamada prototypeem Self e JavaScript ou protoem Io . Existem dois métodos de construção de novos objetos: criação de objetos ex nihilo ("do nada") ou por meio da clonagem de um objeto existente. O primeiro é suportado por meio de alguma forma de literal de objeto , declarações em que os objetos podem ser definidos em tempo de execução por meio de sintaxe especial, como {...}e passados ​​diretamente para uma variável. Embora a maioria dos sistemas suporte uma variedade de clonagem, a criação de objetos ex nihilo não é tão proeminente.

Em linguagens baseadas em classes, uma nova instância é construída por meio de uma função construtora de classe , uma função especial que reserva um bloco de memória para os membros do objeto (propriedades e métodos) e retorna uma referência a esse bloco. Um conjunto opcional de argumentos do construtor pode ser passado para a função e geralmente é mantido nas propriedades. A instância resultante herdará todos os métodos e propriedades que foram definidos na classe, que atua como um tipo de modelo a partir do qual objetos com tipos semelhantes podem ser construídos.

Os sistemas que suportam a criação de objetos ex nihilo permitem que novos objetos sejam criados do zero, sem clonar a partir de um protótipo existente. Esses sistemas fornecem uma sintaxe especial para especificar as propriedades e comportamentos de novos objetos sem fazer referência a objetos existentes. Em muitas linguagens de protótipo, existe um objeto raiz, geralmente chamado de Object , que é definido como o protótipo padrão para todos os outros objetos criados em tempo de execução e que carrega métodos comumente necessários, como uma toString()função para retornar uma descrição do objeto como uma string . Um aspecto útil da criação de objeto ex nihilo é garantir que os nomes de slot de um novo objeto (propriedades e métodos) não tenham conflitos de namespace com o objeto Object de nível superior . (Na linguagem JavaScript , pode-se fazer isso usando um protótipo nulo, ou seja Object.create(null).)

A clonagem se refere a um processo pelo qual um novo objeto é construído copiando o comportamento de um objeto existente (seu protótipo). O novo objeto carrega então todas as qualidades do original. A partir deste ponto, o novo objeto pode ser modificado. Em alguns sistemas, o objeto filho resultante mantém um link explícito (por delegação ou semelhança ) com seu protótipo, e as alterações no protótipo fazem com que as alterações correspondentes sejam aparentes em seu clone. Outros sistemas, como a linguagem de programação parecida com Forth Kevo , não propagam mudanças do protótipo dessa maneira e, em vez disso, seguem um modelo mais concatenativo , onde mudanças em objetos clonados não se propagam automaticamente entre os descendentes.

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

Para outro exemplo:

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

Delegação

Em linguagens baseadas em protótipo que usam delegação , o tempo de execução da linguagem é capaz de despachar o método correto ou encontrar os dados certos simplesmente seguindo uma série de ponteiros de delegação (do objeto para seu protótipo) até que uma correspondência seja encontrada. Tudo o que é necessário para estabelecer esse compartilhamento de comportamento entre objetos é o ponteiro de delegação. Ao contrário do relacionamento entre classe e instância em linguagens orientadas a objetos baseadas em classes, o relacionamento entre o protótipo e seus desdobramentos não requer que o objeto filho tenha uma memória ou similaridade estrutural com o protótipo além desse link. Como tal, o objeto filho pode continuar a ser modificado e emendado ao longo do tempo sem reorganizar a estrutura de seu protótipo associado como em sistemas baseados em classe. Também é importante observar que não apenas dados, mas também métodos podem ser adicionados ou alterados. Por esse motivo, algumas linguagens baseadas em protótipo se referem a dados e métodos como "slots" ou "membros".

Concatenação

Na prototipagem concatenativa - a abordagem implementada pela linguagem de programação Kevo - não há ponteiros visíveis ou links para o protótipo original a partir do qual um objeto é clonado. O objeto protótipo (pai) é copiado em vez de vinculado e não há delegação. Como resultado, as alterações no protótipo não serão refletidas nos objetos clonados.

A principal diferença conceitual nesse arranjo é que as alterações feitas em um objeto de protótipo não são propagadas automaticamente para os clones. Isso pode ser visto como uma vantagem ou desvantagem. (No entanto, Kevo fornece primitivas adicionais para publicar mudanças em conjuntos de objetos com base em sua similaridade - as chamadas semelhanças de família ou mecanismo de família de clones - em vez de por meio de origem taxonômica, como é típico no modelo de delegação.) Às vezes também é afirmado que a prototipagem baseada em delegação tem uma desvantagem adicional, pois as alterações em um objeto filho podem afetar a operação posterior do pai. No entanto, esse problema não é inerente ao modelo baseado em delegação e não existe em linguagens baseadas em delegação, como JavaScript, o que garante que as alterações em um objeto filho sejam sempre registradas no próprio objeto filho e nunca nos pais (ou seja, o filho valor obscurece o valor do pai em vez de alterar o valor do pai).

Em implementações simplistas, a prototipagem concatenativa terá pesquisa de membro mais rápida do que a prototipagem baseada em delegação (porque não há necessidade de seguir a cadeia de objetos pais), mas, inversamente, usará mais memória (porque todos os slots são copiados, em vez de haver um único slot apontando para o objeto pai). Implementações mais sofisticadas podem evitar esse problema, entretanto, embora sejam necessários trade-offs entre velocidade e memória. Por exemplo, sistemas com prototipagem concatenativa podem usar uma implementação copy-on-write para permitir o compartilhamento de dados nos bastidores - e tal abordagem é de fato seguida por Kevo. Por outro lado, os sistemas com prototipagem baseada em delegação podem usar cache para acelerar a pesquisa de dados.

Crítica

Os defensores dos modelos de objetos baseados em classes que criticam os sistemas baseados em protótipos frequentemente têm preocupações semelhantes às preocupações que os proponentes de sistemas de tipo estático para linguagens de programação têm de sistemas de tipo dinâmico (consulte tipo de dados ). Normalmente, essas preocupações envolvem: correção , segurança , previsibilidade , eficiência e falta de familiaridade do programador.

Nos primeiros três pontos, as classes são frequentemente vistas como análogas aos tipos (na maioria das linguagens orientadas a objetos estaticamente tipadas, elas cumprem essa função) e são propostas para fornecer garantias contratuais às suas instâncias e aos usuários de suas instâncias de que se comportarão de alguma forma.

Em relação à eficiência, declarar classes simplifica muitas otimizações do compilador que permitem o desenvolvimento de métodos eficientes e pesquisa de variáveis ​​de instância. Para a linguagem Self , muito tempo de desenvolvimento foi gasto no desenvolvimento, compilação e interpretação de técnicas para melhorar o desempenho de sistemas baseados em protótipos versus sistemas baseados em classes.

Uma crítica comum feita contra linguagens baseadas em protótipos é que a comunidade de desenvolvedores de software não está familiarizada com elas, apesar da popularidade e permeação de mercado do JavaScript . Este nível de conhecimento de sistemas baseados em protótipos parece estar aumentando com a proliferação de frameworks JavaScript e o uso complexo de JavaScript conforme a Web amadurece. O ECMAScript 6 introduziu classes como açúcar sintático sobre a herança baseada em protótipo existente do JavaScript, fornecendo uma maneira alternativa de criar objetos e lidar com a herança.

Linguagens que suportam programação baseada em protótipo

Veja também

Referências

Leitura adicional