Een van de belangrijkste beslissingen die een technisch architect moet nemen is de keuze voor een interface of een abstracte class om zogenaamd polymorph gedrag in een objectmodel te krijgen. De keuze tussen het maken van een interface of een abstracte class ligt echter niet altijd voor de hand. In dit artikel gaan we naar de verschillen tussen deze twee mogelijkheden kijken en zien we wanneer we de één dan wel de ander het beste kunnen gebruiken.
Interfaces definiëren een set van properties, methoden en events, net als classes. Maar in tegenstelling tot een class bevat een interface geen implementatie. Een interface kan je zien als een contract. Een class die een interface implementeert, moet ieder aspect van deze class implementeren, precies zoals in de interface is vastgelegd. Een interface wordt gedeclareerd via het interface keyword. Het volgende stukje code laat een voorbeeld zien:
interface ILog
{
void Write();
string Target {
set;
get;
}
}
Om gebruik te kunnen maken van deze interface, moeten we een class declareren die de interface implementeert. In C# is de syntax gelijk aan een class-definitie die erft van een andere class. In VB.NET gebruiken we het keyword implements.
using System.IO;
public class TextLog : ILog
{
public void Write(string msg)
{
FileInfo fi = new FileInfo(_target);
StreamWriter sw =fi.AppendText();
sw.WriteLine(DateTime.Now + " " + msg);
sw.Close();
}
private string _target;
public string Target
{
get
{ return _target; }
set
{ _target = value; }
}
}
De aanroepende code ziet er zo uit:
public class ClientCode
{
public static void Main()
{
ILog log;
log = new TextLog();
log.Target = "d:\\temp\\test.log";
log.Write("tekstlogger via interface");
}
}
Wanneer je de code uitvoert, wordt de tekst “tekstlogger via interface” in het bestand d:\temp\test.log geplaatst, met vooraan de datum en tijd.
Het is belangrijk om te weten dat een interface iets anders is als een base class. Een interface wordt geïmplementeerd, niet uitgebreid.
Een abstracte class kan niet geïnstantieerd worden. Alleen classes die ervan erven kunnen dit. Een abstracte class kan een volledige implementatie bevatten, maar meestal krijgt het een gedeeltelijke implementatie, of in het geheel geen implementatie. Gemeenschappelijke functionaliteit wordt zo ge-encapsuleerd voor de ervende classes.
Wanneer je een class hebt met methoden zonder implementatie (zogenaamde abstracte methoden), dan wordt ook de class abstract genoemd. Een abstracte method is dan puur een aanduiding voor een methode die in een ervende classes geïmplementeerd wordt. Abstracte methoden hebben tot doel om iedere class die erft van de abstracte class een implementatie af te dwingen. Als de ervende class dat niet doet, wordt ook deze class weer abstract genoemd.
Je declareert een abstracte class met het abstract keyword.
abstract class LogBase
{
abstract public void Write(string msg);
protected string _target;
public string Target
{
get
{ return _target; }
set
{ _target = value; }
}
}
Merk op dat de _target variabele nu “protected” is en niet meer “private”. Het “protected” keyword geeft aan dat de member van de class toegankelijk is binnen deze class, maar ook in alle classes die ervan erven. De member _target is nog steeds niet zichtbaar voor instanties van de class. Zowel de abstracte methode als haar implementatie kunnen overigens eveneens niet private zijn.
Je kunt van de abstracte class net zo erven als van een gewone class.
public class ClientCode
{
public static void Main()
{
SimpleLog log2;
log2 = new SimpleLog();
log2.Target = "d:\\temp\\test2.log";
log2.Write("tekstlogger via abstracte class");
}
}
Wanneer je de code uitvoert, wordt de tekst “tekstlogger via abstracte class” in het bestand d:\temp\test2.log geplaatst.
Nu kennen we het verschil tussen een interface en een abstracte class, maar waarom zou je een abstracte class willen gebruiken? We kunnen een abstracte class heel goed gebruiken in een class-hiërarchie ontwerp. Je creëert nl. niet zo maar een Log object, maar een specifieke implementatie ervan. Een ander voordeel is dat je de code kunt plaatsen op die plek waar het thuis hoort. Problemen in de uitvoering zijn zo makkelijker te traceren. Je hoeft bijvoorbeeld ook niet iedere keer property-implementaties te maken voor die properties die in iedere class terugkomen.
Een class die erft van een abstracte class kan evenwel nog steeds interfaces implementeren.
Abstracte classes zijn handig bij het creëren van componenten omdat je een meer of minder uitgebreide functionaliteit in sommige methodes kunt stoppen, maar de implementatie van andere methodes achterwege laten tot een specifieke implementatie van die class nodig blijkt te zijn. Als er extra functionaliteit in ervende classes nodig is, kan je dit toevoegen aan de base-class zonder de code te doen breken.
In het getoonde voorbeeld is wordt een class getoond met een niet geïmplementeerde methode: Write(string msg). Een class die erft van deze class moet deze methode implementeren.
Wanneer je een implementatie maakt van een abstracte class, moet je iedere abstracte methode in deze class implementeren, en ieder geïmplementeerde methode moet dezelfde, in aantal en type, parameters kennen. Ook moeten ze hetzelfde type teruggeven als gedeclareerd staat in de abstracte class.
Abstracte classes versus interfaces
| Interface | Abstracte class |
| Kan alleen abstracte methoden bevatten (impliciet); keyword abstract mag niet | Kan abstracte en niet abstracte methoden bevatten |
| Niet bedoeld voor fields | Fields zijn mogelijk |
| Wordt geïnstantieerd via haar implementatie | Wordt geïnstantieerd via een subclass |
| Kan een class niet uitbreiden, kan wel een enkele of meerder interfaces uitbreiden | Kan een enkele class uitbreiden en één of meerder interfaces implementeren |
| Zijn geen onderdeel van de class-hiërarchie. Dezelfde interface (bijv ILog) kan gebruikt worden in classes die niets met elkaar te maken hebben (bijv. een database-class en een windows-forms class) | Abstracte classes zijn wel onderdeel van de class-hiërarchie. |
| Kennen geen constructor | Kunnen een constructor bevatten |
| Bieden een vorm van “multiple inheritance” omdat je meerdere interfaces kunt implementeren | Een class kan slechts erven van één andere class. |
| Alleen publieke methoden en constanten zijn mogelijk, zonder implementatie | Statische methoden, protected members en gedeeltelijke implementatie zijn allemaal mogelijk |
Zowel de interface als de abstracte class bieden polymorphisme door het declareren van methoden zonder implementatie. Classes die voortbouwen op een abstracte class of interface zijn verplicht de implementatie van abstracte methoden (of, in het geval van een interface, alle methoden) te bieden. De verschillen tussen de twee maken ze evenwel allebei waardevol in verschillende omstandigheden.
· Interfaces & Abstract classes
· Abstract classes vs. interfaces (pas op: Java! ;-))
2008 markeerde de start van de financiële crisis waarbij financiële instellingen en verzekeringsmaatschappijen op de…
Het is algemeen bekend dat een blockchain een vorm van gedistribueerd grootboek is met transacties.…
Mijn loopbaan als software ontwikkelaar begon ik in een klein team. Elke collega werkte normaalgesproken…
Visual Studio Code is een broncode-editor ontwikkeld door Microsoft voor Windows, Linux en macOS. (meer…)
Veel moderne web toepassingen maken intensief gebruik van JavaScript en JavaScript frameworks. Voor ASP.NET ontwikkelaars…
Met de nieuwe compiler die Microsoft ontwikkelt voor C# en Visual Basic wordt het eenvoudiger…