본문 바로가기
이펙티브 자바

자원을 직접 명시하지 말고 의존 객체 주입을 사용하라 - 팩토리 메서드 패턴

by 이상한나라의개발자 2024. 4. 17.

팩토리 메서드 패턴은 디자인 패턴중의 하나로, 구체적으로 어떤 인스턴스를 만들지는 서브 클래스가 정한다 입니다. 이 방식은 디자인을 좀 더 사용자 정의에 가깝게 하고 약간의 복잡성을 추가하면서도, 객체 지향의 캡슐화 및 우연성 원칙을 지원합니다.

 

팩토리 메서드 패턴의 핵심 구성 요소

  • Product : 생성될 객체들의 공통 인터페이스를 정의합니다.
  • ConcreteProduct : Product 인터페이스를 구현하는 실제 클래스 입니다.
  • Creator : 팩토리 메서드를 선언하고 Product 객체를 반환하는 메서드를 정의하는 클래스 입니다.
  • ConcreteCreator : Creator 클래스를 상속 받고 팩토리 메서드를 구현하여 구체적인 Product를 생성하는 클래스 입니다.

 

팩토리 메서드 패턴의 장점

  • 유연성 : 생성할 객체의 클래스를 바꾸기 쉬워집니다.
  • 확장성 : 새로운 객체 타입을 추가하기 쉬워 크도 변경이 적습니다.
  • 결합도 감소 : 시스템의 다른 부분들과 객체 생성 코드를 분리하여 관리할 수 있습니다.

 

브라우저 탭 생성 예제

웹 브라우저에서 다양한 탭(뉴스, 소셜 등)을 생성하는 간단한 예제로 팩토리 메서드 패턴을 적용해 보겠습니다.

Product ( 공통 인터페이스 정의 )

// Product 인터페이스
public interface Tab {
    void display();
}

 

ConcreteProduct 클래스 ( Product 인터페이스 구현체 ) 

public class NewsTab implements Tab {

    @Override
    public void display() {
        System.out.println("display news tab");
    }
}


public class SocialTab implements Tab{

    @Override
    public void display() {
        System.out.println("display social tab");
    }
}

 

Creator 클래스 ( Product 객체를 반환하는 메서드를 정의하는 클래스 입니다. 인터페이스로 만들어도 됨 )

// Creator 클래스
public abstract class TabFactory {
    abstract Tab createTab(String type);
}

 

ConcreteCreator 클래스 ( Creator 클래스를 상속 받고 팩토리 메서드를 구현하여 구체적인 Product를 생성 )

// ConcreteCreator 클래스
public class ConcreteTabFactory extends TabFactory {

    @Override
    Tab createTab(String type) {
        if ( type.equals("NEWS")) {
            return new NewsTab();
        }
        else if ( type.equals("SOCIAL")) {
            return new SocialTab();
        }
        return null;
    }
}

 

Client 호출

public class BrowserDemo {
    public static void main(String[] args) {
        TabFactory factory = new ConcreteTabFactory();
        Tab tab = factory.createTab("NEWS");
        tab.display();
    }
}

 

위 예시에서 ConcreteTabFactory 는 TabFactory를 상속 받아 createTab 메서드를 통해 실제 객체를 반환합니다. 이 메서드는 타입데 따라 적절한 객체를 생성하고 반환합니다. 클라이언트는 TabFactory를 사용하여 필요한 탭을 생성하며, 실제 생성되는 탭의 타입을 몰라도 됩니다. 하지만 위와 같이 사용하게 되면 ConcreteCreator에서 type을 받아서 객체를 생성하기 때문에 소스 변경이 일어나야 합니다.

두번째 방법으로 어떤 팩토리가 추가 되더라도  변경없이 OCP(개방폐쇄의원칙) 원칙에 따라 작성하겠습니다.

 

Product

public interface Tab {
    void display();
}

 

ConcreteProduct

public class NewsTabFactory implements Tab {
    @Override
    public void display() {
        System.out.println("display tab");
    }
}

public class SocialTabFactory implements Tab {
    @Override
    public void display() {
        System.out.println("social tab");
    }
}

 

Creator

public interface TabFactory {

    Tab getTab();
}

 

ConcreteCreator

public class BrowserClient {

    private final Tab tab;

    public BrowserClient(Supplier<? extends TabFactory> tabFactory) {
        this.tab = tabFactory.get().getTab();
    }

    public void show() {
        tab.display();
    }
}

 

Client

public class BrowserMain {

    public static void main(String[] args) {

        BrowserClient newsTabFactory = new BrowserClient(() -> NewsTabFactory::new);
        newsTabFactory.show();
        BrowserClient socialTabFactory = new BrowserClient(() -> SocialTabFactory::new);
        socialTabFactory.show();
    }
}