Your web browser is out of date. Update your browser for more security, speed and the best experience on this site.

Hoe TypeScript Decorators je leven als developer aangenamer kunnen maken

Typescript voorziet een ingebouwde implementatie van Decorators, als je ze al herkent vanuit het dagdagelijkse ben je misschien nog benieuwd of er meer te ontdekken valt. Een decorator is een functie die je met het teken @ boven een class, method, property, parameter of accessor plaatst om die declaratie van extra gedrag of metadata te voorzien. Decorators krijgen context over het doelwit, zodat ze zaken kunnen lezen, registreren of aanpassen, zonder de kernlogica te vervuilen. Zo schrijf je declaratieve, leesbare, compacte annotaties die voorkomen dat je ellenlange boilerplate moet doorploegen voor je aan je logica kan beginnen.

Belangrijker dan weten hoe ze werken is weten wanneer het loont om ze zelf te gaan ontwikkelen en implementeren, vandaar dit artikel.

Door Dries Sercu, Front End Consultant Axxes.

Deel dit artikel

Case

Probleem

Stel volgend scenario: je ontwikkelt een applicatie die een trage en complexe berekening uitvoert, goed nieuws, je ziet de mogelijkheid om die te versnellen door aan memoization te doen. Memoization is een techniek waar we resultaten van dure operaties gaan cachen volgens input parameters. Je wint aan snelheid voor deterministische functions die herhaaldelijk worden aangeroepen.

Carbon 0

Om de waarde van je werk aan te geven ga je twee dingen doen, je wil meten hoe lang elke berekening duurt met logging & je wil memoization toevoegen om tijd te winnen.

Code die het probleem simuleert

Carbon

heavyCalculation is de methode waar we onze memoization & logging willen aan toevoegen. Om ons voorbeeld helder te houden gebruik ik de sleep method die na 3 seconden een promise resolved, hierdoor kunnen we ons concentreren op het toevoegen van onze logging & memoization.

Probleem oplossen zonder decorators

Carbon 1

Hier werd logging & memoization toegevoegd in onze methode, alles is nu verweven en eigenlijk moet je nu de volledige methode gaan lezen om te kunnen zien wat er gebeurt.Stel dat we deze code willen hergebruiken voor een andere trage method, dan mogen we dit alles opnieuw gaan uitpluizen.

Decorators als oplossing

Carbon 2

Door onze code te gaan abstraheren in decorators krijgen we herbruikbare en leesbare code.

Waarom?

Voor developers (en voor je toekomstige zelf) schrijven we graag leesbare/herbruikbare code zodat we ons ten volle kunnen concentreren op de features waar onze gebruikers van genieten. Door de komst van classes in TypeScript ontstond de vraag om classes & class members te annoteren en uit te breiden, dit is mogelijk via Decorators.

Carbon 3

In een oogopslag zie je dat iemand ooit de nood zag om de complexSlowProcess method te voorzien van een decorator. Zonder de decorator moet een developer de volledige logica uitwerken in een aparte functie die binnen de complexSlowProcess method wordt aangeroepen. Op die manier wordt de method “vervuild” met logica die eigenlijk niet het doel is van de method.

Waar toe te passen?

  • class definition
  • properties
  • methods
  • accessors
  • parameters

Zodra je werkt met typescript classes kan je beginnen.

Carbon 4

Technisch

Decorator Factory

Een decorator factory is een functie die een expression teruggeeft die door de decorator zal aangeroepen worden. Hierdoor is het mogelijk om configuratie mee te geven aan je decorator.

Carbon 5

Composition

Het scenario waar je meerdere decorators wil combineren doet zich al snel voor, zoals eerder gezegd is het hier wel even opletten, hiervan zie je al een voorbeeld onder “Decorators als oplossing” waar logDuration & memoize gecombineerd worden. Stel deze twee decorator factories:

Carbon 7

Die in deze volgorde worden toegepast:

Carbon 8

Geven dit in de console:

Carbon 9

De expression voor elke decorator wordt geëvalueerd van boven naar onder, waarna de resultaten van elke decorator van onder naar boven worden aangeroepen en als allerlaatste de methode zelf.

Het is verleidelijk om bij composition je decorators zo te gaan schrijven dat ze verweven worden en niet meer zonder elkaar kunnen. Let hiervoor op! Een decorator zou altijd op zich moeten kunnen functioneren, op die manier behoud je propere code met separation of concerns.


Decorator design pattern

Een decorator is een function die zich wrapt rond onze eigen code en die op een manier verrijkt. Het wordt mogelijk abstracties weg te werken uit onze business logica. Decorators zijn combineerbaar en ontzorgen hun doel van complexiteit.

Zoals elk design pattern zijn er voor- en nadelen.

Voordelen
  • Modulair uitbreiden van functionaliteit zonder subclass.
  • Combineerbaar gedrag met meerdere decorators.
  • De code in onze class kan zich concentreren op zijn eigen doel zonder uit te weiden. Onze class houdt zich dus aan het single responsibility principe.-
  • Doordat decorators telkens voor de code staan waar ze op toepassen zie je in een oogopslag welke extra functionaliteit toegepast wordt. Leesbaarheid en onderhoudbaarheid zijn essentieel voor goede code.
Nadelen
  • De volgorde van decorators kan belangrijk zijn. In het geval dat je gaat combineren wordt het dus opletten.
  • Het later wegnemen van een decorator kan door de volgorde best een lastige puzzel worden. Het is rap gebeurd, je combineert een aantal decorators op een method, later breid je hun functionaliteit uit en voor je het weet kunnen ze niet meer zonder elkaar.
Voorbeelden

Het decorator patroon wordt vaak gebruikt in volgende gevallen:

  • Logging arguments & return values
  • Performance logging
  • Authentication & authorisation
  • Validation
  • Caching
  • Memoization
  • Event handling
  • Retry mechanisms

Als je een van deze wil voorzien zou ik niet twijfelen. Misschien wil je ook combineren en dan wordt al snel duidelijk waarom het leuk is dat die code leesbaar & herbruikbaar is.

Mijn concrete ervaring komt vanuit angular, maar ook van een project waar enorme winst werd gedaan met een decorator die memoizing doet. De applicatie maakt gebruik van een library om zwaardere berekeningen te doen voor wiskundige berekeningen om NURBS te tekenen. Deze NURBS worden getekend met threejs. Wat je tekent in three.js wordt pas zichtbaar eenmaal je een material toekent aan een geometry, ook hier worden de gemaakte materials via een memoize decorator hergebruikt.

Aanvankelijk had ik de impact van de decorator echt onderschat, tot de performance issues naar boven kwamen. Gelukkig was de decorator super makkelijk toe te passen!

Conclusie

Via decorators breiden we het bestaande gedrag van onze classes, methods, properties, parameters & accessors uit zonder focus te verliezen over het doel van onze class. Decorators zijn declaratief, modulair & springen in het oog. Met de kennis in dit artikel kan je als developer al aanvoelen of je project er baat bij heeft.

Zin om meer te leren?

Check onze Insights hier!
Axxes