디자인패턴 -3- 팩토리 메서드 패턴

디자인패턴 -3- 팩토리 메서드 패턴

의도

객체를 생성하기 위해 인터페이스를 정의하지만, 어떤 클래스의 인스턴스를 생성할지에 대한 결정은 서브클래스가 내리도록 합니다.

GoF의 디자인 패턴(Gang 외 3인)

위키 백과에서의 팩토리 메서드 패턴 UML

Creator는 ConcreteCreator의 인터페이스로 팩토리 메서드가 생성하는 객체의 인터페이스이다.

ConcreteCreator는 Creator의 구현체이며 Product를 생성한다.

Product가 인터페이스나 부모 클래스일 경우 해당 클래스의 sub class나 구체 클래스를 구현할 수 있다.

해당 패턴을 통해 얻는 결과

해당 객체 내에서 직접 생성하는 것보다, 팩토리 메서드로 생성할 경우 응용성이 높아진다. -> 직접 생성자를 접근하는 것보다 팩토리 메서드로 생성할 경우 해당 Creator의 구현체에 따라 변경을 할 수 있어 다형성을 만족하며 손쉽게 원하는대로 생성이 가능하다.

병렬적인 클래스 계통을 연결할 때 사용하면 좋다. -> Creator를 통하여 병렬적으로 서브클래스의 처리를 하기가 쉽다.

내가 구현한 팩토리 메서드 패턴은 팩토리 메서드에 넘어오는 변수에 따라 다른 Product가 생성되게 구현하였다.

Product

package gof.creation.factorymethod; import java.time.LocalDate; public interface Product { String getName(); LocalDate getCreatedDate(); }

ConcreteProductA

package gof.creation.factorymethod; import java.time.LocalDate; public class ConcreteProductA implements Product { private final String name; private final LocalDate createdDate; ConcreteProductA(String name) { this.name = name; this.createdDate = LocalDate.now(); } @Override public String getName() { return this.name; } @Override public LocalDate getCreatedDate() { return this.createdDate; } }

ConcreteProductB

package gof.creation.factorymethod; import java.time.LocalDate; public class ConcreteProductB implements Product { private final String name; private final LocalDate createdDate; ConcreteProductB(String name) { this.name = name; this.createdDate = LocalDate.now(); } @Override public String getName() { return this.name; } @Override public LocalDate getCreatedDate() { return this.createdDate; } }

Creator

package gof.creation.factorymethod; public interface Creator { Product factoryMethod(String kind, String name); }

ConcreteCreator

package gof.creation.factorymethod; public class ConcreteCreator implements Creator { @Override public Product factoryMethod(String kind, String name) { if (kind.equals("A") || kind.equals("a")) { return new ConcreteProductA(name); } else if (kind.equals("B") || kind.equals("b")) { return new ConcreteProductB(name); } throw new IllegalArgumentException("지원하지 않는 종류입니다."); } }

Test

package gof.creation; import gof.creation.factorymethod.*; import org.assertj.core.api.Assertions; import org.assertj.core.internal.bytebuddy.utility.dispatcher.JavaDispatcher; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.Test; import java.time.LocalDate; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) class FactoryMethodTest { @Test void factory_method_test() throws Exception { //given Creator creator = new ConcreteCreator(); //when Product productA = creator.factoryMethod("A", "ProductA"); Product productB = creator.factoryMethod("B", "ProductB"); //then // product A test assertThat(productA).isInstanceOf(ConcreteProductA.class); assertThat(productA.getName()).isEqualTo("ProductA"); assertThat(productA.getCreatedDate()).isEqualTo(LocalDate.now()); // product b test assertThat(productB).isInstanceOf(ConcreteProductB.class); assertThat(productB.getName()).isEqualTo("ProductB"); assertThat(productB.getCreatedDate()).isEqualTo(LocalDate.now()); // exception test assertThatThrownBy(() -> creator.factoryMethod("C", "ProductC")) .isInstanceOf(IllegalArgumentException.class); } }

느낀점

팩토리 메서드 패턴을 이용할 경우 원하는 클래스의 서브클래스를 손쉽게 이용할수 있다고 생각한다. 이를 통하여 같은 기능을 지원하지만, 서로 다른 클래스를 생성하기 쉽다고 생각한다. 예로 할인 정책의 경우 서로 같은 할인 기능을 지원하지만, 이를 정률인지 정가인지 정하는 정책을 하나의 Creator로 생성할 수 있어 손쉽게 이용이 가능하다고 생각한다.

from http://yoojsblog.tistory.com/220 by ccl(A) rewrite - 2021-11-27 21:28:06