Abstract Factory (Abstrakt Fabrik)
Introduktion
Abstract Factory är ett skapande mönster som låter dig skapa familjer av relaterade objekt utan att specificera deras konkreta klasser.
Problem
Du bygger ett UI-ramverk som ska stödja både ljust och mörkt tema. Varje tema har egna varianter av Button, Checkbox och TextField. Om du skapar komponenterna direkt med new LightButton() och new DarkCheckbox() riskerar du att blanda komponenter från olika teman — ett ljust tema-fönster med en mörk knapp.
Lösning
Abstract Factory definierar ett gränssnitt med en fabriksmetod per komponenttyp. Varje konkret fabrik implementerar hela familjen — LightThemeFactory skapar bara ljusa komponenter, DarkThemeFactory bara mörka. Klientkoden väljer fabrik en gång och skapar sedan alla komponenter via den.
När ska Abstract Factory användas?
- När din kod ska fungera med flera familjer av relaterade produkter.
- När du vill garantera att produkter från samma familj används tillsammans.
- När du vill dölja implementationsdetaljerna för produktfamiljer.
Struktur
| Roll | Ansvar |
|---|---|
| Abstract Factory | Deklarerar fabriksmetoder för varje produkttyp. |
| Concrete Factory | Implementerar fabriksmetoderna för en specifik produktfamilj. |
| Abstract Product | Gränssnitt för en produkttyp. |
| Concrete Product | En specifik implementation inom en familj. |
| Klient | Arbetar mot abstrakta fabriker och produkter. |
Exempel — UI-teman (Java)
Scenariot: En applikation som kan rendera antingen ett ljust eller mörkt tema. Alla komponenter inom ett tema skapas av samma fabrik.
// ── Abstract Products ─────────────────────────────────────
public interface Button {
void render();
void onClick();
}
public interface Checkbox {
void render();
void toggle();
}
public interface TextField {
void render();
void onInput(String text);
}
// ── Light Theme — Concrete Products ──────────────────────
public class LightButton implements Button {
@Override public void render() { System.out.println("[ Ljus knapp ]"); }
@Override public void onClick() { System.out.println("Ljus knapp klickad"); }
}
public class LightCheckbox implements Checkbox {
@Override public void render() { System.out.println("☐ Ljus checkbox"); }
@Override public void toggle() { System.out.println("Ljus checkbox togglas"); }
}
public class LightTextField implements TextField {
@Override public void render() { System.out.println("[____________] Ljust textfält"); }
@Override public void onInput(String text) { System.out.println("Ljust textfält input: " + text); }
}
// ── Dark Theme — Concrete Products ───────────────────────
public class DarkButton implements Button {
@Override public void render() { System.out.println("█ Mörk knapp █"); }
@Override public void onClick() { System.out.println("Mörk knapp klickad"); }
}
public class DarkCheckbox implements Checkbox {
@Override public void render() { System.out.println("▣ Mörk checkbox"); }
@Override public void toggle() { System.out.println("Mörk checkbox togglas"); }
}
public class DarkTextField implements TextField {
@Override public void render() { System.out.println("[████████████] Mörkt textfält"); }
@Override public void onInput(String text) { System.out.println("Mörkt textfält input: " + text); }
}
// ── Abstract Factory ──────────────────────────────────────
public interface UiFactory {
Button createButton();
Checkbox createCheckbox();
TextField createTextField();
}
// ── Concrete Factories ────────────────────────────────────
public class LightThemeFactory implements UiFactory {
@Override public Button createButton() { return new LightButton(); }
@Override public Checkbox createCheckbox() { return new LightCheckbox(); }
@Override public TextField createTextField() { return new LightTextField(); }
}
public class DarkThemeFactory implements UiFactory {
@Override public Button createButton() { return new DarkButton(); }
@Override public Checkbox createCheckbox() { return new DarkCheckbox(); }
@Override public TextField createTextField() { return new DarkTextField(); }
}
// ── Klient ────────────────────────────────────────────────
public class LoginForm {
private final Button submitButton;
private final Checkbox rememberMe;
private final TextField emailField;
public LoginForm(UiFactory factory) {
this.submitButton = factory.createButton();
this.rememberMe = factory.createCheckbox();
this.emailField = factory.createTextField();
}
public void render() {
System.out.println("=== Inloggningsformulär ===");
emailField.render();
rememberMe.render();
submitButton.render();
}
public void submit() {
emailField.onInput("user@bytebase.se");
submitButton.onClick();
}
}
// ── Klientkod ─────────────────────────────────────────────
public class Application {
public static void main(String[] args) {
// Välj tema — kan komma från konfiguration eller användarinställning
UiFactory factory = isDarkMode() ? new DarkThemeFactory() : new LightThemeFactory();
LoginForm form = new LoginForm(factory);
form.render();
System.out.println();
form.submit();
}
private static boolean isDarkMode() {
return true; // Simulerar systeminställning
}
}
Output:
=== Inloggningsformulär ===
[████████████] Mörkt textfält
▣ Mörk checkbox
█ Mörk knapp █
Mörkt textfält input: user@bytebase.se
Mörk knapp klickad
Fördelar
- Garanterar att produkter från samma familj används tillsammans.
- Single Responsibility Principle — Produktskapandet är samlat i fabrikerna.
- Open/Closed Principle — Lägg till en ny produktfamilj utan att ändra klientkoden.
Nackdelar
- Att lägga till en ny produkttyp (t.ex.
Tooltip) kräver ändringar i alla fabriker och alla fabriksgränssnitt.
Av Victor Hernandez från Bytebase.se