DCI: een andere blik

Een tijdje geleden heeft André Boonzaaijer een blogpost over DCI geschreven. Zijn conclusie was: “Of DCI (…) de ideale manier is weet ik niet”. Ik was ook niet overtuigd toen ik het artikel van Trygve Reenskaug en James O. Coplien las waar André naar verwees. Trygve Reenskaug, de bedenker van DCI, heeft echter meer hierover geschreven en naar mijn mening zijn zijn ideeën duidelijker gemaakt in het lange artikel “The Common Sense of Object Oriented Programming”. Overtuigd ben ik (nog) niet, maar ik denk wel dat het de moeite waard is om uit te proberen.

 

Het probleem

Is er iets mis met OO? Volgens Trygve Reenskaug wel! Een OO oplossing bestaat uit een aantal klassen en/of interfaces en bijbehorende methodes. Die geven aan hoe individuele instanties (objecten) zich gedragen. Met andere woorden: de code maakt expliciet het gedrag van individuele instanties. Toch wordt relevante functionaliteit meestal bereikt door de collaboratie van meerdere objecten die eventueel instanties van verschillende klassen zijn. Om te begrijpen hoe bepaalde functionaliteit bereikt wordt moeten we eerst weten waar het “entry point” zich bevindt. Hierna gaan we van methode naar methode en van klasse naar klasse afhankelijk van de inhoud van elke bezochte methode. Als er geen methode meer aangeroepen wordt zijn we klaar en weten we hoe de functionaliteit geïmplementeerd is. Dit proces moet herhaald worden voor elke functionaliteit. In het kort: om te begrijpen wat de code doet moeten we eigenlijk de code simuleren. Is het niet mogelijk om OO code te schrijven die ook expliciet aangeeft wat hij doet?

 

De DCI oplossing

DCI is een “nieuw” programmeerparadigma dat veronderstelt dat de code drie verschillende perspectieven op een systeem moet aanbieden.

Het dataperspectief bestaat uit alle domeinklassen en bijbehorende relaties. In tegenstelling tot het OO-paradigma mogen deze klassen geen businesslogica implementeren (in de woorden van Martin Fowler wordt dit een “anemic model” genoemd).

Het contextperspectief bestaat (voorlopig) uit één klasse per systeemoperatie. Elke klasse (contextklasse) heeft een methode die het “entry point” voor de betreffende systeemoperatie is. Net zoals bij een conventionele OO-oplossing wordt de systeemoperatie door middel van collaborerende objecten mogelijk gemaakt. De structuur van deze collaboratie wordt met rollen en connecties tussen rollen in de contextklasse vastgelegd (zie figuur 1).

Een rol is eigenlijk een soort van contextafhankelijk domeinklasse. Denk aan een uitleensysteem voor bibliotheken. Bij het uitlenen van een boek hebben we te maken met drie rollen: de lener, de uitlener en het geleende boek. Deze rollen worden vervuld door een persoon, de bibliotheek en één exemplaar van een boek. Het maakt niet uit wie precies het boek leent, welke bibliotheek het is, of welk boek er geleend wordt. De collaboratie kan gedefinieerd worden aan de hand van de aangegeven rollen. Aan de andere kant moet het mogelijk zijn om rolobjecten aan te maken vanaf bestaande domeinobjecten (bijv. een lener object moet aangemaakt kunnen worden vanaf een bestaand persoon object).

Figuur 1. Collaboratienetwerk voor het uitlenen van boeken.

De connecties tussen rollen geven aan welke rollen rechtstreeks te bereiken zijn vanaf elke rol. Zo wordt het duidelijk hoe het collaboratienetwerk eruit ziet.

Naast het definiëren van het “entry point” en de structuur van het collaboratienetwerk is de contextklasse ook verantwoordelijk voor het aanmaken van rolinstanties.

Het interactieperspectief is niet meer dan een view op de methodes van de rollen die in het contextperspectief gedefinieerd zijn. Deze methodes leggen de manier waarop rollen met elkaar collaboreren en de desbetreffende businesslogica vast.

 

Een voorbeeld

Om het DCI paradigma te illustreren maak ik hier gebruik van het voorbeeld dat Serge Beaumont gebruikt heeft tijdens zijn presentatie “DCI: een nieuw programmeerparadigma gebaseerd op rollen” op J-Spring 2009. Het gaat om een uitleensysteem voor een groep bibliotheken. Dit systeem moet het mogelijk maken om boeken te lenen en te retourneren. Laten we eerst een conventionele OO oplossing hiervoor bekijken (kijk naar figuur 2).

Figuur 2. OO oplossing van het uitleensysteem voor bibliotheken.

Wat valt in deze oplossing op? In principe niets. Een persoon houdt bij welke boeken hij bij welke bibliotheken geleend heeft. Een bibliotheek weet welke boeken uitgeleend zijn en boek exemplaren weten door wie ze geleend zijn. In dit geval is het niet moeilijk om te begrijpen hoe het uitlenen en retourneren van een boek in het model opgelost wordt. Toch is dit niet expliciet aangegeven in de code.

Hoe zou een oplossing volgens het DCI paradigma eruitzien? In het dataperspectief hebben we precies dezelfde klassen als in de OO oplossing, alleen de methodes die het uitlenen/retourneren van boeken mogelijk maken worden daaruit gehaald. In het contextperspectief definiëren we een klasse die het uitlenen en retourneren van boeken mogelijk maakt. Deze klasse heeft twee “entry point” methodes, één voor het uitlenen en één voor het retourneren van boeken. Deze klasse definieert ook de structuur van het collaboratienetwerk (zie figuur 3). De geïdentificeerde rollen zijn lener, uitlener en uitgeleend boek. Deze bevatten de methodes die het uitlenen/retourneren van boeken mogelijk maken (zie figuur 4).

Figuur 3. Het collaboratienetwerk voor het uitlenen/retourneren van boeken.

Figuur 4. De rollen voor het uitlenen/retourneren van boeken.

In het interactieperspectief wordt de code van de methodes in de rollen vastgelegd.

Zoals te merken valt verschilt de DCI oplossing bijna niet van de OO oplossing. Toch word het naar mijn idee duidelijker waar de “entry points” voor de systeemoperaties zijn en welke methodes bij de benodige collaboratie betrokken zijn. Daarnaast hoeven we ook niet van methode naar methode te springen om erachter te komen van welke objecten bijbetrokken zijn.

De meerwaarde van DCI zou nog opmerkelijker zijn als er extra functionaliteit toegevoegd wordt. In de conventionele OO oplossing resulteert dit (waarschijnlijk) in extra methodes in de bestaande klassen. De klassen worden groter en het wordt dus moeilijker om te weten welke methodes bij welke functionaliteit horen. In de DCI oplossing worden extra contextklassen en de bijbehorende rollen toegevoegd. De scheiding tussen de verschillende functionaliteiten wordt expliciet aangegeven in de code. Daarnaast is ook het collaboratienetwerk expliciet aangegeven.

 

Conclusie

DCI staat nog in de kinderschoenen. De voorbeelden die in de verschillende artikelen verschijnen zijn vrij eenvoudig. De algemene aanname is dat een instantie van een contextklasse aangemaakt wordt als de respectievelijke systeemoperatie uitgevoerd moet worden en hierna weer worden weggegooid. Dit is niet het geval in het voorbeeld dat hier gepresenteerd was. Zoals je waarschijnlijk gemerkt hebt moet de toestand van de rollen bewaard worden om te zorgen dat boeken die geleend zijn geretourneerd kunnen worden. De situatie wordt nog ingewikkelder/uitdagender als een probleem als het Mancala spel aangepakt wordt. Hebben we contexten binnen contexten nodig? Toch ben ik van mening dat DCI de moeite waard is om verder te onderzoeken. Even een paar ingewikkelde domeinen met DCI aanpakken. Wie durft? 🙂