Når man utvikler et program starter det ofte i det små, og man programmerer med fokus på det man skal oppnå for å bli ferdig i tide. Programvare og ønsker endrer seg ofte, og det gjør også klassene og funksjonene dine. Når man gjør endringer i funksjonalitet eller legger til ny funksjonalitet er det veldig enkelt å legge til noen nye linjer i de funksjonene man allerede har; en “if” her og en “else” der kan da ikke skade?
Problemet oppstår først når man etter en stund skal inn og gjøre endringer i allerede eksisterende funksjonalitet, helst uten å brekke hele systemet. Kanskje man kaller på funksjoner som man egentlig ikke aner hvor finnes i kodetreet, man har ikke peiling på hvor denne globale variabelen “er_satt” ble satt, enda mindre pailing hvor den kanskje ble endret på veien, og den smarte workarounden som du la inn for et halvt år siden er nå blitt et midtpunkt i systemet ditt. Hva gjør du så? Jo, du legger til en ny “if”…
… eller du starter på nytt. Tanken på å starte fra scratch kan virke skremmende på de fleste, men du har et gigantisk fortrinn foran hvem som helst andre som skulle laget det samme. Du vet hva du skal lage. Du har laget det før, og du skal lage det samme på nytt, men denne gangen med modularitet i fokus.
Det finnes noen små og enkle leveregler når det gjelder programmering. Ingen linjer skal være lenger enn 80 tegn i bredden og ingen funksjoner skal være på flere linjer enn hva skjermbildet ditt kan vise, og gjerne litt mindre. En skummel situasjon er hvor man får løkker inni løkker inni løkker, fordi dette skaper en uorden og det er vanskelig å se på koden og umiddelbart se hva som blir gjort med dataene. Kanskje kan en av løkkene flyttes ut som en ny funksjon, som bare håndterer et sett av dataene. Kanskje denne funksjonen kan benyttes et annet sted i programmet ditt. Tankegangen bak modularitet er å få oversiktlig og gjenbrukbar kode.
Vær gnien på hvilke data du sender mellom funksjonene. Man kan si at dersom antall parametere inn til en funksjon overstiger tre, så gjør enten funksjonen alt for mye eller så hører disse dataene sammen og du kan kanskje lage en klasse som representerer dataene dine istedet. Husk at dataene du sender til en funksjon skal ha høy integritet, eller med andre ord “høre sammen”. Dersom du har behov for å sende data som ikke hører sammen eller som ikke er på samme nivå i systemet ditt, som for eksempel en liste med fotballag sammen med en referanse til databasekontrolleren din, da bør du vurdere å tenke nytt. Kanskje mottakerfunksjonen kan klare å finne referansen til databasekontrolleren selv, eller kanskje du kan sende lista med fotballag til en klasse som arver fra databasekontrolleren som har direkte tilgang til databasen via denne.
For å ta et konkret eksempel på et prosjekt jeg har jobbet med for litt siden, stod vi foran et valg hvor vi kunne lage noen funksjoner for å håndtere forskjellige filtyper og bruke if-else-kontrollstrukturer for å kalle på riktige funksjoner, eller å lage nye klasser for hver filtype og bruke en hashmap for å finne ut hvilken som skulle brukes. Vi gikk for valg nummer to. Vi lagde en felles klasse med en abstrakt metode, som alle de forkjellige filtypene arvet fra. Dermed kunne vi lage noen små og oversiktlige klasser, hvor navnet på klassene indikerte hvilken filtype de håndterte, og disse klassene arvet fra fellesklassen med forskjellige implementasjoner av den abstrakte metoden for å håndtere filtypens unike handling. Det som viste seg var at mange av disse filtypene skulle håndteres likt. Det som kunne blitt et hav av if-elsif ble nå bare en utvidelse av hashmapen for å peke alle filtypene med samme handlingsmønster til samme klasse.
Dette er bare et lite eksempel av hvordan man kan programmere modulært. Det er flere måter å programmere modulært på. Man kan for eksempel lage en plugin-arkitektur hvor man kan stikke inn små moduler som gjør en bestemt oppgave totalt uavhengig av de andre modulene man skulle bruke. Det man må passe på er at disse innstikksmodulene ikke blir for store og monolitiske, men at man også fører modularitetstankegangen videre ut i modulene sine og helt ut i hver funksjon.
Kort oppsummert handler dette om “best practices”. Innen objektorient programmering heter det at enhver klasse skal ha høy kohesjon og svak kobling. Dette gjelder også for strukturell programmering. En funksjon skal ikke kunne alt, men skal være en ekspert på sitt område.