最近大家都在忙着功能优化和BUG,而我却比较闲,开发的模块没有什么BUG,也没什么需要优化的,便抽些时间来改下正在开发项目中的一些坏代码。
//坏代码
SC_ResponseMsgProto.Builder responseMsgBuilder = SC_ResponseMsgProto.newBuilder();//重复一
DataMsgProto.Builder dataMsgBuilder = DataMsgProto.newBuilder();//重复二
dataMsgBuilder.setB(b.build());
responseMsgBuilder.setDataMsg(dataMsgBuilder.build());//重复三
responseMsgBuilder.setA(a.build());
sendMsgToClient(channel, responseMsgBuilder.build());//重复四
message SC_ResponseMsgProto {
optional DataMsgProto dataMsg = 1;
optional A a = 2;
message DataMsgProto {
optional B b = 1;
}
message A {
optional int32 num = 1;
optional C c = 2;
}
message B {
optional int32 num = 1;
}
message C {
optional int32 num = 1;
}
}
项目客户端和服务器采用的是Google的protobuf协议,数据结构设计采用的是森林结构。如上所示,SC_ResponseMsgProto是最顶层对象,在最顶层对象下有多个不同类别的子对象,如A、B、C,子对象下还拥有一些不同类别的子对象C,最终成为一个森林结构,森林里每个根对象都是一颗树。对象间有时有一些引用关系,比如,A对象使用了C引用。这样的设计在开发中是比较常见的,但是我们在项目中使用时比较流水账,大量的顶层对象代码在不同地方重复出现在项目中。对于每个程序员员来说,避免重复代码是大家都想做的。于是我大约花了不到30行代码来避免以上标注的四个重复地方,代码如下:
/**
* 下发给客户端的消息
*/
private final LinkedList<GeneratedMessageLite> responseMsgs = new LinkedList<>();
//添加消息
public void putMsg(GeneratedMessageLite msg) {
synchronized (responseMsgs) {
responseMsgs.add(msg);
}
}
//发送消息
public void sendMsg(Channel channel) {
//和客户端通讯的数据
SC_ResponseMsgProto.Builder responseMsgBuilder = SC_ResponseMsgProto.newBuilder();
DataMsgProto.Builder dataMsgBuilder = DataMsgProto.newBuilder();
try {
for (GeneratedMessageLite messageLite : responseMsgs)
{
if (messageLite instanceof A) {
responseMsgBuilder.setA((A) messageLite);
} else if(messageLite instanceof B) {
dataMsgBuilder.setB((B) messageLite);
}
responseMsgBuilder.setDataMsg(dataMsgBuilder.build());
}
sendMsgToClient(channel, responseMsgBuilder.build());
}catch (Exception e) {
e.printStackTrace();
logger.error(e.getMessage(), e);
} finally {
responseMsgs.clear();
}
}
//优化后代码
role.putMsg(a.build());
role.putMsg(b.build());
role.sendMsg(channel);
虽然避免了重复代码,但是细心的童鞋可能发现在sendMsg()里意外的出现了不是最顶层对象的DataMsgProto.如果当DataMsgProto里出现相同类型的b、b1对象时,那就无法从类型上去区分它们了。我想到的解决方案是使用自定义唯一key来区分或者在定义协议时不允许出现相同类型的(这里并不推荐,毕竟有时候多个类型能解决不少问题)。
有同学可能会想能否使用变量名字b作为唯一区分标识,但是Java能否获取变量的名字?答案是不能的。详细参考R大在知乎上的回答。
message DataMsgProto {
optional B b = 1;
optional B b1 = 2;
}