Alcuni pro e contro
Professionisti per polimorfici:
- Un'interfaccia polimorfica più piccola è più facile da leggere. Devo solo ricordare un metodo.
- Si riferisce al modo in cui si intende utilizzare la lingua: digitazione anatra.
- Se è chiaro quali oggetti voglio estrarre da un coniglio, non ci dovrebbero comunque essere ambiguità.
- Fare un sacco di controlli di tipo è considerato negativo anche in linguaggi statici come Java, dove avere un sacco di controlli di tipo per il tipo di oggetto rende un brutto codice, se il mago ha davvero bisogno di distinguere tra il tipo di oggetti che sta tirando coniglio fuori?
Pro ad hoc:
- È meno esplicito, posso estrarre una stringa da un'istanza
Cat
? Funzionerebbe? se no, qual è il comportamento? Se non limito il tipo qui, devo farlo nella documentazione, o nei test che potrebbero peggiorare il contratto.
- Hai tutta la gestione di tirare un coniglio in un posto, il Mago (alcuni potrebbero considerarlo un problema)
- I moderni ottimizzatori JS differenziano tra funzioni monomorfe (funziona su un solo tipo) e polimorfiche. Loro sanno come ottimizzare i molto monomorfi meglio così la versione
pullRabbitOutOfString
rischia di essere molto più veloce nei motori come V8. Guarda questo video per ulteriori informazioni. Modifica: Ho scritto un perf io stesso, si trasforma che in pratica, questo non è sempre il caso .
Alcune soluzioni alternative:
Secondo me, questo tipo di design non è molto "Java-Scripty" per cominciare. JavaScript è una lingua diversa con idiomi diversi da linguaggi come C #, Java o Python. Questi idiomi hanno origine in anni di sviluppatori che cercano di capire le parti deboli e forti del linguaggio, quello che farei è cercare di attenermi a questi idiomi.
Ci sono due belle soluzioni a cui posso pensare:
- Elevare oggetti, rendere "pulibili" gli oggetti, renderli conformi all'interfaccia in fase di esecuzione, quindi far funzionare il Mago su oggetti pulibili.
- Uso del modello di strategia, insegnando al mago in modo dinamico come gestire diversi tipi di oggetti.
Soluzione 1: elevazione di oggetti
Una soluzione comune a questo problema è quella di "elevare" gli oggetti con la capacità di far estrarre i conigli da essi.
Cioè, avere una funzione che richiede un certo tipo di oggetto e aggiunge la tiratura fuori da un cappello. Qualcosa come:
function makePullable(obj){
obj.pullOfHat = function(){
return new Rabbit(obj.toString());
}
}
Posso creare tali funzioni makePullable
per altri oggetti, potrei creare un makePullableString
, ecc. Sto definendo la conversione su ciascun tipo. Tuttavia, dopo aver elevato i miei oggetti, non ho tipo per usarli in modo generico. Un'interfaccia in JavaScript è determinata da una digitazione anatra, se ha un metodo pullOfHat
I può tirarlo con il metodo del Mago.
Quindi il mago potrebbe fare:
Magician.pullRabbit = function(pullable) {
var rabbit = obj.pullOfHat();
return {rabbit:rabbit,text:"Tada, I pulled a rabbit out of "+pullable};
}
Elevare gli oggetti, usando una sorta di schema di mixin, sembra la cosa più JS da fare.
(Nota questo è problematico con i tipi di valore nella lingua che sono stringa, numero, null, indefinito e booleano, ma sono tutti in box)
Ecco un esempio di come potrebbe essere il codice
Soluzione 2: schema strategico
Quando si discute questa domanda nella chat room di JS in StackOverflow il mio amico phenomnomnominal ha suggerito l'uso di Schema di strategia .
Ciò ti consentirebbe di aggiungere le abilità per estrarre conigli da vari oggetti in fase di esecuzione e creerebbe un codice molto JavaScript. Un mago può imparare come estrarre gli oggetti di diverso tipo dai cappelli, e li tira in base a quella conoscenza.
Ecco come potrebbe apparire in CoffeeScript:
class Magician
constructor: ()-> # A new Magician can't pull anything
@pullFunctions = {}
pullRabbit: (obj) -> # Pull a rabbit, handler based on type
func = pullFunctions[obj.constructor.name]
if func? then func(obj) else "Don't know how to pull that out of my hat!"
learnToPull: (obj, handler) -> # Learns to pull a rabbit out of a type
pullFunctions[obj.constructor.name] = handler
Puoi vedere il codice JS equivalente qui .
In questo modo, trarrai beneficio da entrambi i mondi, l'azione di come tirare non è strettamente accoppiata agli oggetti o al Mago e penso che ciò sia una soluzione molto bella.
L'utilizzo sarebbe qualcosa come:
var m = new Magician();//create a new Magician
//Teach the Magician
m.learnToPull("",function(){
return "Pulled a rabbit out of a string";
});
m.learnToPull({},function(){
return "Pulled a rabbit out of a Object";
});
m.pullRabbit(" Str");