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; 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)result = new List
接下来是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对象的属性反射获取到字典列表中 /// /// object对象 /// 返回Dictionary(属性名,属性值)列表 static Dictionary GetProperties(object data) { Dictionary dict = new Dictionary(); 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; }
接下来是通过正则表达式来匹配短信模板内容。
/// 多个匹配内容 /// /// 输入内容 /// 表达式字符串 /// 分组名, ""代表不分组 static List GetList(string sInput, string sRegex, string sGroupName) { List list = new List(); 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 = @"{(?.*?)}"; List itemList = GetList(template, regex, "name"); //获取模板变量对象 Dictionary 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{ /// /// 组装消息内容 /// /// 动态参数对象 /// 组装后的消息内容 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() { resultCode = ((int)ResultCode.NoPermission).ToString(), resultMsg = "签名或无权限访问" }, JsonRequestBehavior.AllowGet); } var meaage = messageModuleBusiness.GetContent(param); if (string.IsNullOrEmpty(meaage)) { return Json(new Result() { resultCode = ((int)ResultCode.failure).ToString(), resultMsg = "发送失败" }, JsonRequestBehavior.AllowGet); } SMSHelper helper = new SMSHelper(); helper.SendSMS(meaage, param.phone); //发送短信 return Json(new Result() { resultCode = ((int)ResultCode.success).ToString(), resultMsg = "发送成功" }, JsonRequestBehavior.AllowGet); } catch (Exception ex) { return Json(new Result() { resultCode = ((int)ResultCode.failure).ToString(), resultMsg = "发送失败"+ex.Message }, JsonRequestBehavior.AllowGet); } }
这样的话,即便日后通过反射或者IOC来再次解耦也方便。
好了,通过这样一步一步的重构,在不修改原有表结构和不影响客户调用的情况下,我已经将变化点进行了封装,当客户的模板参数变量变化的时候,再也不需要变更代码,只需要修改表中的模板内容就可以了。

重构时,画类图是一个非常好的习惯,代码结构一目了然,这里我附上类图。
以上就是记一次.NET代码重构(下)的内容,更多相关内容请关注PHP中文网(www.php.cn)!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1432064.html
微信扫一扫
支付宝扫一扫