繁体   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