2012-09-25 24 views
5

Ho appena appreso un po 'di Composizione su Eredità. Mi stavo chiedendo se avrei dovuto applicare il concetto a qualcosa che ho scritto di recente.Abstract Base o Helper Class

Abbiamo precedenza aveva due classi che erano quasi identico, a parte un paio di piccole differenze. Contenevano alcune funzionalità di accesso al database di base, ma funzionavano su tipi di oggetti diversi (ma correlati). Così abbiamo in precedenza avuto classe che sono stati strutturati o meno così:

class BallMillDBHandler{ 

    public BallMillDBHandler(){ ... } 

    public void InsertTool(BallMill tool) { ... } 
    public BallMill QueryTool(string toolID) { ... } 
    public void UpdateTool(BallMill tool) { ... } 
    public void DeleteTool(string toolID) { ... } 
} 

class DiamondToolDBHandler{ 

    public DiamondToolDBHandler(){ ... } 

    public void InsertTool(DiamondTool tool) { ... } 
    public DiamondTool QueryTool(string toolID) { ... } 
    public void UpdateTool(DiamondTool tool) { ... } 
    public void DeleteTool(string toolID) { ... } 
} 

ho preso la maggior parte dei metodi vicino-duplicato e refactoring fuori in una classe BaseToolDBHandler(), ed ereditato dalle altre due, che fornisce alcuni metodi astratti e proprietà per gestire le differenze nell'accesso ai parametri del database stessi.

Sarebbe logico rendere la classe di supporto BaseToolDBHandler invece contenuta all'interno degli accessor di database e fornire loro un'interfaccia comune le proprietà/metodi precedentemente astratti? O dovrei lasciarlo come caso di ereditarietà?

+5

Sembra il posto perfetto per l'eredità per me, quindi lo lascerei in quel modo. –

+0

Neil Kennendy ha ragione. Ma se il tuo BaseClass offre solo metodi e proprietà astratti e non offre mai alcuna funzionalità implementata, usa invece un'interfaccia (e.G. IDBHandler ). –

risposta

3

Questo sembra uno scenario che potrebbe avvantaggiare sia i generici che l'ereditarietà tramite una classe base/interfaccia.

class DBHandler<TTool> where TTool : ToolBase // or ITool 
{ 
    public DBHandler(){ ... } 

    public void InsertTool(TTool tool) { ... } 
    public TTool QueryTool(string toolID) { ... } 
    public void UpdateTool(TTool tool) { ... } 
    public void DeleteTool(string toolID) { ... } 
} 

Se è necessario, si potrebbe fare una base (opzionalmente astratta) di classe, o di un'interfaccia da utilizzare come un vincolo di tipo che garantisca alcuni membri che avevi bisogno gli strumenti per avere all'interno dei corpi dei metodi.

Esempi:

var handler = new DBHandler<BallMill>(); 
BallMill value = handler.QueryTool("xyz"); 
value.SomeProperty = "New Value"; 
handler.UpdateTool(value); 
+1

Sembra che potrebbe essere un'opzione, ma dipenderebbe da ciò che accade all'interno di quei metodi; possono o non possono essere abbastanza simili per far funzionare questo. – Servy

+0

@Jamiec Effettivamente. Fatto. – Dan

+0

Penso che rendere la base un generico possa aiutare molto. Devo ancora mantenere un insieme diverso di parametri con cui si accede al DB, e penso che mantenerli in classi separate sia meglio che passarli in ogni istanza. Ma questo è utile, penso che lo contrassegnerò come la risposta accettata. – KChaloux

3

L'ereditarietà tende ad essere sovrautilizzata, ma questo sembra un caso appropriato. In molti casi le persone vedono solo il codice duplicato e pensano "userò l'ereditarietà per rimuovere il codice duplicato attraverso una classe base". In realtà, la maggior parte del codice duplicato può essere completamente refactored in un'altra classe che viene utilizzata in più punti. Di solito le classi che lo usano non sono "correlate", stanno facendo due cose completamente diverse e capita semplicemente di condividere la necessità di fare alcune (o alcune serie di) piccole attività.

L'ereditarietà deve essere utilizzata quando le classi quando si può realmente affermare che la classe di classe "è" un'istanza della classe base, non solo una classe che ha bisogno di accedere a una serie di metodi definiti altrove. Nel tuo caso particolare, è chiaro che entrambe le classi sono gestori di DB. Nel loro punto cruciale, entrambi stanno servendo lo stesso obiettivo generale, ma sono due diverse possibili implementazioni di tale obiettivo. L'unico problema che ho potuto vedere qui (dato che non hai mostrato il contenuto di nessuno dei metodi) è che potresti essere in grado di combinare entrambe le classi in una singola classe, ma avremmo bisogno di saperne di più sui dettagli per sapere se è possibile o preferibile.

+0

Mi è piaciuta la tua definizione di quando utilizzare l'ereditarietà - non è solo la banale "è una" vs "ha una" definizione ", ma in realtà si spiega che le classi hanno lo stesso obiettivo generale (e quindi potrebbe essere necessario fare riferimento ad esse in modo polimorfico quindi c'è una buona ragione per usare l'ereditarietà). Oltre a questo non c'è alcun beneficio reale dall'ereditarietà perché il riutilizzo del codice può essere ottenuto per composizione senza l'accoppiamento e i pericoli che potrebbero verificarsi con l'ereditarietà. – BornToCode

0

direi che questo è sicuramente un compito per l'eredità ... ma lascia in chiaro una cosa prima. La composizione è un modello di progettazione che può e deve essere utilizzato laddove l'ereditarietà multipla delle classi non è una caratteristica della lingua data (C# per esempio)

La composizione implica che la classe Composite instanzia le classi, dove di solito sarebbero ereditate. In questo caso, fornisci il contratto all'implementazione tramite interfacce.

Per darvi un esempio di massima di come l'ereditarietà può essere applicato al codice, considerare questo: (questo non utilizza composizione)

public interface IHandler 
{ 
    void InsertTool(ITool tool); 
    void UpdateTool(ITool tool); 
    void DeleteTool(string toolID); 
    //void DeleteTool(ITool tool) - seems more consistent if possible!? 

    ITool QueryTool(string toolID); 
} 

public interface ITool 
{ 

} 

class BallMill : ITool 
{ 
} 

class DiamondTool : ITool 
{ 
} 

class BallMillDBHandler : IHandler 
{ 

    public BallMillDBHandler(){ ... } 

    public void InsertTool(ITool tool) { ... } 
    public BallMill QueryTool(string toolID) { ... } 
    public void UpdateTool(ITool tool) { ... } 
    public void DeleteTool(string toolID) { ... } 
} 

class DiamondToolDBHandler : IHandler 
{ 

    public DiamondToolDBHandler(){ ... } 

    public void InsertTool(ITool tool) { ... } 
    public DiamondTool QueryTool(string toolID) { ... } 
    public void UpdateTool(ITool tool) { ... } 
    public void DeleteTool(string toolID) { ... } 
} 
+0

'QueryTool' non è sull'interfaccia per questo motivo, in quanto IMO impraticabile. L'interfaccia non ha reale uso. – Jamiec

+0

@Jamiec ... tempo per una modifica ... – series0ne

0

Invece di eredità, utilizzare farmaci generici. Si desidera eseguire la stessa operazione su diversi tipi di oggetti, che è esattamente ciò che i generici fanno meglio.