1. Overview

Factory 패턴은 객체 생성 역할을 별도의 클래스 (Factory) 에게 위임하는 것이 가장 궁극적인 목표입니다.

디자인 패턴 중 Facotry 와 관련된 패턴은 크게 두 가지가 있습니다.

팩토리 메서드 패턴과 추상 팩토리 패턴인데요.

이 두 가지 패턴의 베이스가 되는 가장 단순한 형태의 Factory 패턴이 존재합니다.

엄밀히 따지면 디자인 패턴이 아니라 객체 지향 프로그래밍에서의 자주 쓰이는 관용구 느낌이라 별도의 이름은 없지만 Simple Factory 라는 이름으로 많이 불립니다.

이 글에서는 Simple Factory 에 대해 알아보고 이후 나머지 두 패턴에 대해 알아볼 예정입니다.


2. Simple Factory

Simple Factory 는 굉장히 단순합니다.

객체는 여러 곳에서 생성될 수 있는데, 호출하는 쪽이 객체의 생성자에 직접 의존하고 있으면 나중에 변경되었을 때 수정되어야 하는 코드가 많이 발생합니다.

그래서 생성자 호출 (new) 을 별도의 클래스 (Factory) 에서 담당하고 클라이언트 코드에서는 팩토리를 통해 객체를 생성합니다.


3. Example

public interface Pet {
}

public class Cat implements Pet {
}

public class Dog implements Pet {
}

애완 동물을 한번 예시로 들어봅니다.

공통 인터페이스인 Pet 을 정의하고 이를 구현하는 Cat, Dog 클래스를 만들었습니다.

이제 클라이언트 코드에서 Cat, Dog 을 사용하기 위해 생성할 수 있습니다.


3.1. Before

Pet cat = new Cat();
Pet dog = new Dog();

일반적인 사용법은 new 를 사용해 구현 클래스를 생성한 후 호출하는 겁니다.

하지만 이렇게 하면 Client 와 클래스들 사이에 다음과 같은 의존관계가 생깁니다.


이렇게 구현 클래스를 직접 의존하고 있으면 해당 클래스의 생성자나 전처리 코드가 변경되었을 때 사용하는 모든 Client 코드를 변경해야 합니다.

그래서 객체의 생성만을 담당하는 별도의 Factory 클래스를 만들어 생성 역할을 넘겨봅니다.


3.2. After

public interface Pet {
    enum Type {
        CAT, DOG
    }
}

우선 Pet 인터페이스에 enum 으로 타입을 선언합니다.


public class PetFactory {

    public Pet createPet(Pet.Type petType) {
        switch (petType) {
            case CAT:
                return new Cat();
            case DOG:
                return new Dog();
            default:
                throw new IllegalArgumentException("Pet 타입이 아닙니다");
        }
    }
}

PetFactory 를 만든 후 Pet.Type 에 따라 다른 객체를 생성해서 반환합니다.


PetFactory petFactory = new PetFactory();
Pet cat = petFactory.createPet(Pet.Type.CAT);
Pet dog = petFactory.createPet(Pet.Type.DOG);

PetFactory 를 선언한 후 생성 메서드만 호출하면 실제 구현 클래스인 Cat, Dog 에 의존하지 않은 코드를 작성할 수 있습니다.


의존 관계를 그림으로 표현하면 위와 같이 변경됩니다.

Client 에서 구현 클래스를 직접 의존하지 않기 때문에 나중에 클래스 이름이 변경되거나, 생성자가 변경되는 경우에도 PetFactory 내부만 수정하면 됩니다.


4. Simple Factory 의 한계

Simple Factory 는 앞서 말했듯이 디자인 패턴으로 분류되지는 않습니다.

이 패턴은 객체의 생성 역할을 담당하며 확장이 용이하다는 장점이 있지만 변경에 닫혀 있어야 한다는 OCP 원칙에 위배됩니다.

만약 새로운 애완 동물 구현 클래스로 Bird 가 추가 되었다고 가정합니다.

그럼 PetFactory 내부에 존재하는 switch 문에 해당 클래스를 추가해줘야 합니다.

객체지향 원칙은 확장을 할 때 기존 코드에 영향을 주지 않는 것을 지향합니다.

팩토리 메서드나 추상 팩토리 패턴을 활용한다면 기존 클래스에 영향을 주지 않고 확장이 가능합니다.

+ Recent posts