Dependency Inversion Principle
Dependency Inversion Principle (DIP) er det af de fem S.O.L.I.D. principper, hvis forståelse og efterlevelse har den største betydning for den overordnede arkitektur. DIP kan anvendes som en simpel mekanisme, som i dependency injection (DI), men kan også anvendes som et design princip, der tjener til at adskille arkitekturlag på en måde, der vil ændre på din forståelse af, hvorledes softwarekomponenter kan spille sammen på kryds og tværs.
Bemærk altså, at DIP ikke er ækvivalent med DI (hvilket er en almindelig misforståelse), men er et mere generelt princip.
DIP siger i al sin enkelhed:
- Højniveau komponenter bør ikke afhænge af lav niveau komponenter – begge bør afhænge af abstraktioner
- Abstraktioner bør ikke afhænge af detaljer. Detaljer bør afhænge af abstraktioner
Problem
Antag du er forfatter til tre klasser A, B og C hvor A afhænger af B som afhænger af C. Bemærk hvordan afhængighederne peger “nedad”.
Problemet i denne konstruktion er at ændringer i C afføder mulige ændringer med gentest til følge af B og dermed A.
Løsning
DIP foreskriver, at vi vender afhængighederne om så “lav-niveau” komponenten C ikke bliver bestemmende for høj-niveau komponenten A. Vi kan gøre dette ved at indføre interfaces (abstraktioner) som vist i følgende figur.
Læg mærke til hvordan afhængighederne nu peger opad. A behøver således ikke vide hvilken konkret klasse som implementerer IB (på samme måde for IC) og i praksis vil man anvende DI til at angive, hvilken implementation af IB der skal i brug. A er på denne måde helt afkoblet fra deltaljerne omkring B og vi kan frit udskifte / ændre denne uden at påvirke A og dermed skulle genteste og muligvis redeploye.
Eksempel
Et godt eksempel på en god anvendelse af DIP er til adskillelse af UI og Business lagene i f.eks. en enterprise arkitektur.
En brugerflade bør ikke kende til lav niveau detaljerne omkring hvordan data tilvejebringes eller for dens sags skyld kende til deltaljerne omkring hvilke data som præsenteres. Hvis du tænker denne tanke til ende vil du opdage at mange af de designs som f.eks. udspringer af en (for) forsimplet anvendelse af de mange teknologier som gør det muligt at “skovle data op i et view” (eksempelvis Data Binding) vender forkert! Tit og ofte benyttes data binding til at hente instanser af en Business klasse direkte op i UI og så præsentere de ønskede detaljer her.
Men koblingen mellem den konkrete Business implementation og viewet kan være dyr hvis data f.eks. skal anvendes i flere views. Ønskes en ændring af visningen af data i et View kan dette nødvendiggøre ændringer i Business klassen, hvilket igen kan være besværlig grundet afhængighederne til de øvrige views.
En bedre løsning er at tænke viewet som en “visnings-skal”, der intet konkret kender til sine data, men udelukkende koncentrerer sig om præsentationen. Ideelt spørger viewet til en mængde af data og præsenterer data på den ønskede vis med gruppering, sortering osv.
Patterns som fx DTO (Data Transfer Object) anvendes ofte til at afkoble præsentations- og business lagene og kan endvidere i kombination med DIP benyttes til at skrive composite application like UI behaviour. Motorrummet bag afkoblingen benævnes typisk et Service-lag og er der hvor UI henvender sig for nye data og er der hvor DTO’er oprettes som en samkøring af data fra Business laget.
Referencer
[1] “Agile, Principles, Patterns and Practices in C#”, Martin/Martin
Har du et projekt, som du har brug for hjælp til?
Sig endelig til, hvis vi kan hjælpe med noget
Morten Hoffmann
CEO
T: (+45) 3095 6416 E: mhs@strongminds.dk