Crea molte classi simili o solo una

4

L'obiettivo è creare un'applicazione con oggetti che possano rappresentare alcune operazioni (aggiunta, sottrazione, ecc.)

Tutti questi oggetti avranno funzioni e membri comuni, e quindi implementeranno un'interfaccia o erediteranno da una classe astratta (quale sarebbe una migliore pratica, questo sarà in C # se questo è importante?).

Per quanto posso vedere, ci sono due modi diversi di organizzare tutte queste classi.

  1. Potrei creare una classe di addizione, una classe di sottrazione, ecc. Questo ha il vantaggio di essere altamente modulare, ma la differenza tra le classi è così minima.
  2. Potrei creare una classe e avere un membro che dirà quale tipo di operazione viene rappresentata. Ciò significa un sacco di dichiarazioni switch e la perdita di alcune modularità, oltre a essere più difficili da mantenere.

Qual è la migliore pratica? C'è un modo migliore per farlo che non è elencato sopra? Se è importante, l'elenco delle funzioni che devono essere supportate è lungo.

    
posta soandos 31.05.2012 - 06:50
fonte

3 risposte

8

In un mondo orientato agli oggetti dominato da linguaggi come Java e C #, l'opzione 1 è la strada da percorrere. Esistono diversi modi per modellare i dati, ad esempio un altro modo è con un albero che può essere percorso. Ma tenendo fede allo spirito della tua domanda, ci sono due approcci da considerare. In entrambi i casi è importante utilizzare le interfacce. La scrittura di programmi in cima alle astrazioni è lontana più flessibile rispetto alla loro scrittura in cima alle implementazioni concrete.

Il primo approccio consiste nell'estrarre codice comune in super classi astratte. Questo è davvero facile da fare con linguaggi come Java e C # e, a prima vista, sembra la scelta più ovvia. Il problema principale di questo approccio è che è troppo facile confondere l'ereditarietà con le astrazioni che si sta tentando di creare. Io sostengo che queste classi astratte create interamente per condividere elementi comuni, ma altrimenti non importanti per l'ereditarietà, il codice fa parte dell'implementazione e quindi non dovrebbero essere invocate nel codice client. Questo può o non può essere un problema, ma in entrambi i casi non c'è nulla che possa essere fatto per evitare che ciò accada.

Per rimediare a questo, il secondo approccio sarebbe quello di favorire la composizione rispetto alle classi astratte. Sebbene questo non sia facile da implementare come il primo, risolve il problema lasciato dalle classi astratte. In particolare, non è possibile fare affidamento sugli artefatti lasciati dai dettagli di implementazione delle classi astratte, a patto che vengano incapsulati correttamente gli oggetti composti. Se, tuttavia, le classi astratte (se hanno anche bisogno di essere astratte) sono importanti per la gerarchia dell'ereditarietà e sono quindi più che semplici "bucket di codice comuni", quindi favorire la composizione ridurrà l'utilità delle classi.

Alla fine penso che dipenda se le classi astratte sono importanti per costruire un'astrazione appropriata basata sull'eredità, o se il codice comune è lì solo per alleggerire l'onere di mantenerlo. Potrebbe essere fastidioso implementare se ci sono molti metodi che delegano solo a un oggetto incapsulato, ma il trade-off potrebbe valerne la pena per qualcosa di più puro. Tuttavia, alcuni linguaggi come Ruby facilitano il supporto della composizione con i suoi moduli, che sono fondamentalmente mixins .

    
risposta data 31.05.2012 - 07:33
fonte
11

Dipende da quanto queste classi hanno in comune:

  • Niente affatto: crea un gruppo di classi indipendenti.
  • Hanno gli stessi metodi, ma li implementano in modo diverso: crea un'interfaccia e un gruppo di classi che la implementano.
  • Condividono alcune funzionalità di base, ma altre funzionalità differiscono - crea una classe base e ricava le tue lezioni da quella.

A seconda della complessità delle tue classi, potresti voler avere più di un livello nella tua gerarchia di classi se usi l'ultima opzione.

C'è anche una quarta opzione, che è particolarmente valida se tutta la tua classe ha bisogno di una sola operazione: prendere un approccio di programmazione funzionale e abbandonare del tutto le classi. Invece, implementa una funzione per ogni operazione (in C #, questo dovrà essere un metodo statico da qualche parte, o dovrai restituire lambda da un metodo factory, in quanto la lingua manca di funzioni libere) e passare quelli intorno. Molto probabilmente, vuoi dichiarare un tipo di delegato per questo.

    
risposta data 31.05.2012 - 07:59
fonte
2

0,3. Una classe base, implementazioni sovrascriventi dove necessario nelle classi figlie.

Perché? Motivo principale: eventuali modifiche richieste si verificano solo in una singola posizione e si propagano a tutti i dipendenti.

    
risposta data 31.05.2012 - 07:10
fonte

Leggi altre domande sui tag