생성 패턴(Creational pattern)은 기존 코드의 유연성과 재사용을 증가시키는 객체 생성 방법들을 제공한다.
오늘은 생성 패턴 중 하나인 Factory method pattern(펙토리 메서드 패턴) 에 대해서 공부해보았다.
Factory Method란?
Factory Method는 superclass에서 객체를 생성하기 위한 인터페이스를 제공하고 subclass에서 생성될 객체의 유형을 변경할 수 있도록 하는 디자인 패턴이다.
내가 만든 시나리오
간단한 시나리오로 위의 설명을 보충해본다. 배달 어플리케이션을 만든다고 가정해보자.
처음에는 배달을 항상 오토바이로만 했기 때문에 motorcycle이라는 클래스를 만들어서 관리하였다.
운이 좋게 배달이 잘 되어서 이제는 오토바이 뿐만 아니라 킥보드, 자동차, 뚜벅이 등의 배달방법이 생겨났다.
하지만, 기존에 배달방법을 motocycle이라는 클래스로 한정지었기 때문에 walk, car 등의 클래스가 추가로 필요하게 되었다.
Factory Method를 이용해서 문제를 해결해보자.
처음에 설명한 것처럼 Delivery라는 superclass에서 객체를 생성하기 위한 createTransport 함수를 제공하고, Delivery 클래스를 상속한 WalkDelivery, CarDelivery, MotorcycleDelivery 등의 subclass에서 오버라이딩을 통해서 각자 생성한 객체 유형을 변경할 수 있게 만들었다. 예를 들어, WalkDelivery의 createTransport 함수는 WalkTranport 인스턴스를 리턴하게 될 것이다.
이제는 Transport 인터페이스를 구현하는 새로운 운송수단을 만들고 Delivery 클래스를 상속하는 subclass를 추가하기만 하면 기존의 코드의 수정 없이 새로운 Delivery를 추가할 수 있게 되었다. 혹은, 기존에 존재하는 Transport를 재사용하는 subclass를 이용할 수도 있을 것이다.
언제 사용하면 좋을까?
- 위의 시나리오처럼 작업해야 하는 객체의 타입이나 의존성(dependency)을 미리 알 수 없을 때 사용하면 좋다.
- 라이브러리나 프레임워크 사용자에게 내부 컴포넌트를 확장하는 방법을 제공할 때 사용하면 좋다.
위의 예시처럼 transport 인터페이스를 구현하고, Delivery클래스를 상속하는 subclass를 만들게 하면 된다.
장단점
장점
- 객체 생성자 (예제의 Delivery의 createTransport)와 객체 (예제에서 Transport)가 분리되어 관리가 쉽다.
- 객체 생성코드를 한 곳에 모아서 코드 가독성과 생산성을 높일 수 있다.
- 클라이언트 코드에 영향을 주지 않고 새로운 타입의 인스턴스를 제공할 수 있다.
단점
- 매번 새로운 subclass를 구현해야 하기 때문에 코드가 길어지거나 복잡해질 수 있다.
구현
Factory method를 go언어를 써서 구현해보았다.
go는 클래스에 대한 개념이 없기 때문에 인터페이스를 활용해서 위의 예시를 비슷하게 만들어보았다.
전체 코드는 여기에서 확인할 수 있다.
먼저 시나리오의 Transport 인터페이스를 만들고 WalkTransport, MotorcycleTranport, CarTransport 타입의 struct를 만들었다.
그리고 시나리오의 Delivery 인터페이스를 만들고 WalkDelivery, MotorcycleDelivery, CarDelivery 타입의 struct를 만들었다.
각 struct의 CreateTransport를 보면 각각에 맞는 Transport 객체를 리턴하는 것을 확인할 수 있다.
마지막으로 client의 코드이다. createTransport 함수가 핵심인데, Delivery 인터페이스를 넘겨받으면 그에 맞는 Transport 객체를 생성하게 된다.
위에서 설명한 것처럼 새로운 Delivery나 Transport가 추가되어도 이 client코드의 수정이 필요 없다는 장점이 있다.