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

需求:是这样的,要开发一个短信发送的模板,不同客户可能会使用不同的模板,而不同的客户使用的变量参数也是不同的。

之前为了应急,线上已经完成了一个短信模板发送短信的功能,短信模板表也创建了,而且在表中已经新增了一条记录。我只需要做一个短信模板的增删改查界面就可以了,看上去我的任务挺简单的,老司机应该知道,接了个烂摊子。

下图所示是原来已经创建好了的表

897.jpg

SQL创建脚本如下:

898.jpg

在这之前是已经开发了一个发送短信的API接口供客户调用了的,也就是说调用方(客户),不会修改代码,只能我这边来修改。虽然极不情愿接做了一半的任务,但是没办法,不可能给你的开发任务都是从头开始的。

实体类代码如下:

899.jpg

 DOT类:

900.jpg

这是之前的代码,业务实体类MessageModuleBusiness.cs代码如下:

public class MessageModuleBusiness : GenericRepository    {        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()                {                    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);        }

以上是我接收开发任务之前已经实现了的功能。看上去我的任务挺简单的,可是多年的开发经验告诉我,这里需要重构,如果我现在啥都不管,就只管做一个短信模板的增删改查界面的话,后面维护的人一定会抓狂。

看出什么问题没有?

这个接口方法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 dictionary, Type type, JavaScriptSerializer serializer)    {        if (dictionary == null)            throw new ArgumentNullException("dictionary");        return type == typeof(object) ? new DynamicJsonObject(dictionary) : null;    }    public override IDictionary Serialize(object obj, JavaScriptSerializer serializer)    {        throw new NotImplementedException();    }    public override IEnumerable SupportedTypes    {        get { return new ReadOnlyCollection(new List(new[] { typeof(object) })); }    }  #region Nested type: DynamicJsonObject    private sealed class DynamicJsonObject : DynamicObject    {        private readonly IDictionary _dictionary;        public DynamicJsonObject(IDictionary 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)                {                    new DynamicJsonObject((IDictionary)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)                            new DynamicJsonObject((IDictionary)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代码重构(上)的内容,更多相关内容请关注PHP中文网(www.php.cn)!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1432066.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年12月17日 06:10:35
下一篇 2025年12月17日 06:10:46

相关推荐

发表回复

登录后才能评论
关注微信