記一次.NET程式碼重構(上)

黄舟
發布: 2017-02-06 14:37:00
原創
1197 人瀏覽過

需求:是這樣的,要開發一個簡訊發送的模板,不同客戶可能會使用不同的模板,而不同的客戶使用的變數參數也是不同的。


之前為了應急,線上已經完成了一個短信模板發送短信的功能,短信模板表也創建了,而且在表中已經添加了一條記錄。我只要要做一個簡訊模板的增刪改查介面就可以了,看上去我的任務挺簡單的,老司機應該知道,接了個爛攤子。


下圖所示是原來已經創建好了的表

記一次.NET程式碼重構(上)

SQL創建腳本如下:

記一次.NET程式碼重構(上)

在這之前是已經開發了一個已經開發的API ,也就是說呼叫方(客戶),不會修改程式碼,只能我這邊來修改。雖然極不情願接做了一半的任務,但是沒辦法,不可能給你的開發任務都是從頭開始的。


實體類別代碼如下:

記一次.NET程式碼重構(上)

 DOT類別:

記一次.NET程式碼重構(上)

這是之前的代碼,業務實體類Message呼叫的一個實體對象。物件裡面存在著許多類似簡訊的動態標籤變數。

public class MessageModuleBusiness : GenericRepository<Model.MessageModule>
    {
        private UnitOfWork.UnitOfWork unitOfWork = new UnitOfWork.UnitOfWork();
        /// 获取模版内容
        public string GetContent(MessageContext messageContext)
        {
            string messageContent = "";
            string TypeCode = string.IsNullOrEmpty(messageContext.serviceCode) ? "001" : messageContext.serviceCode;
            try
            {
                var Module = unitOfWork.MessageModule.Get(c => c.Type == messageContext.channel && c.TypeNo == TypeCode).FirstOrDefault();
                //Content的内容:【一应生活】您有一件单号为expressNumbers company,
                已到communityName收发室,请打开一应生活APP“收发室”获取取件码进行取件。点击下载http://a.app.qq.com/o/simple.jsp?pkgname=com.ening.life
                if (!string.IsNullOrEmpty(Module.Content))
                {
                    var content = Module.Content;
                    content = content.Replace("company", messageContext.company);
                    content = content.Replace("expressNumbers", messageContext.expressNumbers);
                    content = content.Replace("communityName", messageContext.communityName);
                    content = content.Replace("Id", messageContext.Id);
                    content = content.Replace("receiveTime", messageContext.receiveTime);
                    content = content.Replace("fetchCode", messageContext.fetchCode);
                    messageContent = content;
                }
                return messageContent;
            }
            catch (Exception ex) {}
            return "";
        } 
        #endregion
}
登入後複製

控制器方法externalMerchantSendMessage,這是外部呼叫的

public class MessageContext{
        /// 手机号码
        public string phone { get; set; }
        /// 发送信息
        public string message { get; set; }
        /// 签名
        public string sign { get; set; }
        /// 渠道
        public string channel { get; set; }
        /// 内容
        public string content { get; set; }
        /// 取件码
        public string fetchCode { get; set; }
        /// 快递公司
        public string company { get; set; }
        /// 快递单号
        public string expressNumbers { get; set; }
        /// 社区名称
        public string communityName { get; set; }
        /// 到件时间
        public string receiveTime { get; set; }
        /// 序号
        public string Id { get; set; }
        /// 业务代码
        public string serviceCode { get; set; }
    }
登入後複製

以上是我接收開發任務之前已經實現了的功能。看起來我的任務挺簡單的,可是多年的開發經驗告訴我,這裡需要重構,如果我現在啥都不管,就只管做一個短信模板的增刪改查界面的話,後面維護的人一定會抓狂。


看出什麼問題沒有?


這個介面方法externalMerchantSendMessage是給所有客戶調用,而不同客戶使用不同的簡訊模板,不同的模板,又存在不同的變數參數。而現在所有的變數參數都封裝在了類別MessageContext中,問題是我們無法一下子把所有的變數參數全部確定下來,並且保持不變。


那麼,也就是說一旦需要加入變數參數,類別MessageContext中的程式碼就必須修改,而且GetContent方法中的程式碼是硬編的,一樣需要跟著修改。這樣就形成了一個循環,不斷加變數參數,不斷改程式碼,不斷發布介面版本.......


時間充裕的情況下,我自然是一個有節操的程式猿,那麼就開始重構吧。


在重構之前,在腦海浮現的並不是各種設計模式,而是物件導向設計的基本原則。各種設計模式就好比各種武學套路或招式,習武之人應該像張無忌練習太極劍一樣,先學會各種套路,然後忘記所有套路,從而融會貫通。


因為招式是死的,人是活得,有招就有破綻,根本沒有必勝招式存在,就好像沒有萬能的設計模式一樣,任何設計模式都存在缺點。


物件導向設計的核心思想就是封裝變化,那麼先找出變化點。從上面的分析中,我們已經發現了變化點,那就是簡訊模板中的變數參數,而這些變數參數都是客戶呼叫方傳過來的,不同客戶傳遞的參數變數又可能是不一樣的。


我們先來看一下,客戶傳遞過來的是什麼?我們看下客戶呼叫程式碼,這裡有Get和Post兩種呼叫方式。

    /// 外部商户发送信息
        public ActionResult externalMerchantSendMessage(MessageContext param)
        {
            logger.Info("[externalMerchantSendMessage]param:" + param);
            bool isAuth = authModelBusiness.isAuth(param.channel, param.phone, param.sign);
            if (!isAuth)
            {
                return Json(new Result<string>()
                {
                    resultCode = ((int)ResultCode.NoPermission).ToString(),
                    resultMsg = "签名或无权限访问"
                }, JsonRequestBehavior.AllowGet);
            }
            var meaage = messageModuleBusiness.GetContent(param);

            if (string.IsNullOrEmpty(meaage))
            {
                return Json(new Result<string>()
                {
                    resultCode = ((int)ResultCode.failure).ToString(),
                    resultMsg = "发送失败"
                }, JsonRequestBehavior.AllowGet);
            }
            SMSHelper helper = new SMSHelper();
            helper.SendSMS(meaage, param.phone);
            return Json(new Result<string>()
            {
                resultCode = ((int)ResultCode.success).ToString(),
                resultMsg = "发送成功"
            }, JsonRequestBehavior.AllowGet);
        }
登入後複製

可見客戶傳遞的是一個鍵值對集合,就是一個JSON格式的物件。根據前面的程式碼 bool isAuth = authModelBusiness.isAuth(param.channel, param.phone, param.sign);,可以分析出有三個參數是所有呼叫客戶都必須傳遞過來的,那就是:channel,phone,sign,而其它的參數就是簡訊模板的變數參數和參數值。


那麼方法externalMerchantSendMessage(MessageContext param)中的參數就是一個可變物件。在C#4.0種存在一個dynamic不正是用來描述可變對象嗎?

那麼第一步修改傳入參數類型,之前是硬編碼的強型別MessageContext,現在不依賴此類,而是動態解析,修改externalMerchantSendMessage方法代碼如

下:

function sendMsg() {
            //var appParam ="phone=15914070649&sign=78a7ce797cf757916c2c7675b6865b54&channel=weijiakeji&content=&fetchCode=1
&company=%E9%A1%BA%E4%B8%B0%E5%BF%AB%E9%80%92&expressNumbers=123456&communityName=%E9%95%BF%E5%9F%8E%E4%B8%80%E8%8A%B1%E5%9B%AD&receiveTime=5&Id=1231";
            //Get("/Message/externalMerchantSendMessage?" + appParam, {});

            var data = {
                "phone": "15914070649", "sign": "78a7ce797cf757916c2c7675b6865b54", "channel": "weijiakeji",
                "fetchCode": 1, "company": "%E9%A1%BA%E4%B8%B0%E5%BF%AB%E9%80%92", "Id": "1231"
            };
            Post(&#39;/Message/externalMerchantSendMessage&#39;, data);
        }
        //WebAPI Post方法
        function Post(url, data) {
            $.ajax({
                url: url,
                contentType: "application/json",
                type: "POST",
                dataType: "json",
                async: true,
                cache: false,
                data: JSON.stringify(data),
                success: function (response) {
                    $(&#39;#response&#39;).text(JSON.stringify(response));
                },
                error: function (XMLHttpRequest, textStatus, errorThrown) {
                    alert(textStatus);
                }
            });
        };
        //// WebApi Get方法
        function Get(url, data) {
            $.ajax({
                url: url,
                contentType: "application/json",
                type: "GET",
                dataType: "json",
                async: true,
                cache: false,
                //data: JSON.stringify(data),
                success: function (response) {
                    $(&#39;#response&#39;).text(JSON.stringify(response));
                },
                error: function (XMLHttpRequest, textStatus, errorThrown) {
                    alert(textStatus);
                }
            });
        };
登入後複製

DynamicJsonConverter的作用是將JSON字串轉為Object對象,程式碼如下:

dynamic param = null;
                string json = Request.QueryString.ToString();

                if (Request.QueryString.Count != 0) //ajax get请求
                {
                    //兼容旧的客户调用写法,暂时硬编了
                    if (json.Contains("param."))
                    {
                        json = json.Replace("param.", "");
                    }
                    json = "{" + json.Replace("=", ":&#39;").Replace("&", "&#39;,") + "&#39;}";
                }
                else  //ajax Post请求
                {
                    Request.InputStream.Position = 0; //切记这里必须设置流的起始位置为0,否则无法读取到数据
                    json = new StreamReader(Request.InputStream).ReadToEnd();
                }
                var serializer = new JavaScriptSerializer();
                serializer.RegisterConverters(new[] { new DynamicJsonConverter() });
                param = serializer.Deserialize(json, typeof(object));
登入後複製

以上就是記一次.NET程式碼重構(上)的內容,更多相關內容請關注PHP中文網(www.php.cn)!

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!