Johnny Hogenbirk: Artikelen, code-snippets, etc.


Home

Double Loop Generator: snel, flexibel en betrouwbaar genereren van software - 07-02-2020


Inleiding

Software kan op vele manieren worden gemaakt. Na de 3e generatie programmeertaal zijn veel pogingen gedaan om tot een hoger niveau te komen, om nog sneller applicaties te ontwikkelen, met minder fouten.
De erop volgende generaties (4 en 5) hebben de 3e generatie niet weggevaagd. Sterker nog, als je zoekt op veel gebruikte talen, tref je 3e generatietalen.
Een verschil is wel dat er steeds meer gebruik wordt gemaakt van frameworks. Zelf gemaakt of open source.
In dit artikel wil ik inzoomen op de zelf gemaakte. Ik heb er geen uitgebreid onderzoek naar gedaan, maar wat ik zie en hoor, is dat deze frameworks meer eenmalige generatoren zijn. Ze genereren bestanden, die code bevatten van een 3e generatie programmeertaal. Ze zijn in de praktijk vaak eenmalig, doordat er nog handmatig veranderen moeten worden gedaan in de bestanden. Bijstellen met de generator kan dan niet.
Dit zijn handige frameworks, maar het kan vele malen handiger. Eerst een stap terug naar de basis van een programmeertaal, daarna door naar een hoger level generator.


Clustering op clustering

Bij de ontwikkeling van machinetaal via de 1e en 2e naar de 3e generatietaal, is telkens een principe toegevoegd: clustering van opdrachten, die via één opdracht is aan te roepen.
Sterker nog: we denken altijd vanaf het besturingssysteem van de computer. Maar het besturingsysteem kent zelf ook dit principe. Onder het besturingssysteem worden op het laagste niveau stroompjes door processoren gejaagd, die doorloopt naar input- of output randapparatuur. Ook dat werkt via clustering.

Terug naar de 3e generatietaal. We bouwen ook zelf classes en/of functies in die taal, zodat we niet alleen overzichtelijke pagina's op ons scherm krijgen, maar we ook geen stukken gelijke code bij een aanpassing allemaal bij langs moeten. En dat we iets maar 1 keer hoeven te testen. Een (goed) geteste class of functie kunnen we eindeloos hergebruiken. (Ok, code includen kan ook, maar een beetje profi ontwikkelaar stapt daar snel van af of past het met beleid toe.)


Maar dan, de praktijk

In praktijk stoppen ontwikkelaars op een bepaald niveau van clustering. Zo wordt voor elk scherm die gevuld moet worden met data, een aparte query geschreven en een apart bestand, die zorgt voor de output van die ene pagina. Al dan niet mooi opgezet op basis van het MVC-model.
En dat terwijl in een applicatie veel pagina's op dezelfde manier zijn opgebouwd. Bijvoorbeeld overzichten en formulieren voor toevoegen of wijzigen van data.
Een class of functie die op basis van parameters een bepaald type pagina kan genereren, is doorgaans een stap te ver.


Ini-bestanden: Double Loop Generator

Toch is dit niet heel ingewikkeld. Door de kenmerken van de pagina op te nemen in een soort ini-bestand, is het niet moeilijk met één class of functie tientallen, honderden of duizenden pagina's op te roepen.
En als je dan een GUI maakt waarmee die ini bestanden zijn te beheren, dan ben je een grote stap verder op de weg naar snelle, flexibele en betrouwbare ontwikkeling van software.
Je hebt dan een 'Double Loop Generator': je genereert ini-bestanden, waarna een generator op basis daarvan pagina's genereert.

Sommige systemen werken ook al op die manier. Google en Microsoft hebben software, waarmee gebruikers via klikken en een paar toetsaanslagen (voor tekst die de gebruiker ziet) formulieren of overzichten kunnen genereren. Er zijn ook talloze generatoren van bijvoorbeeld enquetes of wegpagina's (CMS).
Maar, deze kennen forse beperkingen. De pagina's zijn niet in samenhang te gebruiken, waarbij de data wordt opgehaald uit of opgeslagen in één keurig genormaliseerde database zelf opgezette database. Dat gaat dan net een stap te ver. Bovendien is het vaak geen generiek te gebruiken ontwikkel software, maar is het domein gebonden. Zoals de enquete dus.


Te overwinnen issues

Er zijn twee issues die opspelen
1) De generator van de ini-bestanden moet meer flexibiliteit bieden en toch gebruikersvriendelijk blijven.
2) Er moeten brokken code van de 3e generatie taal in te voegen zijn, die bestand zijn tegen aanpassing van het ini-bestand via de GUI.

Ad 1. Ini-bestanden
Een aantal parameters voor het genereren van pagina's zijn eenvoudig. Bijvoorbeeld de koptekst: in het ini-bestand plaats je dan
<head_text>
  Kopje
</head_text>
(Als in xml opgezet, andere formats zijn natuurlijk ook goed, je zou er ook voor kunnen kiezen om de ini data in tabellen op te nemen.)
Als het gaat om een formulier, zijn de objecten die moeten worden geplaatst op de pagina ook nog niet zo spannend:
<row>
  <title>
    Naam
  </title>
  <object>
    text_input
  </object>
</row>
Natuurlijk, in de row moeten veel meer attributen, ook een id, maximale lengte, veldcontrole, enzovoorts.

De buttons zijn al iets lastiger: welke actie moet worden uitgevoerd bij een klik op welke button. De button 'Volgende' is nog niet zo spannend, maar 'Opslaan' wel. Je moet deze op de server opvangen, nagaan welk formulier is opgestuurd en dan de acties daarvan uitvoeren. Met name de data kant vergt wel aandacht, waar ik bij onder het kopje 'Data' nader op in ga.

Het lastigste punt zit hem in de data die in een formulier moet worden opgenomen (bij een read-only of mogelijke read-write actie, soms ook bij een formulier voor toevoegen waarden, indien er een keuzelijst met waarden in moet zitten).

Data
Software ontwikkelaars denken bij data aan queries op een database. Maar, dat gaat ten koste van de flexibiliteit/parameteriseerbaarheid en betrouwbaarheid van de generator. Je wilt via muisklikken, via instellingen werken. Niet met handmatig geklopte queries.
Maar dat betekent dat je een eigen query generator moet schrijven. Of, nog beter, een database onafhankelijke 3e generatie programmeertaal data bewerkings class/functie.
Dat geeft gelijk een groot voordeel: wie zegt dat de data die je in je formulier nodig bent, altijd uit één database komt? En wie zegt dat het geen (read-only) xml- of csv bestanden kunnen zijn? Of soap request naar andere webservers? Juist: doordat je zelf code schrijft die data samenvoegt, kan de data ook uit verschillende bronnen komen.
Is zoiets eenvoudig te schrijven? Nee, zeker niet. Maar het is de moeite waard.
Een voorbeeld van (een gedeelte van) een data ini deel:
<dataset>
  <id>
    1
  </id>
  <data>
    <id>
      1
    </id>
    <type>
      mysql
    </type>
    <table>
      user
    </table>
    <columns>
      ID,name_user,role_id
    </columns>
  </data>
  <data>
    <id>
      2
    </MAF>
    </iid>
    <type>
      csv
    </type>
    <table>
      role
    </table>
    <columns>
      ID,name_role
    </columns>
    <join>
      user.role_id#role.ID
    </join>
  </data>
</dataset>
Degene die queries kan maken, ziet zo de generatie van SELECT ID,name_user,role_id FROM user hierin. De select op de csv is uiteraard compleet anders, maar ook niet heel spannend: gewoon een bestand inlezen.
De join is eigenlijk ook niet heel spannend: nieuwe array aanmaken ([{id van de dataset}]), in een lus de data uit user erin stoppen, nog een lus met role_id==ID, data toevoegen en klaar is de join.
Natuurlijk, met WHERE clausules erin, eventuele tellingen of andere bekende database functies, wordt het nog iets complexer. Als het te complex wordt, zou je na de laatste <data> ook <code> kunnen invoegen, die een specifieke (3e generatietaal) actie op de dataset uitvoert (zie hieronder voor de uitleg).

Ad 2. Brokken code
Een heel ander punt is dat met een generator gelijk een stukje maatwerk verloren gaat. Je kunt nooit alle situaties die kunnen voorkomen via een generator realiseren. De generator zou oneindig groot worden, je blijft bouwen. En daarmee de stabiliteit ondermijnen, de complexiteit onnodig vergroten.
De oplossing is om in het ini-bestand een extra entiteit toe te voegen, bijvoorbeeld:
<code>
  {naam code bestand}
</code>
En als de code nodig is om een bepaalde unieke rij in een formulier te maken, dan kan deze entititeit natuurlijk als een attribuut in een rij worden toegevoegd.
Op zich is dit betrouwbaar, gaat het bij wijzigingen in het ini-bestand nooit fout. Een risico treedt wel op als in de code andere entiteiten of attributen worden gebruikt. Bijvoorbeeld als de rol x is (op basis van de waarde in een object), maak dan rij y read-only maken. Het is dus handig om in het ini-bestand bij entiteiten of attributen de mogelijkheid van toelichting op te nemen: 'Pas op, de code xxx.php maakt gebruik van de waarde van deze rij'.

Note: er is dus code die het ini-bestand omzet in een pagina ofwel generatorcode. Maar ook pagina gebonden code die tijdens het interpreteren/compileren wordt uitgevoerd ofwel paginacode. De paginacode kan dus gebruik maken van de classes/functies van de generatorcode. Dit levert veel voordelen op: de code kan compact blijven en bij verbeteringen van de generatorcode (bijvoorbeeld performance of error handling) profiteert de paginacode daar ook van. Let wel: bij aanpassingen aan de generatorcode moet dus gecheckt worden of en hoe de paginacode gebruik maakt van de generatorcode. Maar dat is via zoeken door de code eenvoudig te realiseren.


Opmerkingen? Ze zijn van harte welkom: email@johnnyhogenbirk.nl!