Factory Pattern

Learning/Design Pattern 2010.02.15 02:44
인터페이스를 사용해 코드를 유연하게 만들긴 했지만 인터페이스를 사용하려면 어쩔 수 없이 new를 거쳐서 특정 객체를 만들어야만 합니다. 팩토리 패턴을 사용하면 new를 사용하는 부분을 캡슐화하여 클라이언트가 원하는 객체를 말하기만 하면 직접 객체를 생성하여 반환해주는 역할을 합니다.

팩토리 패턴은 new를 캡슐화 합니다. 즉, 객체를 실제로 생성하는 부분을 클라이언트로부터 감춥니다.

여기서 클라이언트가 "만들어!" 라는 명령(create 메소드)을 하게 되면, create 메소드의 파라미터로 만들고자 하는 객체의 타입을 구별하게 됩니다. 그리고 인터페이스의 특정 객체를 리턴하게 되겠죠. create 메소드를 가진 모든 객체는 공장(Factory)입니다. 공장은 외주를 주어 제품을 만들수도 있고, 일정 부분은 자신이 처리할 수도 있죠. 제품을 만들기 위해 어떤 부품들을 제조해야할지 아는 것은 그 제품을 만드는 공장뿐입니다. 일을 시키는 쪽(클라이언트)에선 어찌 되었든 제품만 받으면 되니까요.

여기서 집 만드는 공장인 HouseFactory를 생각해 보도록 합시다. HouseFactory(이하 H.F)에서는 여러 종류의 집을 만들 줄 압니다. 예쁜 정원이 있는 2층짜리 단독 주택을 만들수도 있고, 여러 세대가 모여 사는 아파트형 건물이나 전통적인 한옥을 만들수도 있습니다.

House에는 크게 세 종류가 있다고 합시다.
Apartment, KoreanStyleHouse, WesternStyleHouse
물론 extends House 겠죠.
public abstract class House {
     Roof roof;
     Window window;
     Door door;
     ....
}

이제 클라이언트가 H.F를 찾아옵니다.
HouseFactory HF = new HouseFactory();

그리고 H.F에게 명령을 내립니다.
"아파트를 한 채 짓고 싶은데..."
House clientsHouse = HF.create("Apartment");


create 메소드는 다음과 같이 생겼습니다.
public House create(String houseType){
     if("Apartment".equals(houseType)){
           return new Apartment();
     }
     else if("KoreanStyleHouse".equals(houseType)){
           return new KoreanStyleHose();
     }
     else if("WesternStyleHouse".equals(houseType)){
           return new WesternStyleHose();
     }
}

이 방식을 "간단한 팩토리 패턴"이라고 합니다.

이제 H.F가 많이 성장해서 3가지 타입의 집을 만드는 공장으로 쪼개졌다고 합시다.
이제 H.F는 ApartmentHFWesternStyleHF, KoreanStyleHF로 나누어지고, 각각의 공장은 H.F를 extends합니다. 유일하게 세 공장이 공통적으로 가지는 행동은 "집을 만든다 = create 메소드" 뿐입니다.

클라이언트는 자신이 원하는 집을 짓기 위해 3개의 공장 중 하나를 골라야 합니다. 또한 각 공장은 고객의 요구에 세밀하게 부응할 수 있도록 자신이 만들 수 있는 집을 더욱 세분화했습니다.

클라이언트가 다시 H.F로 찾아와서 WesternStyleHF(이하 W.H.F)를 찾아갑니다.
HouseFactory WHF = new WesternStyleHF();

그리고는 W.H.F에게 명령을 내립니다.
"이번에는 2층짜리 양옥을 짓고 싶네요."
House clients2ndHouse = WHF.create("secondFloorHouse");

이제 WHF에서 전달받은 type(="secondFloorHouse")을 가지고 H.F에서 했던 것과 똑같이 분기문을 마구 거쳐서 집을 만들어 냅니다.

이 방식이 "팩토리 메소드 패턴" 입니다. 자세히 들여다 볼 것도 없이, "간단한 팩토리 패턴"의 확장판이라고 볼 수 있습니다. 클라이언트는 어느 공장에 발을 들이냐에 따라서 2층짜리 아파트도 만들 수 있고, 2층짜리 한옥도 만들 수 있습니다.
팩토리 메소드 패턴은 객체를 생성하기 위한 인터페이스(HouseFactory)를 정의하는데 어떤 클래스의 인스턴스를 만들지는 서브클래스(AHF,KHF,WHF...)에서 결정하게 만듭니다.


이제 3번째 집을 만들 차례로군요.

HF 계열사(오호)는 이제 너무 커져서 재료를 만드는 일을 모두 감당하기 힘들게 됐습니다. 그래서 집을 만드는 데 필요한 재료를 공급받기 위해 외주를 주게 되었습니다. 외주를 받게 된 운명적인 회사의 이름을 HouseMaterialsFactory(이후 H.M.F)라고 합시다.

public interface HouseMaterialsFactory {
     public Roof createRoof();
     public Window createWindow();
     public Door createDoor();
     ...
}

그리고 각각의 공장(AHF, KHF, WHF)에서는 같은 종류의 재료를 만들긴 하지만 각각의 스타일은 다른 세개의 외주업체를 구했습니다. ApartmentHouseMaterialsFactory, KoreanHouseMaterialsFactory, WesternHouseMaterialsFactory. 각각의 클래스는 같은 인터페이스(H.M.F)를 공유합니다.

K.H.F를 예로 들어서 설명해 보도록 하겠습니다.

public class KoreanStyleHF extends HouseFactory {
    public House create(String houseStyle){
        if("secondFloorHouse".equals(houseStyle)){
             // 몇 층인지만 알려주면 됩니다.
             // 나머지는 재료 생산 업체에서 알아서 하겠죠.

             return new SecondFloorHouse(new KoreanHouseMaterialsFactory);
        } else if (...) {...}
    }
}

public class SecondFloorHouse extends House{
        // 재료 공장을 만들고자 하는 집의 구성 요소로 만듭니다.
     HouseMaterialsFactory factory;
     public SecondFllorHouse(HouseMaterialsFactory factory){
          this.factory = factory;
          prepare();
     }
     public void prepare(){
                  // 각각의 재료를 준비합니다.
          this.roof = factory.createRoof();
          this.window = factory.createWindow();
          this.door = factory.createDoor();
          ...
     }
}

이런식으로 House 를 초기화 하는 데 재료 생산 업체명을 기술해 주면, 만들어진 집은 그 업체에서 생산된 재료만 쓰게 됩니다.

다시 클라이언트 등장입니다.
이번엔 K.H.F로 들어가네요.
HouseFactory KHF = new KoreanStyleHouseFactory();

그리고는 이렇게 주문합니다.
"이번엔 한옥 스타일의 2층집을 만들어주시죠."
House Clients3rdHose = KHF.create("SecondFloorHouse");

겉으로 보기에 팩토리 메소드나 추상 팩토리나 클라이언트의 행동 방식은 똑같습니다. 원하는 회사로 들어가서 원하는 스타일을 주문하는 거죠.여기서 쓰인 방식이 "추상 팩토리 패턴" 입니다.
추상 팩토리 패턴에서는 인터페이스를 이용하여 서로 연관된, 또는 의존하는 객체를 구상 클래스를 지정하지 않고도 생성할 수 있습니다.

다른 점을 굳이 꼽으라면 두가지 정도를 들 수 있습니다.

1. 추상 팩토리 패턴은 "구성", 팩토리 메소드 패턴은 "상속"을 사용합니다.
추상 팩토리 패턴에서 결정적인 역할을 하는 녀석은 역시 House의 구성물인 HouseMaterialFactory입니다. 어떤 재료 생산 공장을 쓰느냐에 따라 결과물이 달라지게 되죠.
하지만 팩토리 메소드 패턴에서 결정적인 역할을 하는 녀석은 KoreanStyleHF입니다. HouseFactory의 create 메소드를 구현해서 만들어질 집의 모든 요소를 결정합니다.

2. 추상 팩토리 패턴을 사용하여 "제품군"을 만들어낼 수 있습니다. 여러가지 관련된 제품들의 객체 생성이 하나의 인터페이스만을 사용하여 가능하게 됩니다.


아직도 두 가지 패턴이 헷갈리는데, 구현되어 있는 코드에만 집중했기 때문인 것 같습니다. 결과적으로 위의 코드는 다른 방식으로도 충분히 구현이 가능합니다. KoreanStyleHouseFactory가 클라이언트가 되고 KoreanHouseMaterialsFactory가 공장이 된다는 관점에서 클라이언트가 코드를 어떻게 사용할 것인지를 생각하면 추상 팩토리의 개념이 좀 더 확실하게 구별될 것 같다는 생각이 듭니다.

참고: Head First 디자인 패턴 4장 팩토리 패턴
저작자 표시 비영리
신고
posted by purecolor

Observer Pattern

Learning/Design Pattern 2010.01.16 23:57
<옵저버 패턴을 이해하기 위해 알아야 할 세가지 요소>
1. Observer(java.util.Observer) : 말 그대로 관찰자
2. Subject(java.util.Observable) : 자신의 상태를 1번에게 알리는 발행자
3. Data : 2번이 1번에게 전달하는 정보

Observer Pattern은 일상생활에서 어렵지 않게 접할 수 있습니다. 텔레비전이나 라디오 방송, 신문 구독, 문자 알림 같은 일방적인 broadcasting을 생각하시면 되겠습니다. 어떤 방송시스템과 그것을 구독하기로 한 사람 간에는 1:N 관계가 성립하며, 가지고 있는 정보가 변화할 때마다 구독 신청을 한 사람들에게 알려줍니다.
(라디오의 경우는 불특정 다수에게 보내는 신호이므로 정확한 예는 아니라고 할 수도 있겠네요.)

Observer Pattern은 반드시 느슨한 결합(Loose Coupling)이어야 하며 Observer는 Subject에게 어떠한 영향도 미치지 않아야 합니다. Observer가 Subject에게 새로운 정보를 달라고 요청하고 싶다면 직접 Subject의 메소드를 부르지 않고 다른 중간 객체를 통하는 것이 좋을 것입니다.

* 느슨한 결합(Loose Coupling)이 가능하려면?
두개의 어떤 클래스에서도 상대방의 모습(멤버)과 행동(메소드)에 대한 정보를 최소한으로 알고 있어야 합니다. 자바에서는 인터페이스를 사용합니다.

참고 : Head First Design Patterns 2장 옵저버 패턴
저작자 표시 비영리
신고
posted by purecolor


티스토리 툴바