O "Padrão OLOO (Objetos Vinculados a Outros Objetos)" de Kyle Simpson difere de alguma forma do padrão de projeto Prototype? Além de cunhá-lo por algo que indica especificamente "vinculação" (o comportamento dos protótipos) e esclarecendo que não há "cópia" acontecendo aqui (um comportamento das classes), o que exatamente seu padrão apresenta?
Aqui está um exemplo do padrão de Kyle em seu livro, "You Don't Know JS: this & Object Prototypes":
var Foo = {
init: function(who) {
this.me = who;
},
identify: function() {
return "I am " + this.me;
}
};
var Bar = Object.create(Foo);
Bar.speak = function() {
alert("Hello, " + this.identify() + ".");
};
var b1 = Object.create(Bar);
b1.init("b1");
var b2 = Object.create(Bar);
b2.init("b2");
b1.speak(); // alerts: "Hello, I am b1."
b2.speak(); // alerts: "Hello, I am b2."
javascript
design-patterns
shmuli
fonte
fonte
Respostas:
O OLOO abraça a cadeia de protótipo como ela é, sem a necessidade de sobrepor outras semânticas (confusas de IMO) para obter a ligação.
Portanto, esses dois trechos têm o mesmo resultado EXATA, mas chegam lá de forma diferente.
Forma do construtor:
Formulário OLOO:
Em ambos os trechos, um
x
objeto é[[Prototype]]
vinculado a um objeto (Bar.prototype
ouBarObj
), que por sua vez está vinculado a um terceiro objeto (Foo.prototype
ouFooObj
).Os relacionamentos e delegação são idênticos entre os snippets. O uso de memória é idêntico entre os snippets. A capacidade de criar muitos "filhos" (também conhecido como muitos objetos como
x1
throughx1000
, etc) é idêntica entre os snippets. O desempenho da delegação (x.y
ex.z
) é idêntico entre os snippets. O desempenho de criação de objeto é mais lento com OLOO, mas a verificação de integridade que revela que o desempenho mais lento não é realmente um problema.O que eu argumento que o OLOO oferece é que é muito mais simples apenas expressar os objetos e vinculá-los diretamente, do que vinculá-los indiretamente por meio do construtor /
new
mecanismos. O último finge ser sobre classes, mas na verdade é apenas uma sintaxe terrível para expressar delegação ( nota lateral: aclass
sintaxe ES6 também !).OLOO está apenas eliminando o intermediário.
Aqui está outra comparação de
class
vs OLOO.fonte
Object.create(...)
é muitas vezes mais lento do quenew
. jsperf.com/object-create-vs-crockford-vs-jorge-vs-constructor/…Eu li o livro de Kyle e o achei muito informativo, especialmente os detalhes sobre como
this
é encadernado.Prós:
Para mim, existem alguns grandes prós do OLOO:
1. Simplicidade
O OLOO depende da
Object.create()
criação de um novo objeto que é[[prototype]]
vinculado a outro objeto. Você não precisa entender que as funções têm umaprototype
propriedade ou se preocupar com qualquer uma das armadilhas potenciais relacionadas que vêm de sua modificação.2. Sintaxe mais limpa
Isso é discutível, mas acho que a sintaxe OLOO é (em muitos casos) mais organizada e concisa do que a abordagem javascript 'padrão', especialmente quando se trata de polimorfismo (
super
chamadas de estilo).Contras:
Acho que há uma parte questionável do design (que realmente contribui para o ponto 2 acima), e isso tem a ver com sombreamento:
A ideia por trás disso é que os objetos têm suas próprias funções mais específicas que, então, são delegadas internamente a funções mais abaixo na cadeia. Por exemplo, você pode ter um
resource
objeto com umasave()
função que envia uma versão JSON do objeto para o servidor, mas também pode ter umclientResource
objeto que tem umastripAndSave()
função, que primeiro remove propriedades que não deveriam ser enviadas ao servidor .O problema potencial é: se alguém vier e decidir fazer um
specialResource
objeto, não totalmente ciente de toda a cadeia de protótipos, eles podem razoavelmente * decidir salvar um carimbo de data / hora para o último salvamento em uma propriedade chamadasave
, que obscurece asave()
funcionalidade básica oresource
objeto dois liga-se à cadeia de protótipo:Este é um exemplo particularmente artificial, mas a questão é que, especificamente, não sombrear outras propriedades pode levar a algumas situações embaraçosas e ao uso intenso de um dicionário de sinônimos!
Talvez uma ilustração melhor disso seja um
init
método - particularmente comovente, já que o OOLO evita as funções do tipo construtor. Uma vez que cada objeto relacionado provavelmente precisará dessa função, pode ser um exercício tedioso nomeá-los de maneira apropriada, e a exclusividade pode dificultar a lembrança de qual usar.* Na verdade, não é particularmente razoável (
lastSaved
seria muito melhor, mas é apenas um exemplo).fonte
[[Prototype]]
próprio sistema, não especificamente do OLOO.b.timeStampedSave();
vez dea.timeStampedSave();
na última linha do trecho de código.A discussão em "You Don't Know JS: this & Object Prototypes" e a apresentação do OLOO são instigantes e eu aprendi muito lendo o livro. Os méritos do padrão OLOO são bem descritos nas outras respostas; no entanto, tenho as seguintes queixas de animais de estimação com ele (ou estou faltando algo que me impede de aplicá-lo com eficácia):
1
Quando uma "classe" "herda" outra "classe" no padrão clássico, as duas funções podem ser declaradas sintaxe semelhante ( "declaração de função" ou "instrução de função" ):
Em contraste, no padrão OLOO, diferentes formas sintáticas usadas para definir a base e os objetos derivados:
Como você pode ver no exemplo acima, o objeto base pode ser definido usando a notação literal do objeto, enquanto a mesma notação não pode ser usada para o objeto derivado. Essa assimetria me incomoda.
2
No padrão OLOO, a criação de um objeto consiste em duas etapas:
Object.create
chame algum método personalizado não padrão para inicializar o objeto (que você deve se lembrar, pois pode variar de um objeto para o outro):
Em contraste, no padrão Prototype você usa o operador padrão
new
:3
No padrão clássico, posso criar funções de utilidade "estáticas" que não se aplicam diretamente a um "instante" atribuindo-as diretamente à função de "classe" (em oposição à sua
.prototype
). Por exemplo, função semelhantesquare
no código abaixo:Em contraste, no padrão OLOO, quaisquer funções "estáticas" estão disponíveis (por meio da cadeia [[protótipo]]) nas instâncias do objeto:
fonte
Como Kyle explica quando dois objetos estão
[[Prototype]]
vinculados, eles não são realmente dependentes um do outro; em vez disso, são objetos individuais. Você está ligando um objeto a outro com uma[[Prototype]]
ligação que pode ser alterada a qualquer momento que desejar. Se você considerar dois[[Prototype]]
objetos vinculados criados por meio do estilo OLOO como sendo dependentes um do outro, também deverá pensar o mesmo sobre os criados por meio deconstructor
chamadas.Agora pense por um segundo que você pensa
foo
bar
ebaz
como sendo dependente um do outro?Agora vamos fazer o mesmo com este
constructor
código de estiloA única diferença b / w o último e o primeiro código é que no caso deste último
foo
,bar
,baz
bbjects estão ligados a cada um-a outra através de objectos arbitrários da suaconstructor
função (Foo.prototype
,Bar.prototype
,Baz.prototype
) mas no ex um (OLOO
estilo) que estão ligadas directamente. Ambas as maneiras que você está apenas ligandofoo
,bar
,baz
uns com os outros, diretamente na antiga um e indiretamente no último. Mas, em ambos os casos, os objetos são independentes um do outro porque não é realmente como uma instância de qualquer classe que, uma vez instanciada, não pode ser herdada de alguma outra classe. Você sempre pode alterar qual objeto um objeto deve delegar também.Portanto, são todos independentes uns dos outros.
Sim, isso é realmente possível-
Vamos usar
Tech
como um objeto utilitáriocrie quantos objetos desejar vinculados
Tech
-Você acha que
html
,css
,js
objetos estão conectados uns aos-outros? Não, eles não são. Agora vamos ver como poderíamos ter feito isso com aconstructor
funçãocrie quantos objetos desejar vinculados
Tech.proptotype
-Algumas verificações (evitando console.log) -
Como você acha que esses
constructor
objetos -estilo (html
,css
,js
) Objetos diferente doOLOO
código de estilo? Na verdade, eles têm o mesmo propósito. NoOLOO
estilo, um objeto delegaTech
(a delegação foi definida explicitamente), enquanto noconstructor
estilo um objeto é delegadoTech.prototype
(a delegação foi definida implicitamente). No final das contas, você acaba ligando os três objetos, sem ligação entre si, a um objeto, usando diretamenteOLOO
-style, indiretamente usandoconstructor
-style.Não,
ObjB
aqui não é como uma instância (em linguagens clássicas) de qualquer classeObjA
. Deve-se dizer que oobjB
objeto é delegado aoObjA
objeto no momento de sua criação " . Se você usasse o construtor, teria feito o mesmo 'acoplamento', embora indiretamente usando.prototype
s.fonte
@Marcus @bholben
Talvez possamos fazer algo assim.
Claro, criar um objeto Point3D que se vincula ao protótipo de um objeto Point2D é meio bobo, mas isso não vem ao caso (eu queria ser consistente com seu exemplo). De qualquer forma, no que diz respeito às reclamações:
A assimetria pode ser corrigida com Object.setPrototypeOf do ES6 ou o mais desaprovado
__proto__ = ...
que eu usar. Também podemos usar super em objetos regulares agora, como visto emPoint3D.init()
. Outra maneira seria fazer algo comoembora eu particularmente não goste da sintaxe.
Sempre podemos apenas agrupar
p = Object.create(Point)
e entãop.init()
em um construtor. por exemploPoint.create(x,y)
. Usando o código acima, podemos criar umaPoint3D
"instância" da seguinte maneira.Acabei de criar este hack para emular métodos estáticos no OLOO. Não tenho certeza se gosto ou não. Requer a chamada de uma propriedade especial no topo de quaisquer métodos "estáticos". Por exemplo, tornei o
Point.create()
método estático.Alternativamente, com os Símbolos ES6, você pode estender as classes básicas de Javascript com segurança. Portanto, você pode economizar algum código e definir a propriedade especial em Object.prototype. Por exemplo,
fonte
@james emanon - Então, você está se referindo à herança múltipla (discutida na página 75 do livro "Você não conhece JS: este e protótipos de objeto"). E esse mecanismo podemos encontrar na função "estender" do sublinhado, por exemplo. Os nomes dos objetos que você indicou em seu exemplo são uma mistura de maçãs, laranjas e doces, mas eu entendo o que está por trás. Pela minha experiência, esta seria a versão OOLO:
É um exemplo simples, mas o ponto mostrado é que estamos apenas encadeando objetos em uma estrutura / formação bastante plana e ainda temos a possibilidade de usar métodos e propriedades de vários objetos. Alcançamos as mesmas coisas que com a abordagem de classe / "copiar as propriedades". Resumido por Kyle (página 114, "this & Object Prototypes"):
Eu entendo que a maneira mais natural para você seria declarar todos os objetos "pai" (cuidado :)) em um lugar / chamada de função, em vez de modelar a cadeia inteira.
O que é necessário é uma mudança no pensamento e na modelagem de problemas em nossas aplicações de acordo com isso. Eu também estou me acostumando. Espero que ajude e o veredicto final do próprio Kyle seria ótimo. :)
fonte
@Marcus, assim como você, gostei muito do OLOO e também não gosto da assimetria descrita em seu primeiro comentário. Tenho brincado com uma abstração para trazer de volta a simetria. Você pode criar uma
link()
função que é usada no lugar deObject.create()
. Quando usado, seu código pode ter a seguinte aparência ...Lembre-se de que
Object.create()
tem um segundo parâmetro que pode ser passado. Aqui está a função de link que aproveita o segundo parâmetro. Também permite um pouco de configuração personalizada ...Claro, acho que @Kyle não endossaria o sombreamento da
init()
função no objeto Point3D. ;-)fonte
Object.assign()
comObject.create()
, podemos simplificar muito alink()
função acima. Em seu lugar, poderíamos usar isso:function create(delegate, props) { return Object.assign(Object.create(delegate), props); }
. Ou melhor ainda, podemos usar Sublinhado ou Lodash para torná-lo realmente concisa:_.create(delegate, props)
.Existe uma maneira de OLOO mais do que "dois" objetos .. todos os exemplos consistem no exemplo baseado (veja o exemplo do OP). Digamos que temos os seguintes objetos, como podemos criar um "quarto" objeto que possui os atributos dos "outros" três? ala ...
esses objetos são arbitrários e podem abranger todos os tipos de comportamentos. Mas a essência é que temos 'n' número de objetos, e nosso novo objeto precisa de algo de todos os três.
em vez dos exemplos fornecidos ala:
MAS, nosso novoObjeto = (botão, bicicleta, sapato) ......
Qual é o padrão para fazer isso acontecer no OLOO?
fonte
Object.assign()
- developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… . Se estiver escrevendo em ES5, você pode usar Underscore's_.extend()
ou Lodash's_.assign()
. Aqui está um excelente vídeo para explicar ... youtu.be/wfMtDGfHWpA . Se você tiver qualquer propriedade colidindo, a última vence - então a ordem é importante.