最近在看公司源代码的时候,经常有一些超长函数出现,甚至超过 1000 多行的函数都大有存在,这大大影响我对代码的理解,所以写下此文,当然只是自己的想法,不强加于人,只为勉励自己。
在以往的软件开发中,对于函数我也是想写多长就写多长,从来不去想它有多长这个“无聊的问题”,因为对于一个函数应该写多长并没有一个确切的定义,这应该看具体情况决定。
我个人觉得,无论是类还是函数,都应该满足单一职责原则,如果出现一个函数过长或者代码块嵌套过深的情况,常常是因为没有满足这一原则,这两种情况都能够通过更好的重构来解决。
以我工作中的一个代码片段为例来看一下,函数写多长,如何拆分函数。
1 /**/ /// <summary>
2 /// 时实窗体类
3 /// </summary>
4 public partial class iRealTimeInfo : Form
5 {
6 ArrayList arrRealTime = new ArrayList();
7
8 public iRealTimeInfo()
9 {
10 InitializeComponent();
11 }
12
13 /**/ /// <summary>
14 /// 加载窗体事件
15 /// </summary>
16 private void iRealTimeInfo_Load( object sender, EventArgs e)
17 {
18 从XML文件中读取并获得所有曲线实时模板类型 #region 从XML文件中读取并获得所有曲线实时模板类型
19 string strXmlFile = Application.StartupPath + @" \Templete\SystemTemplate " ;
20 XmlDocument doc = new XmlDocument();
21 doc.Load(strXmlFile + @" \SystemTemplate.xml " );
22 foreach (XmlNode n in doc.ChildNodes)
23 {
24 string sName = n.Name;
25 if (sName == " Info " )
26 {
27 foreach (XmlNode n2 in n.ChildNodes)
28 {
29 sName = n2.Name.ToLower();
30 switch (sName)
31 {
32 case " realtime " : // 找到实时模板根结点
33 foreach (XmlNode n3 in n2.ChildNodes)
34 {
35 string sXmlName = n3.InnerText;
36 string sXmlTitleCn = n3.Attributes[ " name " ].Value;
37 string sXmlTitleEn = n3.Attributes[ " nameEn " ].Value;
38 string sXmlAxisType = n3.Attributes[ " type " ].Value;
39 string sXmlChartModel = n3.Attributes[ " chartmodel " ].Value;
40 string sXmlDataType = n3.Attributes[ " datatype " ].Value;
41
42 TemplateList TL = new TemplateList();
43 TL.TemplateActc = " 000000 " ;
44 TL.TemplateName = sXmlName.Split( ' . ' )[ 0 ];
45 TL.TemplateTitleCn = sXmlTitleCn;
46 TL.TemplateTitleEn = sXmlTitleEn;
47 TL.TemplateAxisType = sXmlAxisType;
48 TL.TemplateType = " 0 " ;
49 TL.RealTimeHistory = " 1 " ;
50 TL.ChartModel = sXmlChartModel;
51 TL.DataType = sXmlDataType;
52
53 验证模板文件是否存在 #region 验证模板文件是否存在
54 String sFilePath = strXmlFile + @" \ " + sXmlName;
55 if (System.IO.File.Exists(sFilePath))
56 {
57 arrRealTime.Add(TL);
58 }
59 #endregion
60 }
61 break ;
62 }
63 }
64 }
65 }
66 #endregion
67
68 加载数据源对象列表 #region 加载数据源对象列表
69 ArrayList ListTemplate = new ArrayList();
70 for ( int ii = 0 ; ii < arrRealTime.Count; ii ++ )
71 {
72 TemplateList tl = arrRealTime[ii] as TemplateList;
73 ListTemplate.Add( new ListStatus(tl.TemplateTitleCn, tl.TemplateName));
74 }
75 #endregion
76
77 绑定模板下拉框 #region 绑定模板下拉框
78 if (ListTemplate.Count != 0 )
79 {
80 this .cmbTemplete.DisplayMember = " StatusName " ;
81 this .cmbTemplete.ValueMember = " StatusValue " ;
82 this .cmbTemplete.DropDownStyle = ComboBoxStyle.DropDownList;
83 this .cmbTemplete.DataSource = ListTemplate;
84 }
85 #endregion
86
87 if (ListTemplate.Count == 0 )
88 {
89 this .btnSubmit.Enabled = false ;
90 }
91 }
92 }
这是一个窗体的加载事件,运行过程是这样,首先从系统模板配置文件中找到实时模板类型信息,然后验证该信息中指定的模板类型文件是否存在,如果存在的话把
XML
文件中的实时模板信息加载到对象列表中,然后通过该模板类型列表得到一个绑定下拉框的数据源对象列表,用来绑定下拉框。
上面函数中算很长的了,函数过长的一个原因是因为临时变量,因为临时变量只有在所属函数中才能使用,所以它会驱使你写出更长的函数,所以我们在开发过程中尽量少用或不用临时变量(可以使用一些重构准则实现)。函数过长的另一个原因是它承担了过多的职责,上面函数承担了多项职责:加载
XML
文件来获取模板信息对象列表;加载数据源对象列表;绑定模板下拉框等,我们首先把这三个职责抽取出来,分别用一个专职的函数来实现。
1 /**/ /// <summary>
2 /// 时实窗体类
3 /// </summary>
4 public partial class iRealTimeInfo : Form
5 {
6 成员属性 #region 成员属性
7 /**/ /// <summary>
8 /// 实时模板列表
9 /// </summary>
10 ArrayList arrRealTime = new ArrayList();
11 #endregion
12
13 public iRealTimeInfo()
14 {
15 InitializeComponent();
16 }
17
18 /**/ /// <summary>
19 /// 加载窗体事件
20 /// </summary>
21 private void iRealTimeInfo_Load( object sender, EventArgs e)
22 {
23 从XML文件中读取并获得所有曲线实时模板类型 #region 从XML文件中读取并获得所有曲线实时模板类型
24 string strXmlFile = Application.StartupPath + @" \Templete\SystemTemplate " ;
25 XmlDocument doc = new XmlDocument();
26 doc.Load(strXmlFile + @" \SystemTemplate.xml " );
27 foreach (XmlNode n in doc.ChildNodes)
28 {
29 if (n.Name == " Info " )
30 {
31 foreach (XmlNode n2 in n.ChildNodes)
32 {
33 switch (n2.Name.ToLower())
34 {
35 case " realtime " : // 找到实时模板根结点
36 foreach (XmlNode n3 in n2.ChildNodes)
37 {
38 TemplateList TL = getTemplateModelByXmlNode(n3);
39
40 验证模板文件是否存在 #region 验证模板文件是否存在
41 String sFilePath = strXmlFile + @" \ " + n3.InnerText;
42 if (System.IO.File.Exists(sFilePath))
43 {
44 arrRealTime.Add(TL);
45 }
46 #endregion
47 }
48 break ;
49 }
50 }
51 }
52 }
53 #endregion
54
55 ArrayList lstTemplate = LoadTemplateDataSource();
56
57 BindCboTemplate(lstTemplate);
58
59 if (lstTemplate.Count == 0 )
60 {
61 this .btnSubmit.Enabled = false ;
62 }
63 }
64
65 /**/ /// <summary>
66 /// 由XML获取模板对象
67 /// </summary>
68 /// <param name="xmlNode"> XML模板结点 </param>
69 private TemplateList getTemplateModelByXmlNode(XmlNode xmlNode)
70 {
71 TemplateList result = new TemplateList();
72
73 result.TemplateActc = " 000000 " ;
74 result.TemplateName = xmlNode.InnerText.Split( ' . ' )[ 0 ];
75 result.TemplateTitleCn = xmlNode.Attributes[ " name " ].Value;
76 result.TemplateTitleEn = xmlNode.Attributes[ " nameEn " ].Value;
77 result.TemplateAxisType = xmlNode.Attributes[ " type " ].Value;
78 result.TemplateType = " 0 " ;
79 result.RealTimeHistory = " 1 " ;
80 result.ChartModel = xmlNode.Attributes[ " chartmodel " ].Value;
81 result.DataType = xmlNode.Attributes[ " datatype " ].Value;
82
83 return result;
84 }
85
86 /**/ /// <summary>
87 /// 绑定模板下拉框
88 /// </summary>
89 /// <param name="lstTemplate"> 模板数据源对象列表 </param>
90 private void BindCboTemplate(ArrayList lstTemplate)
91 {
92 if (lstTemplate.Count != 0 )
93 {
94 cmbTemplete.DisplayMember = " StatusName " ;
95 cmbTemplete.ValueMember = " StatusValue " ;
96 cmbTemplete.DropDownStyle = ComboBoxStyle.DropDownList;
97 cmbTemplete.DataSource = lstTemplate;
98 }
99 }
100
101 /**/ /// <summary>
102 /// 加载数据源对象列表
103 /// </summary>
104 private ArrayList LoadTemplateDataSource()
105 {
106 ArrayList result = new ArrayList();
107
108 for ( int i = 0 ; i < arrRealTime.Count; i ++ )
109 {
110 TemplateList model = arrRealTime[i] as TemplateList;
111 result.Add( new ListStatus(model.TemplateTitleCn, model.TemplateName));
112 }
113
114 return result;
115 }
116 }
通过上面的重构过程,加载事件看起来容易理解一点,但是对于
XML
文件的处理过程还是很复杂,另外,我们在此类中处理模板下拉框并显示实时模板数据的,而处理
XML
的职责也写在了此类中,所以此类也可以抽取出来,放在一个单独的类中,用来专门处理
XML
模板信息配置文件,最后处理结果如下:
1 /**/ /// <summary>
2 /// 处理模板配置xml文件类
3 /// </summary>
4 public class SystemTemplateManager
5 {
6 /**/ /// <summary>
7 /// 模板目录
8 /// </summary>
9 private string directoryPath = "" ;
10
11 /**/ /// <summary>
12 /// 文档对象
13 /// </summary>
14 private XmlDocument xmlDoc = new XmlDocument();
15
16 /**/ /// <summary>
17 /// 系统模板管理类
18 /// </summary>
19 /// <param name="startupPath"> 启动路径 </param>
20 public SystemTemplateManager( string startupPath)
21 {
22 this .directoryPath = startupPath + @" \Templete\SystemTemplate " ;
23 this .xmlDoc.Load(getFilePath( " SystemTemplate.xml " ));
24 }
25
26 公有方法 #region 公有方法
27 /**/ /// <summary>
28 /// 获取实时模板列表
29 /// </summary>
30 /// <returns> 返回实时模板对象列表 </returns>
31 public ArrayList GetRealTimeTemplateList()
32 {
33 return GetTemplateList( " realtime " );
34 }
35
36 /**/ /// <summary>
37 /// 获取历史模板列表
38 /// </summary>
39 /// <returns> 返回历史模板对象列表 </returns>
40 public ArrayList GetHistoryTemplateList()
41 {
42 return GetTemplateList( " history " );
43 }
44 #endregion
45
46 私有方法 #region 私有方法
47 /**/ /// <summary>
48 /// 获取实时模板列表
49 /// </summary>
50 /// <returns> 返回实时模板对象列表 </returns>
51 private ArrayList GetTemplateList( string type)
52 {
53 ArrayList result = new ArrayList();
54
55 foreach (XmlNode node in getInfoNode().ChildNodes)
56 {
57 if (node.Name.ToLower() == type)
58 {
59 foreach (XmlNode tNode in node.ChildNodes)
60 {
61 TemplateList model = getTemplateModelByXmlNode(tNode);
62 // 做文件是否存在的验证。。。。。。。。。。
63 if (System.IO.File.Exists(getFilePath(tNode.InnerText)))
64 {
65 result.Add(model);
66 }
67 }
68 }
69 }
70
71 return result;
72 }
73
74 /**/ /// <summary>
75 /// 获取指定文件的物理路径
76 /// </summary>
77 /// <param name="fileName"> 文件名称 </param>
78 /// <returns> 返回指定文件的物理路径 </returns>
79 private string getFilePath( string fileName)
80 {
81 return directoryPath + @" \ " + fileName;
82 }
83
84 /**/ /// <summary>
85 /// 根结点
86 /// </summary>
87 /// <returns> 返回根结点对象 </returns>
88 private XmlNode getInfoNode()
89 {
90 return xmlDoc.SelectSingleNode( " Info " );
91 }
92
93 /**/ /// <summary>
94 /// 由XML获取模板对象
95 /// </summary>
96 /// <param name="xmlNode"> XML模板结点 </param>
97 private TemplateList getTemplateModelByXmlNode(XmlNode xmlNode)
98 {
99 TemplateList result = new TemplateList();
100
101 result.TemplateActc = " 000000 " ;
102 result.TemplateName = xmlNode.InnerText.Split( ' . ' )[ 0 ];
103 result.TemplateTitleCn = xmlNode.Attributes[ " name " ].Value;
104 result.TemplateTitleEn = xmlNode.Attributes[ " nameEn " ].Value;
105 result.TemplateAxisType = xmlNode.Attributes[ " type " ].Value;
106 result.TemplateType = " 0 " ;
107 result.RealTimeHistory = " 1 " ;
108 result.ChartModel = xmlNode.Attributes[ " chartmodel " ].Value;
109 result.DataType = xmlNode.Attributes[ " datatype " ].Value;
110
111 return result;
112 }
113 #endregion
114 }
115
116 /**/ /// <summary>
117 /// 实时窗体类
118 /// </summary>
119 public partial class iRealTimeInfo : Form
120 {
121 成员属性 #region 成员属性
122 /**/ /// <summary>
123 /// 实时模板列表
124 /// </summary>
125 ArrayList arrRealTime = new ArrayList();
126 #endregion
127
128 public iRealTimeInfo()
129 {
130 InitializeComponent();
131 }
132
133 /**/ /// <summary>
134 /// 加载事件
135 /// </summary>
136 private void iRealTimeInfo_Load( object sender, EventArgs e)
137 {
138 this .arrRealTime = new SystemTemplateManager(Application.StartupPath).GetRealTimeTemplateList();
139
140 ArrayList lstTemplate = LoadTemplateDataSource();
141 BindCboTemplate(lstTemplate);
142
143 if (lstTemplate.Count == 0 )
144 {
145 this .btnSubmit.Enabled = false ;
146 }
147 }
148
149 /**/ /// <summary>
150 /// 获取模板对象列表
151 /// </summary>
152 /// <returns> 返回模板对象列表 </returns>
153 private ArrayList LoadTemplateDataSource()
154 {
155 ArrayList result = new ArrayList();
156
157 for ( int i = 0 ; i < arrRealTime.Count; i ++ )
158 {
159 TemplateList model = arrRealTime[i] as TemplateList;
160 result.Add( new ListStatus(model.TemplateTitleCn, model.TemplateName));
161 }
162
163 return result;
164 }
165
166 /**/ /// <summary>
167 /// 绑定模板下拉框
168 /// </summary>
169 /// <param name="lstTemplate"> 模板对象列表 </param>
170 private void BindCboTemplate(ArrayList lstTemplate)
171 {
172 if (lstTemplate.Count != 0 )
173 {
174 cmbTemplete.DisplayMember = " StatusName " ;
175 cmbTemplete.ValueMember = " StatusValue " ;
176 cmbTemplete.DropDownStyle = ComboBoxStyle.DropDownList;
177 cmbTemplete.DataSource = lstTemplate;
178 }
179 }
180 }
当我们遇到过长的函数或者需要注释才能让人理解的代码块的时候,就应该考虑可不可以使用重构提取函数,不用管函数有多长,哪声只有一句,只要可以强化代码的清晰度,那就去做。就算提取出来的函数名称比函数体还长也无所谓。
过长的函数和嵌套过深的代码块(比如 if 、 for 、 while 和 try 代码块)是使函数更难于理解和维护的密不可分的两大元凶(而且经常毫无必要)。
我们不必担心函数拆分过多的问题,函数调用的开销可以忽略不计,使用小函数有以下几个好处:
1 如果每个函数的粒度都很小,那么函数之间彼此复用的机会就更大;
2 使得高层代码读起来就像是一系列注释;
3 如果函数都很小,那么函数的覆写就比较容易;
在命名函数的时候,每个函数都应该是顾其名而能思其义,我们应该以它“做什么”来命名,而不是以它“怎么做”命名,只有在你可以给小函数很好地命名时,它们才能真正起作用。
更多文章、技术交流、商务合作、联系博主
微信扫码或搜索:z360901061
微信扫一扫加我为好友
QQ号联系: 360901061
您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。
【本文对您有帮助就好】元