这几天,接手一个同事的代码,关于微信接口开发的,那一堆的 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), );
//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间的逻辑代码块,这些代码会因为不同的客户,做出不同的处理,每换一个客户,都泛及到修改里面的代码,所以很有必要对它进行重构。(很多朋友都抓我 switch case 的小辨子,因为特地强调一下这里)

为什么不用命令模式、或者把类型与处理方法保存在键值对。我们先来考虑一个问题,这些类型有没有增加的可能性?有,但是这个慨率比较小的,就算发生了,修改的正本也是非常低的,而且,当然你增加了一个后,以后需要增加一个的慨率更加小了。这些类型的数量,不会大大地增加的。

总结一下这种处理的好处:对于开发人员来说,它非常便于阅读和理解,而对于使用者来说,通过重载来实现,也是很容易接受的。

认同者寡,而反对者众,我是不是可以这么认为,能把代码写好的人,还是少数的。

你们如果觉得我太懂得自我安慰了,请给我一板砖,把我给拍醒吧。^_^

看这位朋友这么热心给我留言,我就花点时间来回答一下这个问题:

========================================================

提出以下疑问
首先:
var msg = PostMessage.FromXml(xml);
这个方法其实已经将msg实例成一个具体的类型了
下面的判断为什么不是针对
msg is Type 而是判断字符串或枚举?
后面的全是强转,不怕报错吗?

然后:
既然第一句就已经区分了msg的类型
为什么不可以直接
var msg = PostMessage.FromXml(xml);
msg.Process();

还需要如此多的判断?

----------------------------------------------------------------------------

回答:

首先,你得了解我的背景,也就是这个代码所应用的情景,面对不同的客户,处理的逻辑都是不同的。

var msg = PostMessage.FromXml(xml);
msg.Process();

能够做到,不同的客户,不同的实现吗?

如果强转换会出错,那肯定是开发人员的错。^_^  难道你写完代码了,不检查,也不测试的吗?

================================================

疑问:

而改用基类
protected virtual ReplyMessage ProcessScanEvent(Message msg)

这样可以把判断类型(或者说转换类型)的工作交给具体实现去完成
可以避免在公共方法中的越权控制(你不能确定实现类是否可以处理一种新类型)

----------------------------------------------------------------------------
回答:
我希望调用者容易使用。如果把这个工作扔给调用者,调用的次数多了,这个量就会变得大了。造成调用者容易出错。
================================================
 
附录一篇另外一位作者对于此文的吐槽:
 
================================================
 
 

大家如果对我做的东西感兴趣,可以和我联系:

QQ: 81932759

Q群一: 71418067

Q群二: 88718955

上海的朋友,可以扫一扫下这面这个公众号,建这个公众号的目的,希望能够通过组织一些线下活动,让更多的程序员相互认识。

代码重构之 —— 一堆if、esle 逻辑的处理的更多相关文章

  1. .NET重构—单元测试的代码重构

    阅读目录: 1.开篇介绍 2.单元测试.测试用例代码重复问题(大量使用重复的Mock对象及测试数据) 2.1.单元测试的继承体系(利用超类来减少Mock对象的使用) 2.1.1.公用的MOCK对象: ...

  2. 如何把if-else代码重构成高质量代码

    原文:https://blog.csdn.net/qq_35440678/article/details/77939999 本文提纲: 为什么我们写的代码都是if-else? 这样的代码有什么缺点? ...

  3. 一次项目代码重构-使用spring容器干掉条件判断

    一次项目代码重构-使用spring容器干掉条件判断 这是在一次公司项目中进行重构时,一些复杂业务时想到的一个去掉一些if else的办法.能够使代码逻辑更加清晰,减少一些业务上的耦合. 业务说明 我所 ...

  4. 6个实例详解如何把if-else代码重构成高质量代码

    本文提纲: 为什么我们写的代码都是if-else?这样的代码有什么缺点?是否有优化的方法?如何重构?异常逻辑处理型重构方法状态处理型重构方法为什么我们写的代码都是if-else?程序员想必都经历过这样 ...

  5. C++代码重构——从C global到C++ template

    在学数据结构的时候,我常有这样目标--写出能够最大程度复用的代码(算法正确,封装优秀).我常想--如何能在短时间内达成"算法正确,封装优秀"这样的目标.经过一段时间的摸索,我的结论 ...

  6. NET代码重构

    记一次.NET代码重构   好久没写代码了,终于好不容易接到了开发任务,一看时间还挺充足的,我就慢慢整吧,若是遇上赶进度,基本上直接是功能优先,完全不考虑设计.你可以认为我完全没有追求,当身后有鞭子使 ...

  7. 代码重构 & 常用设计模式

    代码重构 重构目的 相同的代码最好只出现一次 主次方法 主方法 只包含实现完整逻辑的子方法 思维清楚,便于阅读 次方法 实现具体逻辑功能 测试通过后,后续几乎不用维护 重构的步骤 1  新建一个方法 ...

  8. 谈一谈PHP的代码重构

    随着 PHP 从一种简单的脚本语言转变为一种成熟的编程语言,一个典型的 PHP 应用程序的代码库的复杂性也随之增大.为了控制对这些应用程序的支持和维护,我们可以使用各种测试工具来自动化该流程.其中一种 ...

  9. .NET - 代码重构技巧

    通过面向对象三大特性:封装.继承.多态的学习,可以说我们已经掌握了面向对象的核心.接下来的学习就是如何让我们的代码更优雅.更高效.更易读.更易维护.当然了,这也是从一个普通程序员到一个高级程序员的必由 ...

随机推荐

  1. 编写一个Java应用程序,该应用程序包括2个类:Print类和主类E。Print 类里有一个方法output()功能是输出100 ~ 999之间的所有水仙花数(各位数字的 立方和等于这个三位数本身,如: 371 = 33 + 73 + 13。)在主类E的main方法中来 测试类Print

    package zuoye; public class print { void output() { System.out.println("100-999之间的水仙花数是:") ...

  2. 《CLR via C#》读书笔记--基元类型、引用类型和值类型

    编程语言的基元类型 编译器直接支持的数据类型称为基元类型.基元类型直接映射到Framework类库中存在的类型.例如:C#中的int直接映射到System.Int32类型.下表给出了C#基元类型与对应 ...

  3. 我所了解的各公司使用的 Ceph 存储集群 (携程、乐视、有云、宝德云、联通等)

    Ceph 作为软件定义存储的代表之一,最近几年其发展势头很猛,也出现了不少公司在测试和生产系统中使用 Ceph 的案例,尽管与此同时许多人对它的抱怨也一直存在.本文试着整理作者了解到的一些使用案例. ...

  4. [转]backbone.js template()函数

    本文转自:http://book.2cto.com/201406/43974.html 本文所属图书 > Backbone.js实战 资深Web开发专家根据Backbone js最新版本撰写,对 ...

  5. 边工作边刷题:70天一遍leetcode: day 1

    (今日完成:Two Sum, Add Two Numbers, Longest Substring Without Repeating Characters, Median of Two Sorted ...

  6. POJ 1724 ROADS【最短路/搜索/DP】

    一道写法多样的题,很具有启发性. 具体参考:http://www.cnblogs.com/scau20110726/archive/2013/04/28/3050178.html http://blo ...

  7. Java的cmd配置(也即Java的JDK配置及相关常用命令)——找不到或无法加载主类 的解决方法

    Java的cmd配置(也即Java的JDK配置及相关常用命令) ——找不到或无法加载主类  的解决方法 这段时间一直纠结于cmd下Java无法编译运行的问题.主要问题描述如下: javac 命令可以正 ...

  8. 第16章 调色板管理器_16.4 一个DIB位图库的实现(1)

    16.4.1自定义的 DIBSTRUCT结构体 字段 含义 PBYTE *ppRow ①指向位图视觉上最上面的一行像素.(不管是自下而上,还是自上而下) ②放在第一个字段,为的是后面定义宏时可方便访问 ...

  9. 烈焰SWF解密

    SWF 解密 是用UE编辑器 改 SWF开头 的AA AA AA ,改成43 57 53 就解密了

  10. php一句话后门过狗姿势万千之传输层加工【三】

    既然木马已就绪,那么想要利用木马,必然有一个数据传输的过程,数据提交是必须的,数据返回一般也会有的,除非执行特殊命令. 当我们用普通菜刀连接后门时,数据时如何提交的,狗狗又是如何识别的,下面结合一个实 ...