Factory Method
하위 클래스에서 인스턴스 작성하기
본 글은 'java 언어로 배우는 디자인 패턴 입문' 을 바탕으로 기재한 내용입니다.
factory, 공장.
인스턴스를 생성하는 공장을 Template Method 패턴으로 구성한 것이 Factory Method 패턴이다.
Factory Method 패턴에서는 인스턴스를 만드는 방법을 상위 클래스 측에서 결정하지만 구체적인 클래스 이름까지는 결정하지 않으며 구체적인 내용은 하퀴 클래스 측에서 수행한다.
인스턴스 생성을 위한 골격(framework)과 실제의 인스턴스 생성의 클래스를 분리해서 생각할 수 있다.
예제 프로그램
패키지 | 이름 | 해설 |
framework | Product | 추상 메소드 use만 정의되어 있는 추상 클래스 |
Factory | 메소드 create을 구현하고 있는 추상 클래스 | |
idcard | IDCard | 메소드 use를 구현하고 있는 클래스 |
IDCardFactory | 메소드 createProduct, registerProduct를 수현하고 있는 클래스 | |
Anonymous | Main | 동작 테스트용 클래스 |
- framework : 인스턴스 생성을 위한 골격 역할
- idcard : 구체적인 내용을 구현하고 있는 역할
제품
Product class
package framework;
public abstract class Product {
public abstract void use();
}
Factory class
package framework;
public 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 product);
}
IDCard class
package idcard;
public class IDCard extends Product {
private String owner;
IDCard(String owner) {
System.out.println(owner + "의 카드를 만듭니다.");
this.owner = owner;
}
public void use() {
System.out.println(owner + "의 카드를 사용합니다.");
}
public String getOwner() {
return owner;
}
}
IDCardFactory class
package idcard;
import java.util.ArrayList;
import java.util.List;
public class IDCardFactory extends Factory {
private List owners = new ArrayList<>();
protected Product createProduct(String owner) {
return new IDCard(owner);
}
protected void registerProduct(Product product) {
owners.add(((IDCard) product).getOwner());
}
public List getOwners() {
return owners;
}
}
실행
import framework.*;
import idcard.*;
public class Main {
public static void main(String[] args) {
Factory factory = new IDCardFactory();
Product card1 = factory.create("홍길동");
Product card2 = factory.create("이순신");
Product card3 = factory.create("강감찬");
card1.use();
card2.use();
card3.use();
}
}
패턴의 등장인물
* Product(제품)의 역할
framework 쪽에 포함되어있는 이것은 인스턴스가 가져야할 인터페이스(API)를 결정하는 추상 클래스이다.
구체적인 내용은 하위 클래스의 ConcreateProduct역할이 결정한다.
예제에서는 Product 클래스
* Creator(작성자)의 역할
framework쪽에 가까운 이 추상클래스는 실제로 생성하는 ConcreateProduct 역할에 관련한 정보가 없다.
Creator 역할이 가지고 있는 정보는 Product역할 과 인스턴스 생성의 메소드(Product 가 생성된다)를 호출하는 것 뿐이다.
다시말해 new를 사용해서 실제의 인스턴스를 생성하는 대신에, 인스턴스 생성을 위한 메소드를 호출해서 구체적인 클래스 이름에 의한 속박에서 상위클래스를 자유롭게 만드는 것이다.
예제에서는 Factory 클래스
* ConcreateProduct(구체적인 제품)의 역할
idcard 쪽에 해당되며 예제에서는 IDCard 클래스
* ConcreateCreator(구체적인 작성자)의 역할
idcard 쪽에 해당되며 예제에서는 IDCardFactory 클래스
왜 Factory Method 패턴?
framework 와 구체적인 내용이 나뉘어져있다는 점을 주목하여야 한다 (예제에서는 framework 패키지와 idcard 패키지)
만일 동일한 framework 를 사용해서 전혀다른 'TV제품'과 'TV공장'을 만든다고 가정할때
Television 과 TelevisionFactory를 만들 것이고 framework 패키지를 import한 별도의 Television 패키지를 만든다.
여기서 주목할 점은 framework패키지 안에서 idcard 패키지를 import하지 않듯이 Product 클래스나 Factory클래스 안에서는 Television(IDCard)클래스나 Television(IDCardFactory)클래스의 구체적인 이름이 없다. 따라서 동일한 framework에서 생성할 경우에도 framework 패키지의 내용을 수정할 필요가 전혀 없다.
이것을 'framework' 패키지는 'idcard'패키지에 의존하고 있지 않다 라고 표현할 수 있으며
framework 패키지를 건들이지 않고 전혀 상관없는 제품과 공장을 만들 수 있게 되는 것이다.
보강
인스턴스 생성 - 메소드의 구현 방법
예제프로그램에서 Factory 클래스의 createProduct 메소드는 추상 메소드이며 하위 클래스에서 구현하게 된다.
createProduct 메소드의 기술 방법은 다음과 같이 세 가지로 생각 할 수 있다.
1. 추상 메소드
abstract class Factory {
public abstract Product createProduct(String name);
...
}
예제에서 사용했던 방법이다. 추상 메소드로 구현하면 하위 클래스는 반드시 해당 메소드를 구현해야하며 컴파일때 구현여부를 검출한다.
2. 디폴트의 구현을 준비해 주기
class Factory {
public Product createProduct(String name) {
return new Product(name);
}
...
}
Product 클래스를 Factory 클래스에서 직접 new를 사용하여 구현하고 있는 코드이며 추상클래스로 두지 않는다.
3. 에러를 이용한다.
class Factory {
public Product createProduct(String name) {
throw new FactoryMethodRuntimeException();
}
...
}
디폴트의 구현 내용을 에러로 처리하면 하위 클래스에서 구현하지 않았을 경우에 에러를 발생하여 구현되지 않는 것을 알 수 있게 하는 방법이다. (FactoryMethodRuntimeException 은 구현되어 있다고 가정)
관련 패턴
Template Method 패턴
- Factory Method 패턴은 Template Method 패턴의 전형적인 응용이다. 예제에서 create 메소드가 Template 메소드로 되어 있다.
Singleton 패턴
- Creator 역할(또는 ConcreateCreator 역할)을 수행하는 클래스는 대부분 Singleton 패턴으로 만들 수 있다.
프로그램안에서 인스턴스가 복수로 존재할 필요는 거의 없기 때문이다. (예제에서는 Singleton 패턴 아님)
Composite 패턴
- Product역할(또는 ConcreateProduct 역할)에 Composite 패턴을 적용할 수도 있다.
Iterator 패턴
- Iterator 패턴에서 iterator 메소드가 Iterator의 인스턴스를 작성할 때 Factory Method 패턴이 사용되는 경우가 있다.