Java/Design Pattern

[디자인 패턴] 생성패턴 - 팩토리 메소드 패턴이란 (Factory Method patterns)

민돌v 2023. 2. 8. 21:24
728x90
(인프런) 코딩으로 학습하는 GoF의 디자인 패턴 - 백기선, 강의를 보고 정리한 글입니다.
코드는 GitHub 에 있습니다

 

#1. 객체 생성 관련 패턴

#2. 구조 관련 패턴

 

#3. 행동 관련 패턴

 

 

 


✔️ 팩토리 메소드 패턴

  • 구체적으로 어떤 인스턴스를 만들지를 서브 클래스가 정하는 패턴을 말합니다.
  • 팩토리 메소드 패턴은, 기능혹은 역할에 따른 다양한 구현체가(Product) 가 존재하고, 그 중에서 특정한 구현체를 만들 수 있는 다양한 팩토리(Creator)를 제공할 수 있습니다.

팩토리 메소드

 

✔️ 팩토리 메소드 패턴 구현방법

  • 팩토리 메소드 패턴의 목적"변경에는 닫혀있고, 확장에는 열려있는 구조" 를 만드는 것입니다. 즉, OCP  원칙을 지키기위한 패턴에 가깝다고 생각합니다.

 

📌 1) 예시 코드

  • black 배와 white 배를 만드는 factory가 아래와 같이 정의되어있다고 가정합니다.

Ship.class

@Setter
@ToString
public class Ship {

    private String name;
    private String email;
    private String color;
}

Factory.class

public class ShipFactory {

    public static Ship orderShip(final String name, final String email) {

        //validate
        if (Objects.isNull(name) || name.isBlank()) {
            throw new IllegalArgumentException("배 이름을 지어주세요");
        }

        if (Objects.isNull(email) || email.isBlank()) {
            throw new IllegalArgumentException("연락처를 남겨주세요");
        }

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

        if (name.equalsIgnoreCase("whiteship")) {
            ship.setColor("white");
        }

        if (name.equalsIgnoreCase("blackship")) {
            ship.setColor("black");
        }

        return ship;
    }
}

 

클라이언트에서 white ship을 만들것인가, black ship을 만들 것인가에 따라 반환되는 ship 이 달라지는 코드입니다.

public class Client {

    public static void main(String[] args) {

        Client client = new Client();

        Ship whiteShip = ShipFactory.orderShip("WhiteShip", "one@email.com");
        System.out.println("whiteShip = " + whiteShip.toString());

        Ship blackShip = ShipFactory.orderShip("blackShip", "two@email.com");
        System.out.println("blackShip = " + blackShip.toString());
    }
}

 

Factory 를 통해 요구조건에 따라 다른 Ship 객체가 반환되지만, 만약 요구사항이 변경된다면 Factory 코드가 변경되어야만 합니다.

💡 즉 이 코드는, 변경에 닫혀있지 않은 코드인 것이죠

 


📌 2) 팩토리 메서드 패턴으로 변경

 

1. 먼저 배를만드는, Factory 를 Interface 로 구현합니다.

  • 기존에 ShipFactory에서 진행하던 validate도 ShipFactory로 하며, 배를 직접적으로 생성시키는 createShip( ) 메소드만 추상메소드로 정의합니다.
public interface ShipFactory {

    default Ship orderShip(String name, String email) {
        validate(name, email);

        return createShip();
    }

    Ship createShip();

    private void validate(String name, String email) {
        //validate
        if (Objects.isNull(name) || name.isBlank()) {
            throw new IllegalArgumentException("배 이름을 지어주세요");
        }

        if (Objects.isNull(email) || email.isBlank()) {
            throw new IllegalArgumentException("연락처를 남겨주세요");
        }
    }
}

 

2. 그 다음, 직접적으로 배를 만들어 반환하는 createShip() 메서드를 구현하는 구현체를 각각 책임에 맞게 정의합니다.

public class WhiteShipFactory implements ShipFactory{

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

public class WhiteShip extends Ship {

    public WhiteShip() {
        setName("whiteShip");
        setLogo("white");
        setEmail("whiteLogo");
    }
}

public class BlackShipFactory implements ShipFactory{

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

public class BlackShip extends Ship {

    public BlackShip() {
        setName("BlackShip");
        setLogo("Black");
        setEmail("BlackLogo");
    }
}

 

3. 클라이언트에서 만들 배에 따라서, 해당하는 구현체를 선택해줍니다.

public class Client {

    public static void main(String[] args) {

        Ship whiteShip =new WhiteShipFactory().orderShip("WhiteShip", "one@email.com");
        System.out.println("whiteShip = " + whiteShip.toString());

        Ship blackShip = new BlackShipFactory().orderShip("blackShip", "two@email.com");
        System.out.println("blackShip = " + blackShip.toString());
    }
}

 

👏🏻 여기서 중요한 것은, 다음 RedShip, BludShip 등,, 요구사항 변경에의해 추가적인 배 객체를 만들어야해도, 기존코드를 수정하는 것이 아니라, createShip()메소드를 구현하는 구현체만 추가해주면 됩니다.

즉 변경에는 닫혀있고, 확장에는 열려있는 OCP 를 지키는 구조가 되어집니다.

 

→ 클라이언트의 변경도 최소하고 싶다면, 구현체를 선택하는 계층을 하나 더 두거나, 외부에서 구현체를 주입받거나 하는 구조로 변경을 하는 방법이 존재합니다.

 


✔️ 팩토리 메소드 패턴 복습

1. 팩토리 메소드 패턴의 장점 및 단점

  • 장점
    • 요구사항의 변경에도 기존코드를 변경하지 않고 확장하여 처리할 수 있는 보기좋은 구조가 됩니다.
    • 이는, product와 creater간에 관계를 느스한 결합으로 묶었기 때문에 가능한 것입니다.
  • 단점
    • 각자의 역할을 가지고있는 클래스가 늘어남은 맊을 수 없다. (관리 포인트의 증가)

 

2. "확장에 열려있고 변경에 닫혀있는 객체 지향 원칙"을 설명하세요

  • 변경에 닫혀있다 : 기존 코드를 변경하지 않는다,
  • 확장에 열려있다 : 새로운 코드를 얼마든지 확장한다.

 

3. 자바 8에 추가된 default 메소드에 대해 설명하세요.

  • 기존 자바 버전에서는 interface 에는 추상메소드만이 선언 가능했지만,
  • 자바 8 이후부터는, 기본 메소드를 정의할 수 있기 때문에, 추상 메소드를 상속받는 모든 구현체에서 필요한 기능을 상속받을 수 있도록 되었습니다. 

 

 

끝!

 

 

반응형