编程思想:我现在是这样编程的(转)

代码的世界可能是昏天暗地的,但是我们的思维不能这样随之混乱,否则一切都会前功尽弃。所以我现在编写程序的时候,经常会想一下:我要做什么,我在做什么。更好的方法是把详细需求落实到文档,并时刻核对文档。

AD:

我在做什么

曾经,我试过接到一些需求。一眼带过后,脑袋马上随着高昂的斗志沉溺在代码的世界中 ,马不停蹄地敲着键盘直到最后测试的完成。我从思绪中恢复过来,乍一看自己写的功能,和需求差了十万八千里,我TM都在干嘛?

除此之外,我还见过类似的很好笑的事情。有一个程序员,经理提了需求,然后他在那里折腾了一天。结果不但没做出来,而且和实际需求都是完全搭不上调。经过询问发现,他不知道经理说了什么,也不知道自己到底在做什么。

代码的世界可能是昏天暗地的,但是我们的思维不能这样随之混乱,否则一切都会前功尽弃。所以我现在编写程序的时候,经常会想一下:我要做什么,我在做什么。更好的方法是把详细需求落实到文档,并时刻核对文档。

大局为重

2-8法则告诉我们,一个项目核心的功能只有很少,其它大部分都是对核心功能辅助或增强的。但当任务分发下来,我手头总有一些自己很想开发的模块,不过它们不属于那20%。我以前经常会在这些感兴趣的模块上花费很多时间和精力。

结果项目快要到上线期限,主要的功能却没开发完成,其它一些不起眼的功能却做得很好,但为此项目不得不延期了。如果反过来,只要对整体功能预期不会有太大偏差,可以将就的先上线。重要一点是:即使功能还有遗漏,但项目可以上线了,老板自然不会太追究,自己工作也能图个安心。如果不知道那些功能模块是最重要的,先问问经理。

人总是喜欢做一些自己感兴趣或者有挑战的事。不过在这方面,为了项目和团队着想,应该尽量压制这种诱惑。

性能永远不是优先考虑的问题

我从来不会一开始就考虑性能问题。如果项目成本很低,甚至到项目结束时,如果没有感觉到明显的性能问题,也不会去管。要知道现在已经不是DOS的年代,CPU的计算能力很高,但成本很低了。重要一点是,如果只针对提升性能对代码做改动,很容易破坏代码的复用性和可维护性。而返过来,提高了代码的复用性和可维护性,则很容易提高性能。

下面有一个PHP的代码实例,功能是帮助用户重置密码(代码为了简单说明问题,请不要太在意一些无关的细节)

requestResetPassword是接收用户重置密码的请求并且做了相应的检查。为了更好的复用性,我将重置密码的操作单独分配到一个新的resetPassword的函数,更改完密码的后再调用sendEmail向用户发送一封通知邮件。

  1. /**
  2. * 用户请求重置密码的接收器
  3. */
  4. function requestResetPassword() {
  5. //检查用户是否存在
  6. if( !checkUserExists( $_GET[‘userid‘] ) ) {
  7. exit(‘抱歉,用户不存在,请确认用户帐号。‘);
  8. }
  9. resetPassword( $_GET[‘userid‘] );
  10. //最后向用户发送一封邮件
  11. sendEmail( $_GET[‘userid‘], ‘重置密码成功‘, ‘新的密码是xxxx‘ );
  12. exit(‘新密码已经发送到你的邮箱。‘);
  13. }
  14. /**
  15. * 帮助用户重置密码
  16. */
  17. function resetPassword( $userid ) {
  18. //检查用户是否存在
  19. if( !checkUserExists( $userid ) ) {
  20. return false;
  21. }
  22. //进行重置用户密码的操作
  23. //略...
  24. return true;
  25. }
  26. /**
  27. * 向用户发送一封邮件
  28. */
  29. function sendEmail( $userid, $title, $content ) {
  30. //检查用户是否存在
  31. if( !checkUserExists( $userid ) ) {
  32. return false;
  33. }
  34. //发送邮件操作
  35. //略...
  36. return true;
  37. }
  38. /**
  39. * 检查某个用户是否存在
  40. */
  41. function checkUserExists( $userid ) {
  42. $user = getUserInfo( $userid );
  43. return !emptyempty( $user );
  44. }
  45. /**
  46. * 获取某个用户的数据
  47. */
  48. function getUserInfo( $userid ) {
  49. //假设我有一个query的函数,它用来查询数据库并返回数据
  50. $user = query( "SELECT * FROM `user` WHERE `uid`=" . intval( $userid ) );
  51. return is_array( $user ) ? $user : array() ;
  52. }

现在问题是,这三个函数都同时使用checkUserExists这个函数来检查用户不存在,数据库查询了三次,这样带来了一些额外的开销。

如果要去掉三者之间任意一个checkUserExists,看上去是可能的。但是如果之后有某些功能要调用resetPassword或者sendEmail,用户不存在时,系统可能会发生错误。

还有一个解决方法是,将resetPassword的逻辑写到requestResetPassword里,再过一点,把sendEmail的逻辑也写进去。这样函数调用减少,数据库查询也变成一次了,性能得到了提高。但是重置密码和发送邮件的功能将不能得到复用,并且违背了单一责任的原则,代码复杂度也提高了。

不过,因为函数分离和复用性都很好,如果实际性能受到影响,可能考虑用缓存的方法减少数据库查询,我改动了它们共用的checkUserExists函数:

  1. /**
  2. * 检查某个用户是否存在
  3. */
  4. function checkUserExists( $userid ) {
  5. //增加一个缓存,用以记录检查用户的结果
  6. static $cache = array();
  7. //检查当前用户是否已经检查过一次
  8. if( isset( $cache[ $userid ] ) ) {
  9. return $cache[ $userid ];
  10. }
  11. $user = getUserInfo( $userid );
  12. //把结果记录到缓存中
  13. $cache[ $userid ] = !emptyempty( $user );
  14. return $cache[ $userid ];
  15. }

也可以用同样的方法改动getUserInfo函数。

这里可以看到,当代码的复用性提高时,想提高性能是很简单的,性能的瓶颈也很容易被发现和修改。

尽管这个例子对性能影响还不够大,还有一些影响更大的,比如说遍历,我可能为了复用而将遍历封装到一个函数中,并且多次使用它。这些开销对我的项目根本没有预想中那样有太大的影响,或者说是微乎其微的。所以我更愿意把时间花在如何提高代码的复用性和维护性方面,而不是纠结于浪费多这一点性能。实际性能如果真的达不到要求,也可以权衡增加硬件配置。

名字长一点好

函数名和变量名等除了给机器看,也要给人看的,有时一个简单直接的好名字实在是很难想,这时不妨用长一点的名字更好。可读性更好:

  1. //好名字
  2. class ErasedTypeEquivalence {
  3. }
  4. //坏名字
  5. class ErdTypeEqe {
  6. }
  7. //好名字
  8. function checkUserExists () {
  9. }
  10. //坏名字
  11. function ckUserExt() {
  12. }
  13. //好名字
  14. $result;
  15. //坏名字
  16. $ret;

我见过一些代码,由于简单写过多,整遍代码很多都是4个字母或以下的,可读性非常差,当然不排除是为了偷懒。

但如果想有更多的时间腾出来偷懒,不应该在这上面玩小聪明,否则这时我现在应该在思考前几天的代码是在写什么。

什么?短名字会让代码执行得更快? 那证明给我看,如果真的快,快了多少?

自说明代码很重要,但注释同样重要

代码本身可以说明问题的确是很棒的,但并不是说注释不重要,有时候我更喜欢先看注释,因为它总比我看代码更快的了解这程序是做什么的。

如果我把本文前面说性能的例子去掉注释,哪个能让你更快了解代码的意图?或者说,你更愿意看哪个?

  1. function requestResetPassword() {
  2. if( !checkUserExists( $_GET[‘userid‘] ) ) {
  3. exit(‘抱歉,用户不存在,请确认用户帐号。‘);
  4. }
  5. resetPassword( $_GET[‘userid‘] );
  6. sendEmail( $_GET[‘userid‘], ‘重置密码成功‘, ‘新的密码是xxxx‘ );
  7. exit(‘新密码已经发送到你的邮箱。‘);
  8. }
  9. function resetPassword( $userid ) {
  10. if( !checkUserExists( $userid ) ) {
  11. return false;
  12. }
  13. //进行重置用户密码的操作
  14. //略...
  15. return true;
  16. }
  17. function sendEmail( $userid, $title, $content ) {
  18. if( !checkUserExists( $userid ) ) {
  19. return false;
  20. }
  21. //发送邮件操作
  22. //略...
  23. return true;
  24. }
  25. function checkUserExists( $userid ) {
  26. static $cache = array();
  27. if( isset( $cache[ $userid ] ) ) {
  28. return $cache[ $userid ];
  29. }
  30. $user = getUserInfo( $userid );
  31. $cache[ $userid ] = !emptyempty( $user );
  32. return $cache[ $userid ];
  33. }
  34. function getUserInfo( $userid ) {
  35. $user = query( "SELECT * FROM `user` WHERE `uid`=" . intval( $userid ) );
  36. return is_array( $user ) ? $user : array() ;
  37. }

所以,即使代码本身很清晰,但是加上注释的话,可读性也能提高很多!

适当抽象

编程就是为了解决实际中的问题,在思考如何编码的时候,把问题抽象到一定的高度去思考,更容易把握问题所在。不过更多时候,我发现从代码抽象到现实的例子是有一定难度的,同时我也相信,编程高手也是抽象高手,他们很容易把问题反映到真实生活中去。

不过如果经常留意和思考生活中的细节,会提升自己的抽象能力。

举一个螺丝刀的例子,如果叫你造一个螺丝刀,你会做成什么样子?我这里有三把不同的螺丝刀:

显然第一种螺丝刀是最简单的,比较中规中矩。

第二种螺丝刀中间可以旋转刀柄,让刀柄和刀头成90度,这样的设计让拧螺丝更加轻松。

第三种螺丝刀则可以更换刀头,如果以后有其它类型的螺丝,则只要造一个适合这种螺丝的刀头就可以了。

那反映到编程中的问题,如果项目要增加一个工具类库。

第一种方法,可以直接把类库的所需功能写出来就可以了。

第二种方法,不但把类库写出来,而且针对项目的一些情况做特殊改进,使得在这个项目中更好用。

第三种方法,根据类库的特性,把公共部分的逻辑做成接口,特殊的部分分离出来单独实现,如果以后要增加相同类型的类库,则实现特殊部分的逻辑,然后接入接口即可。

但是在抽象的时候,要避免不合理的抽象,有时也可能造成过渡设计,现在只需要一种螺丝刀,但你却把更多类型的螺丝刀都做出来了(而且还是瑞士军刀的样子。。):

一致性

团队开发中,可能每个人的编程风格都不一样,拿花括号来说,有些人喜欢和代码在同一行,而有些喜欢独自一行

  1. //例一
  2. function func() {
  3. }
  4. //例二
  5. function func()
  6. {
  7. }

命名风格也都不一样,比如说声明变量接收一个函数返回的数据,有些喜欢用result,有些喜欢用data。

它们可能都很好,不过在团队开发中,尽量统一用同一种风格能够很好的减少交叉开发的成本。

将错就错

面对项目一些无关紧要的分歧或错误,应该要接受和理解。承接上面的问题,如果团队中已经有人大量用了data的变量命名,但你认为result的更符合当前状况的描述。这种情况,我优先选择data命名,因为如果再使用result的话,会破坏项目的一致性,对开发没有任何好处。

这只是很少的一方面,如果项目规范没有很好的落实,实际工作中会有大量的一致性问题,必须靠团队每个人的决心和责任心去把它做好。通常,加入一个正在开发中的项目,编写功能前,我都会首先看项目之前的类似的代码,并尽量模仿他们的写法。不过,如果有明显的错误,应该及时指出和修正。

只要坚持把一致性做好,很多方法会成为团队甚至业界的标准,即使它们不是最好的,但是有什么关系呢?

适当休息

编程的时候如果没有思路或者感到混乱,到外边休息10分钟,或者看一下风景,让脑袋清醒一下是很好的。这招很管用,亲测。

至少把代码完整运行一次

有时函数的逻辑过于简单,以至于会认为这个不可能发生错误,但事实上最容易发生错误的通常就是这些代码,常见的单词拼写错误,参数错误,还有一些意料之外的问题。所以无论什么情况,我都会把代码完整运行一遍。

当然更好的做法是用一些系统的测试方法,比如说单元测试。

编程不是艺术

从一开始,编程语言的出现和发展,都是为了解决现实生活中的问题,包括它自身产生的问题。

面向对象、设计模式的出现,是用来解决编程语言自身带来的可读性和维护性等问题,而不是为了让编程语言上升到艺术的层面。尽管编程中有‘优雅’一词,但我更认为它只是用来形容代码更容易让人读懂和维护。

我拒绝一切看起来很‘优雅’,却不能为编程工作带来一点好处的代码。如果你喜欢玩弄语言,应该去当作家。

甘于平凡

程序员真的很高傲,在我接触过的人中,包括我自己也是。我以前经常对一些简单的代码感到不屑,而总想在项目中写一些犀利的代码,让人看起来很NB,但结果总是和想象差太远,代码总是写的很差,逻辑也不够清晰。归根到底,是我带着这样的思想去写代码,而忽略了编程的根本:解决问题。现在我改掉了这个坏毛病,以解决问题为目的去编程,以简单为主。出乎意料的是别人有时会对我说,这里的代码写得很棒。

踏实的做事,会有意想不到的收获。

承认错误

不要怀疑,当别人用自己的程序或者代码无法运行时,首先考虑是否是自己的逻辑哪里有问题。一来别人会觉得我谦虚,二来实际大多数情况的确是自己的问题。

有原则,有决心

做任何事情都坚持原则,并有决心是最好的。有很多道理我们都明白,但经常做不到,没有任何人能帮到自己,未来也是自己争取的。

所以,如果知道什么是好,就尽量去做,什么是不好,就尽量避免。

即使是在公司面对经理和领导,也要坚持自己的做法,一些不合理的需求应该指出或拒绝。我还年轻,大不了换一家公司,而不愿意做一个受欺压的码农。

我在做什么

文章写完了,现在来回想一下,我是在分享自己现在编程的一些习惯,总算没偏离开始的主题。本文的思想都是来自实际工作和一些书籍,想了解更多的话,推荐阅读《整洁代码之道》《代码大全》《重构》这几本书。

如果你有一些认为好的编程方法,不妨拿出来和大家分享一下。

时间: 2024-10-10 18:15:12

编程思想:我现在是这样编程的(转)的相关文章

Java编程思想学习(十六) 并发编程

线程是进程中一个任务控制流序列,由于进程的创建和销毁需要销毁大量的资源,而多个线程之间可以共享进程数据,因此多线程是并发编程的基础. 多核心CPU可以真正实现多个任务并行执行,单核心CPU程序其实不是真正的并行运行,而是通过时间片切换来执行,由于时间片切换频繁,使用者感觉程序是在并行运行.单核心CPU中通过时间片切换执行多线程任务时,虽然需要保存线程上下文,但是由于不会被阻塞的线程所阻塞,因此相比单任务还是大大提高了程序运行效率. 1.线程的状态和切换: 线程的7种状态及其切换图如下: 2.多线

编程思想┊从实例谈面向对象编程(OOP)、工厂模式和重构

有了翅膀才能飞,欠缺灵活的代码就象冻坏了翅膀的鸟儿.不能飞翔,就少了几许灵动的气韵.我们需要给代码带去温暖的阳光,让僵冷的翅膀重新飞起来.结合实例,通过应用OOP.设计模式和重构,你会看到代码是怎样一步一步复活的. 为了更好的理解设计思想,实例尽可能简单化.但随着需求的增加,程序将越来越复杂.此时就有修改设计的必要,重构和设计模式就可以派上用场了.最后当设计渐趋完美后,你会发现,即使需求不断增加,你也可以神清气闲,不用为代码设计而烦恼了. 假定我们要设计一个媒体播放器.该媒体播放器目前只支持音频

编程思想之多线程与多进程(2)——线程优先级与线程安全

原文:http://blog.csdn.net/luoweifu/article/details/46701167 作者:luoweifu 转载请标名出处 <编程思想之多线程与多进程(1)--以操作系统的角度述说线程与进程>一文详细讲述了线程.进程的关系及在操作系统中的表现,这是多线程学习必须了解的基础.本文将接着讲一下线程优先级和线程安全. 线程优先级 现在主流操作系统(如Windows.Linux.Mac OS X)的任务调度除了具有前面提到的时间片轮转的特点外,还有优先级调度(Prior

编程思想之多线程与多进程(4)——C++中的多线程

<编程思想之多线程与多进程(1)--以操作系统的角度述说线程与进程>一文详细讲述了线程.进程的关系及在操作系统中的表现,<编程思想之多线程与多进程(2)--线程优先级与线程安全>一文讲了线程安全(各种同步锁)和优先级,这是多线程学习必须了解的基础.本文将接着讲一下C++中多线程程序的开发.这里主要讲Windows平台线程的用法,创建线程要调用windows API的CreateThread方法. 创建线程 在Windows平台,Windows API提供了对多线程的支持.前面进程和

编程思想之多线程与多进程(2)——Java中的多线程

原文:http://blog.csdn.net/luoweifu/article/details/46673975 作者:luoweifu 转载请标名出处 <编程思想之多线程与多进程(1)--以操作系统的角度述说线程与进程>一文详细讲述了线程.进程的关系及在操作系统中的表现,这是多线程学习必须了解的基础.本文将接着讲一下Java中多线程程序的开发 单线程 任何程序至少有一个线程,即使你没有主动地创建线程,程序从一开始执行就有一个默认的线程,被称为主线程,只有一个线程的程序称为单线程程序.如下面

[技术] 谈谈编程思想

https://zhuanlan.zhihu.com/p/19736530?columnSlug=prattle 作者:陈天链接:https://zhuanlan.zhihu.com/p/19736530来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 这段时间又攒了很多答应了,但还未动手的文章.大概一两周前,有个读者留言:「程序君,能发篇文章有关编程思想的吗?我是编程初学者,对编程思想没啥概念,求传授点经验!」 今天就讲讲编程思想.编程思想是个宏大的主题,我不敢保

什么是JAVA编程思想?

什么是JAVA编程思想?答案可能很会复杂,但也可以很简单.要了解JAVA编程思想,首先就要了解什么是编程思想,让我们来看看什么是编程思想,一句话来讲就是,用计算机来解决人们实际问题的思维方式,即编程思想. 我们学习编程语言的最终目的,就是希望用计算机来解决我们的实际问题.那么学习编程该如何入手,也是很多初学者犯难的一个问题,特别是对与非计算机专业的人来说更是如此.面对现实如此多的编程语言(比如:C,C++,JAVA,C# -)和 种类繁多的应用技术(比如: windows编程, linux编程,

[Hadoop入门] - 1 Ubuntu系统 Hadoop介绍 MapReduce编程思想

Ubuntu系统 (我用到版本号是140.4) ubuntu系统是一个以桌面应用为主的Linux操作系统,Ubuntu基于Debian发行版和GNOME桌面环境.Ubuntu的目标在于为一般用户提供一个最新的.同时又相当稳定的主要由自由软件构建而成的操作系统,它可免费使用,并带有社团及专业的支持应. 作为Hadoop大数据开发测试环境, 建议大家不要在windows上安装CgyWin来学习或研究, 直接用Vmware+ubuntu来学习. 下载 www.vmware.com这里下载vmware,

什么是AOP面向切面编程思想

什么是AOP? 抽象:抽取特别像的东西. 如图,是一种横向的抽取,所以叫做面向切面. 下面两个图简单的体现了AOP的原理↓↓↓ 这种在运行时,动态地将代码切入到类的指定方法.指定位置上的编程思想就是面向切面的编程. AOP是Spring提供的关键特性之一.AOP即面向切面编程,是OOP编程的有效补充. 使用AOP技术,可以将一些系统性相关的编程工作,独立提取出来,独立实现,然后通过切面切入进系统. 从而避免了在业务逻辑的代码中混入很多的系统相关的逻辑——比如权限管理,事物管理,日志记录等等. 这

异常笔记--java编程思想

开一个新的系列,主要记一些琐碎的重要的知识点,把书读薄才是目的...特点: 代码少,概念多... 1. 基本概念 异常是在当前环境下无法获得必要的信息来解决这个问题,所以就需要从当前环境跳出,就是抛出异常.抛出异常后发生的几件事: 1.在堆上创建异常对象. 2.当前的执行路径中止                                          3. 当前环境抛出异常对象的引用.                                         4. 异常处理机制接