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.


Problem

Du bygger ett logistiksystem. I den första versionen hanterar appen bara lastbilstransporter, och koden är full av new Truck()-anrop.

När kunden vill lägga till sjöfrakt måste du gå igenom hela kodbasen. Och när flygtransport tillkommer? Samma sak igen.

Lösning

Factory Method ersätter direkta konstruktoranrop med ett anrop till en fabriksmetod. Underklassen bestämmer vilken konkret klass som skapas. Klientkoden ser bara gränssnittet.

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 ramverk möjlighet att utöka dess komponenter.
  • För att minska beroenden av konkreta klasser.

Struktur

RollAnsvar
CreatorDeklarerar fabriksmetoden; kan ha en default-implementation.
Concrete CreatorÖverskuggar fabriksmetoden och returnerar en specifik produkt.
Product InterfaceDefinierar gränssnittet för alla produkter.
Concrete ProductEn specifik implementation av produktgränssnittet.

Exempel — Logistiksystem (PHP)

Scenariot: Ett logistiksystem som skapar transportobjekt via en fabriksmetod.

<?php

// ── Product Interface ─────────────────────────────────────

interface Transport
{
    public function deliver(string $origin, string $destination): void;
}

// ── Concrete Products ─────────────────────────────────────

class Truck implements Transport
{
    public function deliver(string $origin, string $destination): void
    {
        echo "🚛 Lastbil levererar: $origin$destination (via landsväg)\n";
    }
}

class Ship implements Transport
{
    public function deliver(string $origin, string $destination): void
    {
        echo "🚢 Fartyg levererar: $origin$destination (via havsrutt)\n";
    }
}

class Airplane implements Transport
{
    public function deliver(string $origin, string $destination): void
    {
        echo "✈️  Flygplan levererar: $origin$destination (direktflyg)\n";
    }
}

// ── Creator (abstrakt) ────────────────────────────────────

abstract class Logistics
{
    // Fabriksmetoden — underklassen bestämmer vad som skapas
    abstract public function createTransport(): Transport;

    // Affärslogiken använder produkten via gränssnittet
    public function planDelivery(string $origin, string $destination): void
    {
        $transport = $this->createTransport();
        echo "Planerar leverans...\n";
        $transport->deliver($origin, $destination);
    }
}

// ── Concrete Creators ─────────────────────────────────────

class RoadLogistics extends Logistics
{
    public function createTransport(): Transport { return new Truck(); }
}

class SeaLogistics extends Logistics
{
    public function createTransport(): Transport { return new Ship(); }
}

class AirLogistics extends Logistics
{
    public function createTransport(): Transport { return new Airplane(); }
}

// ── Klientkod ─────────────────────────────────────────────

function getLogistics(string $mode): Logistics
{
    return match ($mode) {
        'road'  => new RoadLogistics(),
        'sea'   => new SeaLogistics(),
        default => new AirLogistics(),
    };
}

// Klienten väljer Creator — inte konkret produkt
$logistics = getLogistics('sea');

// 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 klientkoden.
  • PHP 8:s match-uttryck gör Creator-logiken elegant och läsbar.

Nackdelar

  • Koden kan bli mer komplex med många underklasser.

Av Victor Hernandez från Bytebase.se