記一次.NET代碼重構(上)

來源:互聯網
上載者:User
需求:是這樣的,要開發一個簡訊發送的模板,不同客戶可能會使用不同的模板,而不同的客戶使用的變數參數也是不同的。


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


所示是原來已經建立好了的表

SQL建立指令碼如下:

在這之前是已經開發了一個傳送簡訊的API介面供客戶調用了的,也就是說調用方(客戶),不會修改代碼,只能我這邊來修改。雖然極不情願接做了一半的任務,但是沒辦法,不可能給你的開發工作單位都是從頭開始的。


實體類代碼如下:

DOT類:

這是之前的代碼,業務實體類MessageModuleBusiness.cs代碼如下:

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}

MessageContext類,這個是用戶端傳輸過來調用的一個實體物件。對象裡面存在許多類似於簡訊的動態標籤變數。

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,這是供外部調用的

    /// 外部商戶發送資訊        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);        }

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


看出什麼問題沒有?


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


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


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


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


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


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


我們先來看一下,客戶傳遞過來的是什嗎?我們看下客戶調用代碼,這裡有Get和Post兩種調用方式。

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('/Message/externalMerchantSendMessage', 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) {                    $('#response').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) {                    $('#response').text(JSON.stringify(response));                },                error: function (XMLHttpRequest, textStatus, errorThrown) {                    alert(textStatus);                }            });        };

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


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

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

下:

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("=", ":'").Replace("&", "',") + "'}";                }                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));

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

using System;using System.Collections;using System.Collections.Generic;using System.Collections.ObjectModel;using System.Dynamic;using System.Linq;using System.Text;using System.Web.Script.Serialization;public sealed class DynamicJsonConverter : JavaScriptConverter{    public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)    {        if (dictionary == null)            throw new ArgumentNullException("dictionary");        return type == typeof(object) ? new DynamicJsonObject(dictionary) : null;    }    public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)    {        throw new NotImplementedException();    }    public override IEnumerable<Type> SupportedTypes    {        get { return new ReadOnlyCollection<Type>(new List<Type>(new[] { typeof(object) })); }    }  #region Nested type: DynamicJsonObject    private sealed class DynamicJsonObject : DynamicObject    {        private readonly IDictionary<string, object> _dictionary;        public DynamicJsonObject(IDictionary<string, object> dictionary)        {            if (dictionary == null)                throw new ArgumentNullException("dictionary");            _dictionary = dictionary;        }        public override string ToString()        {            var sb = new StringBuilder("{");            ToString(sb);            return sb.ToString();        }        private void ToString(StringBuilder sb)        {            var firstInDictionary = true;            foreach (var pair in _dictionary)            {                if (!firstInDictionary)                    sb.Append(",");                firstInDictionary = false;                var value = pair.Value;                var name = pair.Key;                if (value is string)                {                    sb.AppendFormat("{0}:\"{1}\"", name, value);                }                else if (value is IDictionary<string, object>)                {                    new DynamicJsonObject((IDictionary<string, object>)value).ToString(sb);                }                else if (value is ArrayList)                {                    sb.Append(name + ":[");                    var firstInArray = true;                    foreach (var arrayValue in (ArrayList)value)                    {                        if (!firstInArray)                            sb.Append(",");                        firstInArray = false;                        if (arrayValue is IDictionary<string, object>)                            new DynamicJsonObject((IDictionary<string, object>)arrayValue).ToString(sb);                        else if (arrayValue is string)                            sb.AppendFormat("\"{0}\"", arrayValue);                        else                            sb.AppendFormat("{0}", arrayValue);                    }                    sb.Append("]");                }                else                {                    sb.AppendFormat("{0}:{1}", name, value);                }            }            sb.Append("}");        }

以上就是記一次.NET代碼重構(上)的內容,更多相關內容請關注topic.alibabacloud.com(www.php.cn)!

  • 相關文章

    聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.