2010-01-11 4 views
7

각 메시지 개체에는 고유 식별자가 해당하는 메시지 개체 집합이 있습니다. 각 메시지는 Map 또는 ByteBuffer (메시지는 바이너리이지만 우리는 바이너리 표현과주고받는 방법을 안다.)로부터 만들 수있다.Java 정적 팩토리 메서드 및 스위치 문

public static Message fromMap(int uuid, Map<String, Object> fields) { 
    switch (uuid) { 
     case FIRST_MESSAGE_ID: 
     return new FirstMessage(fields); 
     . 
     . 
     . 
     default: 
      // Error 
      return null; 
    } 
} 

public static Message fromByteBuffer(int uuid, ByteBuffer buffer) { 
    switch (uuid) { 
     case FIRST_MESSAGE_ID: 
     return new FirstMessage(buffer); 
     . 
     . 
     . 
     default: 
      // Error 
      return null; 
    } 
} 

이제, 항목 1에 대한 Josh Bloch's Effective Java 회담이 : 다음과 같이

이러한 메시지를 구성하는 현재의 구현은 대략입니다 대신 생성자의 정적 팩토리 메소드를 고려, 이것은이 패턴이 어디 장소를 것 같다 유용합니다 (클라이언트는 Message 하위 유형의 생성자에 직접 액세스하지 않고 대신이 메소드를 사용합니다). 그러나 저는 두 개의 switch 문을 항상 업데이트해야한다는 사실을 기억해야합니다 (DRY 원칙을 위반 함).

이 작업을 수행하는 가장 좋은 방법에 대해 통찰력을 주셔서 감사합니다. 우리는 객체를 캐싱하지 않습니다 (fromMap 또는 fromByteBuffer에 대한 각각의 호출은 새로운 객체를 반환합니다). 이렇게하면 정적 팩토리 메소드를 사용하는 이점을 무효화합니다. 이 코드에 관한 어떤 것이 나를 잘못 생각한 것입니다. 그래서 이것이 새로운 객체를 만들 수있는 올바른 방법인지 아니면 더 나은 해결책이 될지에 대한 커뮤니티의 생각을 듣고 싶습니다.

답변

12

어쩌면 당신이 그것의 인터페이스의 MessageFactory 및 구현을 만들 수 있습니다

public interface MessageFactory { 
    Message createMessage(Map<String, Object> fields); 
    Message createMessage(ByteBuffer buffer); 
} 

public class FirstMessageFactory implements MessageFactory { 
    public Message createMessage(Map<String, Object> fields){ 
    return new FirstMessage(fields); 
    } 

    public Message createMessage(ByteBuffer buffer){ 
    return new FirstMessage(buffer); 
    } 

} 

다음, 위의 방법과 동일한 클래스의 메소드 getFactoryFromId : 대신의 그러나

public static MessageFactory getMessageFactoryFromId(int uuid){ 
switch (uuid) { 
    case FIRST_MESSAGE_ID: 
    return new FirstMessageFactory(); 
    ... 
    default: 
     // Error 
     return null; 
    } 
} 

, ID와 팩토리가 포함 된 Hashmap을 생성하는 것이 더 좋으므로 메시지를 작성할 때마다 새 Factory 객체를 작성할 필요가 없습니다. comment below도 참조하십시오.

및 방법 :

public static Message fromMap(int uuid, Map<String, Object> fields) { 
    getMessageFactoryFromId(uuid).createMessage(fields); 
} 

public static Message fromByteBuffer(int uuid, ByteBuffer buffer) { 
    getMessageFactoryFromId(uuid).createMessage(buffer); 
} 

이 방법, 당신은 공장 패턴을 사용하고 두 번 같은 switch 문을 할 필요가 없다.

(이 테스트하지 않았기 때문에 아마도 일부 컴파일 오류/오타는)

+0

팩토리 객체가 맵에 저장되고 uuid로 검색되면 스위치가 필요하지 않습니다. – rsp

+0

그래, 그게 내가 말한거야 :-) 나는 당신의 대답에 대한 링크도 추가했다. :) – Fortega

+0

그것은 좋은 해결책이다. (그래서 +1).하지만 목표가 더블 스위치를 없애는 것이었다면, 스위치 로직을 별도의 방법으로 출력하십시오. (지도는 기본적으로 가장 위장한 스위치입니다.) – wds

0

는 다음과 같은 방법으로하거나 당신에게 메시지를 반환하면 각 메시지 유형에 대한 팩토리 클래스를했을 AbstractFactory 패턴을 사용할 수 있습니다 버퍼 또는 맵. 그런 다음 해당 팩토리 객체를 반환하는 메서드를 만듭니다. 반환 된 팩토리에서 메시지를 작성합니다.

1

ByteBuffer를 Map 또는 다른 것으로 변환 할 수있는 방법이 있습니까? 입력을 정규화 된 형식으로 변환하고 고유 한 스위치를 적용하는 것이 좋습니다.

메시지를 받고 특정 값 (예 : "table : tableName에 colName"이라는 열이 없음)으로 서식을 지정하면 ByteBuffer를 Map으로 변환하고 첫 번째를 호출 할 수 있습니다 방법. 새로운 msgId가 필요하면 fromMap 메소드 만 확장하십시오.

공통 부분을 인수 분해하는 것과 같습니다.당신이 당신의 객체가 같은 팩토리 메소드 선언 인터페이스를 구현하는 경우

3

는 :

Map map = new HashMap(); 

map.put(Integer.valueOf(FirstMessage.UUID), new FirstMessage.Factory()); 
: 정적 중첩 클래스에

public Message newInstance(Map<String, Object> fields); 
public Message newInstance(ByteBuffer buffer); 

을, 당신 공장은 UUID에 의해 색인을 생성 Map 포함하는 공장 개체를 만들 수 있습니다

을 입력하고 스위치를지도 조회로 바꿉니다.

public static Message fromMap(int uuid, Map<String, Object> fields) { 

    Factory fact = map.get(Integer.valueOf(uuid)); 

    return (null == fact) ? null : fact.newInstance(fields); 
} 

public static Message fromByteBuffer(int uuid, ByteBuffer buffer) { 

    Factory fact = map.get(Integer.valueOf(uuid)); 

    return (null == fact) ? null : fact.newInstance(buffer); 
} 

이것은 다른 구성 방법을 지원하도록 쉽게 확장 할 수 있습니다.

+0

작은 댓글 : 새로운 Integer() 대신 Integer.valueOf()를 사용해야합니다. – I82Much

+0

좋은 점, 그것을 바꿨습니다. – rsp

1

나는 다음 예와 같은 추상적 인 방법으로 열거 형을 사용하는 것이 좋습니다 ...

enum MessageType { 

    FIRST_TYPE(FIRST_MESSAGE_ID) { 

     @Override 
     Message fromByteBuffer(ByteBuffer buffer) { 
      return new FirstMessage(buffer); 
     } 

     @Override 
     Message fromMap(Map<String, Object> fields) { 
      return new FirstMessage(fields); 
     } 

     @Override 
     boolean appliesTo(int uuid) { 
      return this.uuid == uuid; 
     } 

    }, 

    SECOND_TYPE(SECOND_MESSAGE_ID) { 

     @Override 
     Message fromByteBuffer(ByteBuffer buffer) { 
      return new SecondMessage(buffer); 
     } 

     @Override 
     Message fromMap(Map<String, Object> fields) { 
      return new SecondMessage(fields); 
     } 

     @Override 
     boolean appliesTo(int uuid) { 
      return this.uuid == uuid; 
     } 

    }; 

    protected final int uuid; 

    MessageType(int uuid) { 
     this.uuid = uuid; 
    } 

    abstract boolean appliesTo(int uuid); 

    abstract Message fromMap(Map<String, Object> map); 

    abstract Message fromByteBuffer(ByteBuffer buffer); 

} 

이 방법은, 기존의 정적 인 방법에서 당신은 단순히이 작업을 수행 할 수

public static Message fromByteBuffer(int uuid, ByteBuffer buffer) { 
    Message rslt = null; 
    for (MessageType y : MessageType.values()) { 
     if (y.appliesTo(uuid)) { 
      rslt = y.fromByteBuffer(buffer); 
      break; 
     } 
    } 
    return rslt; 
} 

이 방법을 사용하면 정적 메소드가 지원하는 MessageTypes 및이를 빌드하는 방법에 대해 알지 않아도되므로 정적 메소드를 리 팩토링하지 않고도 메시지를 추가, 수정 또는 제거 할 수 있습니다.

0

당신이해야 추상적 인 당신의 FirstMessage 객체 : (스위치 반대)

public abstract Message { 
    // ... 
} 

이 그런 다음 공장에서 그들을 캐시 : 당신의 팩토리 메소드에서

private static final Map<Integer, Class<Message>> MESSAGES = new HashMap<Integer, Class<Message>>(); 
static { 
    MESSAGES.put(1, FirstMessage.class); 
} 

:

public static Message fromMap(UUID uuid, Map<String, Object> fields) { 
    return MESSAGES.get(uuid).newInstance(); 
} 

그건 그냥 어쨌든, 당신은 몇 가지 반사 (생성자를 얻을)해야 할 것 같은 생각이야 패스 구역.

0

Message를 수정하여 Map과 ByteBuffer의 두 초기화 메소드 (두 개의 컨스트럭터 버전 대신)를 하나씩 사용할 수 있습니다. 그런 다음 factory 메소드가 생성 된 (그러나 초기화되지 않은) Message를 반환 한 다음 반환 된 객체에서 Map 또는 ByteBuffer를 사용하여 initialize를 호출합니다. 공공 팩토리 메소드가 될 다음

private static Message createMessage(int uuid) { 
    switch (uuid) { 
     case FIRST_MESSAGE_ID: 
     return new FirstMessage(); 
     . 
     . 
     . 
     default: 
      // Error 
      return null; 
    } 

} 

과 : - - :

public static Message fromMap(int uuid, Map<String, Object> fields) { 
    Message message = createMessage(uuid); 
    // TODO: null checking etc.... 
    return message.initialize(fields); 
} 

public static Message fromByteBuffer(int uuid, ByteBuffer buffer) { 
    Message message = createMessage(uuid); 
    // TODO: null checking etc.... 
    return message.initialize(buffer); 
} 
3

그래서 당신은 지금 공장과 같은 방법을 1 : 대신에 정적 팩토리 메소드를 고려하십시오. 생성자

이미 팩터 리 메서드 뒤에 생성자를 숨겨서이 작업을 수행하고 있으므로 다른 팩토리 메서드를 여기에 추가 할 필요가 없습니다.

그래서 Factory 인터페이스와지도로 할 수 있습니다.(기본적으로 모든 사람이 이미 무슨 말을하지만, 차이, 당신은 공장 내부 클래스를 사용하여 인라인 수)

interface MessageFactory { 
    public Message createWithMap(Map<String,Object> fields); 
    public Message createWithBuffer(ByteBuffer buffer); 
} 

Map<MessageFactory> factoriesMap = new HashMap<MessageFactory>() {{ 
    put(FIRST_UUID, new MessageFactory() { 
     public Message createWithMap(Map<String, Object> fields) { 
      return new FirstMessage(fields); 
     } 
     public Message createWithBuffer(ByteBuffer buffer){ 
      return new FirstMessage(buffer); 
     } 
    }); 
    put(SECOND_UUID, new MessageFactory(){ 
     public Message createWithMap(Map<String, Object> fields) { 
      return new SecondMessage(fields); 
     } 
     public Message createWithBuffer(ByteBuffer buffer){ 
      return new SecondMessage(buffer); 
     } 
    }); 
    put(THIRD_UUID, new MessageFactory(){ 
     public Message createWithMap(Map<String, Object> fields) { 
      return new ThirdMessage(fields); 
     } 
     public Message createWithBuffer(ByteBuffer buffer){ 
      return new ThirdMessage(buffer); 
     } 
    }); 
    ... 
}}; 

을 그리고 당신의 호출이 켜집니다 현황 :

public static Message fromMap(int uuid, Map<String, Object> fields) { 
    return YourClassName.factoriesMap.get(uuid).createWithMap(fields); 
} 

public static Message fromByteBuffer(int uuid, ByteBuffer buffer) { 
    return YourClassName.factoriesMap.get(uuid).createWithBuffer(buffer); 
} 

때문에 사용되는 UUID 스위치는 공장의 핵심으로 사용됩니다.

0

열거 형에 전략을 추가하는 전략으로 열거 형을 사용하는 솔루션에 대해 깨끗한 코드 치트 시트 응용 프로그램은 이것이 유지 보수 성 살인자라고 말합니다.
나는 이것을 왜 당신과 공유하고 싶은지 모르겠다.

+0

흥미 롭습니다. 그러나 답이 아닌 의견으로 주석을 추가하십시오. – I82Much