Factory Pattern

김영우 (AvocadoSmasher)·2022년 11월 3일
0

Design Pattern

목록 보기
4/8

🔎 What is Factory Pattern??

팩토리 패턴( Factory Pattern )은 크게 2가지로 나뉘는데 팩토리 메소드 패턴(Factory Method Pattern)과 추상 팩토리 패턴(Abstract Factory Pattern)이 있다. 이 둘의 공통적인 핵심 요점은 객체의 생성을 서브 클래스에게 결정하도록 하여 생성 부분과 실제 그 내용에 대한 부분을 분리하여 객체들 간의 결합도를 줄이는 것이다. 자세한 차이는 아래의 정의를 통해 보도록 하죠.

  • 팩토리 메소드 패턴
    클래스의 인스턴스를 만드는 일을 서브 클래스에게 책임전가 하는 패턴.
  • 추상 팩토리 패턴
    관려된 객체들을 모듈화하여 팩토리 클래스를 만들고, 팩토리를 조건에 따라 생성하도록 또 다른 팩토리를 만들어 객체를 생성하도록 하는 패턴(팩토리를 추상화 한다고 보면 될것 같습니다.)

⚓️ Digging into how it works

일반적으로 객체의 생성 부분은 변경이 될 가능성이 높다. 그렇다면 해당 객체를 생성하여 사용하는 객체들이 많아지다 보면 어떻게 되겠는가? 해당 객체를 생성하는 모든 부분들을 수정해야한다. 그렇기에 객체를 생성에 대한 책임을 다른 서브 클래스에게 맡기고 변경에 대한 요구가 생기면 해당 서브 클래스에서의 수정만 이루어 지면 되도록 하는 것이다.

말로만 보면 난해할 수 있으니 예를 들어 아래와 같은 코드가 있다고 생각해보자.

class Bread {
		public Bread(String kind){
				System.out.println(kind + "bread");
		}
}

빵을 만드는데 빵이 어떤 빵인지 생성자가 인자를 받아서 출력하는 형태입니다. 다음과 같은 Bread 클래스를 여러 코드에서 사용한다고 생각해보죠. 그런 와중에 갑자기 다음과 같은 변경사항이 생깁니다. “아 빵 개수도 인자로 받을 필요가 있네요”

그래서 아래와 같이 코드를 변경하게 됩니다.

class Bread {
		public Bread(String kind,int num){
				System.out.println( num + " " + kind + "bread, ");
		}
}

여기까지는 OK인데 이제 이 Bread의 객체를 생성하여 사용하던 모든 코드를 전부 수정해야겠죠? 큰 규모의 프로젝트의 경험이 없으시다면 간단하지 않나? 라고 생각하실겁니다. 하지만 세상은 녹록치 않죠. 무료로 여러 프로그래밍 온라인 강의를 제공하시는 어떤 분이 말씀하셨죠 “프로그래머는 상상력이 풍부해야합니다.” 그렇다면 그분의 말을 빌려 상상력을 더 불어넣어보도록 하죠. 만약 Bread class의 객체를 생성하여 사용하는 코드가 1억줄 정도 있다고 생각해보죠. 이제 여러분은 Bread class의 생성자가 변경될때마다 1억줄의 코드를 변경해야하는 궁극의 노가다를 경험하시게 될겁니다. 이러한 노가다를 피하기 위해 Factory 패턴이 생긴것이지요.

💻 Code (feat. Java)

제가 디자인 패턴 수업을 수강하는 동안 사용했었던 코드를 재사용 해보도록 하죠. 기본적인 컨셉은 Pizza를 만드는 PizzaStore를 디자인 하는 코드입니다.

Pizza는 Abstract class로 기본적으로 prepare, bake, cut, box과정을 거쳐 Pizza가 완성됩니다. 이러한 Pizza를 만드는 과정을 PizzaStore가 담당하게 되는데 이때 PizzaStore의 createPizza 메소드에서 피자의 타입을 입력으로 받고 orderPizza에서 해당 Pizza를 만드는 형태입니다.

이런 PizzaStore 모델을 우선 팩토리 메소드 패턴에서 알아보도록 하죠.

참고로 각 피자별 코드는 abstract Pizza class를 extend하여 그냥 topping, sauce, dough, name 같은 속성들에 대한 내용만 추가된 형태라 코드를 올리지 않았습니다.

Pizza abstract class

import java.util.ArrayList;

abstract public class Pizza {
	String name;
	String dough;
	String sauce;
	ArrayList<String> toppings = new ArrayList<String>();

	public String getName() {
		return name;
	}

	public void prepare() {
		System.out.println("Preparing " + name);
	}

	public void bake() {
		System.out.println("Baking " + name);
	}

	public void cut() {
		System.out.println("Cutting " + name);
	}

	public void box() {
		System.out.println("Boxing " + name);
	}

	public String toString() {
		// code to display pizza name and ingredients
		StringBuffer display = new StringBuffer();
		display.append("---- " + name + " ----\n");
		display.append(dough + "\n");
		display.append(sauce + "\n");
		for (String topping : toppings) {
			display.append(topping + "\n");
		}
		return display.toString();
	}
}

PizzaStore class

public class PizzaStore {

		public Pizza orderPizza(String type) {
        Pizza pizza = createPizza(type);
        System.out.println("--- Making a " + pizza.getName() + " ---");
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }

    public Pizza createPizza(String type) {
        if (type.equals("cheese")) {
            return new CheesePizza();
        } else if (type.equals("pepperoni")) {
            return new PepperoniPizza();
        } else if (type.equals("clam")) {
            return new ClamPizza();
        } else if (type.equals("veggie")) {
            return new VeggiePizza();
        }
        return null;
    }
}

이렇게 하여서 PizzaStore 모델에 팩토리 메소드 패턴이 적용되었습니다. 그런데 여기서 NewYork 스타일과 Chicago 스타일 피자를 추가하고 해당 피자들을 만드는 뉴욕 스타일 피자가게와 시카고 스타일 피자 가게를 만들고자 한다면 어떻게 될까요? 이러한 경우 각각의 피자가게들을 클래스로 바로 만들기 보다는 PizzaStore를 abstract class로 만들고 각 피자 가게들이 PizzaStore를 상속하고 createPizza 부분에서 피자 만 바꿔주면 될것 같군요 이렇게 되면 확장성도 좋아지고 모듈화도 잘 이루어진 형태가 됩니다. 이러한 패턴을 추상 팩토리 패턴이라고 합니다.(팩토리도 추상화 하는거죠). 그렇다면 코드는 아래와 같이 되겠네요.

PizzaStore abstract class 로 변경.

public abstract class PizzaStore {

		public Pizza orderPizza(String type) {
        Pizza pizza = createPizza(type);
        System.out.println("--- Making a " + pizza.getName() + " ---");
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }

    protected abstract Pizza createPizza(String type);
}

NYPizzaStore (NewYork Style Pizza를 만듦.) class

public class NYPizzaStore extends PizzaStore{
    @Override
    protected Pizza createPizza(String type) {
        if (type.equals("cheese")) {
            return new NYStyleCheesePizza();
        } else if (type.equals("pepperoni")) {
            return new NYStylePepperoniPizza();
        } else if (type.equals("clam")) {
            return new NYStyleClamPizza();
        } else if (type.equals("veggie")) {
            return new NYStyleVeggiePizza();
        }
        return null;
    }
}

ChicagoPizzaStore class

public class ChicagoPizzaStore extends PizzaStore{
    @Override
    protected Pizza createPizza(String type) {

        if (type.equals("cheese")) {
            return new ChicagoStyleCheesePizza();
        } else if (type.equals("pepperoni")) {
            return new ChicagoStylePepperoniPizza();
        } else if (type.equals("clam")) {
            return new ChicagoStyleClamPizza();
        } else if (type.equals("veggie")) {
            return new ChicagoStyleVeggiePizza();
        }
        return null;
    }
}

📌 Summary

Factory라는 키워드를 코드에서 사용하지 않아서 헷갈릴수도 있겠다? 싶었는데 혹시나 오해하시는 분들에게 말씀드리면 PizzaStore가 Factory라고 보면 됩니다. 아무튼 오늘은 Factory Pattern에 대해 알아보았습니다. 해당 패턴은 생성자를 직접 코드에 때려넣는 것 보다는 해당 생성자를 다루는 메소드를 구현한 팩토리를 생성하여 코드가 생성자로부터 자유로울 수 있도록 하는 패턴이었습니다. 팩토리 패턴을 잘 활용한다면 생성자의 족쇄로부터 코드를 해방시켜 줄 수 있으니 여러분들도 이를 고려하여 깔끔하고 유지보수도 좋은 코드를 만들수 있기를 바랍니다.

P.S) 코드에서 보이는 Pizza별 생성자로 객체를 생성하는 부분을 Main에 다 때려넣었다면 코드가 얼마나 어지럽고 수정이 잦게 될지 상상만 해도 벌써 어지럽네요.

profile
Android Studio 공부 중

0개의 댓글