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

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

热门话题

如何解决C++开发中的代码冗余问题代码冗余是指在编写程序时,出现了多个地方有相似或重复的代码。这种问题不仅使得代码难以维护和阅读,还会增加代码量和复杂性。而对于C++开发者来说,解决代码冗余问题尤为重要,因为C++是一种强大的编程语言,但也容易导致代码重复。代码冗余问题的根源在于不合理的设计和编码习惯。要解决这个问题,可以从以下几个方面着手:使用函数和类:C

随着时间的推移和需求的变化,一个项目的代码很容易陈旧,难以维护和扩展。在PHP开发中,重构被认为是提高代码质量和开发效率的必备工作之一。而在这个过程中,使用Rector工具可以大大简化代码重构和优化的工作。Rector是一种开源的PHP代码重构工具,可以帮助PHP开发者自动化代码重构和优化,让开发者可以更专注于业务开发和功能的实现上。通过

当今人工智能(AI)技术的发展如火如荼,它们在各个领域都展现出了巨大的潜力和影响力。今天大姚给大家分享4个.NET开源的AI模型LLM相关的项目框架,希望能为大家提供一些参考。https://github.com/YSGStudyHards/DotNetGuide/blob/main/docs/DotNet/DotNetProjectPicks.mdSemanticKernelSemanticKernel是一种开源的软件开发工具包(SDK),旨在将大型语言模型(LLM)如OpenAI、Azure

在写PHP代码时,重构是一个非常重要的过程。随着应用程序的增长,代码库会变得越来越庞大,难以阅读和维护。重构是为了解决这个问题,让代码更加模块化,并可以更好地组织和扩展。当我们重构代码时,需要考虑以下几个方面:代码风格代码风格是很重要的一点。保持你的代码风格一致性会让代码更易于阅读和维护。请遵循PHP代码规范,并保持一致。尝试使用代码风格检查工具,例如PHP

Java开发:代码重构和质量评估引言:在软件开发的过程中,代码重构是提高代码质量和可维护性的重要手段之一。通过对代码进行重构,可以使代码更加优雅、简洁、易于理解和修改。然而,重构并不仅仅是简单地修改一下代码,而是一个需要理性和系统性思考的过程。本文将介绍如何进行代码重构,并结合具体的代码示例进行说明。同时,我们还将讨论如何评估代码质量以及评估的重要性。代码重

Go语言返回值类型推断自动推断函数返回值类型,简化代码并提高可读性。可省略返回值类型,编译器会根据函数体中的实际返回值自动推断类型。可用于重构现有代码,省去显式类型声明,例如,计算整数数组总和的函数calculateTotal可重构成:funccalculateTotal(items[]int){}。

如何使用Go语言进行代码重构实践引言:在软件开发过程中,我们经常会面临代码重构的挑战。代码重构是指对现有的代码进行优化和重组,以提高代码质量和可维护性。本文将介绍如何使用Go语言进行代码重构实践,并附带相应的代码示例。一、代码重构的原则在进行代码重构之前,我们需要明确一些原则,以确保重构的顺利进行。以下是一些重要的代码重构原则:保持代码功能的一致性:在重构过
