需求:是這樣的,要開發一個簡訊發送的模板,不同客戶可能會使用不同的模板,而不同的客戶使用的變數參數也是不同的。
之前為了應急,線上已經完成了一個短信模板發送短信的功能,短信模板表也創建了,而且在表中已經添加了一條記錄。我只要要做一個簡訊模板的增刪改查介面就可以了,看上去我的任務挺簡單的,老司機應該知道,接了個爛攤子。
下圖所示是原來已經創建好了的表
SQL創建腳本如下:
在這之前是已經開發了一個已經開發的API ,也就是說呼叫方(客戶),不會修改程式碼,只能我這邊來修改。雖然極不情願接做了一半的任務,但是沒辦法,不可能給你的開發任務都是從頭開始的。
實體類別代碼如下:
DOT類別:
這是之前的代碼,業務實體類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('/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); } }); };
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("=", ":'").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));
以上就是記一次.NET程式碼重構(上)的內容,更多相關內容請關注PHP中文網(www.php.cn)!