2 분 소요

Factory Method

출처 : https://refactoring.guru 그림 출처 : https://refactoring.guru

목적

  • super class에서 객체를 생성하기 위한 인터페이스 제공
  • sub class가 생성될 객체의 유형을 변경할 수 있도록 허용
  • 객체 생성을 도맡아 처리하는 공장 클래스와 이를 상속하는 서브 공장 클래스를 통해 여러 제품 생성을 각각 책임지는 것
  • 객체 생성이 필요한 과정을 템플릿화해 객체 생성 전/후처리 가능
  • 객체간 결합도 낮아짐

Context

Ex

물류 관리 애플리케이션을 만드는 경우, 초기 버전은 트럭을 이용한 운송 수단만 처리할 수 있으므로 Truck 클래스 내에 있음.

추후 추가 요구사항에 따라 별도의 해상 물류가 추가되는 경우, 대부분의 코드가 Truck 클래스에 결합되어 있어 선박을 추가하려면 전체 코드베이스 변경이 불가피

Structure

출처 : https://refactoring.guru/ 그림 출처 : https://refactoring.guru

  • Creator
    • 최상위 팩토리 클래스
  • ConcreteCreator
    • 각 서브 팩토리 클래스. 각 제품 객체를 반환하도록 생성 메서드를 정의
  • Product
    • 제품 추상화
  • ConcreteProduct
    • 각 제품

구현

Before

초기 Ship 생성하는 공장

@Getter
@Setter
public class Ship {
    private String name;
    private String color;
    private String logo;
}
public class ShipFactory {

    public static Ship orderShip(String name, String email) {
        // validate
        if (name == null || name.isBlank()) {
            throw new IllegalArgumentException("please ship name");
        }

        if (email == null || email.isBlank()) {
            throw new IllegalArgumentException("please email");
        }

        prepareFor(name);

        Ship ship = new Ship();
        ship.setName(name);

        // Customizing for specific name
        if (name.equalsIgnoreCase("whiteship")) {
            ship.setLogo("\uD83D\uDEE5️");
        } else if (name.equalsIgnoreCase("blackship")) {
            ship.setLogo("⚓");
        }

        // coloring
        if (name.equalsIgnoreCase("whiteship")) {
            ship.setColor("white");
        } else if (name.equalsIgnoreCase("blackship")) {
            ship.setColor("black");
        }

        // notify
        sendEmailTo(email, ship);

        return ship;
    }

    private static void sendEmailTo(String email, Ship ship) {
        System.out.println(ship.getName() + " was done.");
    }

    private static void prepareFor(String name) {
        System.out.println(name + " prepare!");
    }


}

public static void main(String[] args) {
        Ship whiteship = ShipFactory.orderShip("Whiteship", "e@mail.com");
        System.out.println(whiteship);

        Ship blackship = ShipFactory.orderShip("Blackship", "e@mail.com");
        System.out.println(blackship);
    }

ShipFactory 클래스의 orderShip() 메서드가 너무 많은 역할을 수행. 또한, 추가로 별도의 기능이 추가되면 전체적인 코드베이스 수정이 필요

After

각 제품에 대한 추상화. 여기서는 위 Product 클래스 사용

public interface ShipFactory {

    default Ship orderShip(String name, String email) {
        validate(name, email);
        prepareFor(name);
        Ship ship = createShip();
        sendEmailTo(email, ship);
        return ship;
    }

    Ship createShip();

    void sendEmailTo(String email, Ship ship);

    // jdk 9
    // 이전 버전이면 추상 클래스 사용해야 함
    private void validate(String name, String email) {
        if (name == null || name.isBlank()) {
            throw new IllegalArgumentException("배 이름을 지어주세요.");
        }
        if (email == null || email.isBlank()) {
            throw new IllegalArgumentException("연락처를 남겨주세요.");
        }
    }

    private void prepareFor(String name) {
        System.out.println(name + " prepare!");
    }

    private void sendEmailTo(String email, Ship ship) {
        System.out.println(ship.getName() + " was done.");
    }
}

객체 인스턴스 생성에 필요한 과정을 orderShip()메서드에 설정. 각 인스턴스 생성에 필요한 것은 서브 클래스가 구현하도록 함. (createShip() 메서드)

아래는 각 인스턴스를 생성하는 서브 팩토리 클래스

public class BlackshipFactory implements ShipFactory {
    @Override
    public Ship createShip() {
        return new Blackship();
    }
}
public class WhiteshipFactory implements ShipFactory {
    @Override
    public Ship createShip() {
        return new Whiteship();
    }
}

각 Product는 아래와 같음

public class Blackship extends Ship {

    public Blackship() {
        setName("blackship");
        setColor("black");
        setLogo("⚓");
    }
}
public class Whiteship extends Ship {
    public Whiteship() {
        setName("whiteship");
        setLogo("\uD83D\uDEE5️");
        setColor("white");
    }
}

특징

  • 결합도 낮추기
  • 객체 유형과 종속성을 캡슐화해 정보 은닉
  • SRP, OCP 준수
  • 서브 클래스가 많아 질 수 있음
  • 복잡도가 높아질 수 있음

실 사용

  • Spring의 BeanFactory
  • Java의 Calendar.getInstance()
  • etc

댓글남기기