[英]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和工廠的Hashmap,這樣您就不必在每次創建消息時都創建一個新的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.