我正在处理一组消息对象,每个消息对象都有一个与之对应的唯一标识符。每条消息都可以从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's Effective Java讨论第1项:考虑静态工厂方法而不是构造函数,这似乎是这种模式有用的地方(客户端不直接访问Message子类型的构造函数;而是他们通过这种方法)。但我不喜欢这样一个事实,即我们必须记住更新两个switch语句(违反DRY原则)。
我很欣赏任何有关实现这一目标的最佳方法的见解;我们不是缓存对象(每次调用fromMap或fromByteBuffer都会返回一个新对象),这会否定使用像这样的静态工厂方法的一些好处。关于这段代码的一些事情让我感到错误,所以我很想听听社区关于这是否是构建新对象的有效方法的想法,或者如果不是更好的解决方案。
答案 0 :(得分: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语句。
(没有测试这个,所以可能是一些编译错误/拼写错误)
答案 1 :(得分:3)
如果你的对象实现了一个声明工厂方法的接口,如:
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);
}
这可以很容易地扩展到支持其他构造方法。
答案 2 :(得分:3)
tem 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被用作工厂的钥匙。
答案 3 :(得分:1)
有没有办法将ByteBuffer转换为Map或其他东西?如果将输入转换为规范化形式并应用唯一开关,那就太好了。
如果您要做的是获取消息并使用特定值格式化(例如“表:tableName没有名为colName的列”),您可以将ByteBuffer转换为Map并调用第一个方法。如果您需要新的msgId,则只扩展fromMap方法。
这就像分解公共部分一样。
答案 4 :(得分: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;
}
这种方法可以使您的静态方法不必了解您支持的MessageType以及如何构建它们 - 您可以添加,修改或删除消息,而无需重构静态方法。
答案 5 :(得分:0)
您可以使用AbstractFactory
模式,在每个消息类型中都有一个工厂类,它通过缓冲区或映射返回消息。然后,您创建一个方法,返回相应的工厂对象。然后从返回的工厂中创建消息。
答案 6 :(得分: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();
}
这只是一个想法,你必须做一些反思(让构造函数)工作来传递字段。
答案 7 :(得分:0)
你可以修改Message,使它有两个初始化方法,一个用于Map,一个用于ByteBuffer(而不是两个contructor版本)。然后你的工厂方法返回构造的(但未初始化的)消息,然后在返回的对象上使用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);
}
答案 8 :(得分:0)
关于使用枚举作为策略的解决方案(向枚举添加策略方法)清洁代码cheet表应用程序说这是一个可维护性杀手。
虽然我不知道为什么我想和你分享这个。