簡體   English   中英

Java-靜態工廠方法和switch語句

[英]Java - static factory method and switch statements

我正在處理一組消息對象,每個對象都有對應於它們的唯一標識符。 每個消息都可以從Map或ByteBuffer構造(消息是二進制的,但是我們知道如何往返於二進制表示形式)。

用於構造這些消息的當前實現大致如下:

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;
    }
}

現在, 喬什·布洛赫(Josh Bloch)的有效Java討論了第1項:考慮靜態工廠方法而不是構造函數,這似乎是該模式有用的地方(客戶端不會直接訪問Message子類型的構造函數;相反,他們會經歷此過程方法)。 但是我不喜歡我們必須記住保持兩個switch語句保持更新的事實(這違反了DRY原理)。

我將對實現此目標的最佳方法有任何見解; 我們不是在緩存對象(每次對fromMap或fromByteBuffer的調用都將返回一個新對象),這抵消了使用靜態工廠方法的一些好處。 關於此代碼的某些信息使我感到不對,因此,我很想聽聽社區對於這是否是構造新對象的有效方法的想法,或者如果不是一種更好的解決方案,該想法。

也許您可以創建一個接口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和工廠的H​​ashmap,這樣您就不必在每次創建消息時都創建一個新的Factory對象。 另請參閱下面評論

和你的方法:

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語句。

(沒有測試,所以可能有一些編譯錯誤/錯別字)

如果您有對象,請實現一個接口,該接口聲明工廠方法,例如:

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

在靜態嵌套類中,您的工廠可以創建一個包含由uuid索引的工廠對象的Map

Map map = new HashMap();

map.put(Integer.valueOf(FirstMessage.UUID), new FirstMessage.Factory());

並通過地圖查找替換您的開關:

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);
}

這可以輕松擴展以支持其他構造方法。

方法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用作工廠的鍵。

有沒有一種方法可以將ByteBuffer轉換為Map或其他? 如果將輸入轉換為規范化形式並應用唯一開關,那就太好了。

如果您要獲取一條消息並使用特定的值對其進行格式化(例如“表:tableName沒有名為:colName的列”),則可以將ByteBuffer轉換為Map並調用第一個方法。 如果需要新的msgId,則僅擴展fromMap方法。

這就像分解公共部分一樣。

我建議將枚舉類型與抽象方法一起使用,例如以下示例:

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;
}

這種方法使您的靜態方法不必知道所支持的MessageType以及如何構建它們-您可以添加,修改或刪除消息而無需重構靜態方法。

關於使用枚舉作為策略的解決方案(向枚舉添加策略方法),干凈的代碼清單應用程序說這是可維護性的殺手。
盡管我不知道為什么我想與您分享。

您可以使用AbstractFactory模式,其中每種消息類型都有一個工廠類,該類通過緩沖區或映射返回消息。 然后,您創建一個方法,該方法返回給您相應的工廠對象。 然后從返回的工廠中創建消息。

您應該抽象您的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();
}

無論如何,這只是一個主意,您必須做一些反射(獲取構造函數)才能傳遞字段。

您可以修改Message,使其具有兩種初始化方法,一種用於Map,另一種用於ByteBuffer(而不是兩個構造器版本)。 然后,工廠方法將返回構造的(但未初始化的)消息,然后在返回的對象上使用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);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM