首页 后端开发 C#.Net教程 记一次.NET代码重构(上)

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

Feb 06, 2017 pm 02:37 PM

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


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


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

897.jpg

SQL创建脚本如下:

898.jpg

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


实体类代码如下:

899.jpg

 DOT类:

900.jpg

这是之前的代码,业务实体类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(&#39;/Message/externalMerchantSendMessage&#39;, 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) {
                    $(&#39;#response&#39;).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) {
                    $(&#39;#response&#39;).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("=", ":&#39;").Replace("&", "&#39;,") + "&#39;}";
                }
                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)!


本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

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

AI Clothes Remover

AI Clothes Remover

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

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

如何解决C++开发中的代码冗余问题 如何解决C++开发中的代码冗余问题 Aug 22, 2023 pm 05:30 PM

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

PHP开发:使用 Rector 进行代码重构和优化 PHP开发:使用 Rector 进行代码重构和优化 Jun 16, 2023 am 08:38 AM

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

C#的就业前景如何 C#的就业前景如何 Oct 19, 2023 am 11:02 AM

无论您是初学者还是有经验的专业人士,掌握C#将为您的职业发展铺平道路。

分享几个.NET开源的AI和LLM相关项目框架 分享几个.NET开源的AI和LLM相关项目框架 May 06, 2024 pm 04:43 PM

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

PHP入门指南:代码重构 PHP入门指南:代码重构 May 26, 2023 pm 04:21 PM

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

Java开发:如何进行代码重构和质量评估 Java开发:如何进行代码重构和质量评估 Sep 21, 2023 am 09:57 AM

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

Go语言返回值类型推断与代码重构 Go语言返回值类型推断与代码重构 Apr 30, 2024 am 08:06 AM

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

如何使用Go语言进行代码重构实践 如何使用Go语言进行代码重构实践 Aug 02, 2023 am 10:05 AM

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

See all articles