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

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.

  1. een class kan meerdere interfaces implementeren.
  2. een interface bevat geen data declaraties, maar wel properties.
  3. alle methode declaraties in een interface zijn public.
  4. een interface bevat geen implementatie-code.
  5. een class die de interface implementeert moet voor iedere member van de interface een implementatie bevatten.

Abstracte classes

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.

Redenen voor gebruik van een abstracte class

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.

Aanbevelingen voor het gebruik van abstracte classes en interfaces

  1. Als je verwacht dat er meerdere versies van je component gemaakt zullen worden, creëer dan een abstracte class. Abstracte class bieden een eenvoudiger versiebeheer van componenten. Door het updaten van de baseclass worden alle afgeleide classes automatisch ook bijgewerkt. Interfaces daarentegen kunnen niet aangepast worden nadat ze eenmaal zijn gemaakt. Als je een nieuwe versie van de interface wilt maken, moet je een geheel nieuwe interface maken.
  2. Als er functionaliteit van toepassing is op een hele reeks van diverse objecten, gebruik dan een interface. Abstracte classes worden vooral gebruikt ten behoeve van objecten die een nauwe relatie met elkaar hebben. Met interfaces ontwerp je daarentegen gemeenschappelijke functionaliteit in een set van ongerelateerde classes.
  3. Als je kleine, afgebakende functionaliteit ontwerpt, gebruik dan interfaces. Ontwerp je eerder grotere functionele eenheden, gebruik dan liever een abstracte class.
  4. Als je wilt zorgen voor implementatie van gemeenschappelijke functionaliteit in al je implementaties van je component, kies dan voor een abstracte class. Met abstracte classes kan je een class gedeeltelijk al implementeren, terwijl een interface voor geen enkele member een implementatie bevat.

 

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

 

Conclusie

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! ;-))

· All about abstract classes