一个函数应该写多长?

最近在看公司源代码的时候,经常有一些超长函数出现,甚至超过1000多行的函数都大有存在,这大大影响我对代码的理解,所以写下此文,当然只是自己的想法,不强加于人,只为勉励自己。

 

在以往的软件开发中,对于函数我也是想写多长就写多长,从来不去想它有多长这个“无聊的问题”,因为对于一个函数应该写多长并没有一个确切的定义,这应该看具体情况决定。

 

我个人觉得,无论是类还是函数,都应该满足单一职责原则,如果出现一个函数过长或者代码块嵌套过深的情况,常常是因为没有满足这一原则,这两种情况都能够通过更好的重构来解决。

 

 

以我工作中的一个代码片段为例来看一下,函数写多长,如何拆分函数。

 

 

ContractedBlock.gif ExpandedBlockStart.gif Step1
 1ExpandedBlockStart.gifContractedBlock.gif/**//// <summary>
 2/// 时实窗体类
 3/// </summary>

 4public partial class iRealTimeInfo : Form
 5ExpandedBlockStart.gifContractedBlock.gif{
 6    ArrayList arrRealTime = new ArrayList();
 7
 8    public iRealTimeInfo()
 9ExpandedSubBlockStart.gifContractedSubBlock.gif    {
10        InitializeComponent();
11    }

12
13ExpandedSubBlockStart.gifContractedSubBlock.gif    /**//// <summary>
14    /// 加载窗体事件
15    /// </summary>

16    private void iRealTimeInfo_Load(object sender, EventArgs e)
17ExpandedSubBlockStart.gifContractedSubBlock.gif    {
18ContractedSubBlock.gifExpandedSubBlockStart.gif        从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)
23ExpandedSubBlockStart.gifContractedSubBlock.gif        {
24            string sName = n.Name;
25            if (sName == "Info")
26ExpandedSubBlockStart.gifContractedSubBlock.gif            {
27                foreach (XmlNode n2 in n.ChildNodes)
28ExpandedSubBlockStart.gifContractedSubBlock.gif                {
29                    sName = n2.Name.ToLower();
30                    switch (sName)
31ExpandedSubBlockStart.gifContractedSubBlock.gif                    {
32                        case "realtime"//找到实时模板根结点
33                            foreach (XmlNode n3 in n2.ChildNodes)
34ExpandedSubBlockStart.gifContractedSubBlock.gif                            {
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
53ContractedSubBlock.gifExpandedSubBlockStart.gif                                验证模板文件是否存在#region 验证模板文件是否存在
54                                String sFilePath = strXmlFile + @"\" + sXmlName;
55                                if (System.IO.File.Exists(sFilePath))
56ExpandedSubBlockStart.gifContractedSubBlock.gif                                {
57                                    arrRealTime.Add(TL);
58                                }

59                                #endregion

60                            }

61                            break;
62                    }

63                }

64            }

65        }

66        #endregion

67
68ContractedSubBlock.gifExpandedSubBlockStart.gif        加载数据源对象列表#region 加载数据源对象列表
69        ArrayList ListTemplate = new ArrayList();
70        for (int ii = 0; ii < arrRealTime.Count; ii++)
71ExpandedSubBlockStart.gifContractedSubBlock.gif        {
72            TemplateList tl = arrRealTime[ii] as TemplateList;
73            ListTemplate.Add(new ListStatus(tl.TemplateTitleCn, tl.TemplateName));
74        }

75        #endregion

76
77ContractedSubBlock.gifExpandedSubBlockStart.gif        绑定模板下拉框#region 绑定模板下拉框
78        if (ListTemplate.Count != 0)
79ExpandedSubBlockStart.gifContractedSubBlock.gif        {
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)
88ExpandedSubBlockStart.gifContractedSubBlock.gif        {
89            this.btnSubmit.Enabled = false;
90        }

91    }

92}

这是一个窗体的加载事件,运行过程是这样,首先从系统模板配置文件中找到实时模板类型信息,然后验证该信息中指定的模板类型文件是否存在,如果存在的话把XML文件中的实时模板信息加载到对象列表中,然后通过该模板类型列表得到一个绑定下拉框的数据源对象列表,用来绑定下拉框。

 

上面函数中算很长的了,函数过长的一个原因是因为临时变量,因为临时变量只有在所属函数中才能使用,所以它会驱使你写出更长的函数,所以我们在开发过程中尽量少用或不用临时变量(可以使用一些重构准则实现)。函数过长的另一个原因是它承担了过多的职责,上面函数承担了多项职责:加载XML文件来获取模板信息对象列表;加载数据源对象列表;绑定模板下拉框等,我们首先把这三个职责抽取出来,分别用一个专职的函数来实现。

 

 

ContractedBlock.gif ExpandedBlockStart.gif Code Step2
  1ExpandedBlockStart.gifContractedBlock.gif/**//// <summary>
  2/// 时实窗体类
  3/// </summary>
  4public partial class iRealTimeInfo : Form
  5ExpandedBlockStart.gifContractedBlock.gif{
  6ContractedSubBlock.gifExpandedSubBlockStart.gif    成员属性#region 成员属性
  7ExpandedSubBlockStart.gifContractedSubBlock.gif    /**//// <summary>
  8    /// 实时模板列表
  9    /// </summary>

 10    ArrayList arrRealTime = new ArrayList();
 11    #endregion

 12
 13    public iRealTimeInfo()
 14ExpandedSubBlockStart.gifContractedSubBlock.gif    {
 15        InitializeComponent();
 16    }

 17
 18ExpandedSubBlockStart.gifContractedSubBlock.gif    /**//// <summary>
 19    /// 加载窗体事件
 20    /// </summary>

 21    private void iRealTimeInfo_Load(object sender, EventArgs e)
 22ExpandedSubBlockStart.gifContractedSubBlock.gif    {
 23ContractedSubBlock.gifExpandedSubBlockStart.gif        从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)
 28ExpandedSubBlockStart.gifContractedSubBlock.gif        {
 29            if (n.Name == "Info")
 30ExpandedSubBlockStart.gifContractedSubBlock.gif            {
 31                foreach (XmlNode n2 in n.ChildNodes)
 32ExpandedSubBlockStart.gifContractedSubBlock.gif                {
 33                    switch (n2.Name.ToLower())
 34ExpandedSubBlockStart.gifContractedSubBlock.gif                    {
 35                        case "realtime"//找到实时模板根结点
 36                            foreach (XmlNode n3 in n2.ChildNodes)
 37ExpandedSubBlockStart.gifContractedSubBlock.gif                            {
 38                                TemplateList TL = getTemplateModelByXmlNode(n3);
 39
 40ContractedSubBlock.gifExpandedSubBlockStart.gif                                验证模板文件是否存在#region 验证模板文件是否存在
 41                                String sFilePath = strXmlFile + @"\" + n3.InnerText;
 42                                if (System.IO.File.Exists(sFilePath))
 43ExpandedSubBlockStart.gifContractedSubBlock.gif                                {
 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)
 60ExpandedSubBlockStart.gifContractedSubBlock.gif        {
 61            this.btnSubmit.Enabled = false;
 62        }

 63    }

 64
 65ExpandedSubBlockStart.gifContractedSubBlock.gif    /**//// <summary>
 66    /// 由XML获取模板对象
 67    /// </summary>
 68    /// <param name="xmlNode">XML模板结点</param>

 69    private TemplateList getTemplateModelByXmlNode(XmlNode xmlNode)
 70ExpandedSubBlockStart.gifContractedSubBlock.gif    {
 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
 86ExpandedSubBlockStart.gifContractedSubBlock.gif    /**//// <summary>
 87    /// 绑定模板下拉框
 88    /// </summary>
 89    /// <param name="lstTemplate">模板数据源对象列表</param>

 90    private void BindCboTemplate(ArrayList lstTemplate)
 91ExpandedSubBlockStart.gifContractedSubBlock.gif    {
 92        if (lstTemplate.Count != 0)
 93ExpandedSubBlockStart.gifContractedSubBlock.gif        {
 94            cmbTemplete.DisplayMember = "StatusName";
 95            cmbTemplete.ValueMember = "StatusValue";
 96            cmbTemplete.DropDownStyle = ComboBoxStyle.DropDownList;
 97            cmbTemplete.DataSource = lstTemplate;
 98        }

 99    }

100
101ExpandedSubBlockStart.gifContractedSubBlock.gif    /**//// <summary>
102    /// 加载数据源对象列表
103    /// </summary>

104    private ArrayList LoadTemplateDataSource()
105ExpandedSubBlockStart.gifContractedSubBlock.gif    {
106        ArrayList result = new ArrayList();
107
108        for (int i = 0; i < arrRealTime.Count; i++)
109ExpandedSubBlockStart.gifContractedSubBlock.gif        {
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模板信息配置文件,最后处理结果如下:

 

 

ContractedBlock.gif ExpandedBlockStart.gif Code Step3
  1ExpandedBlockStart.gifContractedBlock.gif/**//// <summary>
  2/// 处理模板配置xml文件类
  3/// </summary>

  4public class SystemTemplateManager
  5ExpandedBlockStart.gifContractedBlock.gif{
  6ExpandedSubBlockStart.gifContractedSubBlock.gif    /**//// <summary>
  7    /// 模板目录
  8    /// </summary>

  9    private string directoryPath = "";
 10
 11ExpandedSubBlockStart.gifContractedSubBlock.gif    /**//// <summary>
 12    /// 文档对象
 13    /// </summary>

 14    private XmlDocument xmlDoc = new XmlDocument();
 15
 16ExpandedSubBlockStart.gifContractedSubBlock.gif    /**//// <summary>
 17    /// 系统模板管理类
 18    /// </summary>
 19    /// <param name="startupPath">启动路径</param>

 20    public SystemTemplateManager(string startupPath)
 21ExpandedSubBlockStart.gifContractedSubBlock.gif    {
 22        this.directoryPath = startupPath + @"\Templete\SystemTemplate";
 23        this.xmlDoc.Load(getFilePath("SystemTemplate.xml"));
 24    }

 25
 26ContractedSubBlock.gifExpandedSubBlockStart.gif    公有方法#region 公有方法
 27ExpandedSubBlockStart.gifContractedSubBlock.gif    /**//// <summary>
 28    /// 获取实时模板列表
 29    /// </summary>
 30    /// <returns>返回实时模板对象列表</returns>

 31    public ArrayList GetRealTimeTemplateList()
 32ExpandedSubBlockStart.gifContractedSubBlock.gif    {
 33        return GetTemplateList("realtime");
 34    }

 35
 36ExpandedSubBlockStart.gifContractedSubBlock.gif    /**//// <summary>
 37    /// 获取历史模板列表
 38    /// </summary>
 39    /// <returns>返回历史模板对象列表</returns>

 40    public ArrayList GetHistoryTemplateList()
 41ExpandedSubBlockStart.gifContractedSubBlock.gif    {
 42        return GetTemplateList("history");
 43    }

 44    #endregion

 45
 46ContractedSubBlock.gifExpandedSubBlockStart.gif    私有方法#region 私有方法
 47ExpandedSubBlockStart.gifContractedSubBlock.gif    /**//// <summary>
 48    /// 获取实时模板列表
 49    /// </summary>
 50    /// <returns>返回实时模板对象列表</returns>

 51    private ArrayList GetTemplateList(string type)
 52ExpandedSubBlockStart.gifContractedSubBlock.gif    {
 53        ArrayList result = new ArrayList();
 54
 55        foreach (XmlNode node in getInfoNode().ChildNodes)
 56ExpandedSubBlockStart.gifContractedSubBlock.gif        {
 57            if (node.Name.ToLower() == type)
 58ExpandedSubBlockStart.gifContractedSubBlock.gif            {
 59                foreach (XmlNode tNode in node.ChildNodes)
 60ExpandedSubBlockStart.gifContractedSubBlock.gif                {
 61                    TemplateList model = getTemplateModelByXmlNode(tNode);
 62                    //做文件是否存在的验证。。。。。。。。。。
 63                    if (System.IO.File.Exists(getFilePath(tNode.InnerText)))
 64ExpandedSubBlockStart.gifContractedSubBlock.gif                    {
 65                        result.Add(model);
 66                    }

 67                }

 68            }

 69        }

 70
 71        return result;
 72    }

 73
 74ExpandedSubBlockStart.gifContractedSubBlock.gif    /**//// <summary>
 75    /// 获取指定文件的物理路径
 76    /// </summary>
 77    /// <param name="fileName">文件名称</param>
 78    /// <returns>返回指定文件的物理路径</returns>

 79    private string getFilePath(string fileName)
 80ExpandedSubBlockStart.gifContractedSubBlock.gif    {
 81        return directoryPath + @"\" + fileName;
 82    }

 83
 84ExpandedSubBlockStart.gifContractedSubBlock.gif    /**//// <summary>
 85    /// 根结点
 86    /// </summary>
 87    /// <returns>返回根结点对象</returns>

 88    private XmlNode getInfoNode()
 89ExpandedSubBlockStart.gifContractedSubBlock.gif    {
 90        return xmlDoc.SelectSingleNode("Info");
 91    }

 92
 93ExpandedSubBlockStart.gifContractedSubBlock.gif    /**//// <summary>
 94    /// 由XML获取模板对象
 95    /// </summary>
 96    /// <param name="xmlNode">XML模板结点</param>

 97    private TemplateList getTemplateModelByXmlNode(XmlNode xmlNode)
 98ExpandedSubBlockStart.gifContractedSubBlock.gif    {
 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
116ExpandedBlockStart.gifContractedBlock.gif/**//// <summary>
117/// 实时窗体类
118/// </summary>

119public partial class iRealTimeInfo : Form
120ExpandedBlockStart.gifContractedBlock.gif{
121ContractedSubBlock.gifExpandedSubBlockStart.gif    成员属性#region 成员属性
122ExpandedSubBlockStart.gifContractedSubBlock.gif    /**//// <summary>
123    /// 实时模板列表
124    /// </summary>

125    ArrayList arrRealTime = new ArrayList();
126    #endregion

127
128    public iRealTimeInfo()
129ExpandedSubBlockStart.gifContractedSubBlock.gif    {
130        InitializeComponent();
131    }

132
133ExpandedSubBlockStart.gifContractedSubBlock.gif    /**//// <summary>
134    /// 加载事件
135    /// </summary>

136    private void iRealTimeInfo_Load(object sender, EventArgs e)
137ExpandedSubBlockStart.gifContractedSubBlock.gif    {
138        this.arrRealTime = new SystemTemplateManager(Application.StartupPath).GetRealTimeTemplateList();
139
140        ArrayList lstTemplate = LoadTemplateDataSource();
141        BindCboTemplate(lstTemplate);
142
143        if (lstTemplate.Count == 0)
144ExpandedSubBlockStart.gifContractedSubBlock.gif        {
145            this.btnSubmit.Enabled = false;
146        }

147    }

148
149ExpandedSubBlockStart.gifContractedSubBlock.gif    /**//// <summary>
150    /// 获取模板对象列表
151    /// </summary>
152    /// <returns>返回模板对象列表</returns>

153    private ArrayList LoadTemplateDataSource()
154ExpandedSubBlockStart.gifContractedSubBlock.gif    {
155        ArrayList result = new ArrayList();
156
157        for (int i = 0; i < arrRealTime.Count; i++)
158ExpandedSubBlockStart.gifContractedSubBlock.gif        {
159            TemplateList model = arrRealTime[i] as TemplateList;
160            result.Add(new ListStatus(model.TemplateTitleCn, model.TemplateName));
161        }

162
163        return result;
164    }

165
166ExpandedSubBlockStart.gifContractedSubBlock.gif    /**//// <summary>
167    /// 绑定模板下拉框
168    /// </summary>
169    /// <param name="lstTemplate">模板对象列表</param>

170    private void BindCboTemplate(ArrayList lstTemplate)
171ExpandedSubBlockStart.gifContractedSubBlock.gif    {
172        if (lstTemplate.Count != 0)
173ExpandedSubBlockStart.gifContractedSubBlock.gif        {
174            cmbTemplete.DisplayMember = "StatusName";
175            cmbTemplete.ValueMember = "StatusValue";
176            cmbTemplete.DropDownStyle = ComboBoxStyle.DropDownList;
177            cmbTemplete.DataSource = lstTemplate;
178        }

179    }

180}

 

 

当我们遇到过长的函数或者需要注释才能让人理解的代码块的时候,就应该考虑可不可以使用重构提取函数,不用管函数有多长,哪声只有一句,只要可以强化代码的清晰度,那就去做。就算提取出来的函数名称比函数体还长也无所谓。

 

过长的函数和嵌套过深的代码块(比如ifforwhiletry代码块)是使函数更难于理解和维护的密不可分的两大元凶(而且经常毫无必要)。

 

我们不必担心函数拆分过多的问题,函数调用的开销可以忽略不计,使用小函数有以下几个好处:

 

1 如果每个函数的粒度都很小,那么函数之间彼此复用的机会就更大;

2 使得高层代码读起来就像是一系列注释;

3 如果函数都很小,那么函数的覆写就比较容易;

 

在命名函数的时候,每个函数都应该是顾其名而能思其义,我们应该以它“做什么”来命名,而不是以它“怎么做”命名,只有在你可以给小函数很好地命名时,它们才能真正起作用。

 

 

转载于:https://www.cnblogs.com/wych/archive/2009/07/01/1514607.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值