欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

设计模式-责任链模式引发的思考

程序员文章站 2022-03-03 09:47:47
背景: 在业务频繁迭代的时候,有时候一个菜单页面对应的后台代码会越来越凌乱,一个方法本来就做A、B事情,后面迭代,变成会做A、B、C、D、E事情,且A、B又细分出a1、a2、a3、a4、b1、b2、b3。一个菜单页开始设计代码时,通常写法都较为面向对象,后面经多个人,可能是不同的人迭代后就越来越面向 ......

背景:

在业务频繁迭代的时候,有时候一个菜单页面对应的后台代码会越来越凌乱,一个方法本来就做a、b事情,后面迭代,变成会做a、b、c、d、e事情,且a、b又细分出a1、a2、a3、a4、b1、b2、b3。一个菜单页开始设计代码时,通常写法都较为面向对象,后面经多个人,可能是不同的人迭代后就越来越面向流程。

为什么后面越来越面向流程呢?大约是因为方便吧。如果只是迭代一个小逻辑,一旦要抽出一些通用性的东西,面向对象,写出好扩展的代码,要考虑的情况可能涉及:之前的业务,牵涉到的上下文变量,跟ui的交互(例如弹窗提示报错,询问用户是否继续某操作等等),把之前上线过的东西抽出来放到其他地方又增加了测试的地方等等,太难了。

但代码还能能简单改造一下,这种业务流程像是可以通过来重构代码。但是这种模式有点隆重感,建好多类,又要抽象类,好像把一个菜单的后台代码复杂化了。但是它的本质,职责层层传递确实又让扩展容易,阅读代码容易。

代码重构过程:

重构前:

设计模式-责任链模式引发的思考
 private void buttonold_click(object sender, eventargs e)
        {            
            string fundid = "2323112";
            string fundcode = ""; //跨a3、b1使用
            string poolid = "";//跨a1、a2使用
            string fundname = ""; 

            //a业务
            //a1验证
            //...
            poolid = "787908aa";
            //...

            //a2验证
            int count = 0;
            //...
            if (count == 0)
            {
                messagebox.show($"{fundid}不在配置数据里;");
                loghelper("....");
                return;
            }

            //a3验证
            //....
            if (poolid == "33333")
            {
                //...
            }
            fundname = "新能源混合";
            fundcode = "908898";
            //...

            //b业务
            //b1验证
            list<string> list = new list<string>();
            if (list.contains(fundcode)) 
            {
                //...   
                if (messagebox.show($"{fundname}确认发送给某某用户", "提示", messageboxbuttons.yesno) != dialogresult.yes)
                {
                    return;
                }
            }
        }

重构后:

设计模式-责任链模式引发的思考
using system;
using system.collections.generic;
using system.data;
using system.windows.forms;

namespace windowsformsapp2
{
    public partial class form1 : form
    {
        private datatable configtable;
        private dictionary<string, string> limitdict;

        public form1()
        {
            initializecomponent();
        }

        private void form1_load(object sender, eventargs e)
        {
            configtable = new datatable();
            //...
            limitdict = new dictionary<string, string>();
            //...
        }      

        private void buttonnew_click(object sender, eventargs e)
        {
            string checkerrmesg = "";
            string fundid = "2323112";
            string fundcode;
            string fundname;

            //...
            checkbxfund checkbxfund = new checkbxfund(loghelper,configtable, limitdict);            
            if(checkbxfund.handle(fundid,ref checkerrmesg) == false)
            {
                messagebox.show(checkerrmesg);
                return;
            }
            fundcode = checkbxfund.outfundcode;
            fundname = checkbxfund.outfundname;

            //...
            checkrisk checkrisk = new checkrisk();
            //
            if(checkrisk.handle(fundcode, ref checkerrmesg)==false)
            {
                messagebox.show(checkerrmesg);
                return;
            }
            //...
        }

        private void loghelper(string message)
        {
            
        }
    }

    /// <summary>
    /// 假设是整个a业务
    /// </summary>
    internal class checkbxfund
    {
        //应当返回出去的值 1、基金名称 2 基金代码
        public string outfundname;
        public string outfundcode;

        //外面传进来协助验证的一些变量
        private datatable inconfigtable;
        private dictionary<string, string> inrepeatdict;
        private action<string> logaction; //有一些操作可能要用外面的窗体实例,例如只有窗体可以log:this.log()

        //方法之间传递的变量,方便跨方法调用
        private string poolid;

        //参数变量,不必重复获取的一些变量
        public checkbxfund(action<string> logaction, datatable dt, dictionary<string, string> dict)
        {
            inconfigtable = dt;
            inrepeatdict = dict;
            logaction = logaction;
        }   
                
        public bool handle(string fundid, ref string errormessage)
        {
            if(isrepeat(fundid,ref errormessage) == false)
            {
                return false;
            }
            if (isinconfig(fundid, ref errormessage) == false)
            {
                return false;
            }
            if (iscangetname(fundid, ref errormessage) == false)
            {
                return false;
            }
            return true;
        }

        /// <summary>
        /// 假设是a1步骤
        /// </summary>
        /// <param name="fundid"></param>
        /// <param name="errormessage"></param>
        /// <returns></returns>
        private bool isrepeat(string fundid,ref string errormessage)
        {
            //...
            poolid = "787908aa";
            //...
            return false;
        }

        /// <summary>
        /// 假设是a2步骤
        /// </summary>
        /// <param name="fundid"></param>
        /// <param name="errormessage"></param>
        /// <returns></returns>
        private bool isinconfig(string fundid,ref string errormessage)
        {
            int count = 0;
            //...
            if (count == 0)
            {
                errormessage = $"{fundid}不在配置数据里;";
                logaction("....");
                return false;
            }
            return true;
        }

        /// <summary>
        /// 假设是a3步骤
        /// </summary>
        /// <param name="fundid"></param>
        /// <param name="errmessage"></param>
        /// <returns></returns>
        private bool iscangetname(string fundid,ref string errmessage)
        {
            //....
            if (poolid == "33333")
            {
                //...
            }
            outfundname = "新能源混合";
            outfundcode = "908898";
            return true;
        }
    }

    /// <summary>
    /// 假设是整个b业务
    /// </summary>
    internal class checkrisk
    {
        //...
        public bool handle(string fundcode,ref string errmessage)
        {
            //...
            return true;
        }
        //...
    }
}

 

总结:在一个类中封装一个业务,职责传递通过一个handle方法组装,每一个职责就一个方法。跨大业务(大a大b)小业务(小a1、小a2等)的传值通过公共变量传递,和ui的交互通过委托,或者只有窗体实例才能调的方法也通过委托。这一整个类属于菜单的后台代码业务帮助类,和菜单粘性较强。