Questa potrebbe essere una domanda OOP generica. Volevo fare un confronto generico tra un'interfaccia e una classe astratta sulla base del loro utilizzo.
Quando si vorrebbe usare un'interfaccia e quando si vorrebbe usare una classe astratta ?
Questa potrebbe essere una domanda OOP generica. Volevo fare un confronto generico tra un'interfaccia e una classe astratta sulla base del loro utilizzo.
Quando si vorrebbe usare un'interfaccia e quando si vorrebbe usare una classe astratta ?
Risposte:
Ho scritto un articolo a riguardo:
riassumendo:
Quando parliamo di classi astratte stiamo definendo le caratteristiche di un tipo di oggetto; specificando cos'è un oggetto .
Quando parliamo di un'interfaccia e definiamo le capacità che promettiamo di fornire, stiamo parlando di stabilire un contratto su ciò che l'oggetto può fare.
Interfaces do not express something like "a Doberman is a type of dog and every dog can walk" but more like "this thing can walk"
. Grazie
Use abstract classes and inheritance if you can make the statement “A is a B”. Use interfaces if you can make the statement “A is capable of [doing] as”
Una classe astratta può avere stato o funzionalità condivisi. Un'interfaccia è solo una promessa per fornire lo stato o la funzionalità. Una buona classe astratta ridurrà la quantità di codice che deve essere riscritto perché la sua funzionalità o stato può essere condivisa. L'interfaccia non ha informazioni definite da condividere
Personalmente, non ho quasi mai bisogno di scrivere lezioni astratte.
Molte volte vedo che le classi astratte vengono (mis) utilizzate, è perché l'autore della classe astratta sta usando il modello "Metodo modello".
Il problema con "Metodo modello" è che è quasi sempre rientrato in qualche modo - la classe "derivata" conosce non solo il metodo "astratto" della sua classe base che sta implementando, ma anche i metodi pubblici della classe base , anche se la maggior parte delle volte non è necessario chiamarli.
Esempio (eccessivamente semplificato):
abstract class QuickSorter
{
public void Sort(object[] items)
{
// implementation code that somewhere along the way calls:
bool less = compare(x,y);
// ... more implementation code
}
abstract bool compare(object lhs, object rhs);
}
Quindi qui, l'autore di questa classe ha scritto un algoritmo generico e intende che le persone lo usino "specializzandolo" fornendo i propri "hook" - in questo caso, un metodo di "confronto".
Quindi l'uso previsto è qualcosa del genere:
class NameSorter : QuickSorter
{
public bool compare(object lhs, object rhs)
{
// etc.
}
}
Il problema è che hai accoppiato indebitamente due concetti:
Nel codice di cui sopra, in teoria, l'autore del metodo "confrontare" può rientrante richiamare nella superclasse "Sort" metodo ... anche se in pratica non potranno mai avere bisogno di fare questo.
Il prezzo da pagare per questo accoppiamento non necessario è che è difficile cambiare la superclasse e, nella maggior parte delle lingue OO, impossibile cambiarla in fase di esecuzione.
Il metodo alternativo è invece utilizzare il modello di progettazione "Strategia":
interface IComparator
{
bool compare(object lhs, object rhs);
}
class QuickSorter
{
private readonly IComparator comparator;
public QuickSorter(IComparator comparator)
{
this.comparator = comparator;
}
public void Sort(object[] items)
{
// usual code but call comparator.Compare();
}
}
class NameComparator : IComparator
{
bool compare(object lhs, object rhs)
{
// same code as before;
}
}
Quindi nota ora: tutto ciò che abbiamo sono interfacce e implementazioni concrete di quelle interfacce. In pratica, non hai davvero bisogno di nient'altro per realizzare un progetto OO di alto livello.
Per "nascondere" il fatto che abbiamo implementato "l'ordinamento dei nomi" utilizzando una classe "QuickSort" e un "NameComparator", potremmo comunque scrivere un metodo di fabbrica da qualche parte:
ISorter CreateNameSorter()
{
return new QuickSorter(new NameComparator());
}
Ogni volta che hai una classe astratta puoi farlo ... anche quando c'è una relazione di rientro naturale tra la base e la classe derivata, di solito paga renderli espliciti.
Un ultimo pensiero: tutto ciò che abbiamo fatto sopra è "comporre" una funzione "NameSorting" utilizzando una funzione "QuickSort" e una funzione "NameComparison" ... in un linguaggio di programmazione funzionale, questo stile di programmazione diventa ancora più naturale, con meno codice.
Se stai guardando Java come linguaggio OOP,
"L' interfaccia non fornisce l'implementazione del metodo " non è più valida con l'avvio di Java 8. Ora Java fornisce l'implementazione nell'interfaccia per i metodi predefiniti.
In termini semplici, vorrei usare
interfaccia: per implementare un contratto da più oggetti non correlati. Fornisce funzionalità " HAS A ".
classe astratta: per implementare lo stesso o diverso comportamento tra più oggetti correlati. Stabilisce una relazione" IS A ".
Il sito Web Oracle offre differenze chiave tra interface
e abstract
classe.
Prendi in considerazione l'uso di classi astratte se:
Prendi in considerazione l'utilizzo di interfacce se:
Serializable
interfaccia.Esempio:
Classe astratta ( relazione IS A )
Reader è una classe astratta.
BufferedReader è unReader
FileReader è aReader
FileReader
e BufferedReader
sono utilizzati per scopi comuni: lettura dei dati e sono correlati attraverso la Reader
classe.
Interfaccia (ha una capacità)
Serializable è un'interfaccia.
Supponi di avere due classi nella tua applicazione, che stanno implementando l' Serializable
interfaccia
Employee implements Serializable
Game implements Serializable
Qui non è possibile stabilire alcuna relazione attraverso l' Serializable
interfaccia tra Employee
e Game
, che sono pensati per scopi diversi. Entrambi sono in grado di serializzare lo stato e la comparazione finisce lì.
Dai un'occhiata a questi post:
Come avrei dovuto spiegare la differenza tra un'interfaccia e una classe astratta?
OK, dopo averlo "sgridato" da solo - eccolo in parole povere (sentiti libero di correggermi se sbaglio) - So che questo argomento è oooooold, ma qualcun altro potrebbe inciampare un giorno ...
Le classi astratte ti consentono di creare un progetto e ti consentono inoltre di COSTRUIRE (implementare) proprietà e metodi che desideri possedere TUTTI i suoi discendenti.
Un'interfaccia invece ti consente solo di dichiarare che vuoi che esistano proprietà e / o metodi con un determinato nome in tutte le classi che lo implementano, ma non specifica come dovresti implementarlo. Inoltre, una classe può implementare MOLTE interfacce, ma può estendere solo UNA classe astratta. Un'interfaccia è più uno strumento architettonico di alto livello (che diventa più chiaro se inizi a cogliere i modelli di progettazione): un abstract ha un piede in entrambi i campi e può eseguire anche parte del lavoro sporco.
Perché usarne uno sopra l'altro? Il primo consente una definizione più concreta dei discendenti, il secondo consente un maggiore polimorfismo . Quest'ultimo punto è importante per l'utente finale / programmatore, che può utilizzare queste informazioni per implementare l'AP I (interfaccia) in una varietà di combinazioni / forme per soddisfare le loro esigenze.
Penso che questo sia stato il momento della "lampadina" per me - pensa alle interfacce meno dalla prospettiva dell'autore e più da quella di qualsiasi programmatore che verrà più avanti nella catena che sta aggiungendo l'implementazione a un progetto o estendendo un'API.
I miei due centesimi:
Un'interfaccia in sostanza definisce un contratto a cui qualsiasi classe di implementazione deve aderire (implementare i membri dell'interfaccia). Non contiene alcun codice.
D'altra parte, una classe astratta può contenere codice e potrebbero esserci alcuni metodi contrassegnati come astratti che una classe ereditaria deve implementare.
Le rare situazioni in cui ho usato le classi astratte sono quando ho alcune funzionalità predefinite che la classe ereditaria potrebbe non essere interessante nel sovrascrivere, per esempio una classe base astratta, da cui ereditano alcune classi specializzate.
Esempio (uno molto rudimentale!): Si consideri una chiamata clienti classe di base che ha i metodi astratti come CalculatePayment()
, CalculateRewardPoints()
e alcuni metodi non astratti come GetName()
, SavePaymentDetails()
.
Classi specializzate come RegularCustomer
e GoldCustomer
erediteranno dalla Customer
classe base e implementeranno la propria logica CalculatePayment()
e CalculateRewardPoints()
metodo, ma riutilizzeranno i metodi GetName()
e SavePaymentDetails()
.
È possibile aggiungere più funzionalità a una classe astratta (ovvero metodi non astratti) senza influire sulle classi figlio che utilizzavano una versione precedente. Considerando che l'aggiunta di metodi a un'interfaccia influirebbe su tutte le classi che la implementano poiché ora dovrebbero implementare i membri dell'interfaccia appena aggiunti.
Una classe astratta con tutti i membri astratti sarebbe simile a un'interfaccia.
Quando fare ciò che è una cosa molto semplice se hai in mente il concetto chiaro.
Le classi astratte possono essere derivate mentre le interfacce possono essere implementate. C'è qualche differenza tra i due. Quando derivate una classe astratta, la relazione tra la classe derivata e la classe base è 'è una relazione'. ad esempio, un cane è un animale, una pecora è un animale, il che significa che una classe derivata eredita alcune proprietà dalla classe base.
Considerando che per l'implementazione di interfacce, la relazione è "può essere". ad esempio, un cane può essere un cane spia. Un cane può essere un cane da circo. Un cane può essere un cane da corsa. Ciò significa che si implementano determinati metodi per acquisire qualcosa.
Spero di essere chiaro.
1.Se stai creando qualcosa che fornisce funzionalità comuni a classi non correlate, usa un'interfaccia.
2.Se stai creando qualcosa per oggetti strettamente correlati in una gerarchia, usa una classe astratta.
Ho scritto un articolo su quando usare una classe astratta e quando usare un'interfaccia. C'è molta più differenza tra loro oltre a "un IS-A ... e un CAN-DO ...". Per me, quelle sono risposte in scatola. Cito alcuni motivi per usarli. Spero che sia d'aiuto.
Penso che il modo più conciso di dirlo sia il seguente:
Proprietà condivise => classe astratta.
Funzionalità condivisa => interfaccia.
E per dirla in modo meno succinto ...
Esempio di classe astratta:
public abstract class BaseAnimal
{
public int NumberOfLegs { get; set; }
protected BaseAnimal(int numberOfLegs)
{
NumberOfLegs = numberOfLegs;
}
}
public class Dog : BaseAnimal
{
public Dog() : base(4) { }
}
public class Human : BaseAnimal
{
public Human() : base(2) { }
}
Poiché gli animali hanno una proprietà condivisa - il numero di zampe in questo caso - ha senso creare una classe astratta contenente questa proprietà condivisa. Questo ci consente anche di scrivere codice comune che opera su quella proprietà. Per esempio:
public static int CountAllLegs(List<BaseAnimal> animals)
{
int legCount = 0;
foreach (BaseAnimal animal in animals)
{
legCount += animal.NumberOfLegs;
}
return legCount;
}
Esempio di interfaccia:
public interface IMakeSound
{
void MakeSound();
}
public class Car : IMakeSound
{
public void MakeSound() => Console.WriteLine("Vroom!");
}
public class Vuvuzela : IMakeSound
{
public void MakeSound() => Console.WriteLine("VZZZZZZZZZZZZZ!");
}
Nota qui che Vuvuzelas e Cars sono cose completamente diverse, ma hanno funzionalità condivise: produrre un suono. Pertanto, un'interfaccia ha senso qui. Inoltre, consentirà ai programmatori di raggruppare cose che emettono suoni in un'unica interfaccia, IMakeSound
in questo caso. Con questo disegno, potresti scrivere il seguente codice:
List<IMakeSound> soundMakers = new List<ImakeSound>();
soundMakers.Add(new Car());
soundMakers.Add(new Vuvuzela());
soundMakers.Add(new Car());
soundMakers.Add(new Vuvuzela());
soundMakers.Add(new Vuvuzela());
foreach (IMakeSound soundMaker in soundMakers)
{
soundMaker.MakeSound();
}
Puoi dire cosa sarebbe uscita?
Infine, puoi combinare i due.
Esempio combinato:
public interface IMakeSound
{
void MakeSound();
}
public abstract class BaseAnimal : IMakeSound
{
public int NumberOfLegs { get; set; }
protected BaseAnimal(int numberOfLegs)
{
NumberOfLegs = numberOfLegs;
}
public abstract void MakeSound();
}
public class Cat : BaseAnimal
{
public Cat() : base(4) { }
public override void MakeSound() => Console.WriteLine("Meow!");
}
public class Human : BaseAnimal
{
public Human() : base(2) { }
public override void MakeSound() => Console.WriteLine("Hello, world!");
}
Qui, richiediamo che tutti BaseAnimal
emettano un suono, ma non ne conosciamo ancora l'implementazione. In tal caso, possiamo astrarre l'implementazione dell'interfaccia e delegarne l'implementazione alle sue sottoclassi.
Un ultimo punto, ricordi come nell'esempio di classe astratto siamo stati in grado di operare sulle proprietà condivise di oggetti diversi e nell'esempio di interfaccia siamo stati in grado di invocare la funzionalità condivisa di oggetti diversi? In questo ultimo esempio, potremmo fare entrambe le cose.
Quando preferire una classe astratta rispetto all'interfaccia?
Quando preferire un'interfaccia rispetto alla classe astratta?
Le classi possono ereditare da una sola classe base, quindi se si desidera utilizzare classi astratte per fornire polimorfismo a un gruppo di classi, devono ereditare tutte da quella classe. Le classi astratte possono anche fornire membri che sono già stati implementati. Pertanto, è possibile garantire una certa quantità di funzionalità identiche con una classe astratta, ma non con un'interfaccia.
Ecco alcuni consigli per aiutarti a decidere se utilizzare un'interfaccia o una classe astratta per fornire polimorfismo per i tuoi componenti.
Copiato da:
http://msdn.microsoft.com/en-us/library/scsyfw1d%28v=vs.71%29.aspx
Prendi in considerazione l'uso di classi astratte se una di queste affermazioni si applica alla tua situazione:
Prendi in considerazione l'utilizzo di interfacce se una di queste affermazioni si applica alla tua situazione:
Le risposte variano tra le lingue. Ad esempio, in Java una classe può implementare (ereditare da) più interfacce ma ereditare solo da una classe astratta. Quindi le interfacce ti offrono maggiore flessibilità. Ma questo non è vero in C ++.
Per me, andrei con le interfacce in molti casi. Ma preferisco lezioni astratte in alcuni casi.
Le classi in OO si riferiscono generalmente all'implementazione. Uso le classi astratte quando voglio forzare alcuni dettagli di implementazione ai bambini, altrimenti vado con le interfacce.
Naturalmente, le classi astratte sono utili non solo per forzare l'implementazione, ma anche per condividere alcuni dettagli specifici tra molte classi correlate.
Utilizzare una classe astratta se si desidera fornire alcune implementazioni di base.
in java puoi ereditare da una classe (astratta) per "fornire" funzionalità e puoi implementare molte interfacce per "garantire" la funzionalità
Puramente sulla base dell'eredità, useresti un Estratto in cui stai definendo relazioni chiaramente discendenti e astratte (cioè animale-> gatto) e / o richiedi l'eredità di proprietà virtuali o non pubbliche, in particolare lo stato condiviso (che le interfacce non possono supportare ).
Dovresti cercare di favorire la composizione (tramite iniezione di dipendenza) rispetto all'ereditarietà dove puoi, e notare che le interfacce essendo contratti supportano unit test, separazione delle preoccupazioni e (variazione della lingua) l'ereditarietà multipla in un modo che gli Abstracts non possono.
Una posizione interessante in cui le interfacce funzionano meglio delle classi astratte è quando è necessario aggiungere funzionalità extra a un gruppo di oggetti (correlati o non correlati). Se non puoi dare loro una classe astratta di base (ad esempio, sono sealed
o hanno già un genitore), puoi invece dare loro un'interfaccia fittizia (vuota) e quindi semplicemente scrivere metodi di estensione per quell'interfaccia.
Questa può essere una chiamata molto difficile da fare ...
Un puntatore che posso dare: un oggetto può implementare molte interfacce, mentre un oggetto può ereditare solo una classe di base (in un linguaggio OO moderno come c #, so che C ++ ha eredità multipla - ma non è disapprovato?)
Una classe astratta può avere implementazioni.
Un'interfaccia non ha implementazioni, definisce semplicemente una specie di contratto.
Possono esserci anche alcune differenze dipendenti dalla lingua: ad esempio C # non ha ereditarietà multipla, ma in una classe possono essere implementate più interfacce.
La regola di base del pollice è: per "Nomi" usa la classe astratta e per "Verbi" usa l'interfaccia
Ad esempio: car
è una classe astratta e drive
possiamo renderla un'interfaccia.
drive
in macchina - questa è una classe astratta.