MyException - 我的异常网
当前位置:我的异常网» 编程 » 通向高级软件工程师之路

通向高级软件工程师之路

www.MyException.Cn  网友分享于:2013-09-03  浏览:34次
通向高级程序员之路

通向高级程序员之路:论程序设计方法
文/扬老师

  如果你是初学者----------------请不要阅读;
  但有志成为中高级程序员--------请务必阅读;
  如果你是中级程序员------------请务必阅读;
  如果你高级程序员--------------请批评指正。

  本文是我在“软件工程师班”开学第一节课的讲义,和“计算机软件设计发展”讲座上的内容整理而成。写作本文的目的是引导学生从更高的层次来看待程序设计方法,为将来成为高级程序员而做好理论准备。

  一、计算机硬件环境对软件设计方法的限制

  计算机的发明到现在已经60年了,计算机程序设计方法也伴随着计算机

硬件技术的提高而不断发展。硬件环境对软件设计既有严重的制约作用,也有积极的推动作用。

  在我的大学母校(此处删除6个字),数学系的一些老师,有幸成为了我国第一代的计算机DIY一族。呵呵,不要以为是组装PC机呦,他们组装的可是小型机。一人多高铁皮柜大小的主机,加上纸带机(后期改进为读卡机),组装好后,除了供学校自己的科研使用外,还在全国各地销售了十几台。当时(七十年代)一台的售价是10几万元人民币,如果换算到今天,相当于价值大约为100多万元,非常高档的小型计算机了。下面大家猜猜,这么高档的计算机,它的内存是多少那?(都把嘴闭好了,我要公布答案了)—— 4K。

  一块50公分见方的内存板,

  插入到主机箱中,好了------ 1K;
  再插一块内存板,好了------ 2K;
  再插一块内存板,好了------ 3K;
  再插一块内存板,好了------ 4K;
  再......不行了,插不起了,太贵了!这就是当时的环境。这样的环境下,用什么写程序那?当然只有机器码了。先用汇编写,然后翻阅手册手工改写为机器码,然后打卡或穿纸带,输入运行。可以想象,在当时的条件下,什么叫好的程序那?什么叫优秀的程序那?—— 技巧!

  程序设计的最初始阶段,是讲究技巧的年代。如何能节省一个字节,如何能提高程序运行的效率,这些都是要严肃考虑的问题。而所谓的程序的易读性,程序的可维护性根本不在考虑范围之内。

  今天,35岁以上的学习过计算机的朋友可能都使用过一种个人计算机——APPLE-II(中国也生产过这种计算机的类似产品“中华学习机”)。主频1M,内存48K(扩展后,最多可达到64K)。我就是使用这样的计算机长大的 。当年,类似的个人计算机产品,还有PC1500,Layser310等。这种计算机上已经固化了 BASIC 语言,当然只是为学习使用。要想开发出真正的商业程序,则必须使用汇编,否则的话,程序就比蜗牛还要慢了。于是,程序设计中对于技巧的运用,是至关重要的了。

比尔盖茨是 BASIC 的忠实拥护和推动者。当年,他在没有调式环境的状况下,用汇编语言写出了一款仅有 4K 大小的 BASIC 解释器,且一次通过。确实另人佩服。(不象现在微软出品的程序,动辄几十兆。)这也许就是比尔对 BASIC 情有独忠的原因,每当微软推出(临摹)一个新技术,则他会立刻在 BASIC 中提供支持。



 在 APPLE-II 上有一款游戏软件“警察抓小偷”,当年熬夜玩游戏,乐趣无穷。后来这款游戏被移植到了PC上,咳~~~根本没有办法玩,因为小偷还没跑就被警察抓到了。硬件的速度提升,另我无法再回味以前的时光了。

 二、结构化程序设计

  随着计算机的价格不断下降,硬件环境不断改善,运行速度不断提升。程序越写越大,功能越来越强,讲究技巧的程序设计方法已经不能适应需求了。记得是哪本书上讲过,一个软件的开发成本是由:程序设计 30% 和程序维护 70% 构成。这是书上给出的一个理论值,但实际上,从我十几年的工作经验中,我得到的体会是:程序设计占 10%,而维护要占 90%。也许我说的还是太保守了,维护的成本还应该再提高。下面这个程序,提供了两种设计方案,大家看看哪个更好一些那?

  题目:对一个数组中的100个元素,从小到大排序并显示输出。(BASIC)

  方法1:冒泡法排序,同时输出。



FOR I=1 TO 100
 FOR J=I+1 TO 100
  IF A[I] > A[J] THEN T=A[J]: A[J]=A[I]: A[I]=T
 NEXT J
 ? A[I]
NEXT I

  方法2:冒泡法排序,然后再输出。

FOR I=1 TO 100
FOR J=I+1 TO 100
IF A[I] > A[J] THEN T=A[J]: A[J]=A[I]: A[I]=T
NEXT
NEXT

FOR I=1 TO 100
? A[I]
NEXT


  显然,“方法1”比“方法2”的效率要高,运行的更快。但是,从现在的程序设计角度来看,“方法2”更高级。原因很简单:(1)功能模块分割清晰——易读;(2)也是最重要的——易维护。程序在设计阶段的时候,就要考虑以后的维护问题。比如现在是实现了在屏幕上的输出,也许将来某一天,你要修改程序,输出到打印机上、输出到绘图仪上;也许将来某一天,你学习了一个新的高级的排序方法,由“冒泡法”改进为“快速排序”、“堆排序”。那么在“方法2”的基础上进行修改,是不是就更简单了,更容易了?!这种把功能模块分离的程序设计方法,就叫“结构化程序设计”。


三、对程序设计中技巧使用的思考

  我可以肯定,大家在开始学习程序设计的时候,一定都做过这样一个题目:求100以内的素数。老师在黑板上,眉飞色舞地写出了第一个程序:(C程序)

  方法1:

for(i=1; i<100; i++)
{
 for(j=2; j< i; j++)
  if(i%j == 0) break;
  if(j >= i) printf("%d,", i);
}

  然后,老师开始批判这个程序“这个叫什么呀?太慢了!因为我们都知道大偶数不可能是素数了,因此,要排除掉!” 于是,意尤未尽地写出了第二个程序:

  方法2:

printf("2,");
for(i=3; i<100; i+=2)
{
 for(j=2; j< i; j++)
  if(i%j == 0) break;
  if(j >= i) printf("%d,", i);
}



  老师说:“看!我们只改动了一点点,程序运行的速度就提高了一倍多”。然后运用诱导式教学法继续提问“程序的效率,还能再提高吗?能!”,得意地写出第三个程序:

  方法3:

printf("2,");
for(i=3; i<100; i+=2) ''不考虑大偶数
{
 for(j=3; j< i/2; j+=2) ''不考虑用偶数去测试,而且只验算到一半就足够了
  if(i%j == 0) break;
  if(j >= i) printf("%d,", i);
}



  “大家看,我们又只改动了一点点,运行速度又提高了一倍多。可以了吗?不可以!我们还能再提高”。于是又高傲地写出了第四个程序:

  方法4:



printf("2,");
for(i=3; i<100; i+=2)
{
 int k = sqrt(i);
 for(j=3; j<= k; j+=2)
  if(i%j == 0) break;
  if(j >= k ) printf("%d", i);
}



  然后,开始证明为什么我们判断素数的时候,只需要验算到平方根就足够了:

  假设p是合数,那么令:p=a*b。反正法:由于我们已经判断了p的平方根以内的整数都不能被p整除,于是 a>SQRT(p)。基于同样的理由 b>SQRT(p)。于是 p = a * b > SQRT(p) * SQRT(p) = p 得出矛盾, 命题得正。

  的确,“方法4”的确比“方法1”的运行速度要提高了好几倍,甚至好几十倍。但我们仔细分析测试看看。

  (1)“程序4”到底比“程序1”快了多少那?我在某台计算机上进行测试(P4,1.5G)得到的速度对比表:



计算范围 100 1000 10000 100000
速度差 0.00秒 0.01秒 0.18秒 15秒


  
  (2) 在10万以上,才会看出一些差别。而这种差别根本就不够底偿程序设计阶段的付出。如果计算的范围再大,那么不管是“方法1”,还是“方法4”都不是好的算法。(计算素数的另外一个比较优秀的算法叫“漏筛法”)

  (3)写出“方法1”,只要具有小学四年级的数学水平就够了,而“方法4”则需要初中三年级的水平并且还要具备一些“数论”的知识。

  (4)从维护性看,如果你写的程序需要另外一个程序员来维护,或者若干时间以后,你重新来阅读这段程序,那么就会对这个程序产生很多疑问:这个求平方根是干什么用的?其实,就这个题目来说,使用到“方法3”就已经足够了。


总结发言:

  I. 计算机的价格每年下降一半,而运算速度每年提高一倍”,因此我们应该把速度提高的任务交给硬件实现。

  II. 从易读性、维护性出发,程序员只负责按定义给出软件实现。算法的问题是数学家解决的。

  题外话:

  多年以来,人们一直在寻找动态图象(影视)的存储和回放的算法,但效果都不理想。直到有人发现,原来在200 [被屏蔽广告]

多年前的数学家早就帮我们解决了这个问题——傅立叶(Fourier)级数展开。因此我要说,优秀的算法不是我们程序员要考虑的问题,我们的任务只要按照数学家给出的算法翻译为计算机程序语言而已。(这句话恐怕要遭到大多数程序员抛出的板砖袭击)再比如,计算一元多次方程解的问题。我们使用的就是牛顿的迭代算法。不要怪我瞧不起你,你能发明这个方法的话,那就是当代的牛顿了。

  四、程序的易读性与书写方法

  程序是否容易阅读和维护,与怎么书写有很大的关系。说实在的,C语言中为了方便程序员书写,允许使用++,--,<<,&&,?......这些运算符号。但很多人经常乱用,以为自己写的程序多么简洁,效率多高。其实,当你分行书写的话则更加容易阅读和维护,效率也不会降低,因为编译程序早就帮你优化为最快捷的代码了。先看一个简单的例子:

  计算一个整数乘 255(C语言)

  方法1:a *= 255;

  方法2:因为移位运算比乘法运算要快很多倍,因此a*255的运算书写为:



a =(a<<8)-a; //a*255 = a*256 - a = (a<<8) - a



  方法1的书写非常简单,直截了当,显然更容易维护。而方法2的书写运用了移位的技巧,不容易阅读,但效率最高。是不是真的是这样那?把这两个程序编译为汇编代码看看。原来无论是方法1还是方法2,它们的汇编代码都是一样的:



mov ecx, eax
shl eax, 8
sub eax, ecx



  也就是说,你认为非常技巧的书写方法,其实编译器的优化功能早就帮你想到了。那么方法2的方式就很值得批判了。下面是几个有关C语言书写方面的重要原则:
 
  1)尽量表达愿义,多加注释;

  2)变量名称和函数名称,要使用有意义的符号,并且遵守“匈牙利命名法”;

  3)不要为俭省内存,使一个变量在一个模块中表达多个含义。

  4)在某个模块中,前半部分用i表示计数器,由于后半部分不再使用计数器了,于是又用i来保存某个中间的结果。等你维护这段程序的时候,保证你肯定会犯傻的。

  5)在使用条件表达式的时候,不要混合书写运算表达式;

  经常有人在书写for循环的时候,使用这样的方式: for(int a=1,s=0; a<=100 && (s+=a); a++);

  天呀,这样写是不会提高程序运行效率的,尤其是当运算表达式复杂的时候,就更不容易阅读了,还是把运算写到for的循环体中吧。



int s = 0;
for(int a=1; a<=100; a++)
 s += a; //计算1+2+...+100 这不很好吗?!

再比如,if(a=b)这个写法在语法上是允许的,但不要使用。要使用也要if(0!=(a=b))这样的方式。 还有值得一提的是慎用“,”(逗号运算符)。

  不要连续使用++,--,<<,*,& .....这样的运算符号。

  a = b++-(--c<<1+e&0x0f>>1); //这个人有病。出这个题目考试的老师,也有病。

  常量要写在条件表达式的左边;

  if(5 == a) 这是正确的写法,这样书写可以避免勿输入而导致的 if(a=5)这样的错误。

  避免程序中{ }的嵌套层次太深;

  最多4层。如果必须大于4层,那么写成调用子函数或宏的方式。

  尽量多地使用断言;

  当你在书写程序的过程中,凭你的智慧,你一定是知道:程序运行到我正书写的这行代码的时候某个变量一定是某个值。好啦,那么不要忧郁,马上加上一句代码:ASSERT(nnn == xxx);。将来在调式维护这段代码的时候,你会得到无限美妙的回报。

  书写需要“成对匹配”使用的代码的时候,在写使用代码之前,就先把结束写出来;



file.Open(...); //当要打开文件的时候 char *lp=new char [100]; //当要申请内存的时候
...... //先不要写这段代码 ...... //先不要写这段代码
file.Close(); //马上写关闭 delete [] lp; //马上写释放

xxx.Loack(); //当某个对象需要锁定的时候 for(....)
...... //先不要写这段代码 { //写大括号的时候
xxx.Unlock(); //马上写解锁 } //马上写大括号结束



  和这个道理相同,在C++的类中,如果需要申请内存,那么先在构造函数中给出 lp=NULL;然后马上在析构函数中书写 if(lp) delete []lp;

  可以适当地使用goto;

  在结构化程序设计中,goto 是被排斥的。但是,如果适当地使用 goto 不但不影响斜率,而且还能提高程序的可读性。

  题目:合并2个文件到一个新文件中。(不要挑我的毛病呀~~~~~,我使用的是类C的方式书写的。)

  方法1:



FILE *f1,*f2,*f3;
if(Open(f1)成功)
{
 if(Open(f2)成功)
 {
  if(Open(f3)成功)
  {
   ...... //这里是真正干活的地方
   Close(f1);
   Close(f2);
   Close(f3);
  }
  else //f3不成功
  {
   Close(f1);
   Close(f2);
   ......
  }
 }
 else //f2不成功
 {
  Close(f1);
  ......
 }
}
else //f1不成功
{
 ......
}
==========================================================



  方法2:



FILE *f1=NULL,*f2=NULL,*f3=NULL;
if(Open(f1)不成功) goto err;
if(Open(f2)不成功) goto err;
if(Open(f3)不成功) goto err;
...... //这里是真正干活的地方
err:
if(f3) Close(f3);
if(f2) Close(f2);
if(f1) Close(f1);



  方法1是最最标准的结构化设计,好吗?不好!尤其是当{ }的层次比较深的时候,估计你寻找真正干活的代码的地方都找不到。而使用方法2的程序,不但程序容易读,而且没有{ } 的深度。在C++中,又提供了异常try/catch的设计结构,而异常的结构则比 goto 的结构更好、更完善了。

五、面向对象的程序设计

  随着程序的设计的复杂性增加,结构化程序设计方法又不够用了。不够用的根本原因是“代码重用”的时候不方便。面向对象的方法诞生了,它通过继承来实现比较完善的代码重用功能。很多学生在应聘工作,面试的时候,常被问及一个问题“你来谈谈什么是面向对象的程序设计”,学生无言,回来问我,这个问题应该怎么回答。我告诉他,你只要说一句话就够了“面向对象程序设计是对数据的封装;范式(模板)的程序设计是对算法的封装。”后来再有学生遇到了这个问题,只简单的一句对答,对方就对这个学生就刮目相看了(学生后来自豪地告诉我的)。为什么那?因为只有经过彻底的体会和实践才能提炼出这个精华。

  面向对象的设计方法和思想,其实早在70年代初就已经被提出来了。其目的就是:强制程序必须通过函数的方式来操纵数据。这样实现了数据的封装,就避免了以前设计方法中的,任何代码都可以随便操作数据而因起的BUG,而查找修改这个BUG是非常困难的。那么你可以说,即使我不使用面向对象,当我想访问某个数据的时候,我就通过调用函数访问不就可以了吗?是的,的确可以,但并不是强制的。人都有惰性,当我想对 i 加1的时候,干吗非要调用函数呀?算了,直接i++多省事呀。呵呵,正式由于这个懒惰,当程序出BUG的时候,可就不好捉啦。而面向对象是强制性的,从编译阶段就解决了你懒惰的问题。

  巧合的是,面向对象的思想,其实和我们的日常生活中处理问题是吻合的。举例来说,我打算丢掉一个茶杯,怎么扔那?太简单了,拿起茶杯,走到垃圾桶,扔!注意分析这个过程,我们是先选一个“对象”------茶杯,然后向这个对象施加一个动作——扔。每个对象所能施加在它上面的动作是有一定限制的:茶杯,可以被扔,可以被砸,可以用来喝水,可以敲它发出声音......;一张纸,可以被写字,可以撕,可以烧......。也就是说,一旦确定了一个对象,则方法也就跟着确定了。我们的日常生活就是如此。但是,大家回想一下我们程序设计和对计算机的操作,却不是这样的。拿DOS的操作来说,我要删除一个文件,方法是在DOS提示符下:c:> del 文件名<回车>。注意看这个过程,动作在前(del),对象在后(文件名),和面向对象的方法正好顺序相反。那么只是一个顺序的问题,会带来什么影响那?呵呵,大家一定看到过这个现象:File not found. “啊~~~,我错了,我错了,文件名敲错了一个字母”,于是重新输入:c:> del 文件名2<回车>。不幸又发生了,计算机报告:File read only. 哈哈,痛苦吧。所以DOS的操作其实是违反我们日常生活中的习惯的(当然,以前谁也没有提出过异议),而现在由于使用了面向对象的设计,那么这些问题,就在编译的时候解决了,而不是在运行的时候。obj.fun(),对于这条语句,无论是对象,还是函数,如果你输入有问题,那么都会在编译的时候报告出来,方便你修改,而不是在执行的时候出错,害的你到处去捉虫子。

  同时,面向对象又能解决代码重用的问题——继承。我以前写了一个“狗”的类,属性有(变量):有毛、4条腿、有翘着的尾巴(耷拉着尾巴的那是狼)、鼻子很灵敏、喜欢吃肉骨头......方法有(函数):能跑、能闻、汪汪叫......如果它去抓耗子,人家叫它“多管闲事”。好了,狗这个类写好了。但在我实际的生活中,我家养的这条狗和我以前写的这个“狗类”非常相似,只有一点点的不同,就是我的这条狗,它是:卷毛而且长长的,鼻子小,嘴小......。于是,我派生一个新的类型,叫“哈巴狗类”在“狗类”的基础上,加上新的特性。好了,程序写完了,并且是重用了以前的正确的代码——这就是面向对象程序设计的好处。我的成功只是站在了巨人的肩膀上。当然,如果你使用VC的话,重用最多的代码就是MFC的类库。

六、组件(COM)程序设计

  有了面向对象程序设计方法,就彻底解决了代码重用的问题了吗?答案是:否!硬件越来越快,越来越小了,软件的规模却也越来越大了,集体合作越来越重要,代码重用又出现的新的问题。 我用C++写的类,不能被BASIC重用——不能夸语言;你要干什么,想重用我的代码?不行,这样你就看见了我的设计思想——只能在源程序级别重用,不能在二进制级别(可执行代码及)重用。

  我耗尽毕生的精力,写了一个包罗万象的类库,但没有人用。因为他们说:你这个太大了,我的程序只有1K,你却给我一个 10000MB 的库——MFC 的尴尬。

  太好了,我终于找到了程序中的一个BUG,已经修改完成,而且是只改动了一个字节。接下来我要重新向我的用户分发新的版本,我的用户有......10万个——升级的非鲁棒性,不是我分发累死了,就是用户重新安装累死了。(鲁棒:robust。意为强壮性的,平顺的,顺滑的.....鬼知道是哪个不懂计算机的人翻译的这个词汇。)

  我想写一个集大成的软件,这个软件的功能是我中有你,你中有我。既能实现文字编辑,又能实现电子表格计算,又能实现自动翻译,还能画图,还能实现数据库检索,还可以看电影.....只要用了我的这个软件,想要什么就有什么,我要强占整个软件的市场------OLE实现的重用功能,只要学会了COM,这些都不是问题了。

  用户甲要求我的软件窗口上下分割,用户乙要求我的软件窗口左右分割......我需要在我的软件基础上,派生出100个类型,可怎么办呀?将来怎么维护呀?------在脚本的支持下,实现同一程序的的灵活配置而重用,问题迎刃而解了。


  我是个老板,你知道我有多痛苦吗?我手下的员工向我提出加工资的要求,我不得不答应呀。因为如果这个员工跳槽了,他的代码要维护起来有多难!!!——现在好啦,我要求员工统统用组件写模块,想加工资?门都没有,威胁我要走?那你走吧,这个月的工资也不发了。反正用组件写的代码,我可以很容易地进行包容和聚合实现维护。(老板的福音,程序员的悲哀)。

  还有好多那,现在想不起来了......

  COM程序设计方法,就是解决以上问题的一个方式。有很多朋友觉得COM非常复杂难懂,不想学习了。你一定学习过程序设计的最基本的方法(非结构化设计:汇编、gwBasic......),然后,你又学习了结构化程序设计(C、Pascal......),然后,你又努力学习并熟练掌握了面向对象的程序设计方法(C++、Delphi、Java......),那么不要怕,要有信心去学习组件程序设计,它只是一个设计方法和思想,并且是目前较高级的方法,如果不掌握,就太可惜了。

  学习了结构化程序设计,你就会“藐视”那些不遵守结构化设计思想而写出的代码;

  学习了面向对象设计,你就会“嘲笑”那些为找BUG而晕头转向的程序员;

  同样,学习了组件程序设计,你就会站在更高的层次看待程序设计。

  七、结束语

  写程序的目的是什么?养家糊口、兴趣使然、我的事业......这些都对。但我要强调的是:写程序的目的是为了修改程序。在这个观点上,那么写注释、写文档、选择语言、选择结构......都是为这个服务的。本文从软件设计方法的进化角度来反复阐述这个观点,希望爱好者能有所体会和思考。

  文中所讨论的技术和观点,适合于大多数情况下的程序设计,而对于特殊的应用的(驱动开发,嵌入式开发,网络通讯,实时视频......),这些领域中,由于硬件环境的限制和极限效率的要求,有些观点就不合适了,需要具体情况具体分析。另外就是对于程序设计的初学者,可以先不考虑这么多问题,以掌握基本技巧方法和思想为要。

转载出处:http://www.blogjava.net/yjjlovewjf/archive/2008/01/01/171935.html

文章评论

看13位CEO、创始人和高管如何提高工作效率
看13位CEO、创始人和高管如何提高工作效率
60个开发者不容错过的免费资源库
60个开发者不容错过的免费资源库
亲爱的项目经理,我恨你
亲爱的项目经理,我恨你
老程序员的下场
老程序员的下场
科技史上最臭名昭著的13大罪犯
科技史上最臭名昭著的13大罪犯
漫画:程序员的工作
漫画:程序员的工作
程序员眼里IE浏览器是什么样的
程序员眼里IE浏览器是什么样的
做程序猿的老婆应该注意的一些事情
做程序猿的老婆应该注意的一些事情
编程语言是女人
编程语言是女人
我的丈夫是个程序员
我的丈夫是个程序员
程序员的一天:一寸光阴一寸金
程序员的一天:一寸光阴一寸金
 程序员的样子
程序员的样子
Java程序员必看电影
Java程序员必看电影
每天工作4小时的程序员
每天工作4小时的程序员
程序员的鄙视链
程序员的鄙视链
写给自己也写给你 自己到底该何去何从
写给自己也写给你 自己到底该何去何从
团队中“技术大拿”并非越多越好
团队中“技术大拿”并非越多越好
程序员和编码员之间的区别
程序员和编码员之间的区别
要嫁就嫁程序猿—钱多话少死的早
要嫁就嫁程序猿—钱多话少死的早
旅行,写作,编程
旅行,写作,编程
初级 vs 高级开发者 哪个性价比更高?
初级 vs 高级开发者 哪个性价比更高?
那些争议最大的编程观点
那些争议最大的编程观点
为什么程序员都是夜猫子
为什么程序员都是夜猫子
中美印日四国程序员比较
中美印日四国程序员比较
当下全球最炙手可热的八位少年创业者
当下全球最炙手可热的八位少年创业者
总结2014中国互联网十大段子
总结2014中国互联网十大段子
鲜为人知的编程真相
鲜为人知的编程真相
不懂技术不要对懂技术的人说这很容易实现
不懂技术不要对懂技术的人说这很容易实现
聊聊HTTPS和SSL/TLS协议
聊聊HTTPS和SSL/TLS协议
程序员应该关注的一些事儿
程序员应该关注的一些事儿
5款最佳正则表达式编辑调试器
5款最佳正则表达式编辑调试器
Web开发人员为什么越来越懒了?
Web开发人员为什么越来越懒了?
代码女神横空出世
代码女神横空出世
程序员周末都喜欢做什么?
程序员周末都喜欢做什么?
程序员必看的十大电影
程序员必看的十大电影
十大编程算法助程序员走上高手之路
十大编程算法助程序员走上高手之路
10个帮程序员减压放松的网站
10个帮程序员减压放松的网站
一个程序员的时间管理
一个程序员的时间管理
老美怎么看待阿里赴美上市
老美怎么看待阿里赴美上市
为啥Android手机总会越用越慢?
为啥Android手机总会越用越慢?
什么才是优秀的用户界面设计
什么才是优秀的用户界面设计
程序员都该阅读的书
程序员都该阅读的书
如何区分一个程序员是“老手“还是“新手“?
如何区分一个程序员是“老手“还是“新手“?
程序员最害怕的5件事 你中招了吗?
程序员最害怕的5件事 你中招了吗?
我是如何打败拖延症的
我是如何打败拖延症的
软件开发程序错误异常ExceptionCopyright © 2009-2015 MyException 版权所有