Come evitare di scrivere molte funzioni pass-through in un wrapper?


13

Ho una classe, che avvolge un'altra classe di un tipo base comune. Poiché l'interfaccia del tipo di base è piuttosto ampia, ciò implica la scrittura di molte funzioni pass-through. Sto cercando un modo per evitarlo.

Facciamo un esempio:

      Car
     /   \
Volvo     VolvoWithTrailer

Ora devo implementare tutte le funzioni nell'interfaccia dell'automobile per VolvoWithTrailer e chiamare la funzione appropriata sull'oggetto Volvo avvolto tranne forse GetMaxSpeed ​​() che restituirà qualcosa di più basso. Fondamentalmente avrò molte funzioni simili

int VolvoWithTrailer::GetNumSeats() {
  return mVolvo.GetNumSeats()
}

Un modo ovvio per risolverlo sarebbe rendere VolvoWithTrailer una sottoclasse di Volvo

    Car
     |
   Volvo
     |
VolvoWithTrailer

ma ciò sembra violare il principio di favorire la composizione rispetto all'eredità.

In quale altro modo potrei evitare di scrivere tutti quei wrapper (la lingua è C ++)? Oppure - se questa è la tua posizione - perché dovrei semplicemente scriverli / usare solo l'eredità? C'è qualche modello magico che potrebbe aiutare in questo?


2
Non ci ho pensato troppo e ho pensato a Java. Che dire di un'interfaccia "Trailer"? VolvoWithTrailer estenderebbe la Volvo e implementerebbe l'interfaccia del rimorchio?

7
Favorire la composizione sull'eredità solo quando ha senso farlo.
Robert Harvey,

@RobertHarvey: +1. Questa è una linea guida, non un "principio" e certamente non un comandamento assoluto.
Mason Wheeler,

In Python puoi risolverlo con l'introspezione (ciò che C # chiama riflessione).
user16764,

1
Il tuo IDE non gestisce la generazione del codice?
Amy Blankenship,

Risposte:


5

La mia opinione è che dovresti scrivere tutto ciò di cui hai bisogno per migliorare la stabilità a lungo termine e la manutenibilità del programma. Che senso ha evitare di scrivere 20 righe di codice oggi, se ogni volta che tocchi qualcosa che usa questo codice o torni per mantenere questa Classe, ti costa 5 minuti in più o più perché non hai inserito l'ora?

Quindi la domanda diventa "cosa devi scrivere?" Non credo che fornisci informazioni sufficienti per essere in grado di dare una risposta definitiva, quindi mi limiterò a elencare alcune cose che mi avrebbe pensato nel prendere una decisione:

1. Devi avere l'oggetto Trailer da solo, ora o nel prossimo futuro?
Se è così, hai una buona argomentazione per creare il wrapper attorno ad entrambi gli oggetti compositi, poiché dovrai già avvolgere l'uno o l'altro. Potrebbe anche essere coerente.

2. C'è uno dei tre tipi (Auto, Trailer, CarWithTrailer) in un'area del codice in cui gli sviluppatori vanno frequentemente o sembrano verosimili per diventarlo?
In tal caso, fai molta attenzione, perché le ramificazioni della scelta della cosa sbagliata possono essere ammortizzate molto ogni volta che viene toccato il codice. In caso contrario, potrebbe non fare alcuna differenza ciò che decidi. Basta scegliere una direzione e seguirla.

3. Cosa è più facile da capire?
Un approccio ti salta fuori che qualcuno che ti sta dietro avrebbe immediatamente "capito" quello che stai cercando di fare? I tuoi compagni di squadra hanno particolari preconcetti che potrebbero rendere più difficile il loro approccio? Se tutto è uguale, scegli la soluzione che impone il carico cognitivo più basso.

4. Ci sono ulteriori vantaggi nell'usare un approccio rispetto a un altro?
Una cosa che mi colpisce è che l'idea del wrapper ha un netto vantaggio se la scrivi che il tuo CarWithTrailerWrapper può avvolgere qualsiasi auto e qualsiasi rimorchio in un CarWithTrailer. Quindi, finisci per scrivere tutti i tuoi metodi pass-through, ma li scrivi solo una volta , piuttosto che una volta per ogni Classe di auto.

Questo rende il tuo investimento iniziale nella scrittura dei metodi pass-through molto più economico quando aggiungi più auto e rimorchi. Aiuta anche a ridurre la tentazione di accoppiare le cose troppo direttamente a Volvo o VolvoWithTrailer, riducendo allo stesso tempo le conseguenze dell'accoppiamento con CarWithTrailerWrapper, poiché puoi fare molto di più con quella sola implementazione.

Forse ci sono diversi vantaggi che potresti ottenere gratuitamente usando il metodo di estensione, ma non li vedo.

OK, quindi sembra che io abbia parlato di me stesso per andare avanti e compilare i metodi pass-through, per applicazioni non banali.

Non sono un programmatore C ++, quindi non ho idea di quali strumenti di generazione del codice sono disponibili per te. L'IDE che utilizzo può generare metodi da un'interfaccia e posso aggiungere modelli di codice che renderebbero abbastanza indolore la scrittura di pass-through. Se non hai accesso a un IDE che lo fa, puoi effettivamente ottenere abbastanza lontano lasciando che Excel scriva il codice per te .


3

Poiché l'interfaccia del tipo di base è piuttosto ampia, ciò implica la scrittura di molte funzioni pass-through.

E c'è il tuo problema.

Le interfacce dovrebbero gestire una sola responsabilità. Mantenerli sottili e mirati limita la quantità di lavoro passthrough che potrebbe essere necessario eseguire in caso di decoratore, proxy o altro wrapper (oltre a rendere il simpiler di classe da utilizzare, testare, mantenere ed estendere).


5
Cosa? Una classe può implementare più interfacce e le interfacce possono ereditare l'una dall'altra. Quindi, anche se un'interfaccia è piuttosto piccola, ciò non ha nulla a che fare con il fatto che le Classi che le implementano richiedano molte funzioni o funzioni pass-through. Il più piccolo e più ben definito le classi , più è probabile diventa che sarà pass-through funzioni necessità.
Amy Blankenship,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.