Principper og mønstre
Med udgangspunkt i den arkitektur, vi har set på i forrige kapitel (fx monolit eller lagdelt arkitektur), kan vi gøre koden mere robust og fleksibel ved at følge en række grundlæggende principper og mønstre. Der findes flere kendte principper for objektorienteret programmering, og de mest udbredte og anerkendte er SOLID-principperne, formuleret af Robert C. Martin (også kaldet “Uncle Bob”), samt en lang række Design Patterns, særligt dem, der blev populariseret af “Gang of Four” i bogen Design Patterns: Elements of Reusable Object-Oriented Software (1994).
Her er et forslag til, hvordan du kan tilføje mutabilitet som et ekstra punkt, samtidig med at du bevarer opdelingen i de “fire grundsøjler” og skaber en god overgang til SOLID og patterns:
Objektorienterede principper
Før vi går i dybden med SOLID og designmønstre, er det værd at genopfriske de fire grundprincipper, der kendetegner objektorienteret programmering:
-
Encapsulation (indkapsling)
Indkapsling handler om at skjule data og unødvendig kompleksitet for omverdenen. Man bestemmer, hvilke dele af klassen der skal være offentligt tilgængelige (public) og hvilke, der skal holdes skjult (private). På den måde beskytter man klassens indre tilstand og undgår, at anden kode utilsigtet ændrer på data. -
Abstraction (abstraktion)
Abstraktion går hånd i hånd med indkapsling og betyder, at vi kun fremhæver de vigtigste dele af et objekt og undlader de detaljer, som brugeren af klassen ikke har behov for at vide. -
Inheritance (arv)
Arv gør det muligt at oprette nye klasser baseret på eksisterende, hvor de “arver” felter, egenskaber og metoder. Man kan således genbruge kode og specialisere den i underklasser. -
Polymorphism (polymorfi)
Polymorfi (mange former) handler om, at et metodekald kan afvikles på forskellige måder alt efter, hvilken konkret klasse vi arbejder med. I praksis betyder det fx, at en metode kan have forskellige implementeringer, afhængigt af om den kaldes på enDog
,Cat
ellerBird
, selvom de alle nedstammer fraAnimal
.
Mutabilitet i OOP
Selvom mutabilitet (om et objekts tilstand kan ændres eller ej) ikke er en “klassisk” søjle i OOP, spiller det en stor rolle for, hvor let det er at teste, fejlsøge og vedligeholde kode:
- Mutable objekter kan ændre deres indre tilstand efter oprettelse. Det er nyttigt i mange scenarier (f.eks. en terning, der skal vise nye værdier ved hvert kast).
- Immutable objekter ændrer ikke tilstand, når de først er instantieret. De er nemmere at debugge og trådsikre men kan være mindre fleksible, hvis dit domæne faktisk kræver, at data kontinuerligt ændres.
Ved at overveje mutabilitet bevidst opnår du en klarere fordeling af ansvar i dine klasser og færre uforudsigelige ændringer af data i din applikation.
SOLID-principperne – et historisk blik
Robert C. Martin introducerede SOLID-principperne i starten af 2000’erne for at hjælpe udviklere med at skrive mere vedligeholdelig og fleksibel objektorienteret kode. SELVE forkortelsen SOLID henviser til fem principper:
-
Single Responsibility Principle (SRP): Hver klasse eller komponent bør have ét klart ansvarsområde – eller sagt på en anden måde: kun én grund til at ændre sig.
- Hvis din klasse har flere ansvarsområder, risikerer du, at ændringer i ét område bryder funktionaliteten i et andet.
-
Open/Closed Principle (OCP): Koden skal være åben for udvidelse, men lukket for ændring.
- Du bør kunne tilføje nye funktioner eller variationer uden at ændre den eksisterende, gennemtestede kode for meget. Ofte løses dette ved hjælp af arv, interfaces eller abstraktioner.
-
Liskov Substitution Principle (LSP): Subklasser skal kunne erstatte deres superklasse uden at bryde den overordnede logik.
- Dette handler om, at hvis du har en metode, der forventer et objekt af typen
Animal
, så skal etDog
-objekt (en subklasse) fungere i præcis samme sammenhæng, uden at skabe fejl eller uventet opførsel.
- Dette handler om, at hvis du har en metode, der forventer et objekt af typen
-
Interface Segregation Principle (ISP): Mange små, specifikke interfaces er bedre end ét stort, altomfavnende interface.
- Udviklere bør ikke være tvunget til at implementere metoder, de ikke har brug for.
-
Dependency Inversion Principle (DIP): Høj-niveaulag må ikke afhænge direkte af lav-niveaulag. Begge lag bør afhænge af abstraktioner, ikke konkrete implementeringer.
- Dette princip ses ofte i brug via “Dependency Injection”, hvor du gennem konstruktører eller konfigurationsfiler kan udskifte implementationer uden at ændre i koden, der bruger dem.
Litteratur om SOLID
- Robert C. Martins bog Agile Software Development, Principles, Patterns, and Practices (2002) er et godt udgangspunkt.
- Clean Code (2008) af samme forfatter er også en populær bog, der fokuserer på læsbar og vedligeholdelsesvenlig kode, men som indirekte berører SOLID-principperne.
Design Patterns – historik og formål
Design Patterns er dokumenterede løsninger på gentagne problemer i softwaredesign. De blev for alvor kendte med “Gang of Four”–bogens (Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides) udgivelse i 1994. Formålet med design patterns er at genbruge gode løsninger i stedet for at opfinde den dybe tallerken hver gang.
Man inddeler ofte mønstrene i tre overordnede kategorier:
- Creational (f.eks. Singleton, Factory Method, Builder)
- Fokus på hvordan man opretter objekter på en fleksibel måde.
-
Kan gøre koden mere robust over for fremtidige ændringer i, hvordan objekter bliver instantieret.
-
Structural (f.eks. Adapter, Decorator, Facade)
- Håndterer sammensætningen af objekter og klasser til større strukturer.
-
Koden bliver lettere at vedligeholde og ændre, fordi man har klare måder at koble moduler eller klasser sammen.
-
Behavioral (f.eks. Observer, Strategy, Command)
- Løser, hvordan objekter interagerer med hinanden, og hvordan ansvar fordeles.
- Hjælper ofte med at reducere afhængigheder mellem klasser og fremme udvidelsesmuligheder.
Praktiske fordele ved patterns
- Nemt at kommunikere: Når du siger “Vi bruger et Singleton-mønster her” til en anden udvikler, ved de præcis, hvilken arkitekturidé du har.
- Dokumenteret viden: Patterns kommer med gennemprøvede fordele og kendte faldgruber.
- Forbedret struktur: De hjælper med at undgå “spaghetti-kode”, fordi mønstrene typisk kræver en klar opsplitning af ansvar.
Videre læsning
- “Gang of Four”-bogen, Design Patterns: Elements of Reusable Object-Oriented Software (1994)
- Head First Design Patterns (2004) af Freeman & Freeman, som er en mere letlæst indføring med mange eksempler.
Hvorfor er principper og mønstre vigtige?
- De gør din arkitektur nemmere at forstå for nye udviklere og for dig selv over tid.
- Du undgår unødigt komplekse afhængigheder og sikrer, at klasserne kan testes og genbruges.
- En velovervejet anvendelse af principper og mønstre sparer tid og frustration i længden, fordi man ikke skal “opfinde” alt fra bunden hver gang.
Brug af SOLID og patterns i praksis
Når du planlægger din arkitektur, kan du med fordel stille dig selv spørgsmål som: - Har denne klasse flere ansvarsområder? Hvis ja, skal den måske deles op (SRP).
- Kunne et velkendt mønster løse denne udfordring uden at jeg koder det hele fra scratch?
- Kan jeg abstrahere afhængigheder, så jeg let kan erstatte dem (DIP)?
- Er der et strukturelt mønster, som gør klasserne nemmere at integrere (Adapter, Facade osv.)?
Disse spørgsmål hjælper dig med at navigere i, hvilke løsninger der bedst passer til projektets behov – uden at overkomplicere tingene.