zoukankan      html  css  js  c++  java
  • 设计模式-责任链模式引发的思考

    背景:

    在业务频繁迭代的时候,有时候一个菜单页面对应的后台代码会越来越凌乱,一个方法本来就做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;
                    }
                }
            }
    View Code

    重构后:

    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;
            }
            //...
        }
    }
    View Code

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

    量变会引起质变。
  • 相关阅读:
    Zabbix 3.0编译安装
    用Python发布自己的代码
    欧拉公式为世界上最完美的公式
    通过别人远端安装anaconda来说配置文件
    jupyter安装与使用
    make在latex中的妙用
    latex源码到PDF文件
    anconda配置国内源
    git命令速查
    一张色环图教你搞定配色(转)
  • 原文地址:https://www.cnblogs.com/bibi-feiniaoyuan/p/14959967.html
Copyright © 2011-2022 走看看