找回密码
 骑士注册

QQ登录

微博登录


从把三千行代码重构成15行代码谈起

2014-12-08 10:10    评论: 33 收藏: 4 分享: 37    

如果你认为这是一个标题党,那么我真诚的恳请你耐心的把文章的第一部分读完,然后再下结论。如果你认为能够戳中您的G点,那么请随手点个赞。

把三千行代码重构为15行

那年我刚毕业,进了现在这个公司。公司是搞数据中心环境监控的,里面充斥着嵌入式、精密空调、总线、RFID的概念,我一个都不懂。还好,公司之前用Delphi写的老客户端因为太慢,然后就搞了个Webform的替代,恰好我对Asp.Net还算了解,我对业务的不了解并不妨碍我称成为这个公司的一个程序员。小公司也有小公司的好,人少,进去很快负责代码开发。我当然也就搞这个数据中心智能管理系统啦。

这个系统非常的庞大,尤其牛逼的是支持客户端组态,然后动态生成网页,数据还能通过Socket实时监控(那时我还真就不懂网络编程)。这个对于当时的我来说,真真是高、大、上呐!!当时跟着了解整个系统大半个月才算能够调试,写一些简单的页面。

在维护系统的过程中,时不时要扩展一些功能,也就接触了下面这个类:

看到没有,就是当年最最流行的三层架构的产物,对于刚出茅庐的毛头小子来说,这是多么专业的文件头注释,还有反射也就算了,这构造函数还能静态的,还能私有的?那时刚接触这么高大上的代码的我,瞬间给跪了!

但是,类写多了,我就感觉越来越别扭,就是下面这段代码:

每增加一个表,除了要改接口、要改DAL、要改BLL之外,还得在这个工厂类添加一个方法,真真是累到手抽筋,即使有当时公司了的G工给我推荐的神器——动软代码生成器,这粘贴复制的几遍,也是让我感觉到异常繁琐,有时候打键盘稍微累了点,还把复制出来代码改错了,你妹的,难道这就是程序员该干的事情,不,绝对不是!我想起了一句至理名言:当你觉得代码重复出现在程序中的时候,就应该重构了。是的,在这句话的指导下,我开始了折腾,决定挑战这个高大上的代码,事实证明,思想的力量是无穷的。

那么,怎么修改呢,仔细观察之后,发现其中className的生成跟返回的类型非常类似,只是一个是类名,一个是字符串,这两者之间应该能够关联起来。于是google了一下(当时GFW还没猖獗起来哈),隐隐约约就找到了“反射”这两个字,深入了解之后,确定可以完成。

接下来,就是返回的类型了,返回的类型并不固定,但是似乎很有规律……这个似乎好像在哪里见过,对了,模板,C++课程上有讲过的,于是再次google,了解到了C#中使用了泛型代替了C++中的模板。在学习完泛型和反射之后,并参考了网上的一些文章,我捣鼓出了下面的代码:

没错,就是它了,三层架构年代最流行的工厂类……

看着原来滚十几屏幕的代码,变成了十多行的代码,真是爽到了骨子里去了,太干净了!唯一让我担忧的是,我进公司的时候,帮忙整理公司申请软件著作权都是需要代码量的,根据代码多少行来评估软件的大小,万一老板知道了我非但没有帮公司增加代码量,还减少了,会不会立即把我开掉?我没敢给我们老板展示我优秀的成果,所幸,这段代码非但没有出过任何问题,还避免了以前同事老是在新增一个类之后,把代码复制过来,但是没有正确修改的问题,大大提高了效率。虽然,我没敢大事宣布我的劳动成果,但是这次成功的修改,则彻底让我走上了代码重构的不归路。

看到这里,大家应该知道这个案例是否真实的了吧。我相信,从08年开始的码农们,看到这种类似的代码绝对不比我少。那么,我想告诉你们的是什么呢?

  • 要在编程过程中多思考
  • 编程的思想很重要,请多看点经典的书
  • 从小处着眼,慢慢重构,尤其在应对一个大型的系统
  • 当重复出现的时候,你应该考虑重构了
  • 粘贴复制的代码越少,你的系统越稳定

少用代码生成器

我们来分析一下,为什么我之前的前辈会写出上面的代码。我归结起来有以下几点:

  • 因为使用了动软代码生成器,生成代码方便,就没多想了。
  • 三层架构的概念倒是了解了,但是没有去深入思考就拿来应用
  • 遇到重复的代码,没有重构的概念,这是思想的问题——思想比你的能力重要

至今为止,还是很多人使用代码生成器,那么我们应该怎么对待这个问题呢。我认为,代码生成器确实可以减少你不少工作,但是少用,那些重复性的工作,除了部分确实是没有办法的,其他大部分都是可以通过框架解决的,举例来说,像三层架构,真正需要用到代码生成器的,也就是Model类而已,其他的完全可以在框架中完成。因此你要竭尽全力的思考怎么在框架中来减少你的重复性工作,而不是依赖于代码生成器

另外,如果你还是在用相关的代码生成工具,请重新定义“动软代码生成器”的代码模板,自己写一个模板;或者使用CodeSmith来完全制定自己的代码生成,因为动软给的代码模板真心乱,比如下面这段代码:

for (int n = 0; n < rowsCount; n++)
{
	model = new DBAccess.Model.eventweek();
	if(dt.Rows[n]["GroupNo"].ToString()!="")
	{
		model.GroupNo=int.Parse(dt.Rows[n]["GroupNo"].ToString());
	}
	if(dt.Rows[n]["Week0"].ToString()!="")
	{
		model.Week0=int.Parse(dt.Rows[n]["Week0"].ToString());
	}
	if(dt.Rows[n]["Week1"].ToString()!="")
	{
		model.Week1=int.Parse(dt.Rows[n]["Week1"].ToString());
	}
}

首先,你就不能用 var row=dt.Rows[n] 替代吗?其次,直接用int.Parse效率多低?再次,dt.Rows[n]["Week0"]为NULL怎么办?

减少造轮子

我们再来看看其他的一些代码:

public List<string> GetDevices(string dev){
	List<string> devs=new List<string>();

	int start=0;
	for(int i=0;i<dev.Length;i++){
		if(dev[i]=='^'){
			devs.Add(dev.SubString(start,i));
			start=i+1;
		}
	}

	return devs;
}

有没有很眼熟,没错,这就是对String.Split()函数的简单实现。我的前辈应该是从c++程序员转过来的,习惯了各种功能自己实现一遍,但是他忽略了C#的很多东西。我们不去评判这段代码的优劣,而实际上他在很长一段时间都运行得很好。我们来看看使用这一段代码有什么不好的地方:

  • 重复制造了轮子。花费了额外的时间,函数的健壮性和很差
  • 可读性差。其实是一个很简单的功能,但是用上了这么一段函数,起初我还以为有什么特别的功能。

那么,我们应该怎样去避免制造轮子呢?我从个人的经历来提出以下几点,希望能够对各位有所帮助:

  • 了解你所学的编程语言的特性。你可以看一本基础的入门书籍,把所有的特性浏览一遍,或者上MSDN,把相关的内容过一遍。
  • 在你决定动手造轮子之前,先搜索一下现成的解决方案。你还可以到CodeProject、GitHub之类的网站搜索一下。在知乎上有很多大牛其实都在批评,为什么你提问之前,不能首先去搜一下是否有现成的答案,反而指责没有回答他的问题。
  • 你有一定的基础之后,还应该去读一下相关的经典书籍,深入了解其中的原理。比如,你觉得你有一定的基础了,我建议你去吧《CLR Via C#》多读几遍,你了解原理越多,你越是能够利用这编程语言的特性,从而来实现原本那些你认为要靠自己写代码的功能。

这里我再举一个我自己的例子。在我现有的程序中,我发现我需要越来越多的线程来执行一些简单的任务,比如在每天检测一下硬盘是否达到90%了,每天9点要控制一下空调的开启而在网上6点的时候把空调关掉。线程使用越来越多,我越是觉得浪费,因为这些现场仅仅只需完成一次或者有限的几次,大部分时间都是没有意义的,那么怎么办呢?我决定自己写一个任务类,来完成相关的事情。说干就干,我很快把这个类写出来了。

public abstract class MissionBase : IMission
{
    private DateTime _nextExecuteTime;
    protected virtual DateTime[] ExecuteTimePoints { get; private set; }
    protected virtual int IntervalSeconds { get; private set; }
    protected IEngine Engine { get; private set; }

    public bool IsCanceled{get{……}}
    public bool IsExecuting{get{……}}
    public bool IsTimeToExecute{get{……}}

    public abstract bool Enable { get; }
    public abstract string Name { get; }

    protected MissionBase(IEngine engine)
    {
        ExecuteTimePoints = null;//默认采用间隔的方式
        IntervalSeconds = 60 * 60;//默认的间隔为1个小时

        Engine = engine;
    }

    /// 任务的执行方法
    public void Done()
    {
        if (Interlocked.CompareExchange(ref _isExecuting, 1, 0) == 1) return;

        try
        {
			……
        }
        finally
        {
            Interlocked.CompareExchange(ref _isExecuting, 0, 1);
        }
    }

	///实际方法的执行
    protected abstract void DoneReal();
}

但是,实际上这个任务方法,并不好用,要写的代码不少,而且可靠性还没有保障。当然,我可以继续完善这个类,但是我决定搜索一下是否还有其他的方法。直到有一天,我再次阅读《CLR Via C#》,看到线程这一章,讲到了System.Threading.Timmer以及ThreadPoole类时,我就知道了,使用Timer类完全可以解决我的这个用尽量少的线程完成定时任务的问题

因为从原理上来说,Timer类无论你声明了多少个,其实就只有一个线程在执行。当你到了执行时间时,这个管理线程会用ThreadPool来执行Timer中的函数,因为使用的ThreadPool,执行完成之后,线程就马上回收了,这个其实就完全实现了我所需要的功能。

12下一页
查看其它分页:

最新评论

我也要发表评论

qq229614108 2014-12-15 15:19 回复
高大上
来自 - 江苏南京 的 Safari/Linux 用户 2014-12-10 00:39 回复
当年的神器,不搞.net好多年了
理工动物园副园长 2014-12-08 13:33  新浪微博网友评论 回复
//@Linux中国:→_→//@Viz_V:Design pattern 学好没有错,重构糟糕的代码也没有错,但这么在国内做就是错的。为什么呢?很多国内码农的工资与代码量挂钩,印度程序员还知道loop里的代码复制粘贴十遍呢~有的时候不是不会写好程序,而是……(此处省略1000字)
韦恩卑鄙 2014-12-08 13:33  新浪微博网友评论 回复
反射的.ctor都不做成delegate 缓存 不是新手难道是老鸟。有IoC容器不用也挺愁的。//@thomas--y: 这特么是新手文章//@Linux中国:但是这篇文章的思路其实是更值得学习的;再次这篇我觉得也不是新手级别 //@韦恩卑鄙: linux 中国转博客园的.net 新手文章合适嘛?
飞-翔-鸟521 2014-12-08 13:33  新浪微博网友评论 回复
mark
我是铁人007 2014-12-08 13:33  新浪微博网友评论 回复
//@Linux中国:首先我觉得不应该有狭隘的门户之见,非Linux的技术也应该传播,只是我们偏重 Linux;其次即便不用.NET,但是这篇文章的思路其实是更值得学习的;再次这篇我觉得也不是新手级别吧;最后转载也明确注明了来源和作者,谢谢作者@马非码。
绿色圣光 2014-12-08 13:21 7 回复
还没干过这么夸张的,最多把几百行精简成几十行。
thomas--y 2014-12-08 13:03  新浪微博网友评论 回复
mdemo 2014-12-08 13:03  新浪微博网友评论 回复
[哈哈]//@赌场游侠:动软代码生成器!想起大二时候做项目用的时候,还有些小激动呢!满满的回忆啊!@mdemo
大栗哥 2014-12-08 13:03  新浪微博网友评论 回复
收下看//@Linux中国:首先我觉得不应该有狭隘的门户之见,非Linux的技术也应该传播,只是我们偏重 Linux;其次即便不用.NET,但是这篇文章的思路其实是更值得学习的;再次这篇我觉得也不是新手级别吧;最后转载也明确注明了来源和作者,谢谢作者@马非码。您看呢? //@韦恩卑鄙: linux 中国转博客园的.
LiuWei_Find 2014-12-08 13:03  新浪微博网友评论 回复
→_→//@Linux中国: →_→//@Viz_V:Design pattern 学好没有错,重构糟糕的代码也没有错,但这么在国内做就是错的。为什么呢?很多国内码农的工资与代码量挂钩,印度程序员还知道loop里的代码复制粘贴十遍呢~有的时候不是不会写好程序,而是……(此处省略1000字)
yueliangku 2014-12-08 12:33  新浪微博网友评论 回复
快去看看你家的sqlsrv又死机了没[拜拜]//@Linux中国: (删)其次即便不用.NET,但是这篇文章的思路其实是更值得学习的;再次这篇我觉得也不是新手级别吧;(删) //@韦恩卑鄙: linux 中国转博客园的.net 新手文章合适嘛?
郑淳猫奴日记 2014-12-08 12:33  新浪微博网友评论 回复
//@Linux中国:→_→//@Viz_V:Design pattern 学好没有错,重构糟糕的代码也没有错,但这么在国内做就是错的。为什么呢?很多国内码农的工资与代码量挂钩,印度程序员还知道loop里的代码复制粘贴十遍呢~有的时候不是不会写好程序,而是……(此处省略1000字)
赌场游侠 2014-12-08 12:33  新浪微博网友评论 回复
动软代码生成器!想起大二时候做项目用的时候,还有些小激动呢!满满的回忆啊!@mdemo
韦恩卑鄙 2014-12-08 12:33  新浪微博网友评论 回复
右边先把自己性别搞清楚行么//@_小噗: 为什么软狗随时随地都能gc呢?快去看看你家的sqlsrv又死机了没[拜拜]//@Linux中国: (删)其次即便不用.NET,但是这篇文章的思路其实是更值得学习的;再次这篇我觉得也不是新手级别吧;(删) //@韦恩卑鄙: linux 中国转博客园的.net 新手文章合适嘛?
韦恩卑鄙 2014-12-08 12:33  新浪微博网友评论 回复
我觉得你的订户不觉得犯 我就没问题。是不是新手文吗.net专业的都知道 您做linux的小编就别嘴硬啦。//@Linux中国: 首先我觉得不应该有狭隘的门户之见,非Linux的技术也应该传播,只是我们偏重 Linux;其次即便不用.NET,但是这篇文章的思路其实是更值得学习的;再次这篇我觉得也不是新手级别吧;
ZengYi 2014-12-08 12:06 回复
构想主题都很好,但是用C#做为例子不太好。
uptown1919 2014-12-08 12:03  新浪微博网友评论 回复
还有和代码量挂钩的..如果公司懂的话一般不会看代码量 不懂的代码完全都不会看//@Linux中国:→_→//@Viz_V:Design pattern 学好没有错,重构糟糕的代码也没有错,但这么在国内做就是错的。为什么呢?很多国内码农的工资与代码量挂钩,印度程序员还
_小噗 2014-12-08 12:03  新浪微博网友评论 回复
为什么软狗随时随地都能gc呢?快去看看你家的sqlsrv又死机了没[拜拜]//@Linux中国: (删)其次即便不用.NET,但是这篇文章的思路其实是更值得学习的;再次这篇我觉得也不是新手级别吧;(删) //@韦恩卑鄙: linux 中国转博客园的.net 新手文章合适嘛?
果壳中的米特尼克 2014-12-08 12:03  新浪微博网友评论 回复
还有缺陷密度,项目总缺陷数目,甚至Lint,klocwork的分析。甚至关注一下UT,ST?可惜可执行性呢?另外不是所有程序员都会为新feature努力,可惜没人看的到
请听第7段材料回答第9至11题 2014-12-08 12:03  新浪微博网友评论 回复
可怜程序猿→_→//@Linux中国:→_→//@Viz_V:Design pattern 学好没有错,重构糟糕的代码也没有错,但这么在国内做就是错的。为什么呢?很多国内码农的工资与代码量挂钩,印度程序员还知道loop里的代码复制粘贴十遍呢~有的时候不是不会写好程序,而是……(此处省略1000字)
Linux中国 2014-12-08 12:03  新浪微博网友评论 回复
首先我觉得不应该有狭隘的门户之见,非Linux的技术也应该传播,只是我们偏重 Linux;其次即便不用.NET,但是这篇文章的思路其实是更值得学习的;再次这篇我觉得也不是新手级别吧;最后转载也明确注明了来源和作者,谢谢作者@马非码。您看呢? //@韦恩卑鄙: linux 中国转博客园的.net 新手文章合适嘛?
cjacker 2014-12-08 12:03  新浪微博网友评论 回复
一直觉得通过LOC量化码农工作是很落后的。真要这样作,这个3000变15的工资都要扣光了。那么,问题来了,怎么通过量化指标度量码农工作performance呢?这3k行和15行如何量化?哪个绩效更高呢?为什么我读了那么多“关于如何评价码农绩效”的书,能拿的出手的指标都是LOC呢? //@Linux中国:→_→
Linux中国 2014-12-08 12:03  新浪微博网友评论 回复
→_→//@Viz_V:Design pattern 学好没有错,重构糟糕的代码也没有错,但这么在国内做就是错的。为什么呢?很多国内码农的工资与代码量挂钩,印度程序员还知道loop里的代码复制粘贴十遍呢~有的时候不是不会写好程序,而是……(此处省略1000字)
我的图书我做主 2014-12-08 11:33  新浪微博网友评论 回复
把三千行代码重构为15行 ,原来是反射!!!!!
Viz_V 2014-12-08 11:03  新浪微博网友评论 回复
Design pattern 学好没有错,重构糟糕的代码也没有错,但这么在国内做就是错的。为什么呢?很多国内码农的工资与代码量挂钩,印度程序员还知道loop里的代码复制粘贴十遍呢~有的时候不是不会写好程序,而是……(此处省略1000字)
任重致远_Cyg 2014-12-08 11:03  新浪微博网友评论 回复
超赞...
im若石 2014-12-08 10:33  新浪微博网友评论 回复
怎么不合适,ASP.NET很快就要一统江湖了,哼哼[左哼哼][右哼哼]//@韦恩卑鄙:linux 中国转博客园的.net 新手文章合适嘛。。。?//@祖传贴膜刷机清灰灰灰灰灰灰灰灰: 转发微博
祖传贴膜刷机清灰灰灰灰灰灰灰灰 2014-12-08 10:33  新浪微博网友评论 回复
哈哈哈哈哈哈看看就好//@韦恩卑鄙: linux 中国转博客园的.net 新手文章合适嘛。。。?//@祖传贴膜刷机清灰灰灰灰灰灰灰灰: 转发微博
韦恩卑鄙 2014-12-08 10:33  新浪微博网友评论 回复
linux 中国转博客园的.net 新手文章合适嘛。。。?//@祖传贴膜刷机清灰灰灰灰灰灰灰灰: 转发微博
TC_Akarin 2014-12-08 10:33  新浪微博网友评论 回复
1.功能拆分,一个程序只做一件事; 2.简单工作可以用一些脚本语言来完成,读文本流;3.提高性能的前提下,尽可能的用简单的办法实现
石景山老王 2014-12-08 10:33  新浪微博网友评论 回复
这只是在黑代码生成器吧
徐士超 2014-12-08 10:33  新浪微博网友评论 回复
先转发。

收藏

返回顶部

分享到微信

打开微信,点击顶部的“╋”,
使用“扫一扫”将网页分享至微信。