需求:是這樣的,要開發一個簡訊發送的模板,不同客戶可能會使用不同的模板,而不同的客戶使用的變數參數也是不同的。
之前為了應急,線上已經完成了一個簡訊模板傳送簡訊的功能,簡訊模板表也建立了,而且在表中已經新增了一條記錄。我只需要做一個簡訊模板的增刪改查介面就可以了,看上去我的工作挺簡單的,老司機應該知道,接了個爛攤子。
所示是原來已經建立好了的表
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)!