Get There ICT professionals in Leek, Groningen. Gedreven door ICT willen wij samen meer bereiken, als betrokken en professionele ICT partner.
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.
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.
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 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.
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?
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.
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.
Inspireren en zelf geïnspireerd worden door andere IT professionals? Het gebeurde allemaal op Connect.frl, 6 december 2017!
Connect.frl is een stichting die als doel heeft het aanwezige IT-talent in het noorden te inspireren en te behouden voor de regio. Middelen? Events, kennissessies, netwerkbijeenkomsten, traineeprogramma’s en nog veel meer.
Hoogtepunt elk jaar is het event in december. Thema 2017? Jezelf opladen met inspiratie.
Get There is niet alleen partner (ontmoet ons eind dit jaar weer op onze stand!), maar we hebben ook wat te vertellen: graag tot dan!
Goed bezochte sessie van Arnoud en Richard
Richard de Zwart, 1-11-2017
Er is natuurlijk al het nodige geschreven over het hoe en waarom van het Builder Pattern. Dus ik ga er vanuit dat je hier gekomen bent omdat je zeker weet dat je een Builder nodig hebt, en dat je zoekt naar een manier om het anders te doen.
De implementaties die ik tot zover vond (zoals die van Cameron McKay of Nat Pryce) volgen uiteraard altijd hetzelfde patroon: een builder class met een methode voor elk stukje van het object wat er gebouwd moet worden (aantal deuren, kleur, type brandstof). Die code ziet eruit als heel veel meer-van-hetzelfde, terwijl je juist een pattern zoekt om je code te vereenvoudigen. Je Director daarentegen is heel netjes. Stel dat je een Fluent Interface wilt (nee, niet het Fluent Design waar Microsoft het nu veel over heeft) dan ziet het er ongeveer als volgt uit:
Car car = new CarBuilder() .WithDoors(4) .WithColor("red") .WithABS(true) .Build();
(met dank aan Tobiask).
Maar stel nou dat ik het bakken van de objecten generiek en herbruikbaar zou kunnen maken, met een stuk configuratie dat de bakker vertelt wat ie moet maken?
Gelukkig had ik enige tijd geleden een briljante collega (@JelleHissink) die de code die ik zocht in 3 minuten uit zijn hoofd intikte. En vervolgens nog 5 minuten doorratelde over verbeteringen die ik totaal niet kon volgen…
Code first.
public class Builder<T> where T : new() { IList<Action<T>> actions = new List<Action<T>>(); public T Build () { var built = new T (); foreach (var action in actions) { action (built); } return built; } public Builder<T> With(Action<T> with) { actions.Add (with); return this; } }
Ik zal eerst uitleggen hoe je ‘m gebruikt, dan hoe het werkt.
De eenvoudigste manier om een Builder te configureren is:
var pet = new Builder<Pet> () .With (p => p.Name = "Dolly") .With (p => p.Age = 3) .With (p => p.Type = "Cat") .Build();
Waarbij de Pet class in het voorbeeld gewoon een class is met 3 publieke velden.
Wat win je daar nou mee? Nou, je hoeft niet een aparte Builder class te maken, dat scheelt. Je moet wel alle namen van de properties die je wilt zetten aangeven, en dat is nou eigenlijk wat we net niet wilden. Maar ik vind het wel beter leesbaar dan een rijtje constructor overloads; en je kunt zelf de volgorde bepalen, velden weglaten en het is duidelijk wat een parameter betekent.
Een voorbeeld van dat laatste. Als je defaults wilt gebruiken kun je het volgende doen:
public class DogBuilder : Builder<Pet> { public DogBuilder () { With(a => a.Type = "dog"); } }
Een eenvoudige afgeleide class waar je de defaults zet in de constructor. Je kunt dan bij het bakken van je hond zulke code gebruiken:
var dog = new DogBuilder() .With(d => d.Name = "Tarzan") .With(d => d.Age = 1) .Build ();
En omdat je een lambda gebruikt kun je de 2 aanroepen ook nog samenvoegen.
var dog = new DogBuilder() .With(d => { d.Name = "Tarzan"; d.Age = 1; }) .Build ();
De Builder heeft een Build() methode die het gewenste type T teruggeeft. De methode is verantwoordelijk voor het feitelijke creëren van het object. In regel 6-10 gebeurt dat door het aanroepen van de default constructor, gevolgd door het doorlopen van alle Actions die je hebt gespecificeerd door de With() methode aan te roepen.
Dat is de essentie van het verhaal: je neemt als het ware de acties die je uitgevoerd wilt hebben op, en die worden afgespeeld zodra je Build() aanroept. Niet eerder dan dat je Build() aanroept krijg je echt een object.
Het is een beetje als een verlangslijstje maken: eerst schrijf je op wat je wilt en dan stuur je het naar de Sint om te zorgen dat je het ook allemaal daadwerkelijk krijgt.
Je hebt een default constructor nodig en publieke velden of properties. Dat zou in een Domain Driven Design natuurlijk uit den boze zijn. In DDD vaak een private constructor gemaakt en een static Create() functie. Dat heeft wel iets van een Builder behalve dat je weer een functie krijgt met soms een enorme bak aan parameters.
Separation of concerns. Het mooie van deze oplossing is dat hoe je een object bouwt de verantwoordelijkheid is van de Builder; en dat wat je bouwt de verantwoordelijkheid is van de Director of de afgeleide class. Of zoals Zoran Horvat het zou zeggen: de infrastructuur is losgetrokken van de business logica.
Op dit moment ben ik Builders aan gebruiken om test data te genereren voor een integratie test. Ik bouw een entiteit op met zijn/haar sub-entiteiten en voeg in dat proces van alles toe aan een Entity Framework context. Pas als de Build() wordt aangeroepen doe ik een context.SaveChanges()
. Daarmee functioneert mijn Builder in feite als een Unit of Work.
Scrum is een Agile Software Development Framework om productontwikkeling (dagelijks) te bewaken. Door middel van stand ups met alle betrokkenen worden (details van) de vooraf gedefinieerde ‘brokken’ werk besproken en eventueel aangepast. Dit flexibele proces houdt daarbij rekening met nieuwe inzichten; zo kunnen klanten tijdens het proces tot het inzicht komen dat hun wensen veranderd zijn en teamleden kunnen hun oplossingen voor de meest efficiënte werkwijze inbrengen. Dagelijkse afstemming hierover verbetert het proces om tot optimale klanttevredenheid te komen. En daar doen we het voor.
We bedanken opleidingsinstituut StarTel voor de professionele training.
Vanaf 1 oktober 2017 heeft het Centraal Justitieel Incassobureau (CJIB), een uitvoeringsorganisatie van het ministerie van Veiligheid en Justitie, een nieuwe raamovereenkomst voor het tijdelijk inhuren van ICT-professionals. Get There is er trots op dat zij één van de geselecteerde partijen is, samen met Seven Stars Noord (Penvoerder) en Brunel. In totaal zijn zes partijen geselecteerd.
Ook de mantel voor detavast-constructies van ICT-professionals heeft Get There icm Seven Stars en Brunel gegund gekregen. Bij een detavast-constructie is het de bedoeling dat de ICT’er na een opleidings- en trainingsperiode bij goed functioneren instroomt in een functie bij CJIB.
Het mantelcontract vertegenwoordigd een verwachte inhuurwaarde van EUR 32.000.000 met een doorlooptijd van 4 jaar.
Natuurlijk is Get There, als sponsor en Microsoft Gold partner, aanwezig. Onze collega’s Pieter Leijenaar en Richard de Zwart verzorgen twee presentaties: ‘Angular frontend zonder backend’ en ‘Azure functions’. De sessies zijn praktisch van aard, met aandacht voor het laatste nieuws van Microsoft en het schrijven van code.
Ben jij er ook bij?
Richard de Zwart, 26-07-2017
Ik ben echt dol op Test Driven Development. Sterker nog, ik kan me geen (werkend) leven voorstellen zonder unit testen. Als ik geen test suite heb die me vertrouwen geeft, hoe kan ik dan ooit mijn code refactoren om de onderhoudbaarheid te verhogen –en ondertussen zeker weten dat ik niks kapot maak?
Het werkt. Als je de juiste packages installeert en wegblijft van .NET Core of het nieuwe CSPROJ formaat.
Update VS2017 15.3 en 15.4: het werkt nog beter! Er is nu ook ondersteuning voor .NET Core.
Helaas heeft Visual Studio nooit echt goede ondersteuning geboden voor unit testen, laat staan voor TDD. Om een beetje fatsoenlijk TDD te kunnen doen heb je een test-runner nodig die snel en bij voorkeur continue draait. Om dat te kunnen doen in Visual Studio gebruik ik al jaren NCrunch. Het is een plugin die voortdurend je code compileert en je testen draait. Daarnaast geeft het je per regel informatie over de coverage van je code: is er uberhaupt coverage? Gecovered door een falende test? Gecovered door een geslaagde test?
Tijdens het debuggen wordt daar nog eens timing aan toegevoegd voor elke regel. Het is een heel plezierige ervaring. Terwijl je je test schrijft begint Ncrunch te compileren en zodra je code compileert heb je een falende test. Rode fase compleet, door naar de groene fase! En terwijl je je implementatie schrijft zie je direct een groene bal verschijnen zodra je genoeg code hebt geschreven om de test te laten slagen. Door naar de refactor fase! Uncle Bob zal trots op je zijn…
Toegegeven, je kunt unit testen draaien. Je kunt zelfs niet-Microsoft frameworks zoals NUnit en xUnit gebruiken als je de juiste adapters installeert. Maar het resultaat is niet veel meer dan dit:
Een lijst van geslaagde en falende testen. Geen zinvolle manier om je testen te organiseren, zoals met een tree-view die je op het nivo van classes of assemblies laat open- en dichtklappen.
En elke keer dat je je testen wilt draaien moet je wachten op een volledige build, zelfs als je net nog gebuild en gerund hebt. Dat verhoogt de drempel om je testen te draaien zodanig dat je ze steeds minder gaat draaien. En als je je testen niet meer draait dan verliezen ze alle waarde als vangnet.
Als je Resharper gebruikt (en dat moet je sowieso doen), dan ben je iets beter af. Het lijkt alsof je testen eerder starten en sneller draaien. Maar je moet er nog steeds aan denken om ze te starten. En nee, “run after each build” is geen alternatief, omdat het build process dat volgt je hele Visual Studio blokkeert.
VS2017 heeft iets dat Microsoft “Live Unit Testing” noemt. Het is in essentie hetzelfde als wat Ncrunch doet: build en run terwijl je je code schrijft. En het voegt ook tekentjes toe aan elke regel code om te laten zien of deze gecovered/geslaagd/gefaald is.
Als je op zo’n X of V klikt dan krijg je een overzicht welke testen voor coverage van deze regel code zorgen.
Nou, om te beginnen heb je Visual Studio 2017 Enterprise Edition nodig. Ik hoop echt dat Live Unit Testing uiteindelijk ook beschikbaar komt voor de Community Edition van VS2017. Het is zo belangrijk dat de drempel voor TDD zo laag mogelijk is. Microsoft zou daar een mooie bijdrage aan kunnen leveren door Live Unit Testing voor iedereen beschikbaar te maken.
Vervolgens ga je in het menu naar Test > Live Unit Testing > Start.
And then (zoals in het Heineken spotje), we wait. Hoe weet je of het werkt? Nou, het kan even duren voordat je dat doorhebt. Je moet het Output Window open hebben:
En zoals je kunt zien, gaat het in mijn geval fout. Daarover straks meer…
Bij grotere projecten kan het even duren voordat de streepjes, vinkjes en kruisjes (sympatieke keuze voor kleurenblinde programmeurs) tevoorschijn komen. Eigenlijk komen de streepjes (geen dekking) vrij snel, ook bij code waarvoor wel dekking is. Uiteindelijk veranderen ze wel in kruisjes en vinkjes; een beetje verwarrend in het begin, maar je went er wel aan.
Uiteindelijk wordt het Test Explorer Window ook bijgewerkt.
Nog steeds dezelfde view, maar gelukkig is in VS2017 15.2 dit window in sync met je Live Unit Testen. Daarvoor waren het twee volkomen naast elkaar levende systemen.
Als je begint met “Add new project”, “Unit Test” enzovoorts –zoals je in de meeste voorbeelden ziet- werkt alles natuurlijk meteen, right out of the box. Maar als je al een class library met testen hebt, misschien met Nunit testen, en je wilt profiteren van Live Unit Testen dan krijg je bovenstaande foutmelding om je oren.
Je moet de juiste adapters installeren, zelfs voor MsTest gebaseerde testen. Dat is ok, het hoort bij het moderne Microsoft dat zegt: „Je moet zelf weten wat je wilt gebruiken, het is jouw keuze“.
Als je testen hebt met MsTest moet je er wel even aan denken de referentie naar de oude Microsoft.VisualStudio.QualityTools.UnitTestFramework weg te halen.
MsTest: https://www.nuget.org/packages/MSTest.TestFramework/1.1.18 en https://www.nuget.org/packages/MSTest.TestAdapter/1.1.18
NUnit: https://www.nuget.org/packages/NUnit3TestAdapter
xUnit: https://www.nuget.org/packages/xunit/2.2.0 en https://www.nuget.org/packages/xunit.runner.visualstudio/2.3.0-beta3-build3705
Zodra je de adapter van je keuze hebt geinstalleerd beginnen de blauwe streepjes tevoorschijn te komen en (afhankelijk van je code-coverage natuurlijk…) te veranderen in rode kruisjes en groene vinkjes. Nu kun je de Red-Green-Refactor cyclus in met onmiddellijke feedback!
Ja, uiteraard. Ik ook. Maar helaas Visual Studio vertelt je dat .NET Core nog niet ondersteund wordt in versie 15.2. De belofte is dat het in VS2017 15.3 komt, dus hopelijk komt die heel snel.Tot die tijd, als je geen bezwaar hebt om de command-line te gebruiken, kun je een referentie opnemen naar de DotNet Watcher Tools die bij elke file wijziging “dotnet test” voor je draait.
Update 22-10-2017: Inmiddels zijn zowel 15.3 en 15.4 beschikbaar. Daar is de ondersteuning voor .NET Core helemaal in orde. Het is zelfs zo dat versie 15.4 bij het openen van een Solution iets roept van “Er zijn unit-testen aanwezig, wil je Live Unit Testen proberen?”.
O, zeker! Heb je al gezien hoe je CSPROJ opgeruimd kan worden als je een paar simpele stappen volgt zoals Nate McMaster laat zien? Het werkt met je gewone .NET Full projecten! Je kunt de packages.config weggooien en je krijgt spontaan edit support in je Solution Explorer zonder dat je het project eerst hoeft te unload-en. Het nieuwe formaat is zoveel schoner en duidelijker.
MAAR. Live Unit Test kan nog niet overweg met het nieuwe formaat. Het besluit dat het “.NET Core” is en niet gebruikt kan worden. Nu maar hopen dat het in VS2017 15.3 wel gaat werken…
Onze collega Tom organiseerde deze IoT Makers Day voor allen die geïnteresseerd waren in het verkrijgen van inzicht in, en het manipuleren van je eigen omgevingsklimaat. Iedere deelnemer bouwde 2 sensor units (voor eigen gebruik) die data via WiFi naar een gateway brengt om vervolgens de data te pushen naar een cloud (thinger.io).
Orange Pi componenten (Gateway), WiFi Sensor modules, WiFi stroomschakelaars, soldeerapparaten, programmeerkennis, koffie, lunch, collega’s én fun waren de ingrediënten van een zeer geslaagde dag.
Met onze nieuwe HoloLens stappen we in de wereld van ongekende mogelijkheden van mixed reality technologie. Onze Microsoft Unit neemt ons mee in de beleving van hologrammen in onze directe omgeving en verkent de mogelijkheden om ons met al onze digitale content te koppelen.
Met de mogelijkheden van de HoloLens staan we aan de vooravond van ‘going beyond the screen’.