티스토리 뷰

728x90
반응형

팩토리 메서드 패턴이란?


  • 객체를 생성하기 위한 인터페이스를 정의하고 어떤 클래스의 인스턴스를 생성할지에 대한 처리는 서브 클래스가 결정하는 디자인패턴입니다.
  • new 키워드를 호출해 객체를 생성하는 역할을 서브 클래스에 위임하는 것입니다. 그렇기 때문에 팩토리 메서드 패턴은 객체를 만들어 내는 공장 패턴이라 할 수 있습니다.
  • 클래스의 인스턴스를 만드는 일은 서브 클래스에게 맡깁니다.

 

클래스 다이어 그램


💡 다양한 구현체(Product)가 있고, 그중에서 특정한 구현체를 만들 수 팩토리(Creator)를 제공할 수 있습니다.

💡 Product

  • 인스턴스 생성시 큰 틀을 결정하는 추상 클래스 및 인터페이스입니다. 구체적인 내용(구현부)은 ConcreteProduct에서 결정합니다.

💡 ConcreteProduct

  • 인스턴스 생성시 구체적인 인스턴스 생성을 결정합니다.

💡 Creator

  • Product 타입의 객체를 반환하는 팩토리 메서드를 선언하는 클래스입니다.
  • 팩토리 메서드를 기본적으로 구현하며, ConcreteProduct 객체를 반환합니다.
  • Product 객체의 생성을 위해 팩토리 메서드를 호출합니다.

💡 ConcreteCreator

  • 구체적인 인스턴스를 만드는 클래스입니다.

 

팩토리 메서드 패턴을 사용하지 않은 예제


@Getter
@Setter
@ToString
public class Ship {

    private String name;
    private String color;
}

// Ship을 생성하는 Factory
public class ShipFactory {

    public static Ship createShip(String name, String buyer) {

        if (name == null || name.isBlank()) {
            throw new IllegalArgumentException("배 이름을 지어주세요.");
        }
        if (buyer == null || buyer.isBlank()) {
            throw new IllegalArgumentException("구매자를 남겨주세요.");
        }

        prepareFor(name);

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

        if (name.equalsIgnoreCase("whiteShip")) {
            ship.setColor("white");
        } else if (name.equalsIgnoreCase("blackShip")) {
            ship.setColor("black");
        }

        sendAlarmTo(buyer, ship);
        return ship;
    }

    private static void prepareFor(String name) {
        System.out.println(name + " 만들 준비 중");
    }

    private static void sendAlarmTo(String buyer, Ship ship) {
        System.out.println(buyer + "님 " + ship.getName() + " 다 만들었습니다.");
    }
}

public class Client {
    public static void main(String[] args) {
        Ship WhiteShip = ShipFactory.createShip("WhiteShip", "홍길동");
        System.out.println(WhiteShip);
        // WhiteShip 만들 준비 중
        // 홍길동님 WhiteShip 다 만들었습니다.
        // Ship(name=WhiteShip, color=white)

        Ship BlackShip = ShipFactory.createShip("BlackShip", "이순신");
        System.out.println(BlackShip);
        // BlackShip 만들 준비 중
        // 이순신님 BlackShip 다 만들었습니다.
        // Ship(name=BlackShip, color=black)
    }
}

 

팩토리 메서드 패턴을 사용한 예제 1)


// Creator의 역할을 하는 인터페이스
public interface ShipFactory {

    Ship creatingShip();

    default Ship orderShip(String name, String buyer) {
        validate(name, buyer);
        prepareFor(name);
        Ship ship = creatingShip();
        sendAlarmTo(buyer, ship);
        return ship;
    }

    private void validate(String name, String buyer) {
        if (name == null || name.isBlank()) {
            throw new IllegalArgumentException("배 이름을 지어주세요.");
        }
        if (buyer == null || buyer.isBlank()) {
            throw new IllegalArgumentException("구매자를 남겨주세요.");
        }
    }

    private void prepareFor(String name) {
        System.out.println(name + " 만들 준비 중");
    }

    private void sendAlarmTo(String buyer, Ship ship) {
        System.out.println(buyer + "님 " + ship.getName() + " 다 만들었습니다.");
    }
}

// ConcreteCreator의 역할을 하는 인터페이스
public class WhiteShipFactory implements ShipFactory {

    @Override
    public Ship creatingShip() {
        return new WhiteShip();
    }
}

public class BlackShipFactory implements ShipFactory {

    @Override
    public Ship creatingShip() {
        return new BlackShip();
    }
}

// Product의 역할을 하는 클래스 및 인터페이스
@Getter
@Setter
@ToString
public class Ship {

    private String name;
    private String color;
}

// ConcreteProduct의 역할을 하는 클래스
public class WhiteShip extends Ship {

    public WhiteShip() {
        setName("WhiteShip");
        setColor("White");
    }
}

public class BlackShip extends Ship {

    public BlackShip() {
        setName("BlackShip");
        setColor("Black");
    }
}

public class Client {

    public static void main(String[] args) {
        Ship whiteShip = new WhiteShipFactory().orderShip("WhiteShip", "홍길동");
        System.out.println(whiteShip);
        // WhiteShip 만들 준비 중
        // 홍길동님 WhiteShip 다 만들었습니다.
        // Ship(name=WhiteShip, color=White)

        Ship blackShip = new BlackShipFactory().orderShip("BlackShip", "이순신");
        System.out.println(blackShip);
        // BlackShip 만들 준비 중
        // 이순신님 BlackShip 다 만들었습니다.
        // Ship(name=BlackShip, color=Black)
    }
}

 

팩토리 메서드 패턴을 사용한 예제 2)

💡해당 예제는 필자의 생각대로 만들어본 예제입니다. 

// Creator의 역할을 하는 인터페이스
public interface ShipFactory {

    Ship creatingShip(Type type, String name, String color, String buyer);
}

// ConcreteCreator의 역할을 하는 클래스
public class DefaultFactory implements ShipFactory {

    @Override
    public Ship creatingShip(Type type, String name, String color, String buyer) {
        validate(name, buyer);
        prepareFor(name);

        Ship ship = null;
        switch (type) {
            case BLACK_SHIP:
                ship = new BlackShip(name, color);
                break;
            case WHITE_SHIP:
                ship = new WhiteShip(name, color);
                break;
            default:
                throw new RuntimeException(type.toString() + " is not existed");
        }
        sendAlarmTo(buyer, ship);
        return ship;
    }

    private void validate(String name, String buyer) {
        if (name == null || name.isBlank()) {
            throw new IllegalArgumentException("배 이름을 지어주세요.");
        }
        if (buyer == null || buyer.isBlank()) {
            throw new IllegalArgumentException("구매자를 남겨주세요.");
        }
    }

    private void prepareFor(String name) {
        System.out.println(name + " 만들 준비 중");
    }

    private void sendAlarmTo(String buyer, Ship ship) {
        System.out.println(buyer + "님 " + ship.getName() + " 다 만들었습니다.");
    }
}

// Product의 역할을 하는 클래스 및 인터페이스
@Getter
@Setter
@ToString
public abstract class Ship {

    private String name;
    private String color;
}

// ConcreteProduct의 역할을 하는 클래스
public class WhiteShip extends Ship {

    public WhiteShip(String name, String color) {
        this.setName(name);
        this.setColor(color);
    }
}

public class BlackShip extends Ship {

    public BlackShip(String name, String color) {
        this.setName(name);
        this.setColor(color);
    }
}

// 타입
public enum Type {

    WHITE_SHIP,
    BLACK_SHIP
}

public class Client {
    public static void main(String[] args) {
        Ship whiteShip = new DefaultFactory().creatingShip(Type.WHITE_SHIP, "멋쟁이배", "white", "홍길동");
        System.out.println(whiteShip);
        // 멋쟁이배 만들 준비 중
        // 홍길동님 멋쟁이배 다 만들었습니다.
        // Ship(name=멋쟁이배, color=white)

        Ship blackShip = new DefaultFactory().creatingShip(Type.BLACK_SHIP, "빠른배", "black", "이순신");
        System.out.println(blackShip);
        // 빠른배 만들 준비 중
        // 이순신님 빠른배 다 만들었습니다.
        // Ship(name=빠른배, color=black)
    }
}

 

 

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


장점
  • 팩토리 메서드 패턴은 직접 사용하는 객체를 생성하지 않고 팩토리 메서드 클래스를 통하여 객체를 대신 생성시키고 그 객체를 받환 받아 사용하기 때문에 결합도를 낮출 수 있습니다.
단점
  • 객체가 늘어날때마다 하위 클래스를 재정의 해줘야하므로 너무 많은 클래스를 만들게 됩니다.

 

 

728x90
반응형