0

0

记一次.NET代码重构(下)

黄舟

黄舟

发布时间:2017-02-06 14:40:29

|

1255人浏览过

|

来源于php中文网

原创

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(arrayList.Cast>().Select(x => new DynamicJsonObject(x)));
                else
                    result = new List(arrayList.Cast());
            }
            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对象的属性反射获取到字典列表中
        /// 
        /// 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,长得太像了,一开始我重构的时候害我弄错了,建议不要在同一个方法中使用相似的变量名称,以免弄混淆。妈蛋,老司机的我又被坑了,愤怒,无可忍受,果断重命名。)

原来控制器调用业务逻辑代码是直接这样的

精美淘宝客单页面 zblog模板
精美淘宝客单页面 zblog模板

采用zblog修改的模板,简单方便,直接解压上传到空间即可使用,页面简单,适合SEO,导航,次导航,最新文章列表,随机文章列表全部都有,网站采用扁平结构,非常适用淘宝客类小站,所有文章都在根目录下。所有需要修改的地方在网页上各个地方都有标注说明,一切在网站后台都可以修改,无须修改任何程序代码,是新手的不二选择。后台登陆地址: 域名/login.asp用户名:admin (建议不要修改)密码:adm

下载
MessageModuleBusiness message
ModuleBusiness = new MessageModuleBusiness()

依赖于具体类的实现,而我们知道,具体是不稳定的,抽象才是稳定的,我们应该面向接口编程。今天是发送短信,明天可能就是发邮件,又或者要加日志记录等等等。

public interface IMessageModuleBusiness
{
        /// 
        /// 组装消息内容
        /// 
        /// 动态参数对象
        /// 组装后的消息内容
        string GetContent(dynamic messageContext);
}

然后调用的代码修改为:

private IMessageModuleBusiness message
ModuleBusiness = 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来再次解耦也方便。

好了,通过这样一步一步的重构,在不修改原有表结构和不影响客户调用的情况下,我已经将变化点进行了封装,当客户的模板参数变量变化的时候,再也不需要变更代码,只需要修改表中的模板内容就可以了。

901.jpg

重构时,画类图是一个非常好的习惯,代码结构一目了然,这里我附上类图。

以上就是记一次.NET代码重构(下)的内容,更多相关内容请关注PHP中文网(www.php.cn)!

相关专题

更多
php与html混编教程大全
php与html混编教程大全

本专题整合了php和html混编相关教程,阅读专题下面的文章了解更多详细内容。

6

2026.01.13

PHP 高性能
PHP 高性能

本专题整合了PHP高性能相关教程大全,阅读专题下面的文章了解更多详细内容。

6

2026.01.13

MySQL数据库报错常见问题及解决方法大全
MySQL数据库报错常见问题及解决方法大全

本专题整合了MySQL数据库报错常见问题及解决方法,阅读专题下面的文章了解更多详细内容。

6

2026.01.13

PHP 文件上传
PHP 文件上传

本专题整合了PHP实现文件上传相关教程,阅读专题下面的文章了解更多详细内容。

5

2026.01.13

PHP缓存策略教程大全
PHP缓存策略教程大全

本专题整合了PHP缓存相关教程,阅读专题下面的文章了解更多详细内容。

3

2026.01.13

jQuery 正则表达式相关教程
jQuery 正则表达式相关教程

本专题整合了jQuery正则表达式相关教程大全,阅读专题下面的文章了解更多详细内容。

1

2026.01.13

交互式图表和动态图表教程汇总
交互式图表和动态图表教程汇总

本专题整合了交互式图表和动态图表的相关内容,阅读专题下面的文章了解更多详细内容。

15

2026.01.13

nginx配置文件详细教程
nginx配置文件详细教程

本专题整合了nginx配置文件相关教程详细汇总,阅读专题下面的文章了解更多详细内容。

4

2026.01.13

nginx部署php项目教程汇总
nginx部署php项目教程汇总

本专题整合了nginx部署php项目教程汇总,阅读专题下面的文章了解更多详细内容。

5

2026.01.13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
C# 教程
C# 教程

共94课时 | 6.6万人学习

python编程入门系列图文教程
python编程入门系列图文教程

共65课时 | 24.5万人学习

vscode其实很简单
vscode其实很简单

共72课时 | 28.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号