zoukankan      html  css  js  c++  java
  • C#大型电商项目优化(三)——扩展性与支付

    上一篇文章引来不少非议,笔者并非对EF有看法,而是针对不同的业务场景和框架背景,挑选不同的方案。每个方案都有其优势劣势,挑选最快速,最简单的方案,是笔者的初衷。

    看评论也是学习的过程,然而有些只做评价,却不道明原委的评论,也确实让笔者感受到了些许来自世界的恶意^_^

    开个玩笑,下面进入正题,之前系统的支付部分只需要支持支付宝和财付通,且支付代码是写在一个页面文件里的,也就是说,这个页面文件包含了支付宝和财付通的支付请求、支付同步回调和异步回调。且不说代码混乱可读性差,其可扩展性也实在不敢恭维。其功能像这样:

    可将来需要支持其他支付平台呢,类似于快钱等支付平台,我们预期的是这样甚至更多:

    难道这些将来亟待扩展的功能还一一写在页面里吗?当然不能。在扩展支付功能之前,必须对原来的代码做重构。

    重构前我们需要对目前的支付做分析:支付需要哪些方法?哪些是公共的?经过总结,我们发现支付宝和财付通支付最主要的就是这三个方法:

    1.提交支付请求

    2.支付同步回调(重定向)

    3.异步回调

    当然,有些支付平台可能有不同,比如支付宝的退款接口,是没有同步回调的。如果有更多的场景,我们可以扩展接口。

    我们定义了这样一个接口类:

    public interface IPayment
        {
            /// <summary>
            /// 支付
            /// </summary>
            void GoToPay(PaymentParameter parameters);
            /// <summary>
            /// 支付同步回调
            /// </summary>
            void PayReturn();
            /// <summary>
            /// 支付异步回调
            /// </summary>
            void PayNotify();
        }

    接下来构建基础类,类名为PaymentBase,让它继承我们的接口类。基础类中,我们定义各种平台支付所需要调用的公共方法,声明支付所必需的对象,代码如下:

            protected string _orderId = string.Empty;
            protected string _orderNum = string.Empty;
            protected MerchantPayInfo _merchantPayInfo = null;
            protected string _orderPrice;
            protected string _productName = string.Empty;
            protected string _orderType = string.Empty;
            protected HttpResponse Response;
            protected HttpRequest Request;
            log4net.ILog log = log4net.LogManager.GetLogger("myDemo");
    
            /// <summary>
            /// 去支付
            /// </summary>
            public abstract void GoToPay(PaymentParameter parameters);
            /// <summary>
            /// 支付同步回调
            /// </summary>
            public abstract void PayReturn();
            /// <summary>
            /// 支付异步回调
            /// </summary>
            public abstract void PayNotify();
            /// <summary>
            /// 支付前的初始化
            /// </summary>
            /// <param name="parameters"></param>
            protected void PaymentInitial(PaymentParameter parameters)
            {...}

    在PaymentInitial中为部分对象做初始化,这里不做赘述。

    准备工作就绪,接下来我们开始调用支付接口,首先是支付宝,我们定义一个类,可以叫作AliPayment,在其构造函数中为支付商家账号对象赋值,然后开始为我们的三个最重要的方法编写实现:     

            public override void GoToPay(PaymentParameter parameters)
            {
                base.PaymentInitial(parameters);
    
                ......var submit = new Submit(_merchantPayInfo.Akey);
                //建立请求
                string sHtmlText = submit.BuildRequest(sParaTemp, "get", "确认");
                Response.ContentType = "text/html";
                Response.Write(sHtmlText);
                Response.End();
            }
            /// <summary>
            /// 支付后同步调用
            /// </summary>
            public override void PayReturn()
            {
                ......
            }
            /// <summary>
            /// 支付后异步调用
            /// </summary>
            public override void PayNotify()
            {
           ......
    }

    这三个方法的实现在支付宝接口文档的demo里有提供,大家可以参考。考虑到篇幅问题,这部分代码就不贴出来了。

    支付类型和方法都定义且实现完成,接下来就是调用了。考虑到可扩展性,我们希望调用部分的代码在以后扩展时,都不需要修改,这里我们可以通过配置实现。类似于我们所熟知的“控制反转,依赖注入”:调用者不构造被调用者的实例,由容器构造被调用者的实例,再注入到调用者。当前的系统框架并没有引入控制反转策略,没关系,框架里没有,我们可以自己写个类似的。

    这里可以通过反射来构造支付类型的实例,创建类型实例的代码如下:

         /// <summary>
            /// 创建对象实例
            /// </summary>
            /// <typeparam name="T">要创建对象的类型</typeparam>
            /// <param name="assemblyName">类型所在程序集名称</param>
            /// <param name="nameSpace">类型所在命名空间</param>
            /// <param name="className">类型名</param>
            /// <returns></returns>
            public static T CreateInstance<T>(string assemblyName, string nameSpace, string className)
            {
                try
                {
                    string fullName = nameSpace + "." + className;//命名空间.类型名
                    //此为第一种写法
                    object ect = Assembly.Load(assemblyName).CreateInstance(fullName);//加载程序集,创建程序集里面的 命名空间.类型名 实例
                    return (T)ect;//类型转换并返回
                    //下面是第二种写法
                    //string path = fullName + "," + assemblyName;//命名空间.类型名,程序集
                    //Type o = Type.GetType(path);//加载类型
                    //object obj = Activator.CreateInstance(o, true);//根据类型创建实例
                    //return (T)obj;//类型转换并返回
                }
                catch
                {
                    //发生异常,返回类型的默认值
                    return default(T);
                }
            }

    这里需要注意:我们所创建的支付类型必须要自己写构造函数,否则无法通过此方法创建实例。

    在辅助方案都完成之后,实际调用的地方代码量是非常少的,我们看调用的代码:

                string className = ConfigurationManager.AppSettings[payCode];
                        payment = ReflectionHelper.CreateInstance<IPayment>("MyTestAssembly", "MyTestAssembly.Payment", className);
    
                        PaymentParameter parameter = new PaymentParameter();
                        parameter.LocalPath = "/Pay/Payment";
                        parameter.OrderId = OrderId;
                        parameter.OrderType = OrderType;
                        payment.GoToPay(parameter);

    配置的内容如下:

    <!--支付宝支付方案类型配置,用于反射 Leon-->
        <add key="0" value="AliPayment" />
        
        <!--财付通支付方案类型配置,用于反射 Leon-->
        <add key="1" value="TecentPayment" />

    以上,我们完成了支付模块的重构,当给到不同的参数时,能够通过不同的支付平台进行支付,如支付宝:

    财付通:

    后续需要扩展支付平台时,这套方案也能轻松应对:只需添加支付类型并增加该类型的配置即可。

    本篇文章重点描述可扩展性,其实归根结底是降低模块间的藕合度。系统开发初期追求效率,未必能考虑到系统后续的发展需求,此时适当的代码重构是很必要的。“高内聚,低耦合”的思想应贯穿设计到开发的始终。其他模块也可以按这套思路进行重构,增加代码的可读性、可维护性和健壮性。

    曾有一位前辈和我说过:“重构的过程,是程序员进步最快的过程。”这句话分享给大家。

  • 相关阅读:
    不同编码字符所占大小
    期末考点总结--------多元统计分析
    博客网站设计
    java 事件举例
    zookerper总结
    Spring java配置
    Sense and Sensibility
    栈的出栈序列个数
    闭算子
    线性空间结论总结
  • 原文地址:https://www.cnblogs.com/smartLeon/p/4335432.html
Copyright © 2011-2022 走看看