new 키워드
Pizza pizza = new CheesePizza();
new라는 키워드는 자바의 키워드 중 하나로,
객체를 생성한다는 점에서 한 번도 사용하지 않고 코딩하기는 사실상 불가능합니다.
Pizza pizza;
if(type == "cheese") {
pizza = new CheesePizza();
} else if(type == "veggie") {
pizza = new VeggiePizza();
} else if (type == "pepperoni") {
pizza = new PepperoniPizza();
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
일련의 구상 클래스가 있을 때, 위와 같은 형태가 나타나게 됩니다.
인터페이스를 사용해 코드를 유연하게 하려 했으나,
새로운 피자가 추가되거나 기존 피자 메뉴를 없앨 때 마다
인스턴스를 만들 때 구성 클래스를 선택하는 부분이 변경됩니다.
수정해야 할 코드가 늘고, 유연성이 떨어지게 되는것이죠.
new 키워드 자체에 문제가 있다기 보단, 코드의 변경이 일어난다는 점에서 문제가 발생합니다.
Simple Factory
객체를 생성하는 부분을 따로 빼내어,
객체 생성을 전담으로 하는 클래스로 분리해봅시다.
public class SimplePizzaFactory {
public Pizza createPizza(String type) {
return switch (type) {
case "cheese" -> new CheesePizza();
case "veggie" -> new VeggiePizza();
case "pepperoni" -> new PepperoniPizza();
default -> null;
};
}
}
-----------------------------------------------------
public class PizzaStore {
SimplePizzaFactory simplePizzaFactory = new SimplePizzaFactory();
void orderPizza(String type) {
Pizza pizza = simplePizzaFactory.orderPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
}
}
객체 생성을 전담하는 Factory 클래스를 만들고,
해당 피자 객체를 만들 부분에서는 해당 클래스를 사용해 객체를 받아옵니다.
공장에 객체를 요청하는거죠.
new 키워드 대신 createPizza 메소드를 사용함으로써
형식을 전달하고, 그에 맞는 피자를 받아오기만 하면 되는 생성 구조가 탄생했습니다.
(소곤소곤) 간단한 팩토리(Simple Factory)는 사실 디자인 패턴이라기 보다는 관용구에 가깝습니다.
정적 팩토리 메소드 (Static Factory Method)
public class SimplePizzaFactory {
public static Pizza createPizza(String type) {
return switch (type) {
case "cheese" -> new CheesePizza();
case "veggie" -> new VeggiePizza();
case "pepperoni" -> new PepperoniPizza();
default -> null;
};
}
}
===============================================================
public class PizzaStore {
void orderPizza(String type) {
Pizza pizza = SimplePizzaFactory.createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
}
}
팩토리 메소드를 정적으로 만들어 사용하는 방법도 꽤 흔합니다.
이렇게 하면 Factory 클래스를 인스턴스화하지 않고도 사용할 수 있죠.
하지만 서브클래스를 만들어 객체 생성 메서드의 행동을 변경할 수는 없다는 단점이 있습니다.
Factory Method Pattern
이제 서로 다른 스타일의 피자를 여럿 만들어봅시다.
뉴욕 피자, 시카고 피자, 캘리포니아 스타일 피자를 만들어볼까요?
뉴욕 스타일 피자 : 빵이 얇다
시카고 스타일 피자 : 내용물이 잔뜩 들어갔다
캘리포니아 스타일 피자 : 바삭바삭한 크래커 느낌
FizzaStore를 Abstract Class로 전환
public abstract class PizzaStore {
public Pizza orderPizza(String type) {
Pizza pizza = createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
protected abstract Pizza createPizza(String type);
}
이제 팩토리 메서드 대신 PizzaStore에 createPizza 추상 메서드를 사용할겁니다.
이제 스타일에 맞게 PizzaStore의 자식 클래스를 만들어봅시다.
public class NYPizzaStore extends PizzaStore {
@Override
protected Pizza createPizza(String type) {
return switch (type) {
case "cheese" -> new NYCheesePizza();
case "pepperoni" -> new NYPepperoniPizza();
case "veggie" -> new NYVeggiePizza();
default -> null;
};
}
}
createPizza는 abstrac class PizzaStore의 abstract method인 createPizza를 오버라이딩하여 재정의합니다.
Pizza의 서브클래스 중, PizzaStore의 서브클래스에 의해 어떤 인스턴스가 생성될지 결정됩니다.
PizzaStore를 상속하기에, orderPizza는 자동으로 상속받습니다.
이제 피자를 한 번 만들어봅시다.
public class Main {
public static void main(String[] args) {
PizzaStore nyStore = new NYPizzaStore();
PizzaStore chicagoStore = new ChicagoPizzaStore();
// 뉴욕 치즈 피자 주문
Pizza nyCheesePizza = nyStore.orderPizza("cheese");
// 시카고 veggie 피자 주문
Pizza chicagoVeggiePizza = chicagoStore.orderPizza("veggie");
}
}
특정 피자 스토어에서 피자를 주문하면 (orderPizza)
PizzaStore에서는 서브클래스에서 구체화된 createPizza를 통해 피자를 받아옵니다.
PizzaStore의 서브 클래스들은 각 스타일(뉴욕, 시카고)에 맞는 피자를 전달해줍니다.
이후, 피자를 준비하고, 자르고, 굽고, 포장하는 일련의 작업은 PizzaStore의 orderPizza()에서 처리합니다.
public abstract class PizzaStore {
public Pizza orderPizza(String type) {
Pizza pizza = createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
protected abstract Pizza createPizza(String type);
}
생산자 (Creator) 클래스
제품(Product)
정리
팩토리 메서드 패턴(Factory Method Pattern)은
사용하는 서브클래스에 따라 생성되는 객체의 인스턴스가 결정됩니다.
객체의 생성을 캡슐화하고 의존성을 감소시킴으로써 유연성을 높일 수 있죠.
다른 디자인 패턴과 마찬가지로 여러 디자인 패턴과 함께 섞어 사용할 수도 있습니다.
Java Standard Library에서 찾아보는 Factory Method 패턴
// Calendar 클래스
Calendar calendar = Calendar.getInstance();
// Collection 프레임워크 : List
List<String> list = List.of("a", "b", "c");
// DriverManager 클래스
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost");
// NumberFormat 클래스
NumberFormat numberFormat = NumberFormat.getNumberInstance();
NumberFormat currencyFormat = NumberFormat.getCurrencyInstance();
NumberFormat percentFormat = NumberFormat.getPercentInstance(Locale.KOREA);
팩토리 메소드 패턴은 Java 표준 라이브러리에서도 찾아볼 수 있습니다.
Calender, List, DriverManager, NumberFormat 등
여러 클래스에서 코드의 유연성과 가독성을 높이고, 의존성을 낮추는데 향상하는데 도움을 줍니다.
'CS 지식 > 디자인 패턴' 카테고리의 다른 글
[디자인 패턴] Adaptor(어댑터) 패턴 (1) | 2024.05.08 |
---|---|
[디자인 패턴] Singleton(싱글톤) 패턴 (1) | 2024.03.16 |
[디자인 패턴] Decorator (데코레이터) 패턴 (1) | 2023.09.12 |
[디자인 패턴] Observer(옵저버) 패턴 (0) | 2023.09.08 |
[디자인 패턴] Strategy(전략) 패턴 + Template Callback Pattern (0) | 2023.06.20 |