제 홈페이지의 모든 글은 anti-nhn license에 따릅니다.



자바 디자인 패턴 3 - Factory Method

1. Factory Method패턴은..

factory는 공장이죠. 객체를 막 찍어내는 놈입니다. 객체 선언은 보통 new 객체() 이런식으로 하죠. factory는 내부에서 그런 일을 해줍니다. 즉 factory를 가져다가 쓰는 부분에서는 new 객체()와 같은 식으로 변수를 선언할 필요가 없습니다. Abstract class나 인터페이스에 대해서 다양한 하위 구현체가 있을 경우에 사용하면 좋습니다. 사용법은 Factory.create(인자는 맘대로) 와 같이 됩니다.

2. 예제

package chap03_StaticFactory;
public interface Animal {
    public void printDescription();
}


package chap03_StaticFactory;
public class AnimalFactory {
    public static Animal create(String animalName){
        if (animalName == null) {
            throw new IllegalArgumentException("null은 안 되지롱~");
        }
        if (animalName.equals("소")) {
            return new Cow();
        }else if (animalName.equals("고양이")) {
            return new Cat();
        }else if (animalName.equals("개")) {
            return new Dog();
        }else{
            return null;
        }
    }
}


package chap03_StaticFactory;
public class Cat implements Animal {
    public void printDescription() {
        System.out.println("쥐잡기 선수");
    }
}


package chap03_StaticFactory;
public class Cow implements Animal {
    public void printDescription() {
        System.out.println("우유 및 고기 제공");
    }
}


package chap03_StaticFactory;
public class Dog implements Animal {
    public void printDescription() {
        System.out.println("주로 집 지킴");
    }
}


package chap03_StaticFactory;
public class Test {
    public static void main(String[] args) {
        Animal a1= AnimalFactory.create("소");
        a1.printDescription();
        Animal a2= AnimalFactory.create("고양이");
        a2.printDescription();
        Animal a3= AnimalFactory.create("개");
        a3.printDescription();
    }
}

이번 것은 소스가 좀 깁니다. 일단 Animal이라는 인터페이스가 있습니다. Cat, Cow, Dog 는 이 인터페이스의 구현체들입니다.
그리고 AnimalFactory가 있는데, 여기서 Animal의 구현체를 돌려줍니다.
Test에서 new Cow()와 같이 하지 않고, AnimalFactory.create("소")를 호출하는 게 일반적인 방법과의 차이입니다.
Animal의 구현체가 더 늘어나면 어떻게 될까요? 전부 new AnotherAnimal()과 같이 생성하는 것보다는 Facotry의 create()메쏘드만 수정하는 게 좀 편하겠죠?

3. Factory 의 유용성

Animal a1 = AnimalFactory.create("소"); 와 같은 코드에서 a1이 Cow라는 것을 굳이 신경쓰지 않겠다는 겁니다. Test클래스 안에는 new 라는 구문 자체가 없습니다. 정확히 어떤 클래스의 인스턴스인지 신경쓰지 않고 구현할 수 있는 장점이 있습니다. 객체 타입이 굉장히 유연해 질 수 있죠.

4. JAVA API에 있는 Factory Method

Factory 패턴의 중요한 특징 중 하나는 Factory에서 리턴할 때는 매번 객체를 새로 만들지 않을 수도 있다는 겁니다.
Boolean.valueOf(boolean) 을 먼저 살펴 보죠.
        Boolean a = Boolean.valueOf(true);
        Boolean b = Boolean.valueOf(true);
        System.out.println(a==b);
이 코드를 실행시키면 어떤 결과가 나올까요? true 가 나옵니다. 왜냐하면  Boolean.valueOf(true) 는 Boolean.TRUE 라는 상수를 리턴합니다. 즉, 인스턴스를 새로 만드는 것이 아니라 기존에 있는 것을 그냥 리턴합니다. 매번 새로 만들지 않는다는 거죠. 각종 Wrapper 클래스에 있는 많은 메쏘드 들이 이렇게 구현되어 있습니다.
Calendar.getInstance() 를 호출하면, 사용자 환경에 맞는 Calendar 객체가 리턴됩니다. 보통은 GregorianCalendar가 리턴된죠.
(이 메쏘드의 이름은 좀 잘못지어진 것 같습니다. 보통 getInstance()는 singleton 패턴에서 쓰이는 이름입니다.)

5. Factory Method의 종류

예제에서는 Factory의 인스턴스를 만들지 않고, static 메쏘드인 create()만을 호출했습니다. 이런 방식을 static factory method라고 합니다.
그냥 factory method라고 하면, factory의 인스턴스를 만들어서 쓰는 방식입니다. static factory에 비해 사용 빈도는 좀 떨어지지만, factory의 인스턴스에 귀속되는 객체를 생성해야 할 때는 이런 방식을 씁니다.(static factory에 비해 많이 쓰지 않으므로 자세한 것은 생략합니다.

by 삼실청년 | 2007/09/20 15:13 | 컴터질~ | 트랙백 | 핑백(3) | 덧글(13)

트랙백 주소 : http://iilii.egloos.com/tb/3791596
☞ 내 이글루에 이 글과 관련된 글 쓰기 (트랙백 보내기) [도움말]
Linked at 건실성실착실 3실 청년! : .. at 2008/06/16 22:56

... ;String b="머시기";와 같이 동일한 값이 다시 호출될 때는 풀에 등록된 값을 리턴해 줍니다. Singleton으로 처리된다고 이해하면 되겠습니다.(Singleton 은 여기와 여기를 읽어보시면 됩니다.)2번의 경우는 new String으로 String 객체를 생성하기 때문에 일반적인 객체 생성과 같은 맥락입니다. ==는 메모리상의 주소만 비교하지 값을 비 ... more

Linked at java design patt.. at 2017/05/31 09:25

... 3. 자바 디자인 패턴 3 &#8211; Factory Method</A> 4. 자바 디자인 패턴 4 &#8211; Template Method 5. 자바 디자인 패턴 5 – Singleton 6. 자바 디자인 패턴 6 – Strategy 7. 자바 디자인 패턴 7 – Composite 8. 자바 디자인 패턴 8 – Decorator 9. 자바 디자인 패턴 9 <a href="http://blog.s ... more

Linked at 자바 디자인 패턴 종류 &#8.. at 2017/09/26 09:15

... &middot;0 Comments 1. 자바 디자인 패턴 1 – Iterator 2. 자바 디자인 패턴 2 – Adapter 3. 자바 디자인 패턴 3 &#8211; Factory Method 4. 자바 디자인 패턴 4 &#8211; Template Method 5. 자바 디자인 패턴 5 – Singleton ... more

Commented by dev용식 at 2008/11/04 11:26
질문이 있는데요 ^^
Animal을 구현하고 있는 클래스가 늘어나면, if로 인한 분기문도 많이 늘어나게 될텐데요
이런 부분을 해소 할 수 있는 방법은 없을까요?
Commented by 삼실청년 at 2008/11/04 16:41
제일 간단한 방법은 Class.forName()을 이용하는 방법입니다.

예제는 아래와 같습니다. (exception 처리는 일단 생략합니다.)

public static Animal create(String animalName){
Class<?> clazz = Class.forName(animalName);
Animal ret = (Animal)clazz.newInstance();
return ret;
}

호출하는 코드는 아래와 같습니다.


Animal a4= AnimalFactory.create("ch03_StaticFactory.Cow");
a4.printDescription();

여기서 문제점은 인자로 클래스의 이름(패키지 포함)을 넘겨줘야 한다는 것입니다. 따라서 나중에 리팩토링을 해서 이름이 바뀌면(클래스 이름이 바뀌는 경우는 흔치는 않지만, 패키지는 바뀌는 경우가 종종있습니다.) 호출하는 클라이언트 코드에 가서 전부 수정해주어야 한다는 것입니다. 구찮은 작업이죠.

그래서 일반적으로는 DI 를 사용하는데, 주로 xml에 기술하는 방식입니다. Spring의 예를 보면, xml을 아래와 같이 만듭니다.

<bean id="소" class = "ch03_StaticFactory.Cow">
</bean>

그리고 factory에서는 처음에 이 xml을 읽어와서 map으로 관리를 합니다. 대략 코드로 설명을 드리자면,

public class AnimalFactory {
private static Map<String, Class> animalMap = new HashMap<String, Class>();
static{
//xml에서 읽어와서 animalMap에 세팅.
}
public static Animal create(String animalName){
return (Animal)animalMap.get(animalName).newInstance();
}
}

와 같이 처리가 됩니다.

기본적인 것은 이정도고 Spring이나 Hibernate의 경우 여러가지 확장적인 기능을 많이 제공합니다. DI (Dependency injection)를 검색해보시면 추가적인 사항을 찾아보실 수 있습니다.
Commented by dev용식 at 2008/11/05 11:24
spring의 Injection 방식이 왜 유용한지 알 수 있는 설명이네요 ^^

루씬 관련된 오픈소스 프로젝트인 solr에서도 설명하신 부분 처럼 xml을 사용해 팩토리 클래스를
구현하는 예를 본적이 있습니다.

설명을 보고나니 왜 그렇게 구현했는지 확실히 알겠네요.

너무나도 자세한 답변에 정말 감사드립니다 ^^
Commented by 김경모 at 2009/02/04 10:31
좋은글 감사합니다.. ^^
Commented by 삼실청년 at 2009/02/04 10:45
디자인 패턴 공부 중이신가 보군요^^
도움이 좀 되셨으면 좋겠네요.
Commented by ssun at 2009/02/11 20:47
와!! 이해가 막 잘되는거 같아요 +_+
(죽어가던 해마가 살아나는 것인지.... 오호오호)


감사합니다.꾸벅(--)(__)(^^);;

나머지 패턴도 계속와서 공부할께요!!
Commented by 삼실청년 at 2009/02/13 10:54
하핫~
나머지 패턴도 계속 써야겠군요.^^
Commented by 들개 at 2009/04/30 17:06
환상적인 설명과 답변입니다. 쥔장님 글 읽으면서 완전 흥분중..;
Commented by 삼실청년 at 2009/05/07 18:26
ㅋㅋ 고맙습니다. 요즘 구찮아서 또 글 뜸한데...
이제 자바스크립트 쪽으로 좀 써볼까 생각 중임다.
Commented by 패턴공부중 at 2014/11/17 17:31
프로토타입 패턴 공부중에 타고 넘어와서 보고 갑니다.
제가 인프라가 약해서 물어볼사람도 없고 토론할 사람도 없어서 이렇게 글 남깁니다.

Gof의 팩토리 메소드 패턴 이야기라면 예제가 적절치 않다고 생각합니다.
오히려 추상 팩토리 패턴의 예제에 더 가까운 코드라고 생각합니다.
(혹은 심플 팩토리의 예제. 하지만 심플 팩토리도 추상 팩토리 패턴의 일종이라고 생각합니다.)

팩토리 메소드 패턴은 서브클래스에서 객체를 생성하게 하고 그렇게 때문에 객체를 인터페이스화하는것이고 추상 팩토리 패턴은 객체를 생성하기 위해 인터페이스를 만든다고 생각합니다.

Gof책에서 두 패턴의 참여객체를
Creator 와 Factory라고 달리 표현하는 이유는 거기에 있다고 생각합니다.

Commented by 삼실청년 at 2015/01/22 21:23
아, 그러네요. 이건 심플 팩토리가 맞는 거 같네요.
그러나.. 고칠라니.. 막막... 몇년 전에 쓴거야 당췌..ㅜㅜ
Commented by devkim at 2015/05/06 14:25
글 잘 보았습니다.
animal을 상속하고 있는 클래스의 파라미터 두개를 갖는 생성자가 존재한다고 할때 그 생성자를 호출하는 사항은 어떻게 될가요? newInstance하게 되면 디폴트 생성을 하게 되는데,
Commented by 들개 at 2015/07/28 15:03
Class classToLoad = MyClass.class;

Class[] cArg = new Class[3]; //Our constructor has 3 arguments
cArg[0] = Long.class; //First argument is of *object* type Long
cArg[1] = String.class //Second argument is of *object* type String
cArg[2] = int.class; //Third argument is of *primitive* type int

Long l = new Long(88);
String s = "text";
int i = 5;

classToLoad.getDeclaredConstructor(cArg).newInstance(l, s, i);

이렇게 사용하시면 되겠습니다.

출처 : http://stackoverflow.com/questions/234600/can-i-use-class-newinstance-with-constructor-arguments

:         :

:

비공개 덧글

◀ 이전 페이지          다음 페이지 ▶