需求:是这样的,要开发一个短信发送的模板,不同客户可能会使用不同的模板,而不同的客户使用的变量参数也是不同的。
之前为了应急,线上已经完成了一个短信模板发送短信的功能,短信模板表也创建了,而且在表中已经新增了一条记录。我只需要做一个短信模板的增删改查界面就可以了,看上去我的任务挺简单的,老司机应该知道,接了个烂摊子。
下图所示是原来已经创建好了的表

SQL创建脚本如下:

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

DOT类:

这是之前的代码,业务实体类MessageModuleBusiness.cs代码如下:
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类,这个是客户端传输过来调用的一个实体对象。对象里面存在许多类似于短信的动态标签变量。
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<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);
}以上是我接收开发任务之前已经实现了的功能。看上去我的任务挺简单的,可是多年的开发经验告诉我,这里需要重构,如果我现在啥都不管,就只管做一个短信模板的增删改查界面的话,后面维护的人一定会抓狂。
看出什么问题没有?
这个接口方法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<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("}");
}以上就是记一次.NET代码重构(上)的内容,更多相关内容请关注PHP中文网(www.php.cn)!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号