Fabryka jest wzorcem kreacyjnym, którego celem jest ułatwienie tworzenia obiektów tego samego typu bez specyfikowania ich klas. Przykładowo jest przydatne w grach strategicznych, gdzie jeden budynek (fabryka) produkuje kilka typów jednostek z różnymi zmiennymi.

Ogólna struktura tego wzorca wygląda następująco:

W praktyce załóżmy, że mamy aplikację „symulującą” dwie restauracje: jedna serwująca burgera XXL i napój L, a druga burgera L i napój XXL w różnych cenach. Bez zastosowania tego wzorca mogłaby ona wyglądać tak:

public class Main {
    public static void main(String[] args) {

        Dish burger1 = new Burger(20, "XXL");
        Dish soda1 = new Soda(5, "L");

        Dish burger2 = new Burger(15, "L");
        Dish soda2 = new Soda(10, "XXL");
    }
}
public abstract class Dish {
    private int price;
    private String size;

    public Dish(int price, String size) {
        this.price = price;
        this.size = size;
    }
}

public class Burger extends Dish{
    public Burger(int price, String size) {
        super(price, size);
    }
}

public class Soda extends Dish{
    public Soda(int price, String size) {
        super(price, size);
    }
}

W takim przypadku trzeba zawsze pamiętać, która restauracja jakie serwuje obiekty. Po zastosowaniu wzorca fabryka program mógłby prezentować się w taki sposób:

import dishes.*;

public class Main {
    public static void main(String[] args) {

        Restaurant leftRestaurant = new LeftRestaurant();
        Restaurant rightRestaurant = new RightRestaurant();

        Dish leftBurger = leftRestaurant.createBurger();
        Dish leftSoda = leftRestaurant.createSoda();

        Dish rightBurger = rightRestaurant.createBurger();
        Dish rightSoda = rightRestaurant.createSoda();
    }
}
package dishes;

public abstract class Dish {
    private int price;
    private String size;

    public Dish(int price, String size) {
        this.price = price;
        this.size = size;
    }
}

package dishes;

public class Burger extends Dish{
    public Burger(int price, String size) {
        super(price, size);
    }
}

package dishes;

public class Soda extends Dish{
    public Soda(int price, String size) {
        super(price, size);
    }
}

package dishes;

public abstract class Restaurant {
    public abstract Dish createBurger();
    public abstract Dish createSoda();
}

package dishes;

public class LeftRestaurant extends Restaurant{
    @Override
    public Dish createBurger() {
        return new Burger(20, "XXL");
    }

    @Override
    public Dish createSoda() {
        return new Soda(5, "L");
    }
}

package dishes;

public class RightRestaurant extends Restaurant{
    @Override
    public Dish createBurger() {
        return new Burger(15, "L");
    }

    @Override
    public Dish createSoda() {
        return new Soda(10, "XXL");
    }
}

W tym konkretnym przypadku wzorzec fabryka dorobił trochę kodu, ale jednocześnie go uprościł. W bardziej rozbudowanych aplikacjach zmiana ta jest diametralna.

Niestety największą wadą tego wzorca jest to, że za każdym razem jak chcemy dodać nową fabrykę lub produkty to musimy ingerować w większość poprzednich klas.