PostCover

디자인 패턴을 쉽게 풀어보자 - 팩토리 메서드 패턴편

GoF의 23가지 디자인 패턴 중 하나인 '팩토리 메서드 패턴(Factory Method Pattern)'을 쉽게 풀어보기

팩토리 메서드(Factory Method) 패턴

Factory Method 패턴은 객체를 생성하는 로직을 서브 클래스에서 정의하도록 하여 객체 생성을 캡슐화하는 패턴입니다. 이 패턴은 객체 생성 로직을 클라이언트 코드와 분리하여 유연한 확장이 가능하도록 설계합니다. 이때, 객체의 타입 결정을 클라이언트가 아닌 팩토리 메서드에게 위임하여 코드의 결합도를 줄이고 유지 보수성을 높입니다.

문제점: 객체 생성 코드가 중복되고 코드 결합도가 높을 때

동물을 통해서 한번 알아보도록 합시다. 아래 코드에서는 Animal 객체를 생성할 때, 객체의 타입에 따라 조건문이 필요합니다. 이러한 방식은 새로운 타입의 객체가 추가될 때마다 코드가 수정되어야 하는 문제를 유발합니다.

class Dog {
    public void sound() {
        System.out.println("멍멍!");
    }
}

class Cat {
    public void sound() {
        System.out.println("냥냥!");
    }
}

public class Main {
    public static void main(String[] args) {
        String animalType = "dog";

        if (animalType.equals("dog")) {
            Dog dog = new Dog();
            dog.sound();
        } else if (animalType.equals("cat")) {
            Cat cat = new Cat();
            cat.sound();
        }
    }
}

위의 코드는 Main 클래스가 DogCat 객체의 구체적인 타입을 알아야 하므로 결합도가 높고, 새로운 동물이 추가될 때마다 Main 클래스의 코드를 수정해야 합니다. 또한 여러 곳에서 동일한 객체 생성 로직을 반복해야 해서 유지보수가 어렵습니다.

해결 방법: 팩토리 메서드 패턴 적용

팩토리 메서드 패턴을 사용하면 객체 생성 로직을 서브 클래스에 위임하고, 클라이언트는 팩토리 메서드를 통해 객체를 생성합니다.

// 공통 인터페이스
interface Animal {
    void sound();
}

// 구체적인 동물 클래스
class Dog implements Animal {
    @Override
    public void sound() {
        System.out.println("멍멍!");
    }
}

class Cat implements Animal {
    @Override
    public void sound() {
        System.out.println("냥냥!");
    }
}

// 팩토리 클래스
abstract class AnimalFactory {
    public abstract Animal createAnimal();
}

// 구체적인 팩토리 클래스 (Dog 생성 담당)
class DogFactory extends AnimalFactory {

    @Override
    public Animal createAnimal() {
        return new Dog();
    }
}

// 구체적인 팩토리 클래스 (Cat 생성 담당)
class CatFactory extends AnimalFactory {

    @Override
    public Animal createAnimal() {
        return new Cat();
    }
}

이러한 경우 클라이언트에서는 다음 코드로 사용할 수 있습니다.

public class Main {
    public static void main(String[] args) {
        AnimalFactory dogFactory = new DogFactory();
        AnimalFactory catFactory = new CatFactory();

        Animal dog = dogFactory.createAnimal();
        Animal cat = catFactory.createAnimal();

        dog.sound();  // 멍멍!
        cat.sound();  // 냥냥!
    }
}

위에 있는 Animal Factory라는 추상 클래스는 바로 다음번에 나올 예정이니 크게 신경쓰지 않아도 됩니다.

결국 클라이언트에서는 팩토리 메서드를 통해 구체적인 생성 로직을 알 필요가 없이 객체를 생성할 수 있습니다.

팩토리 메서드 패턴의 장단점

이처럼 팩토리 메서트 패턴을 사용하는 경우 클랑이언트와 객체 생성 로직을 분리하여 코드의 결합도를 낮춥니다. 또한 새로운 객체를 추가할 대 기존 코드를 수정하지 않고, 새로운 팩토리 클래스만 추가하면 됩니다. 따라서 확장성이 증가하죠. 그리고 객체 생성 로직을 한 곳에 모아 관리하므로 코드의 중복을 줄이고 유지보수를 쉽게 합니다.

그러나 각 객체에 대한 별도의 팩토리 클래스를 구현해야 하므로 클래스가 많아질 수 있고, 작은 애플리케이션에서는 오히려 코드를 복잡하게 만들 수 있습니다.

결론

팩토리 메서드 패턴은 객체 생성 로직을 서브 클래스에 위임하여 결합도를 낮추고 확장성을 높이는 강력한 패턴입니다. 이 패턴은 다양한 객체 생성이 필요한 경우, 객체의 타입이 변경될 가능성이 있는 경우에 특히 유용합니다. 하지만 작은 애플리케이션에서는 불필요한 복잡성을 초래할 수 있으므로 상황에 맞게 사용하는 것이 중요합니다.