设计模式-责任链模式引发的思考
程序员文章站
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的交互通过委托,或者只有窗体实例才能调的方法也通过委托。这一整个类属于菜单的后台代码业务帮助类,和菜单粘性较强。