Heim > Artikel > Backend-Entwicklung > Erinnern Sie sich an ein .NET-Code-Refactoring (Teil 1)
Voraussetzung: Um eine Vorlage zum Versenden von Textnachrichten zu entwickeln, können verschiedene Kunden unterschiedliche Vorlagen verwenden, und auch die von verschiedenen Kunden verwendeten variablen Parameter sind unterschiedlich.
Für den Notfall wurde eine SMS-Vorlagenfunktion zum Versenden von Textnachrichten online erstellt und ein neuer Datensatz hinzugefügt zum Tisch. Ich muss nur eine Schnittstelle zum Hinzufügen, Löschen, Ändern und Überprüfen von Textnachrichtenvorlagen erstellen. Ein erfahrener Fahrer sollte wissen, dass ich in ein Chaos geraten bin.
Das Bild unten zeigt die ursprünglich erstellte Tabelle
Das SQL-Erstellungsskript lautet wie folgt:
Zuvor wurde eine API-Schnittstelle zum Senden von Textnachrichten entwickelt, die der Anrufer (Kunde) nicht ändern kann, sondern nur ich. Obwohl ich äußerst ungern halbfertige Aufgaben übernehme, bleibt mir nichts anderes übrig, als alle Entwicklungsaufgaben von vorne zu beginnen.
Der Entitätsklassencode lautet wie folgt:
DOT-Klasse:
Dies ist der vorherige Code. Der Code der Geschäftsentitätsklasse MessageModuleBusiness.cs lautet wie folgt:
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-Klasse, dies ist ein vom Client übertragenes und aufgerufenes Entitätsobjekt. Es gibt viele dynamische Tag-Variablen, die Textnachrichten im Objekt ähneln.
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; } }
Controller-Methode externalMerchantSendMessage, die für externe Aufrufe vorgesehen ist
/// 外部商户发送信息 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); }
Die oben genannten Funktionen sind die Funktionen, die ich implementiert habe, bevor ich die Entwicklungsaufgabe erhalten habe. Es scheint, dass meine Aufgabe recht einfach ist, aber viele Jahre Erfahrung in der Entwicklung sagen mir, dass dies überarbeitet werden muss Die Wartungsleute werden in Zukunft definitiv verrückt werden.
Sehen Sie ein Problem?
Diese Schnittstellenmethode externalMerchantSendMessage wird für alle Kunden aufgerufen, und verschiedene Kunden verwenden unterschiedliche SMS-Vorlagen, und unterschiedliche Vorlagen haben unterschiedliche variable Parameter. Jetzt sind alle variablen Parameter in der MessageContext-Klasse gekapselt. Das Problem besteht darin, dass wir nicht alle variablen Parameter auf einmal ermitteln und unverändert lassen können.
Das heißt, sobald variable Parameter hinzugefügt werden müssen, muss der Code in der MessageContext-Klasse geändert werden, und der Code in der GetContent-Methode ist hart. codiert und muss entsprechend angepasst werden. Dies bildet einen Zyklus, der ständig variable Parameter hinzufügt, den Code ständig ändert und ständig Schnittstellenversionen veröffentlicht ...
Wenn ich genug Zeit habe, werde ich natürlich Wenn Sie ein sind disziplinierter Programmierer, dann beginnen Sie mit dem Refactoring.
Vor dem Refactoring fallen mir nicht verschiedene Designmuster ein, sondern die Grundprinzipien des objektorientierten Designs. Verschiedene Designmuster sind wie verschiedene Kampfsportroutinen oder -bewegungen. Kampfsportler sollten wie Zhang Wuji beim Tai Chi-Schwert zuerst verschiedene Routinen lernen und dann alle Routinen vergessen, um sie zu meistern.
Denn Bewegungen sind tot, Menschen leben und jede Bewegung hat ihre Fehler. Es gibt überhaupt keine sichere Entscheidung, genauso wie es kein universelles Designmuster gibt. jedes Design Alle Modelle weisen Mängel auf.
Die Kernidee des objektorientierten Designs besteht darin, Änderungen zu kapseln. Finden Sie daher zunächst die Änderungspunkte. Aus der obigen Analyse haben wir den Änderungspunkt entdeckt, bei dem es sich um die variablen Parameter in der SMS-Vorlage handelt. Diese variablen Parameter werden vom Kundenanrufer übergeben, und die von verschiedenen Kunden übergebenen Parametervariablen können unterschiedlich sein.
Werfen wir zunächst einen Blick darauf, was vom Kunden weitergegeben wird? Werfen wir einen Blick auf den Kundenaufrufcode. Es gibt zwei Aufrufmethoden: Get und 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); } }); };
Es ist ersichtlich, dass der Kunde eine Sammlung von Schlüssel-Wert-Paaren übergibt, bei denen es sich um ein Objekt im JSON-Format handelt. Gemäß dem vorherigen Code bool isAuth = authModelBusiness.isAuth(param.channel, param.phone, param.sign); kann analysiert werden, dass alle anrufenden Kunden drei Parameter übergeben müssen, nämlich: Kanal, Telefon, Zeichen Die anderen Parameter sind die variablen Parameter und Parameterwerte der SMS-Vorlage.
Dann ist der Parameter in der Methode externalMerchantSendMessage(MessageContext param) ein variables Objekt. Gibt es in C# 4.0 nicht eine Dynamik, die zur Beschreibung veränderlicher Objekte verwendet wird?
Dann besteht der erste Schritt darin, den eingehenden Parametertyp zu ändern. Früher war es ein fest codierter, stark typisierter MessageContext. Jetzt wird der Code der externalMerchantSendMessage-Methode wie folgt geändert :
:
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 wird zum Konvertieren von JSON-Strings in Objektobjekte verwendet. Der Code lautet wie folgt:
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("}"); }
Das Obige ist der Inhalt von a .NET-Code-Rekonstruktion (Teil 1), weitere Informationen zu verwandten Inhalten finden Sie auf der chinesischen PHP-Website (www.php.cn)!