Factory Method (Fabriksmetod)
Introduktion
Factory Method är ett skapande mönster som definierar ett gränssnitt för att skapa objekt, men låter underklasser bestämma vilken klass som ska instansieras. Factory Method skjuter upp instansieringen till underklasserna.
Problem
Du bygger ett logistiksystem. I den första versionen hanterar appen bara lastbilstransporter, och koden är full av new Truck()-anrop överallt.
När kunden sen vill lägga till sjöfrakt inser du att du måste gå igenom hela kodbasen och ändra varje new Truck() till ett villkorsuttryck. Och när flygtransport tillkommer? Du måste göra samma sak igen.
Lösning
Factory Method-mönstret ersätter direkta konstruktoranrop med ett anrop till en fabriksmetod. Underklassen bestämmer vilken konkret klass som skapas. Klientkoden ser bara gränssnittet — den vet inte och behöver inte veta vilken konkret klass den faktiskt arbetar med.
När ska Factory Method användas?
- När du inte vet i förväg exakt vilka klasser din kod ska arbeta med.
- När du vill ge användare av ditt bibliotek eller ramverk möjlighet att utöka dess interna komponenter.
- När du vill spara systemresurser genom att återanvända befintliga objekt istället för att alltid skapa nya.
Struktur
| Roll | Ansvar |
|---|---|
| Creator | Deklarerar fabriksmetoden; kan ha en default-implementation. |
| Concrete Creator | Överskuggar fabriksmetoden och returnerar en specifik produkt. |
| Product Interface | Definierar gränssnittet för alla produkter. |
| Concrete Product | En specifik implementation av produktgränssnittet. |
Exempel — Logistiksystem (Java)
Scenariot: Ett logistiksystem som skapar transportobjekt via en fabriksmetod. Klientkoden arbetar mot Transport-gränssnittet och bryr sig inte om det är en lastbil eller ett fartyg.
// ── Product Interface ─────────────────────────────────────
public interface Transport {
void deliver(String origin, String destination);
}
// ── Concrete Products ─────────────────────────────────────
public class Truck implements Transport {
@Override
public void deliver(String origin, String destination) {
System.out.println("🚛 Lastbil levererar: " + origin + " → " + destination + " (via landsväg)");
}
}
public class Ship implements Transport {
@Override
public void deliver(String origin, String destination) {
System.out.println("🚢 Fartyg levererar: " + origin + " → " + destination + " (via havsrutt)");
}
}
public class Airplane implements Transport {
@Override
public void deliver(String origin, String destination) {
System.out.println("✈️ Flygplan levererar: " + origin + " → " + destination + " (direktflyg)");
}
}
// ── Creator (abstrakt) ────────────────────────────────────
public abstract class Logistics {
// Fabriksmetoden — underklassen bestämmer vad som skapas
public abstract Transport createTransport();
// Affärslogiken använder produkten via gränssnittet
public void planDelivery(String origin, String destination) {
Transport transport = createTransport();
System.out.println("Planerar leverans...");
transport.deliver(origin, destination);
}
}
// ── Concrete Creators ─────────────────────────────────────
public class RoadLogistics extends Logistics {
@Override
public Transport createTransport() {
return new Truck();
}
}
public class SeaLogistics extends Logistics {
@Override
public Transport createTransport() {
return new Ship();
}
}
public class AirLogistics extends Logistics {
@Override
public Transport createTransport() {
return new Airplane();
}
}
// ── Klientkod ─────────────────────────────────────────────
public class Application {
public static void main(String[] args) {
// Klienten väljer Creator — inte konkret produkt
Logistics logistics;
String mode = "sea"; // Kan komma från config eller användarval
if (mode.equals("road")) {
logistics = new RoadLogistics();
} else if (mode.equals("sea")) {
logistics = new SeaLogistics();
} else {
logistics = new AirLogistics();
}
// Klientkoden behöver inte ändras när nya transporttyper läggs till
logistics.planDelivery("Stockholm", "Hamburg");
}
}
Output:
Planerar leverans...
🚢 Fartyg levererar: Stockholm → Hamburg (via havsrutt)
Fördelar
- Single Responsibility Principle — Produktskapandet är på ett ställe.
- Open/Closed Principle — Lägg till nya produkttyper utan att ändra befintlig klientkod.
- Löst kopplad kod — klienten arbetar mot gränssnittet, inte konkreta klasser.
Nackdelar
- Koden kan bli mer komplex — du introducerar många nya underklasser.
Av Victor Hernandez från Bytebase.se