2013-04-06 11 views
8

Ho cercato di eliminare il double-dispatch pattern e di avere difficoltà. Alla fine ho tentato un programma di esempio per aiutarmi a capire. Here's l'essenza. Ma poi ho deciso di provarlo without Double dispatch e la soluzione non sembrava più terribile del solito. Che cosa sto facendo di sbagliato?Tentativo di comprendere il modello di doppia spedizione

Modifica: come da suggerimento, ho postato questa domanda here. Mantenere questo link in giro per i reindirizzamenti.

+0

È possibile avere maggiore fortuna su http: // http: //programmers.stackexchange.com/. Anche se questa è una domanda interessante, non è particolarmente adatta allo Stack Overflow; questo sito tende a preferire domande concrete con una "risposta giusta". –

+1

Non sono d'accordo; questa è una bella domanda da porre a SO –

risposta

17

Nella singola spedizione --- ciò che si vede nella maggior parte delle lingue OO moderne --- un metodo è inviato in base al tipo di runtime di un singolo oggetto. Questo si presenta come l'operatore punto (in ruby, java, javascript, ecc.) O l'operatore freccia (perl, C++).

# look, ma single dispatch! 
# method on obj's run-time type that is called 
dentist.work_on(patient) 

doppio invio, quindi, sarebbe basato sul tipo run-time di due oggetti. Ci sono alcuni modi in cui questo potrebbe apparire; e su quale oggetto dovrebbe vivere il metodo?

# Hmm, this looks weird. 
# Is the method in to dentist.class or patient.class? 
(dentist, patient).do_dentistry() 


# okay, this looks more familiar; the method lives on obj1.class 
# This only works in static-typed languages which support double dispatch 
# in which you declare the type of the method parameters. 
dentist.work_on(patient) 

class Dentist 
    def work_on(Adult patient); ...; end 
    def work_on(Child patient); ...; end 
end 

Lingue come groovy con invio multiplo generalizzare il secondo esempio sopra; essi considerano i tipi di runtime di tutti i parametri quando si sceglie quale metodo eseguire. Vedi ad esempio this blog post su groovy e invio multiplo.

La maggior parte delle lingue OO moderne ha una sola spedizione e il Dispatch multiplo Il modello è un tentativo di ottenere i vantaggi di una spedizione multipla nella lingua. Funziona anche per linguaggi dinamici come Ruby. Funziona facendo un'unica spedizione due volte di seguito. La prima chiamata al metodo chiamerà un metodo sul secondo oggetto.

class Dentist 
    def work_on(patient) 
     patient.dispatch_work(self) 
    end 
    def work_on_adult(patient) 
     drill_as_hard_as_you_can(patient) 
    end 
    def work_on_child(patient) 
     use_bubble_gum_toothpaste(patient) 
     give_toothbrush_to(patient) 
    end 
end 

class Doctor 
    def work_on(patient) 
     patient.dispatch_work(self) 
    end 
    def work_on_adult(patient) 
     do_checkup(patient) 
    end 
    def work_on_child(patient) 
     assure_presence_of(patient.guardian) 
     ask_questions_to(patient.guardian) 
     do_checkup(patient) 
     give_cheap_toy_to(patient) 
    end 
end 



class Adult 
    def dispatch_work(dentist) 
     dentist.work_on_adult(self) 
    end 
end 

class Child 
    def dispatch_work(dentist) 
     dentist.work_on_child(self) 
    end 
end 

La doppia spedizione modello è quello che io chiamo un modello di basso livello perché altri modelli sono costruiti su di esso. Ad esempio, il pattern Visitor si basa molto sul modello di doppia spedizione.


Aggiornamento appena visto i tuoi GIST. Il tuo primo gist non sta davvero facendo doppio invio. Certo, stai spedendo due volte, ma non stai cambiando il comportamento in quella seconda spedizione. Per cambiarlo in doppia spedizione farei qualcosa di simile.

class Chicken 
    def make_dispatch dish 
    dish.make_with_chicken self 
    end 
end 

class Beef 
    def make_dispatch dish 
    dish.make_with_beef self 
    end 
end 


module Dish 
    def make meat 
    meat.make_dispatch self 
    end 
end 

class Sandwich 
    include Dish 

    def make_with_chicken chicken 
    puts "Grilled Chicken Sandwich" 
    end 

    def make_with_beef beef 
    puts "Roast Beef Sandwich" 
    end 
end 

class Stew 
    include Dish 

    def make_with_chicken chicken 
    puts "Thai curry" 
    end 

    def make_with_beef beef 
    puts "Beef stew" 
    end 
end 

class Casserole 
    include Dish 

    def make_with_chicken chicken 
    puts "Chicken Pot Pie--or something" 
    end 

    def make_with_beef beef 
    puts "Shepard's Pie" 
    end 
end 

Sandwich.new.make(Chicken.new) 
Stew.new.make(Chicken.new) 
Casserole.new.make(Beef.new) 
+0

Penso che lo schema dei visitatori sia in realtà un modo comune per implementare più spedizioni. Vedi per es. http://en.wikipedia.org/wiki/Double_dispatch#Double_dispatch_in_C.2B.2B Anche il tuo esempio di dentista potrebbe confondere un po 'perché c'è solo un dentista, quindi non hai davvero bisogno di una doppia spedizione. :) – Sarien

+0

@Sarien buon punto, ho aggiunto un medico per l'esempio. Sì, il visitatore è un modo molto comune di fare doppio invio. –

Problemi correlati