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

來源:互聯網
上載者:User
public override bool TryGetMember(GetMemberBinder binder, out object result){            if (!_dictionary.TryGetValue(binder.Name, out result))            {                result = null;                return true;            }            var dictionary = result as IDictionary<string, object>;            if (dictionary != null)            {                result = new DynamicJsonObject(dictionary);                return true;            }            var arrayList = result as ArrayList;            if (arrayList != null && arrayList.Count > 0)            {                if (arrayList[0] is IDictionary<string, object>)result = new List<object>(arrayList.Cast<IDictionary<string, object>>().Select(x => new DynamicJsonObject(x)));                else                    result = new List<object>(arrayList.Cast<object>());            }            return true;        }    }    #endregion}

接下來是GetContent方法,此方法的目的很簡單,就是要根據客戶傳遞的模板變數參數索引值對和簡訊模板內容,拼裝成最後的簡訊發送內容,之前此方法裡面是硬式編碼,現在我們需要變成動態擷取。

簡訊模板的內容樣本:

【一應生活】您有一件單號為expressNumbers company,已到communityName收發室,請開啟一應生活APP“收發室”擷取取件碼進行取件。點擊下載http://a.app.qq.com/o/simple.jsp?pkgname=com.ening.life

我發現這樣的模板內容有問題,模板中的變數參數是直接用的英文單詞表示的,而我們的簡訊內容中可能有時候也會存在英文單詞,那麼我就給所有的變數參數加上{}。修改後如下:

【一應生活】您有一件單號為{expressNumbers} {company},已到{communityName}收發室,請開啟一應生活APP“收發室”擷取取件碼進行取件。點擊下載http://a.app.qq.com/o/simple.jsp?pkgname=com.ening.life

我們需要根據客戶傳遞過來的對象,將簡訊模板中的變數參數,替換成變數參數對應的值。那麼我們首先就要解析這個對象中的索引值對資訊。

  /// 把object對象的屬性反射擷取到字典列表中        /// </summary>        /// <param name="data">object對象</param>        /// <returns>返回Dictionary(屬性名稱,屬性值)列表</returns>         static Dictionary<string, string> GetProperties(object data)        {            Dictionary<string, string> dict = new Dictionary<string, string>();            Type type = data.GetType();            string[] propertyNames = type.GetProperties().Select(p => p.Name).ToArray();            foreach (var prop in propertyNames)            {                object propValue = type.GetProperty(prop).GetValue(data, null);                string value = (propValue != null) ? propValue.ToString() : "";                if (!dict.ContainsKey(prop))                {                    dict.Add(prop, value);                }            }            return dict;        }

接下來是通過Regex來匹配簡訊模板內容。

  /// 多個匹配內容        /// </summary>        /// <param name="sInput">輸入內容</param>        /// <param name="sRegex">運算式字串</param>        /// <param name="sGroupName">分組名, ""代表不分組</param>        static List<string> GetList(string sInput, string sRegex, string sGroupName)        {            List<string> list = new List<string>();            Regex re = new Regex(sRegex, RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace | RegexOptions.Multiline);            MatchCollection mcs = re.Matches(sInput);            foreach (Match mc in mcs)            {                if (sGroupName != "")                {                    list.Add(mc.Groups[sGroupName].Value);                }                else                {                    list.Add(mc.Value);                }            }            return list;        }        public static string ReplaceTemplate(string template, object data)        {            var regex = @"\{(?<name>.*?)\}";            List<string> itemList = GetList(template, regex, "name"); //擷取模板變數對象            Dictionary<string, string> dict = GetProperties(data);            foreach (string item in itemList)            {                //如果屬性存在,則替換模板,並修改模板值                if (dict.ContainsKey(item))                {                    template = template.Replace("{"+item+"}", dict.First(x => x.Key == item).Value);                }            }            return template;}

這樣就講客戶傳遞的對象和我們的解析代碼進行瞭解耦,客戶傳遞的對象不再依賴於我們的代碼實現,而是依賴於我們資料表中模板內容的配置。

這幾個方法我是寫好了,順便弄個單元測試來驗證一下是不是我要的效果,可憐的是,這個項目中根本就沒用到單元測試,沒辦法,我自己建立一個單元測試

[TestClass]    public class MatchHelperTest    {        [TestMethod]        public void ReplaceTemplate()        {            //模板文本            var template = "【一應生活】您有一件單號為{expressNumbers} {company},已到{communityName}收發室,            請開啟一應生活APP“收發室”擷取取件碼進行取件。點擊下載http://a.app.qq.com/o/simple.jsp?pkgname=com.ening.life";            //資料對象            var data = new { expressNumbers = "2016", company = "長城", communityName = "長怡花園"};            string str = "【一應生活】您有一件單號為2016 長城,已到長怡花園收發室,            請開啟一應生活APP“收發室”擷取取件碼進行取件。點擊下載http://a.app.qq.com/o/simple.jsp?pkgname=com.ening.life";            string str1=MatchHelper.ReplaceTemplate(template, data);            Assert.AreEqual(str1,str);            //重複標籤的測試            template = "【一應生活】您有一件單號為{expressNumbers} {company},已到{communityName}收發室,單號:{expressNumbers}";            str = "【一應生活】您有一件單號為2016 長城,已到長怡花園收發室,單號:2016";            str1=MatchHelper.ReplaceTemplate(template, data);            Assert.AreEqual(str1, str);        }    }

說到單元測試,我相信在許多公司都沒有用起來,理由太多。我也覺得如果業務簡單的話,根本沒必要寫單元測試,國內太多創業型公司項目進度都非常趕,如果說寫單元測試不費時間,那絕對是騙人的,至於說寫單元測試能提高開發效率,減少返工率,個人感覺這個還真難說,因為即便不寫單元測試也還是可以通過許多其它手段來彌補的,個人觀點,勿噴。

接下來修改GetContent方法如下:

public string GetContent(dynamic messageContext){            string strMsg = "";            string TypeCode = string.IsNullOrEmpty(messageContext.serviceCode) ? "001" : messageContext.serviceCode;            string channel = messageContext.channel;            try{var Module = unitOfWork.MessageModule.Get(c => c.Type == channel && c.TypeNo == TypeCode).FirstOrDefault();                if (!string.IsNullOrEmpty(Module.Content))                {                    var content = Module.Content;                    strMsg = MatchHelper.ReplaceTemplate(content, messageContext);                }                return strMsg;            }            catch (Exception ex)            {                strMsg = ex.Message;            }            return strMsg;        }

(話外:先吐槽一下之前這個變數命名,MessageContext messageContext 和string messageContent,長得太像了,一開始我重構的時候害我弄錯了,建議不要在同一個方法中使用相似的變數名稱,以免弄混淆。媽蛋,老司機的我又被坑了,憤怒,無可忍受,果斷重新命名。)


原來控制器調用商務邏輯代碼是直接這樣的

MessageModuleBusiness messageModuleBusiness = new MessageModuleBusiness()

依賴於具體類的實現,而我們知道,具體是不穩定的,抽象才是穩定的,我們應該面向介面編程。今天是傳送簡訊,明天可能就是發郵件,又或者要加日誌記錄等等等。

public interface IMessageModuleBusiness{        /// <summary>        /// 組裝訊息內容        /// </summary>        /// <param name="messageContext">動態參數對象</param>        /// <returns>組裝後的訊息內容</returns>        string GetContent(dynamic messageContext);}

然後調用的代碼修改為:

private IMessageModuleBusiness messageModuleBusiness = new MessageModuleBusiness();

最終的externalMerchantSendMessage代碼為:

    /// 外部商戶發送資訊        public ActionResult externalMerchantSendMessage()        {            try            {                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));                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);            }            catch (Exception ex)            {                return Json(new Result<string>()                {                    resultCode = ((int)ResultCode.failure).ToString(),                    resultMsg = "發送失敗"+ex.Message                }, JsonRequestBehavior.AllowGet);            }        }

這樣的話,即便日後通過反射或者IOC來再次解耦也方便。


好了,通過這樣一步一步的重構,在不修改原有表結構和不影響客戶調用的情況下,我已經將變化點進行了封裝,當客戶的模板參數變數變化的時候,再也不需要變更代碼,只需要修改表中的模板內容就可以了。

重構時,畫類圖是一個非常好的習慣,代碼結構一目瞭然,這裡我附上類圖。

以上就是記一次.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.