Non penso che sia un o / o. Penso che dovresti utilizzare entrambe Interfacce e Classi astratte.
TL; DR
Classi astratte
Pro:
- Relativamente semplice
- Puoi fornire funzionalità di base e "hook" che le sottoclassi possono utilizzare.
Contro:
- Sei "bloccato" con la gerarchia dell'ereditarietà
- Il codice client conosce la classe base
Interfacce
Pro:
- Flessibilità quasi illimitata
Contro:
- Implementazione zero fornita
- Potrebbe essere più difficile capire i programmatori meno esperti.
Scenario
Immagina di creare una classe astratta oggi. Lo chiameremo BaseReport
. BaseReport fornisce un metodo di "salvataggio" vuoto e tutti i tuoi attuali tipi di rapporti si estendono da BaseReport e hanno la loro implementazione di salvataggio.
Per ragioni, vuoi anche visualizzare il testo del rapporto prima di salvarlo, quindi il tuo BaseReport estende un semplice componente View fornito dalla tua lingua di scelta che ti permetterà di mettere alcuni oggetti di testo e un salvataggio pulsante sullo schermo e non molto altro. È davvero leggero, quindi è una buona scelta basata su tutti i report che hai di fronte. Lo chiamerò SimpleContainer
.
Ora hai nove mesi di tempo e devi avere un rapporto con più pagine e qualcuno ha chiesto che uno dei report sia visualizzabile in modalità verticale o orizzontale. Nessuno di questi requisiti può essere soddisfatto con una sottoclasse di SimpleContainer.
Tuttavia, hai un sacco di codice client che conosce BaseReport, e ci potrebbe anche essere un posto o due in cui hai eseguito il cast su SimpleContainer (dov'è il danno?). Non solo, il codice è più simile al codice framework personalizzato e hai generato quattro progetti che lo utilizzano. Quindi tutti di quei progetti dovranno passare di nuovo al QA se si fa il refactoring su IReport in modo da poter estendere PagedContainer
per il report e RotatableContainer
per l'altro. Più che probabile, a questo punto la tua risposta è "scusa, non possiamo farlo." Non perché non puoi davvero farlo, ma perché è troppo rischioso per tutte le altre cose che ti siedono sopra.
Ma negli ultimi 9 mesi, ti sei evoluto come gangbusters perché avevi quel codice base in BaseReport che ti permetteva di strappare un nuovo tipo di rapporto in tempi record, quindi non vuoi buttare il bambino fuori con l'acqua sporca. Invece, rendi il tuo BaseReport implementare IReport e assicurati che il codice client solo si riferisca ad esso come IReport. Quando arriva il requisito per un PagedContainer, crea un BasePagedContainer che implementa IReport e poi crea i tuoi singoli rapporti in cima.
Non c'è davvero uno svantaggio di questo approccio, perché la quantità di lavoro necessaria per creare l'interfaccia e implementarla nelle classi astratte è minuscola rispetto alla quantità di flessibilità e a prova di futuro che ottieni.
E potrei aver cambiato qualche dettaglio per proteggere l'innocente, ma lo scenario è reale, e mi è successo. :)