"Define an interface for creating an object, but let subclasses decide which class to instantiate. The Factory method lets a class defer instantiation it uses to subclasses."
"객체를 생성하기 위해 인터페이스를 정의하지만, 어떤 클래스의 인스턴스를 생성할지에 대한 결정은 서브클래스가 내리도록 합니다."
-Gang Of Four-
🤔언제 사용하나요?
1. 어떤 클래스가 자신이 생성해야 하는 객체의 클래스를 알지 못할 때
2. 객체의 생성을 서브클래스에게 위임하고 싶을 때
▶ 객체지향 디자인패턴 원칙 중 Open - Close Principle
즉, 변화가 일어날 수 있는 객체 생성 부분을 한 곳에서 관리(==변화에 대해서 닫힘)하여 수정하는데 편함(== 확정에 대해 열림)
🤔 인터페이스 사용 해도 되나요?
사용해도 된다. 다만 인터페이스는 추상 메서드만 작성 가능하기 때문에 일련의 고정적인(공통적인) 과정을 구현한 메서드가 필요하면 추상 클래스를 사용해야 한다.
/* Room 관련 클래스*/
public abstract class Room { // 추상클래스
abstract void connect(Room room); // 방을 연결해주는 추상 method
}
public class MagicRoom extends Room { // 구체적인 타입의 클래스
public void connect(Room room) {}
}
public class OrdinaryRoom extends Room { // 구체적인 타입의 클래스
public void connect(Room room) {}
}
makeRoom()
이 factory Method/* Factory 관련 클래스 */
public abstract class MazeGame { // 추상 Factory 클래스
private final List<Room> rooms = new ArrayList<>();
public MazeGame() { // 게임 판이 열리면 방이 2개 생기고 서로 연결해줌
Room room1 = makeRoom();
Room room2 = makeRoom();
room1.connect(room2);
rooms.add(room1);
rooms.add(room2);
}
abstract protected Room makeRoom(); // abstract factory method
}
public class MagicMazeGame extends MazeGame { // 구체적인 Facotry 클래스
@Override
protected MagicRoom makeRoom() {
return new MagicRoom();
}
}
public class OrdinaryMazeGame extends MazeGame { // 구체적인 Facotry 클래스
@Override
protected OrdinaryRoom makeRoom() {
return new OrdinaryRoom();
}
}
// 클라이언트 부분, 객체 생성 호출 부분
public class Main {
public static void main(String[] args) {
MazeGame ordinaryGame = new OrdinaryMazeGame();
MazeGame magicGame = new MagicMazeGame();
}
}
즉, 객체 생성 부분을 한 클래스에서 담당하는 것, 즉 한 Factory 클래스에서 각기 다른 Product 클래스를 생성한다.
/*Product*/
public interface Shape { // 객체 정의 인터페이스
void draw();
}
public class Rectangle implements Shape { // 구현 클래스
@Override
public void draw() {
System.out.println("Inside Rectangle::draw() method.");
}
}
public class Square implements Shape { // 구현 클래스
@Override
public void draw() {
System.out.println("Inside Square::draw() method.");
}
}
public class Circle implements Shape { // 구현 클래스
@Override
public void draw() {
System.out.println("Inside Circle::draw() method.");
}
}
/*Factory*/
public class ShapeFactory {
//use getShape method to get object of type shape
public Shape getShape(String shapeType){
if(shapeType == null){
return null;
}
if(shapeType.equalsIgnoreCase("CIRCLE")){
return new Circle();
} else if(shapeType.equalsIgnoreCase("RECTANGLE")){
return new Rectangle();
} else if(shapeType.equalsIgnoreCase("SQUARE")){
return new Square();
}
return null;
}
}
public class FactoryPatternDemo { // 객체 생성 호출 부분
public static void main(String[] args) {
ShapeFactory shapeFactory = new ShapeFactory();
//get an object of Circle and call its draw method.
Shape shape1 = shapeFactory.getShape("CIRCLE");
//call draw method of Circle
shape1.draw();
//get an object of Rectangle and call its draw method.
Shape shape2 = shapeFactory.getShape("RECTANGLE");
//call draw method of Rectangle
shape2.draw();
//get an object of Square and call its draw method.
Shape shape3 = shapeFactory.getShape("SQUARE");
//call draw method of square
shape3.draw();
}
}
// Inside Circle::draw() method.
// Inside Rectangle::draw() method.
// Inside Square::draw() method.
나중에 추가 정리 필요!!
new Object
로 간단하게 구현 const num = new Object(42)
const str = new Object('abc')
num.constructor.name; // 숫자 전달 시 Number 객체 생성
str.constructor.name; // 문자열 전달 시 String 객체 생성
즉, 전달받은 값에 따라 다른 객체를 생성하며 인스턴스의 타입 등을 정함.
class CoffeeFactory {
static createCoffee(type) {
const factory = factoryList[type]
return factory.createCoffee()
}
}
class Latte {
constructor() {
this.name = "latte"
}
}
class Espresso {
constructor() {
this.name = "Espresso"
}
}
class LatteFactory extends CoffeeFactory{
static createCoffee() {
return new Latte()
}
}
class EspressoFactory extends CoffeeFactory{
static createCoffee() {
return new Espresso()
}
}
const factoryList = { LatteFactory, EspressoFactory }
const main = () => {
// 라떼 커피를 주문한다.
const coffee = CoffeeFactory.createCoffee("LatteFactory")
// 커피 이름을 부른다.
console.log(coffee.name) // latte
}
main()
abstract class Product {
public abstract void use();
}
class IDCard extends Product {
private String owner;
public IDCard(String owner) {
System.out.println(owner + "의 카드를 만듭니다.");
this.owner = owner;
}
@Override
public void use() {
System.out.println(owner + "의 카드를 사용합니다.");
}
public String getOwner() {
return owner;
}
}
abstract class Factory {
public final Product create(String owner) {
Product p = createProduct(owner);
registerProduct(p);
return p;
}
protected abstract Product createProduct(String owner);
protected abstract void registerProduct(Product p);
}
class IDCardFactory extends Factory {
private List<String> owners = new ArrayList<>();
@Override
protected Product createProduct(String owner) {
return new IDCard(owner);
}
@Override
protected void registerProduct(Product p) {
owners.add(((IDCard) p).getOwner());
}
public List<String> getOwners() {
return owners;
}
}
Factory factory = new IDCardFactory();
Product card1 = factory.create("홍길동");
Product card2 = factory.create("이순신");
Product card3 = factory.create("강감찬");
card1.use();
card2.use();
card3.use();
홍길동의 카드를 만듭니다.
이순신의 카드를 만듭니다.
강감찬의 카드를 만듭니다.
홍길동의 카드를 사용합니다.
이순신의 카드를 사용합니다.
강감찬의 카드를 사용합니다.
interface Pizza {
public void prepare();
public void bake();
public void box();
}
abstract class PizzaStore {
public Pizza orderPizza(String type) {
Pizza pizza = createPizza(type); // factory method 사용
pizza.prepare();
pizza.bake();
pizza.box();
return pizza;
}
// factory method
abstract Pizza createPizza(String type);
}
class NYPizzaStore extends PizzaStore {
@Override
Pizza createPizza(String type) {
if ("cheese".equals(type)) {
return new NYStyleCheesePizza();
} else if ("pepperoni".equals(type)) {
return new NYStylePepperoniPizza();
}
return null;
}
}
class ChicagoPizzaStore extends PizzaStore {
@Override
Pizza createPizza(String type) {
if ("cheese".equals(type)) {
return new ChicagoStyleCheesePizza();
} else if ("pepperoni".equals(type)) {
return new ChicagoStylePepperoniPizza();
}
return null;
}
}
PizzaStore nyStore = new NYPizzaStore();
PizzaStore chicagoStore = new ChicagoPizzaStore();
Pizza pizza = nyStore.orderPizza("cheese");
Pizza pizza1 = chicagoStore.orderPizza("pepperoni");
면접을 위한 CS 전공지식 노트
Java 언어로 배우는 디자인 패턴 입문
GOF Design Pattern
디자인패턴 이해하기
Factory method pattern 위키백과
[Design Pattern] GoF(Gang of Four) 디자인 패턴
Design Pattern - Factory Pattern
팩토리 패턴, 도대체 왜 쓰는거야? - 기본 이론편
팩토리 메소드 패턴 - 기계인간