Factory method pattern : 팩토리 메서드 패턴
- 인스턴스를 생성하는 책임을 추상적인 인터페이스의 메서드로 감싸는 것이다.
- 즉, 서브 클래스가 구체적으로 인스턴스를 생성할 지 결정한다.
- 다양한 구현체(Product)가 있고 그 중에서 특정한 구현체를 만들 수 있는 다양한 팩토리(Creator)를 제공할 수 있다.
패턴을 구현해보자.
1. Car class
public class Car {
private String name;
private String color;
private String logo;
}
- 자동차 클래스를 정의해 준다.
- 이름, 색상, 로고에 대한 데이터를 갖고 있다.
2. CarFactory interface
public interface CarFactory {
default Car orderCar(String name, String email) {
validate(name, email); // 검사
prepareFor(name); // 준비
Car car = createCar(); // 생성
sendEmailTo(email, car); // 알림
return car;
}
Car createCar();
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 + " 만들 준비 중..");
}
private void sendEmailTo(String email, Car car) {
System.out.println(car.getClass() + " 다 만들었습니다.");
}
}
- Factory를 인터페이스로 만들어 준다.
- default를 통해 자동차 주문에 대한 인터페이스를 생성해 준다.
- 인터페이스 내에서
검 사
준비
생성
알림
을 진행한다.
3. WhiteCar class
public class WhiteCar extends Car {
public WhiteCar() {
setName("White Car");
setLogo("White Logo");
setColor("White");
}
}
- Car 클래스를 상속 받아서 WhiteCar를 만들어 준다.
4. WhiteCarFactory class
public class WhiteCarFactory implements CarFactory {
@Override
public Car createCar() {
return new WhiteCar();
}
}
- CarFactory 인터페이스에 있는 createCar()를 오버라이드 해서 WhiteCar를 리턴해준다.
5. Client class
public class Client {
public static void main(String[] args) {
Car whiteCar = new WhiteCarFactory().orderCar("WhiteCar", "test@mail.com");
System.out.println(whiteCar);
}
}
:
WhiteCar 만들 준비 중..
class WhiteCar 다 만들었습니다.
Car{name='White Car', color='White', logo='White Logo'}
- WhiteCarFactory는 CarFactory 인터페이스의 구현체이다.
- CarFactory 인터페이스에 default로 정의되어 있는 orderCar()를 통해 whiteCar에 대한 주문을 넣는다.
- White Car가 잘 생성 되었다.
그럼, Black Car를 생성하려면 어떻게 해야 하나?
1. BlockCar class
public class BlackCar extends Car {
public BlackCar() {
setName("Black Car");
setLogo("Black Logo");
setColor("Black");
}
}
- Car를 상속받아 Black를 만들어준다.
2. BlockCarFactory class
public class BlackCarFactory implements CarFactory {
@Override
public Car createCar() {
return new BlackCar();
}
}
- CarFactory 인터페이스를 구현해서 BlackCar를 리턴해 준다.
3. Client class
public class Client {
public static void main(String[] args) {
Car whiteCar = new WhiteCarFactory().orderCar("WhiteCar", "test@mail.com");
System.out.println(whiteCar);
Car blackCar = new BlackCarFactory().orderCar("BlackCar", "test@mail.com");
System.out.println(blackCar);
}
}
:
WhiteCar 만들 준비 중..
class WhiteCar 다 만들었습니다.
Car{name='White Car', color='White', logo='White Logo'}
BlackCar 만들 준비 중..
class BlackCar 다 만들었습니다.
Car{name='Black Car', color='Black', logo='Black Logo'}
- Black Car가 잘 생성 되었다.
- 체크해 봐야 하는 부분은 Black Car를 생성하면서 White Car의 코드의 변경이 없었다는 것이다.
- 이는 OCP를 만족한다 볼 수 있다.
그런데! 문제는 클라이언트 코드가 바뀌었다.
- 새로운 색상의 자동차가 생성 될 때 마다 클라이언트 코드가 변경되는 게 아닌가? 이것이 과연 변경에 닫혀있는 게 맞는가? 이런 의문을 가질 수 있다. 때문에 의존성 주입을 통해 클라이언트 코드를 최대한 변경하지 않는 방법으로 구현해야 한다.
1. 변경되는 클라이언트 코드는 DI를 통해 해결할 수 있다.
public class Client {
public static void main(String[] args) {
Client client = new Client();
client.print(new WhiteCarFactory(), "WhiteCar", "test@mail.com");
client.print(new BlockCarFactory(), "BlockCar", "test@mail.com");
}
private void print(CarFactory carFactory, String name, String email) {
System.out.println(carFactory.orderCar(name, email));
}
}
:
WhiteCar 만들 준비 중..
class WhiteCar 다 만들었습니다.
Car{name='White Car', color='White', logo='White Logo'}
BlackCar 만들 준비 중..
class BlackCar 다 만들었습니다.
Car{name='Black Car', color='Black', logo='Black Logo'}
- 한 클래스 내에 있어서 옳지는 않지만*(DI라고 할 순 없지만)* 다른 클래스로 빼어서 DI를 받아 사용할 수 있다.
프로젝트 다이어그램
Block → Black으로 보면 된다.(오타)
- 제품이나 생성 부분에 각각 계층 구조가 있어서 구체적인 팩토리 안에서 구체적인 제품을 만들어내는 게 중요하다.