Pattern di comando per più tipi di ricevitori

4

Ho una struttura ad albero di oggetti. Gli oggetti hanno diversi tipi, ma tutti sono ereditati dalla stessa classe base. Ora voglio inviare qualche tipo di oggetto comando ai nodi. L'oggetto comando ha l'indirizzo del nodo di destinazione e deve essere passato attraverso il nodo principale e i nodi intermedi fino a raggiungere il nodo di destinazione. Il nodo di destinazione accetta il comando e gli consente di eseguire alcune azioni sul nodo:

void Node::processCommand (Command& cmd) {
    if(cmd.address == this->address)
        cmd.execute(this);
    else
        child[cmd.address]->processCommand(cmd);
}

Prima domanda: è uno schema di comando, o può essere un po 'del suo genere?

Successivamente, considera di avere nodi di classi ClassA e ClassB e voglio inviare alcuni comandi agli oggetti di entrambe le classi, e alcuni comandi solo per oggetti di ClassB, qualcosa del genere:

class CommandForBoth {
    void execute (BaseClass* obj) {
         obj->doSomething();
    }
}

class CommandForB {
    void execute (ClassB* obj) {
         obj->doSomethingSpecial();
    }
}

È possibile implementarlo senza calchi e senza più metodi per passare comandi di tipi diversi, come:

void Node::processCommand (CommandForBoth& cmd);
void Node::processCommand (CommandForClassA& cmd);
void Node::processCommand (CommandForClassB& cmd);
    
posta qloq 20.02.2017 - 13:00
fonte

1 risposta

2

È un modello di comando?

Il schema di comando implica che:

  1. Tutti i comandi condividono la stessa interfaccia. In C ++ questo di solito viene fatto con l'ereditarietà di un Command astratto.
  2. A Client crea un comando concreto e imposta Receiver .
  3. Un Invoker lancia il comando, che eseguirà le sue azioni per il Receiver già definito.

Il tuo approccio non soddisfa queste aspettative:

  1. CommandForBoth e CommandB non condividono la stessa interfaccia (ma forse non l'hai menzionata nello snippet).
  2. Il tuo comando non conosce il suo destinatario: al posto di Client che lo definisce al momento della creazione, è il Invoker che definisce il target all'esecuzione.
  3. Il Invoker è il tuo% co_de iniziale. Ma il tuo Invoker potrebbe non lanciare il comando, ma inoltrarlo a uno dei suoi figli.

O è un modello di visitatore?

Un modello di visitatore è progettato per eseguire operazioni su Node . La struttura è responsabile di attivare il visitatore per il suo elemento pertinente. Ogni elemento è responsabile di invocare il visitatore per i relativi sottoelementi pertinenti.

Questo sembra molto vicino a quello che stai facendo:

  • L'iniziale ObjectStructure corrisponde a Node ,
  • Ogni ObjectStructure corrisponde a un Node
  • Element corrisponde a Node::ProcessCommand(Command)
  • Element::accept(Visitor) corrisponde a Command
  • Visitor corrisponde a Command::execute(Node)

L'unica cosa insolita è che in un pattern di visitatore, la catena successiva di Visitor::visit(Node) viene in genere eseguita dagli elementi per chiamare il visitatore per tutti i bambini, mentre qui, accept() guarda solo l'indirizzamento e esegue il visitatore se l'indirizzo corrisponde.

Implementazione senza cast

Il pattern visitor ha una soluzione per il tuo problema di casting: ogni accept() (così per te, ogni Visitor ) ha un override di Command (quindi per te: visit() ) per ciascuno dei sottotipi di execute() (cioè Elements ) esistente. Nel tuo caso, tutti gli override ma non implementano nulla (o generano un'eccezione).

Questo è uno svantaggio del modello di visitatore, che potresti non volere. In alternativa, se Nodes è polimorfico, potresti facilmente deviare dal modello di visitatore "puro" e utilizzare solo un Nodes :

class CommandForB {
    void execute (BaseClass* obj) {
         ClassB bobj = dynamic_cast<ClassB*>(obj); 
         if (bobj) 
             bobj->doSomethingSpecial();
         else {  // ouch execute was invoked for an Node it shouldn't 
                // either do nothing, or log the unexpected situation, 
                // or throw an exception
         }
    }
}
    
risposta data 20.02.2017 - 19:59
fonte

Leggi altre domande sui tag