最近在看公司源代码的时候,经常有一些超长函数出现,甚至超过 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元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。
【本文对您有帮助就好】元

