这几天,接手一个同事的代码,关于微信接口开发的,那一堆的 if,看得哥蛋痛了,这个毛病也是很多新手容易犯的,所以特地把这次重构写出来。
下面来我们看看这个代码的问题所在,if else 里面的代码块逻辑,不好改,使得它的重用性为 0,并且难以阅读。当然,如果 if
只有一两个,或者3个,这样写是问题不大的。
但是如果多了,这种代码便会让维护变得困难起来。
if (strMsgType == "text")
{
textContentClient = rootElement.SelectSingleNode("Content").InnerText;
strResult = SetMsgType_Text(strClientName, textContentClient, db, strServerName, Identity);
System.Diagnostics.Trace.WriteLine(strResult);return Content(strResult);
}
else if (strMsgType == "event")
{
string eventType = rootElement.SelectSingleNode("Event").InnerText.ToLower();
if (eventType == "subscribe")
{
string keyCode = "";
if (rootElement.SelectSingleNode("EventKey") != null)
keyCode = rootElement.SelectSingleNode("EventKey").InnerText.ToLower();strResult = FormatEventSubscribe(keyCode);
RecordReplyMessage();
return Content(strResult);
}
else if (eventType == "scan")
{
string keyCode = rootElement.SelectSingleNode("EventKey").InnerText.ToLower();var outLetName = "欢迎关注";
var outletDB = ShoppingContext.CreateInstance(Identity);
var outLetModel = outletDB.Outlets.FirstOrDefault(o => o.SceneId == Int32.Parse(keyCode));
if (outLetModel != null)
outLetName += outLetModel.Name;return Content(GetTextTemp(strClientName, strServerName, outLetName));
}
else if (eventType == "click")
{
string keyCode = rootElement.SelectSingleNode("EventKey").InnerText.ToLower();
strResult = FomatMenuMessage(keyCode);
return Content(strResult);}
else if (eventType == "unsubscribe")
{
var subIds = db.ReplyRecords.Where(r => r.FromOpenId == this.ClientId.ToString() && r.EMessType == EEventType.Subscribe.ToString() && r.KeyWord != null).Select(o => o.KeyWord).ToArray();
var unSubIds = db.ReplyRecords.Where(r => r.FromOpenId == this.ClientId.ToString() && r.EMessType == EEventType.Unsubscribe.ToString() && r.KeyWord != null).Select(o => o.KeyWord).ToArray();var SencesId = "";
foreach (var k in subIds)
{
if (!unSubIds.Contains(k))
{
this.ReplyModel.KeyWord = k;
break;
}
}this.ReplyModel.EMessType = EEventType.Unsubscribe.ToString();
RecordReplyMessage();
}
}
else if (strMsgType.ToLower() == "location")
{
string strLocation_X = rootElement.SelectSingleNode("Location_X").InnerText;
string strLocation_Y = rootElement.SelectSingleNode("Location_Y").InnerText;strResult = FormatOutLetLBS(double.Parse(strLocation_X), double.Parse(strLocation_Y), 10);
//strResult = FormatTextMessage(strLocation_X + "|" + strLocation_Y);
return Content(strResult);
}
else if (strMsgType.ToLower() == "image")
{
string strImgUrl = rootElement.SelectSingleNode("PicUrl").InnerText;
}
一种比较好的处理方法就是把语句块内的代码抽出来,写成函数,如下面所示:
public class MessageProcesser
{
public ReplyMessage Process(string xml)
{
var msg = PostMessage.FromXml(xml);
switch (msg.MsgType)
{
case Models.PostMessageType.Event:
var eventType = ((EventMessage)msg).Event;
switch (eventType)
{
case EventType.Click:
return ProcessClickEvent((ClickEvent)msg);
case EventType.Location:
return ProcessLocationEvent((LocationEvent)msg);
case EventType.Scan:
return ProcessScanEvent((ScanEvent)msg);
case EventType.Subscribe:
return ProcessSubscribeEvent((SubscribeEvent)msg);
case EventType.Unsubscribe:
return ProcessUnsubscribeEvent((UnsubscribeEvent)msg);
}
break;
case Models.PostMessageType.Image:
return ProcessImageMessage((ImageMessage)msg);
case Models.PostMessageType.Link:
return ProcessLinkMessage((LinkMessage)msg);
case Models.PostMessageType.Location:
return ProcessLocationMessage((LocationMessage)msg);
case Models.PostMessageType.Text:
return ProcessTextMessage((TextMessage)msg);
case Models.PostMessageType.Video:
return ProcessVideoMessage((VideoMessage)msg);
case Models.PostMessageType.Voice:
return ProcessVoiceMessage((VoiceMessage)msg);
}
return null;
}protected virtual ReplyMessage ProcessClickEvent(ClickEvent msg)
{
return DefaultProcess(msg);
}protected virtual ReplyMessage ProcessLocationEvent(LocationEvent msg)
{
return DefaultProcess(msg);
}protected virtual ReplyMessage ProcessScanEvent(ScanEvent msg)
{
return DefaultProcess(msg);
}protected virtual ReplyMessage ProcessSubscribeEvent(SubscribeEvent msg)
{
return DefaultProcess(msg);
}protected virtual ReplyMessage ProcessUnsubscribeEvent(UnsubscribeEvent msg)
{
return DefaultProcess(msg);
}protected virtual ReplyMessage ProcessImageMessage(ImageMessage msg)
{
return DefaultProcess(msg);
}protected virtual ReplyMessage ProcessLinkMessage(LinkMessage msg)
{
return DefaultProcess(msg);
}protected virtual ReplyMessage ProcessLocationMessage(LocationMessage msg)
{
return DefaultProcess(msg);
}protected virtual ReplyMessage ProcessTextMessage(TextMessage msg)
{
return DefaultProcess(msg);
}protected virtual ReplyMessage ProcessVideoMessage(VideoMessage msg)
{
return DefaultProcess(msg);
}protected virtual ReplyMessage ProcessVoiceMessage(VoiceMessage msg)
{
return DefaultProcess(msg);
}protected virtual ReplyMessage DefaultProcess(PostMessage msg)
{
var reply = new TextReply(msg);
if (msg.MsgType == PostMessageType.Event)
{
reply.Content = string.Format("{0} event is not processed.", ((EventMessage)msg).Event);
}
else
{
reply.Content = string.Format("{0} message is not processed.", msg.MsgType);
}
return reply;
}}
在使用的时候,继承上面的类就行了。
public class MyMessageProcesser : WeiXin.MessageProcesser
{
public MyMessageProcesser()
{
}protected override ReplyMessage ProcessSubscribeEvent(SubscribeEvent msg)
{
var reply = new TextReply(msg);
reply.Content = "你好,欢迎关注";
return reply;
}protected override ReplyMessage ProcessUnsubscribeEvent(UnsubscribeEvent msg)
{
var reply = new TextReply(msg);
reply.Content = "取消关注";
return reply;
}
}
欢迎讨论,欢迎板砖。
=======================================================
有朋友说,我只是把 if 换成了 switch,这个没错,但更重要的是,换成了一个可重写方法的类。
有朋友说,使用命令模式会不会更好点。是这样的,因为是微信的接口,以后就算是增加,也是很少会发生的,并且要作的改动也不多。所以不想变得太复杂了。现在是
if 内的语句块需要变动,因为要面对不同的用户,他们的处理都是不同的。
有不少朋友都误会了,以为我是为了消除 if,else,在这里,if、else
带来的问题只是阅读读上的不便,真正要害的的地方是if、else间的逻辑代码块,这些代码会因为不同的客户,做出不同的处理,每换一个客户,都泛及到修改里面的代码,所以很有必要对它进行重构。
为什么不用命令模式、或者把类型与处理方法保存在键值对。我们先来考虑一个问题,这些类型有没有增加的可能性?有,但是这个慨率比较小的,就算发生了,但是修改的正本也是非常低的,而且,当然你增加了一个后,以后需要增加一个的慨率更加小了。
总结一下这种处理的好处:对于开发人员来说,它非常便于阅读和理解,而对于使用者来说,通过重载来实现,也是很容易接受的。
代码重构之 —— 一堆if、esle 逻辑的处理