Inspiring people. Excellent results.

Gedreven door ICT willen wij samen meer bereiken. Als betrokken partner in Noord-Nederland. Dat is wie wij zijn. Wij zijn Get There.

  • Deel:

Strings: the root of all evil

Richard de Zwart, 31-12-2017

Okay, de titel is een beetje click-bait. Strings zijn niet de oorzaak van ALLE kwaad in software; we hebben immers ook nog “Magic Numbers”, “Code Duplication”, “Premature Optimization” en geld (volgens de Bijbel, 1 Timoteüs 6). Maar we gebruiken strings wel te vaak op plekken waar we beter iets anders kunnen gebruiken.

Strings zijn lekker makkelijk, er mag immers van alles in. Het maakt niet uit of je een string hebt met het sprookje van Roodkapje, een bedrag, een datum, een postcode of een BSN.

Het probleem is dan alleen dat die dingen die voor het menselijk oog herkenbaar zijn en betekenis hebben, meteen betekenisloos worden. Een compiler kent niet het verschil tussen een string met een datum en een string met een postcode. En die compiler is nou net onze vriend als het gaat om het voorkomen van bugs: hoe beter de compiler weet wat voor informatie we -bijvoorbeeld als een parameter- doorgeven hoe beter hij kan helpen voorkomen dat we het verkeerde doorgeven.

Stringly typed code

Er is een term die opgetekend werd door Jeff Atwood op zijn blog Coding Horror: stringly typed code:

A riff on strongly typed. Used to describe an implementation that needlessly relies on strings when programmer & refactor friendly options are available.

  • . parse it and create an enum, then you have strong typing throughout the rest of your codebase).
  • Message passing without using typed messages etc.

Excessively stringly typed code is usually a pain to understand and detonates at runtime with errors that the compiler would normally find.

De laatste zin is voor mij waar het om gaat: code die veel strings gebruikt is moeilijk te begrijpen en faalt pas tijdens het runnen. Is dat zo? Dat het moeilijk te lezen is? var person = FindPerson("Jan", "Janssen"); is vrij duidelijk. De parameters zijn een voornaam en een achternaam. En normaliter zou je variabelen doorgeven  en als je die meer beschrijvende  namen geeft dan p1 en p2 dan is het nog duidelijker wat de functie verwacht.

Enums

Wat is dan code die beter zou kunnen? Allerlei typeringen zoals “Eenrichtingverkeer” (typefout met opzet) voor een straat of “Granen” voor een stuk uit de schijf van vijf. Die typeringen kun je altijd modeleren als een  enum. En dan is de compiler weer in staat om voor je te controleren of je wel een geldige waarde gebruikt. Maar dit soort informatie komt vaak als string je systeem binnen; soms als een keuze van een dropdown op een scherm, soms als een waarde uit een database of uit een API van een derde partij.

Die moet je dus op de plek waar ze binnen komen omzetten naar een enum zodat de rest van je code getypeerd kan zijn.

Helaas hebben Enums wat nare eigenschappen. De belangrijkste is dat het eigenlijk een  int is  (well actually: het kan ook een long of een byte zijn…). Het ziet eruit als een leesbare tekst, maar het is een getal. Dat was 400 jaar geleden toen computers nog op stoom liepen misschien heel slim en efficient, maar ik zou best blij zijn geweest met een implementatie die meer op die van Java lijkt. Je moet dus her en der dat getal omzetten naar een tekst. En daarmee komen we op de tweede nare eigenschap van  enum in C#: de ondersteuning voor die omzetting  is niet geweldig. We hebben allemaal te vaak dit soort code geschreven:

var rijrichting = (Rijrichting) Enum.Parse(typeof(Rijrichting), "Eenrichtingsverkeer", true);

In .NET Core is het gelukkig zoals je het zou willen:

var rijrichting = Enum.Parse<Rijrichting>("Eenrichtingsverkeer");

Datum velden

Datum velden hebben natuurlijk al problemen genoeg van zichzelf. De omzetting van de ene tijdzone naar de andere, het feit dat een DateTime in C# altijd een tijd heeft ook als je alleen maar een datum nodig hebt, maar vooral natuurlijk het parsen van verschillende formaten. Is “1-12-2017” nou 1 december of 12 januari?

Ook hier geldt natuurlijk dat je de string zo snel mogelijk omzet naar een DateTime voordat je de informatie verder je applicatie in stuurt. Maar datum velden hebben altijd ook nog een andere betekenis dan alleen maar een punt in de tijd. Het kan een geboortedatum zijn, of een ShipBeforeDate of een combinatie van twee datums die samen een periode vormen. Daar zit altijd bepaalde business-logica aan vast. Een ShipBefore datum mag misschien nooit meer dan 14 dagen na een OrderPaidAt datum liggen. Die business-logica hoort vaak bij de datum en verdient een eigen class. Maar dat is eigenlijk een heel ander verhaal dat valt onder de Primitive Obsession code smell.

Postcodes

Eerlijk gezegd heb ik nog nooit een Postcode class gezien in een systeem. Niet zo heel raar omdat je deze meestal alleen maar via een formulier van een gebruiker door krijgt, opslaat in je database en misschien weer eens ophaalt om een brief (zo’n papieren ding met een envelop eromheen en een postzegel erop) te kunnen samenstellen. Maar als je ook maar enige validatie op een postcode doet is het al voor de hand liggend om een Postcode class te maken. En NIET een PostcodeValidator class… Je maakt het je leven in eerste instantie niet makkelijker, omdat je dan ook moet nadenken over postcodes in andere landen. Met een string-veld kun je alle varianten aan van over de hele wereld. Tja, heeft iemand je ooit beloofd dat progammeren makkelijk zou zijn?

EAN, IBAN, BSN, E-mail

Ik werk nu aan een project waar heel veel EANs worden gebruikt. Dat zijn getallen van 13 of 18 cijfers die bijvoorbeeld een elektriciteitsmeter in je meterkast identificeren. Deze worden als string in de applicatie verwerkt. De validatie zit in aparte validatie-helper classes. Je komt dat ook tegen met IBANs en BSNs: allemaal tekenreeksen die absoluut niet willekeurig zijn en smeken om in aparte classes te worden opgenomen. Het is geen tekst, het is een IBAN. Het is geen tekst, het is een e-mail adres.

Veel werk? Welnee, alleen verplaatsen van code die je toch al hebt. Je komt er dan ook achter hoe vaak je die code gedupliceerd hebt in je systemen. En het zijn ook van die zeldzame objecten in je systeem die je daadwerkelijk misschien nog wel over projecten heen kunt hergebruiken.

Samenvattend (TL;DR)

Als het werkelijk om een willekeurige reeks tekens gaat (zoals een sprookje of een achternaam) mag je een string gebruiken. In alle andere gevallen verdient het (en verdien jij als vakman) een echte class.

Kwaden