2010-03-25 20 views
22

Proveniente da uno sfondo C++, Im è utilizzato per ereditarietà multipla. Mi piace la sensazione di un fucile puntato esattamente al mio piede. Oggigiorno, lavoro di più in C# e in Java, dove puoi ereditare solo una baseclass ma implementare un numero qualsiasi di interfacce (ho avuto la terminologia giusta?).Modello di progettazione da utilizzare al posto dell'ereditarietà multipla

Ad esempio, immobili considerare due classi che implementano un'interfaccia comune, ma differenti baseclasses (ancora necessarie):

public class TypeA : CustomButtonUserControl, IMagician 
{ 
    public void DoMagic() 
    { 
     // ... 
    } 
} 

public class TypeB : CustomTextUserControl, IMagician 
{ 
    public void DoMagic() 
    { 
     // ... 
    } 
} 

Entrambe le classi sono UserControl s così non posso sostituire la classe di base. Entrambi devono implementare la funzione DoMagic. Il mio problema ora è che entrambe le implementazioni della funzione sono identiche. E odio il codice copia-e-incolla.

I (possibili) soluzioni:

  1. ho naturalmente vogliono TypeA e TypeB di condividere baseclass comuni, dove posso scrivere che la definizione funzione identica solo una volta. Tuttavia, a causa del limite di una sola baseclass, non riesco a trovare un posto lungo la gerarchia in cui si inserisce.
  2. Si potrebbe anche provare a implementare una sorta di composite pattern. Mettendo la funzione DoMagic in una classe helper separata, ma la funzione qui ha bisogno (e modifica) di un sacco di variabili/campi interni. L'invio di tutti come parametri (di riferimento) sarebbe solo un aspetto negativo.
  3. Il mio istinto mi dice che il adapter pattern potrebbe avere un posto qui, un po 'di classe per convertire tra i due quando necessario. Ma si sente anche hacky.

L'ho taggato con un linguaggio indipendente dal momento che si applica a tutte le lingue che utilizzano questo approccio one-baseclass-many-interfaces.

Inoltre, si prega di indicare se mi sembra di aver frainteso uno dei modelli che ho chiamato.

In C++ vorrei solo creare una classe con i campi privati, l'implementazione della funzione e inserirla nell'elenco di ereditarietà. Qual'è l'approccio corretto in C#/Java e simili?

risposta

10

è possibile utilizzare il modello di strategia o qualcosa di simile per usare ha un (composizione) al posto di è una (eredità):

public class TypeA : CustomButtonUserControl, IMagician { 
    IMagician magicObj = new Magical(); 
    public void DoMagic() { 
     magicObj.DoMagic(); 
    } 
} 

public class TypeB : CustomButtonUserControl, IMagician { 
    IMagician magicObj = new Magical(); 
    public void DoMagic() { 
     magicObj.DoMagic(); 
    } 
} 

public class Magical : IMagician { 
    public void DoMagic() { 
     // shared magic 
    } 
} 

ci sono altri modi di creare un'istanza di Your Private Membri di IMagician (come passarli come parametro tramite costruttore) ma quanto sopra dovrebbe iniziare.

3

Sostituire l'ereditarietà con la composizione.

Sposta la funzione 'comune' per separare la classe, creare un'istanza di quella classe e inserirla nell'oggetto TypeA e nell'oggetto TypeB.

3

L'intestino è corretto in questo caso. Il modello dell'adattatore è quello che stai cercando.

DoFactory ha buone esempi NET (che dovrebbe essere abbastanza vicino alle loro controparti Java pure):

Adapter Design Pattern in C# and VB.NET

1

La composite è destinato per oggetti complessi, che significa che l'obiettivo è un oggetto essendo costituito da altri oggetti. Il strategy-pattern può essere considerato un caso speciale di questo, ma una strategia non deve essere un oggetto. Penso che questo si applicherebbe di più al tuo caso. Poi di nuovo, questo dipende fortemente dalla natura di ciò che fa DoMagic().

9
  • In .Net, è possibile applicare i metodi di estensione alle interfacce.È davvero bello quando è possibile e applicabile per te perché è un modo raro di applicare un'implementazione comune a a un'interfaccia. Consideralo sicuramente, ma potrebbe non funzionare per te dal momento che dici che lo DoMagic funziona con molti membri privati. Potete impacchettare queste variabili private internal? In questo modo il metodo di estensione potrebbe accedervi.
  • Avere la funzionalità comune in un'altra classe. Se c'è un posto logico in cui mettere questa funzionalità comune, passa i tuoi oggetti a questo altro metodo di classe (forse questa è la funzionalità dell'interfaccia utente e hai già un assistente di interfaccia utente ...). Ancora una volta, puoi esporre i dati privati ​​con una proprietà interna/pubblica? (La sicurezza/incapsulamento è una preoccupazione in tutto questo ovviamente. Non so se le tue classi sono solo per uso interno o saranno esposte pubblicamente.)
  • Altrimenti, passare una classe funzionale separata (o un puntatore a funzione specifica) in il metodo definito dall'interfaccia. Dovresti avere un po 'di codice duplicato per passare le tue variabili private a questo riferimento di funzione esterna, ma almeno non sarebbe molto, e la tua implementazione sarebbe in un unico posto.
  • Potremmo renderlo troppo complicato. Non ti farà sentire tutto orientato agli oggetti quando vai a dormire stanotte, ma potresti avere una routine statica nella tua libreria da qualche parte che tutti gli implementatori di IMagician chiamano?
  • Alla fine, Adapter potrebbe davvero essere quello che stai cercando. Meno probabile ma ancora degno di considerazione è il Decorator pattern.

Se nulla sembra particolarmente buono, scegliere quello che si sente meglio, usano un paio di volte, e riorganizzare domani. :)

-3
abstract class Magical: CustomButtonUserControl 
{ 
    public void DoMagic() 
    { 
     // ... 
    } 
} 

public class TypeA : Magical 
{ 

} 

public class TypeB : Magical 
{ 

} 
+0

Questo è esattamente ciò che farei per istinto. Purtroppo, 'CustomButtonUserControl' e' CustomTextUserControl' sono diversi per fare questo lavoro, quindi la mia domanda. O mi sto perdendo qualcosa qui? È difficile giudicare la tua risposta quando pubblichi solo il codice. – Mizipzor

+2

OP ha detto specificamente: non posso sostituire la classe base. –

0

È possibile utilizzare la composizione di avere mago come una proprietà di tipo A e typeB

class Magician : IMagician 
{ 
    public void DoMagic() 
    {} 
} 

Class TypeA : CustomButtonUserControl 
{ 
    //property 
    Magician magicianInTypeA 
} 

Class TypeB : CustomTextUserControl 
{ 
    //property 
    Magician magicianInTypeB 
} 
1
public interface IMagician{ /* declare here all the getter/setter methods that you need; they will be implemented both in TypeA and TypeB, right? */ } 

public static class MyExtensions { 
    public static void doMagic(this IMagician obj) 
    { 
      // do your magic here 
    } 
} 

Ora, il problema è che se si VERAMENTE necessario utilizzare le proprietà private/metodi (come opposto a quelli "interni"), questo approccio non funzionerà. Beh, in realtà, potresti essere in grado di fare la tua magia se riesci a leggere queste proprietà attraverso la riflessione, ma anche se funziona, è una soluzione piuttosto brutta :)

[Nota che "doMagic" apparirà automaticamente come un parte di TypeA e TypeB, semplicemente perché implementate IMagician - non è necessario avere alcuna implementazione lì]

Problemi correlati