代碼重構之 —— 一堆if、esle 邏輯的處理

這幾天,接手一個同事的代碼,關於微信接口開發的,那一堆的 if,看得哥蛋痛了,這個毛病也是不少新手容易犯的,因此特意把此次重構寫出來。html

下面來咱們看看這個代碼的問題所在,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;
}

一種比較好的處理方法就是把語句塊內的代碼抽出來,寫成函數,以下面所示:ide

    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;
    }
}

歡迎討論,歡迎板磚。測試

=======================================================this

有朋友說,我只是把 if 換成了 switch,這個沒錯,但更重要的是,換成了一個可重寫方法的類。spa

 

有朋友說,使用命令模式會不會更好點。是這樣的,由於是微信的接口,之後就算是增長,也是不多會發生的,而且要做的改動也很少。因此不想變得太複雜了。如今是 if 內的語句塊須要變更,由於要面對不一樣的用戶,他們的處理都是不一樣的。code

 

有很多朋友都誤會了,覺得我是爲了消除 if,else,在這裏,if、else 帶來的問題只是閱讀讀上的不便,真正要害的的地方是if、else間的邏輯代碼塊,這些代碼會由於不一樣的客戶,作出不一樣的處理,每換一個客戶,都泛及到修改裏面的代碼,因此頗有必要對它進行重構。(不少朋友都抓我 switch case 的小辨子,由於特意強調一下這裏)orm

爲何不用命令模式、或者把類型與處理方法保存在鍵值對。咱們先來考慮一個問題,這些類型有沒有增長的可能性?有,可是這個慨率比較小的,就算髮生了,修改的正本也是很是低的,並且,固然你增長了一個後,之後須要增長一個的慨率更加小了。這些類型的數量,不會大大地增長的。

總結一下這種處理的好處:對於開發人員來講,它很是便於閱讀和理解,而對於使用者來講,經過重載來實現,也是很容易接受的。

 

認同者寡,而反對者衆,我是否是能夠這麼認爲,能把代碼寫好的人,仍是少數的。

大家若是以爲我太懂得自我安慰了,請給我一板磚,把我給拍醒吧。^_^

 

看這位朋友這麼熱心給我留言,我就花點時間來回答一下這個問題: 

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

提出如下疑問
首先:
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

上海的朋友,能夠掃一掃下這面這個公衆號,建這個公衆號的目的,但願可以經過組織一些線下活動,讓更多的程序員相互認識。

相關文章
相關標籤/搜索