소프트웨어 관련/Design Pattern

[Design Pattern] Factory Method Pattern

JJangGu 2022. 5. 10. 21:03

이번에는 팩토리 메소드 패턴에 대해서 학습하겠습니다. 

 

Factory Method Pattern

팩토리 메소드 패턴에서는 객체를 생성하기 위한 인터페이스를 정의하는데, 어떤 클래스의 인스턴스를 만들지는 서브클래스에서 결정하게 만든다. 

 

우리는 객체를 만들때 new 를 사용해왔습니다. 그런데 new 를 사용해서 인스턴스를 생성하면 결국은 특정 구현을 이용한다는 뜻이기도 합니다. new 에 문제가 있는 것은 아니지만 특정 객체를 만드는 형태여서 수정이 일어난다면 고쳐야 하는 코드가 계속해서 생기게 될 것이고, OCP 원칙을 위배할 가능성이 높아집니다. 

 

책에 있는 피자 가게 예시로 어떤 패턴인지 알아보겠습니다. 

 

위의 다이어그램만 보아도 구조가 다 보여서 굳이 코드를 작성하진 않겠습니다. Pizza 인터페이스를 구현한 CheesePizza, PepperoniPizza 가 있고, 피자객체를 생성하는 SimplePizzaFactory 가 존재합니다. 

 

사실 이런 구조를 갖는 것을 팩토리 패턴이라고 하지 않습니다. 이건 그냥 Factory 라는 의미를 가지고 있는 구조일 뿐 입니다. 

 

그러면 위에서 언급했던 팩토리 메서드 패턴이 되려면 어떤 형태가 되어야 할 지 알아보겠습니다. 

 

 

위의 구조에 대해서 설명해보겠습니다. 가장 위에 PizzaStore 라는 추상 생산자 클래스가 존재합니다. 여기에 나중에 서브클래스에서 제품을 생산하기 위해 구현할 팩토리 메소드를 정의합니다. 

 

NYPizzaStore 와 ChicagoPizzaStore 는 createPizza() 라는 팩토리 메소드로 제품을 생산하고, 이 두 클래스를 Concrete Creator (구상 생산자)라고 부릅니다. 가게마다 createPizza() 메서드의 구현을 통해 가게 고유의 피자를 만들 수도 있습니다. 

 

팩토리 메서드 패턴에서는 서브클래스에서 어떤 클래스를 만들지를 결정하게 함으로써 객체 생성을 캡슐화 합니다. 

 

Creator 클래스와 Product 클래스의 구조를 보면 비슷한 것을 알 수 있습니다. 둘 다 추상클래스로 시작하고, 그 클래스를 확장하는 구상 클래스들을 가지고 있습니다. 그리고 구체적인 구현은 구상 클래스들이 책임을 지고 있죠. NYPizzaStore 에는 뉴욕스타일의 피자를 만드는 것에 대한 지식이 캡슐화 되어있고, 이는 ChicagoPizzaStore 도 마찬가지 입니다. 

 

이것을 "병렬 클래스 계층 구조"라고 부릅니다. 이 용어가 중요한 것은 아니지만 굳이 언급한 이유는 추상클래스로 시작해서 구상클래스에 세부 내용이 구현되는 이 구조를 기억하기 위함입니다. 

 

 

최종적으로는 이 형태가 가장 기본이 된다고 볼 수 있습니다. Creator 에는 제품을 가지고 원하는 일을 하기 위한 메소드들이 구현되어 있습니다. 하지만 제품을 만들어주는 factoryMethod() 는 추상 메소드로 정의하고 구현하지 않습니다. 실제 구현은 ConcreteCreator 가 구현하죠. 여기서 중요한 것은 Creator 클래스가 ConcreteProduct 와 느슨하게 결합되어 있는 것 입니다. 결국 만들고자 하는 객체의 정보가 변경이 된다고 하더라도 Creator 가 변경될 일은 없죠.

 

맨처음 SimplePizzaFactory 하고의 차이를 짚고 넘어가자면, 구상 클래스를 만들때 createPizza() 추상 메소드가 정의되어 있는 추상 클래스를 확장해서 만들었다는 점이 다릅니다. 이는 구상 클래스가 createPizza() 에서 어떤 일을 할 지 결정할 수 있다는 뜻 입니다. SimplePizzaFactory 는 PizzaStore 안에 있는 별개의 객체를 생성하는 것에 그쳤죠.

 

위의 내용들을 종합해보면 또 한가지 알아야 할 디자인 원칙이 있습니다. 

바로 "의존성 뒤집기 원칙 (Dependency Inversion Principle)" 입니다. 구상 클래스에 의존하지 말고, 추상화된 것에 의존하도록 만들어야 한다는 원칙입니다. 

 

특정 구현이 아니라 인터페이스에 맞춰서 프로그래밍한다. 이 원칙과 비슷한 점이 있지만 DIP 는 추상화가 더 많이 강조되어 있습니다. 고수준 구성요소가 저수준 구성요소에 의존하면 안된다는 것을 의미합니다. 위의 예로 보면 PizzaStore 는 고수준 구성요소라고 할 수 있고, NYCheesePizza 와 같은 클래스들이 저수준 구성요소라고 할 수 있습니다. 

 

 

팩토리 메소드 패턴에 대해서는 이렇게 마무리를 하고, 다음 글에 이어서 추상 팩토리 패턴을 학습하겠습니다. 👍