구체적으로 어떤 인스턴스를 만들지는 구체 클래스가 정한다.
UML 다이어그램으로 나타내면 다음과 같은 구조를 가진다
- 다양한 구현체(Ship)을 가질 수 있고, 그 중에서 특정한 구현체를 만들 수 있는 다양한 팩토리를 제공한다.
위와 같은 구조를 단순히 Ship
, ShipFactory
로 단순화해서 구현하게 되면, ShipFactory
클래스에서 if~else
문을 통해 어떤 배를 생산할 것인지에 대해 분기문을 작성해주어야 하고 변경에 닫혀있고 확장에 열려있는 OCP 원칙 (SOLID) 설계에 반하게 된다.
다시 말하면 또 다른 제품을 추가하고자 하는 경우 이름에 따라 if~else
문을 추가로 작성해야하고 Ship
의 특성이 변경되는 경우 변경된 요구사항에 따라 필드의 변화가 생길 수 있다.
이에 따라 Ship
클래스에 추가가 있을 때, 기존의 Ship
클래스 코드를 수정해야 할 수 있어서 OCP 원칙을 위배한다.
SOLID원칙의 OCP는 기존의 구성에 확장이 필요하더라도 기존 코드를 수정하지 않고 추가하는 방식의 유연한 설계의 필요성을 역설한다.
따라서 팩터리 메서드 패턴은 인스턴스 생성의 책임을 추상적 인터페이스의 메서드로 감싸는 방식.
추상적 인터페이스의 메서드 - ShipFactory
인터페이스의 createNewShip()
로 감싸는 형식으로 확장에 용이한 클래스를 구성한다.
이전에 이야기한 것 처럼 모든 생성의 책임을 구체 클래스에 담게되면 코드의 복잡도(depth)나 if~else
문의 개수가 많아지면서 다소 가독성이 떨어지는 코드가 발생할 수 있다, 따라서 우리는 ShipFactory
인터페이스에 복잡해질 수 있는 if~else
문 대신 추상화를 통한 인터페이스 분리를 진행했다.
이로써 인스턴스 제작은 구체 클래스가 담당하게 된다. 즉 어떤 구체 클래스를 추가로 생성하려고 해도 기존의 코드 수정 없이 새로운 구체 클래스를 제작하고 관련된 생성코드 또한 구체 클래스에 기록하면 된다.
기존에 생성한 Interface와 다른 구체 클래스는 손을 댈 필요가 없게 된다.
Java 8의 경우 인터페이스에서 private
메서드를 지원하지 않는다. 이는 Java 9부터 지원하는 기술이다.
따라서 대안책으로 DefaultShipFactory
클래스를 생성하고 abstract
메서드를 기술하여 중간에 계층을 하나 더 추가하는 방식으로 대응할 수 있다.
default
메서드는 8에서도 사용가능하다, private
메서드만 불가능하다.
public class Client {
public static void main(String[] args) {
Client client = new Client();
client.print(new WhiteshipFactory(), "whiteship", "keesun@mail.com");
client.print(new BlackshipFactory(), "blackship", "keesun@mail.com");
}
private void print(ShipFactory shipFactory, String name, String email) {
System.out.println(shipFactory.orderShip(name, email));
}
}
다음과 같은 클라이언트 코드를 기술하면 클라이언트에 대한 코드를 수정해야하는 단점이 생기는데...?print
메서드를 클래스로 추출하면 DI(Dependency Injection)
방식처럼 클라이언트의 코드 변경 없이도 확장에 용이하게 설계할 수 있다.
'전공 > Design Pattern' 카테고리의 다른 글
Design Pattern - Prototype Pattern (2) | 2023.10.10 |
---|---|
Design Pattern - Builder Pattern (0) | 2023.10.09 |
Design Pattern - Abstract Factory Pattern (0) | 2023.10.07 |
Design Pattern - Singleton Pattern (1) | 2023.10.05 |