Qual è il nome di questa variante di Adapter Pattern?

4

Introduzione

Un adattatore normalmente avvolge un altro oggetto, in modo che possa essere utilizzato in un'interfaccia per la quale non è stato progettato, ad es., quando si desidera utilizzare

interface Node {
    Node parent();
    Iterable<Node> children();
}

insieme a

class TreeModel {
    private Node root;
    // example method (stupid)
    Node grandparent(Node node) {
        return node.parent().parent();
    }
}

e ti viene assegnata una classe come

class File {
    File getParent() {...}
    File[] listFiles() {...}
}

devi scrivere del FileToNodeAdapter .

Sfortunatamente, significa che devi avvolgere ogni singolo oggetto e hai anche bisogno sia di un modo per ottenere da FileToNodeAdapter a File (che è banale, dato che è incorporato), ma anche da File a FileToNodeAdapter , che porta a creare un nuovo oggetto ogni volta o a utilizzare un po 'di Map , che deve essere accessibile globalmente o referenziato in ogni FileToNodeAdapter .

Il modello

Sostituisci l'interfaccia Node di

interface NodeWorker<T> {
    T parentOf(T node);
    Iterable<T> childrenOf(T node);
}

e modifica il TreeModel come

class TreeModel<T> {
    private NodeWorker<T> nodeWorker;
    private T root;
    // example method (stupid)
    T grandparent(T node) {
        return nodeWorker.parentOf(nodeWorker.parentOf(node));
    }
    ...
}

Questo modello ha un nome?

Ci sono degli svantaggi, oltre al fatto che è un po 'più prolisso e applicabile solo quando sei responsabile del codice TreeModel ?

    
posta maaartinus 08.10.2011 - 22:45
fonte

3 risposte

2

tl; dr

È lo schema di strategia menzionato prima da Cameron MacFarland. Ma ho anche qualche problema con la tua domanda.

L'interfaccia del tuo nodo non sta usando i generici. Pertanto, per utilizzare un nodo è necessario conoscere il tipo di nodo che si sta tentando di utilizzare (file nell'esempio). Quindi, se risolviamo questo problema aggiungendo il nodo, i due esempi diventano più chiari.

Ora quello che vediamo è che entrambi sono molto simili con l'eccezione che il Node è statico vs stateless come nel NodeWorker.

Puoi vederlo chiaramente aggiungendo un veloce: T getAdapted () all'interfaccia del nodo.

Ora hai praticamente lo stesso codice del NodeWorker con una riga di codice in più ma un metodo grandParent leggermente (probabilmente) più pulito.

Per quanto riguarda il numero di classi che devi implementare usando questo nuovo Nodo vs il tuo NodeWorker? È esattamente lo stesso ora.

Quindi ora la domanda diventa una domanda in più parti. Se si desidera un adattatore, non si dovrebbe mai sapere realmente quale adattatore si stava adattando (idealmente). Perché l'adattatore è il tuo modo "standard" per interagire con più sottosistemi che non vuoi dover gestire. Questo significa che non dovresti mai sapere quale tipo di oggetto è stato effettivamente adattato. Cosa volevi fare con il file? Cosa vorresti fare con la modifica del file se stavi usando qualche altro tipo di nodo? In caso contrario, aggiungere i metodi di interazione necessari all'interfaccia nodo.

Qual è il vantaggio di ciò? Il vantaggio è che il Nodo NON ha bisogno di tipi generici e può effettivamente restituire altri tipi di NODI senza doverli informare o curare i clienti.

Ad esempio supponiamo di avere tipi di file tipi di directory e tipi di dischi.

la chiamata a grandParent potrebbe in teoria restituire Directory come genitore di un file e Disc come padre della Directory che in sostanza restituire DirectoryNodeAdapter e DiscNodeAdapter tutti sotto l'interfaccia di Node senza che il client debba preoccuparsi di questo!

Metodi comuni (diciamo eliminazione) potrebbero essere aggiunti all'interfaccia e non ti dovrai mai preoccupare del fatto che si trattava di una directory o di un disco o di un file da eliminare mentre gli adattatori del nodo ne estraevano la distanza.

La tua strategia NodeWorker non avrà questa capacità come è stata digitata nel primo tipo di file e la chiamata del nonno presuppone che questo stesso tipo verrà restituito (Questo può essere modificato nell'interfaccia della strategia, ovviamente).

Fondamentalmente ciò che si riduce a è il tuo caso d'uso. Personalmente mi piace usare il modello di strategia quando si lavora con il set di oggetti SAME, ma le specifiche di come voglio gestirle durante lo svolgimento delle attività SAME varieranno.

Un buon esempio di ciò sarebbe l'interfaccia di Comparator in java, dove so che ho a che fare con un insieme di oggetti e so che ho bisogno di variare (o cambiare) come li paragono.

Personalmente uso schemi di adattatori quando voglio nascondere il sistema sottostante, se prima o poi volessi sostituire il sistema sottostante.

Un buon esempio di ciò è java jdbc e l'architettura di plugin di eclipse.

    
risposta data 10.10.2011 - 10:13
fonte
1

Sembra che sia il Pattern di strategia .

L'esempio reale fornito a dofactory sembra adattarsi al tuo esempio il migliore.

    
risposta data 09.10.2011 - 17:24
fonte
1

Non penso che ci sia un nome specifico per questo; Penso che sia una sorta di adattatore glorificato con composizione, ma reso molto generico. Che è davvero potente.

Con NodeWorker e TreeModel, stai scambiando l'estensione per la composizione, usando i generici. Stai usando i generici per parametrizzare il tipo sottostante dei nodi. In modo che qualsiasi tipo di oggetto possa semplicemente cadere nell'albero senza modifiche.

C'è un passo che ti serve, però, e questa è l'implementazione concreta di NodeWorker, che potrebbe essere basata su tipi concreti di T. (Oppure potresti semplicemente fare di NodeWorker una classe, fornire un metodo get () e trasportare l'oggetto T lungo senza sapere o preoccuparsi di cosa sia veramente.)

FWIW In Haskell, c'è questa idea di "sollevare" le funzioni in uno spazio monadico e ti stai muovendo in quella direzione. Stai mettendo oggetti di tipo T nello spazio TreeModel. TreeModel è fondamentalmente sulla strada per diventare una monade. Ma quell'argomento diventa piuttosto profondo e ho sempre capito male.

EDIT: ad esempio (pseudocodice)

*class* NodeWorker<T> {
    T object;
    T get() { return object; }
    Nodeworker(T object) { this.object = object; }
    ... other parent/child information ...
    T parentOf(T node) { .... }
    Iterable<T> childrenOf(T node) { ... }
}

Il vantaggio è che hai solo un nodello che porta un oggetto di tipo arbitrario. Lo svantaggio è che non si conosce nulla sull'oggetto e non si possono richiamare i suoi metodi (eccetto per le cose di base sull'oggetto).

    
risposta data 07.03.2014 - 20:11
fonte

Leggi altre domande sui tag