Patternskolen del 5 - Strategy vs. Dependency Injection

Er Strategy og Dependency Injection det samme?

Bjørn Herve Moslet

I forrige del av denne serien lovet jeg å se på to design patterns: Strategy og Dependency Injection. Spørsmålet er om Strategy og Dependency Injection er det samme? Det korte svaret er: nei. De er to forskjellige patterns og vi ser nok en gang to patterns som har samme implementasjon, men vidt forskjellig intensjon.

Strategy

Strategy ble beskrevet i mer detalj i forrige del. Kort oppsummert kan vi si at oppførselen til en algoritme velges i runtime. Dette implementeres ved at den valgfrie oppførselen representeres ved et interface. En konkret implementasjon av interfacet kan settes i klassens konstruktør eller sendes som parameter til metodekallet.

Dependency Injection

Dependency Injection frigjør en klasse fra hardkodete eksterne avhengigheter. Et eksempel kan være en relasjonsdatabase og kode for å aksessere denne, f.eks. ved bruk av Repository. Disse avhengighetene kan bli gjort tilgjengelig for klassen i enten runtime eller compile time. Ofte gjøres dette ved å tilby avhengigheten i klassens konstruktør. Hensiktene med patternet kan være flere: Forenkle enhetstesting ved bruk av mock-objekter, separation of concerns eller for å kunne ha flere alternative implementasjoner av en ekstern avhengighet (f.eks. database eller webtjeneste). I det siste tilfellet begynner det å ligne veldig mye på Strategy.

Eksempler (i Java)


Strategy

interface Encryption {
byte[] encrypt(byte[] bytes);
}

class Overføring {
private Encryption kryptering;

Overføring (Encryption kryptering) {
this.kryptering = kryptering;
}

void Overfør() {
byte[] ukryptert = lesFraDisken();
byte[] kryptert = kryptering.encrypt(ukryptert);
sendTilMottaker(kryptert);
}
}

En kan ha flere implementasjoner av Encryption-interfacet, f.eks. 3DES og AES, avhengig av hva mottakeren er i stand til å ta imot.

Dependency Injection

interface Repository {
boolean save(byte[] bytes);
}

class Lagring {
private Repository repository;

Lagring (Repository repository) {
this.repository = repository;
}

void Lagre() {
byte[] data = lesFraDisken();
boolean status = repository.save(data);
loggStatus(status);
}
}

For å enhetsteste dette kan en bruke et mock-objekt for Repository-interfacet for å slippe å forholde seg til fysisk lagring ved testing.

For å holde eksempelet så enkelt som mulig, har jeg ikke gjort det mer avansert enn dette. Med fordel kunne klasser for lesing fra disken og logging av status også vært håndtert av dependency injection.

Konklusjon

Som en ser av eksemplene er koden helt lik (fordi jeg brukte kopier-og-lim), men det man ønsker å oppnå er forskjellig. I tilfellet med Strategy ønsker man å støtte flere krypteringsalgoritmer, mens i tilfellet med Dependency Injection ønsker man å enhetsteste koden uten å konfigurere en database (webtjeneste, filområde på disken eller lignende).