`
ihuashao
  • 浏览: 4666694 次
  • 性别: Icon_minigender_1
  • 来自: 济南
社区版块
存档分类
最新评论

谈谈编程(3) 编程实践

阅读更多

谈谈编程(3) 编程实践

鸠集遗失,鉴玩整理,昼夜精勤,每获一卷,遇一画,毕孜孜葺缀,竟日宝玩,可致者必货敝衣, 减粮食。妻子童仆切切嗤笑,或曰:终日为无益之事何补哉。既而叹曰:若复不为无益之事,则安能悦有涯之生。
--- 唐 张彦远 《历代名画记》

1 编程的要素

编程有3个要素:语言、环境和思想。

1.1 语言

有人喜欢争论语言的优劣。其实,除了汇编语言,各种语言、脚本、标准库、类库、框架都蕴含着大量成熟的编程经验和思想。程序员应该多熟悉一些语言,特别是有代表性的语言。

个人觉得,一个程序员应该掌握一两种汇编语言(CISC的X86、RISC的ARM)、一种面向过程语言(C)、两、三种面向对象语言(C++、Java、Delphi)、一两种脚本语言(perl、python、ruby)。如果有时间,可以再学习一些学术性较强的语言,例如Scheme。

学习新的语言,不仅可以吸收语言中蕴含的设计理念,还可以打开连接新空间的大门,使我们可以学习、复用使用该语言的各种资源,例如源代码、文章、书籍。

1.2 环境

1.2.1 开发时和运行时

环境可以被理解为程序所有外界环境的总和,包括开发时环境、运行时环境。我们在写一段程序时,应该对该程序的相关环境有清楚的了解。

开发时环境包括我们使用的编译(链接)环境、复用的代码(框架、类库、控件等),系统的逻辑结构、代码的文件组织、需要的工具软件、调试环境等等。

运行时环境包括程序运行时环境中所包含、发生的一切,特别是与我们的程序相关的部分。从小处看,我们应该知道如何确定每段代码运行的context(线程和堆栈),每个变量所使用的空间的信息。从大处看,我们应该了解系统运行的来龙去脉,各个模块(逻辑概念)如何相互配合,各个线程(调度单位)如何相互通信,什么时候可能发生调度,系统中有哪些不确定的因素等等。

1.2.2 软件类型

比较“典型”的软件类型包括:

  1. Windows操作系统;
  2. Linux操作系统;
  3. 编译器;
  4. 虚拟机;
  5. 调试器;
  6. Windows的单机应用程序;
  7. Windows的驱动程序;
  8. Linux的应用程序和驱动程序;
  9. 基于socket的客户端-服务器程序;
  10. 数据库应用程序;
  11. 使用数据库的Web应用程序;
  12. 使用RTOS的嵌入式软件;
  13. 不使用RTOS的单片机程序;
  14. DSP程序;
  15. 嵌入式环境的第三方程序(J2ME应用、BREW应用、symbian的应用程序等等);
  16. 各种中大型程序的脚本环境,插件的开发、运行环境;
  17. Office应用程序开发;
  18. flash编程;
软件的种类实在太多,所谓“典型”只是我个人的理解,肯定还有很多软件类型没有被列出来。程序员应该了解开发和运行这些软件时,究竟发生了什么。

对于Windows、Linux操作系统,我们应该有个概要的了解:从BIOS程序读Master Boot Record,到系统的装载运行;应用程序或动态连接库(Linux的SO)的装载;操作系统的基本模块(包括内核)的功能;Windows如何通过COM机制将各个功能模块组合起来等等。这些内容是PC程序的基本运行环境。

嵌入式环境相对PC环境要简单一些,特别是用NOR flash,代码直接从ROM运行的系统。一方面,程序员通常可以看到系统运行的所有代码;另一方面,嵌入式环境有时不提供第三方程序运行机制,有时提供比较简单的机制,有时用java虚拟机当作第三方程序运行机制。不过,智能手机的应用处理器一般使用NAND flash,从BIOS运行一小段启动代码,将系统装载到RAM运行,同时支持应用程序装载运行,已经很接近PC环境。

编写单机应用程序,除了语言外,主要要熟悉各种库、框架、组件。一些通用库提供了常用函数、各种容器和算法、GUI、典型的程序框架,系统API的封装等等,例如:

  • 移植性比较好的C标准库、STL、boost、TK等;
  • Windows上的MFC、VCL(Delphi/C++ Builder);
  • Linux上的ncurses、X Windows、GTK、Qt等;
  • 访问数据库:VC++的ODBC、DAO、ADO,Borland的BDE等;
  • Borland做了一些在源代码级跨平台的库:Delphi/Kylix的CLX、dbExpress;
  • CPAN上的大量perl模块、java的类库等等;

上面列出的是一些比较通用的库。还有用于各种语言的大量专用库,各种提供二进制接口的组件、控件。

编译器、虚拟机也是单机应用程序(在嵌入式环境,虚拟机可能是系统软件的一个模块)。不过它们的地位比较特殊。作为程序员,我们应该了解编译器、集成开发环境、软件框架、虚拟机、操作系统分别为我们做了什么。

作为程序员,我们同样应该理解调试器也是一个应用程序,调试器的基本原理,它能做什么,有什么限制。如果调试器与目标程序运行在不同的CPU上,调试是如何实现的,有哪些不同的实现方式?例如JTAG调试利用了目标CPU的调试接口直接调试目标程序;串口调试要求将一段调试代码和目标程序链接在一起下到目标CPU中,由嵌入的调试代码与PC机的调试器通信实现调试。不同的实现手段决定了调试器的能力和限制。

目前最热门的软件类型就是使用数据库的Web应用了,例如各种网站、网络游戏、各种企业、行业、政府机构的管理系统,在这个领域集中了处于食物链不同环节的大量厂商,各种Web服务器、基础平台、应用开发框架。随便列一下,也能列出一堆名词:

  • HTML/CSS和CGI;
  • java applet、java script、ActiveX控件;
  • php、asp、jsp、servlet;
  • .net家族:asp.net、ado.net等;
  • J2EE with/without EJB、Spring、Struts、Hibernate;
  • Ruby on Rails、Plone等;

在一种软件类型上,集中了这么多开发技术、框架、模式,也可以称得上蔚为壮观了。不过,这个领域里确实是各种最新的编程思想、方法、设计模式的演武厅,如同当年的编译器,值得所有程序员研究、学习。

1.3 思想

COM可以被看作OLE发展的衍生品。但它的重要性远远超过了OLE。它首被独立出来,成为OLE、ActiveX的基础,然后逐步成为在Windows进行二进制集成的基础。COM和RPC的结合产生了DCOM。DCOM和MTS的结合产生了COM+。虽然这些技术都是用于Windows平台的,但组件技术的基本思想是独立于具体环境的。也就是说,对程序员而言,存在着独立于语言和环境之外的领域,这就是编程的思想。

例如看看Qualcomm的BREW,就会发现它从COM中学习了多少东西。嵌入式平台的程序员使用PC平台的技术,这就是编程思想的价值。对于程序员来说,各种编程思想、设计模式,是最为宝贵的东西。这里所说的设计模式并不局限于GOF的《设计模式》,任何惯用的手法都可以被看作模式。

一些基本的编程思想起源于更普遍的智慧。例如:

心智的活动,除了尽力产生各种简单的认识之外,主要表现在以下三个方面:1)将若干简单认识组合成一个复合认识,由此产生出各种复杂的认识。2)将两个认识放在一起对照(在这样做时并不将它们合而为一),不管它们如何简单或者复杂,由此得到有关它们的相互关系的认识。3)将有关认识与那些在实际中和它们同在的所有其他认识隔离开,这就是抽象。所有具有普遍性的认识都是这样得到的。
--- John Locke, 有关人类理解的随笔

程序员从这段1690年的文字中能看到什么。组合、对照、抽象,这些基本的思维工具同样也是程序员最基本的工具。

2 编程规则

编程是一门技艺,如同我们必须跳到水里才能学会游泳,我们必须多看、多写程序,才能学会编程。

任何技艺的学习都包括知识和能力两个部分。知识是明确的,可以用时间换取。能力是说不清的,不易掌握的。一般而言,能力高的人能够更快地积累知识,善于从已有的实践中总结规律,善于复用已有的模式,他们能更快地对复杂的环境形成清晰的认识,用最简洁、优雅的方式解决问题。如何提高编程能力应该是因人而异,每个人应该找到自己的学习路径,有自己的规划。

编程的表象千变万化,但在表象背后,还是有一定规律的。在某些条件下,这样做会比那样做更好一些。我们可以将这些东西称作经验、原则、规则,或者其它任何名词。

下面松散地列举了一些规则。我们应该在实践中检验已知的规则,总结自己的规则。

规则一 这世界上唯一的真理就是不要盲目相信真理

从某种意义看,原则、规矩这类东西就是用来打破的。所谓原则,就是对历史上某种经验的总结。如果我们踏入的河流和前人非常接近,我们可以参考前人的经验。我们是主动地拿来,而不是被动的遵守。

规则二 未蕴而变,自欺也;知律而变,智者之道也

这句话谈的是学词,必须先了解格律,然后才谈得上变化,否则就是自欺欺人了。在编程上,也是一样。在有资格打破一条原则前,首先要了解这条原则。先了解事物的规律,然后才是变化和灵活的应用。不要打倒自己不了解的东西。

规则三 寻找结构和成本的平衡点。当结构不能容纳变化时,重构代码到新的平衡点

开闭原则要求:一个软件实体应该对扩展开放,对修改关闭。即软件的结构要达到:允许在不修改软件的前提下扩展软件功能。

但好结构是要花成本的。程序员总是在折衷,寻找结构和成本的平衡点。我们会放一些余量,让结构能承受一定的变化。

规则四 要针对接口编程,不要针对应用编程

前面谈过了。我们总希望通过工作得到一些收获,积累一些经验。只有将代码分解成尽可能独立的模块,然后针对接口编程,才能保护我们的成果,让我们有所积累。

规则五 最少知识原则:模块对外界的了解应当尽可能少

前面也谈过了。所谓“圣人之治”,经常是“虚其心,实其腹;弱其志,强其骨,常使民无知无欲”。让被管理的对象尽可能得弱,对象间的关系尽可能得简单,可以降低管理的成本。

在面向对象编程中“组合优于继承”,就是因为继承将基类的实现细节暴露给子类,两者的耦合性太强了。另外,继承的实现是静态的,而组合可以更灵活地实现。

规则六 尊重习惯

在任何领域编程,应该尽量了解这个领域的习惯,尽量符合这些习惯。这样,别人可以更容易地理解和使用我们的程序,更不容易出错。

规则七 程序中不要出现magic number或其它神奇的东西

magic number就是诸如17、42这类数字。请用有描述性的常数名替代它们。常数名、变量名、函数名就是最好的注释。结构清晰的代码甚至可以不需要其它注释。

“需要很多注释的代码”是代码坏味道的一种,常常意味着需要重构代码,使它更容易被理解。

规则八 “两顶帽子”和“小步前进”

“两顶帽子”前面讲过了。面对复杂的问题,我们不仅要从结构上分解它,还要将实现步骤尽可能地分小,在一时一地以压倒性优势解决问题的一小部分。n-1个总比n个好对付,我们越来越强大,敌人越来越弱小,我们要做的只是将优势保持到最后。“保持优势”的关键是正确的理解、合理的划分。

规则九 适当地引入中间层,可以解决所有问题

这条规则是实质是“抽象”。在遇到问题时,多引入一层接口,将问题封装起来。

抽象本身并没有消除系统的复杂性,但它减少了在某个时刻需要处理的细节的数量。或者说,抽象将复杂性控制在接口和模型的后面,可以推迟我们处理这些复杂性的时刻。

这条规则的另一个说法是“多做白日梦”。假设我们已经得到了需要的东西,然后会怎么样?通过“白日梦”我们可以在更高的层次考察问题,可能找到解决问题的线索,也有可能发现这个问题毫无价值。即使毫无收获,我们也的损失也很小。

规则十 不要依赖调试,尽可能地依赖分析和推理

我经常会在头脑中预演调试的过程,计算我要做什么,我能得到什么。“预演”的结果经常是我没有必要这样调试,或者应该试一试另一个方法。

根据测不准原理,任何实际的测量、调试都会改变、影响被研究的对象。用调试辅助、验证我们的推测,但不要依赖调试。

分享到:
评论

相关推荐

    C语言开发入门与编程实践

    接下来,我们来谈谈编程实践。在这个压缩包中,你可能会找到各种练习题和实例代码,涵盖了从简单的输入输出到复杂的数据结构处理。例如,你可能看到如何使用`printf`和`scanf`函数进行基本的输入输出,如何通过数组...

    3D 游戏编程大师技巧源码

    首先,让我们谈谈3D游戏编程的基础——三维坐标系统。在3D空间中,每个对象都有一个x、y、z坐标,这构成了游戏世界的骨架。源码中可能会包含用于创建和管理这种空间的类或结构,如Vector3D和Matrix,它们是进行位置...

    Proxy源代码分析--谈谈如何学习linux网络编程

    对于想要深入了解Linux网络编程的初学者来说,《Proxy源代码分析——谈谈如何学习Linux网络编程》是一篇非常有价值的资料。它不仅提供了具体的源代码分析,还通过实例帮助读者理解Linux网络编程的基本原理和技术要点...

    Scratch少儿编程项目作品图片素材-愤怒的小鸟.zip

    "童程童美"可能是指一个专注于少儿编程教育的品牌或机构,他们可能提供了这套"愤怒的小鸟"的教程或课程,帮助孩子们在实践中学习编程。游戏案例是教学中的重要部分,因为它能激发孩子们的兴趣,使学习过程更加有趣。...

    谈谈使用VFW在windows下编程控制摄像头

    3. **初始化捕获**:调用`capSetVideoFormat`函数配置视频格式,如分辨率、帧率等。然后使用`capGrabFrameNoStop`或`capStart`启动视频捕获。 4. **图像处理**:捕获到的每一帧图像可以通过VFW的消息机制传递到应用...

    精彩编程与编程技巧-地大物博——谈谈在VB6.0中如何使用资源文件进行编程...

    在探讨“精彩编程与编程技巧—地大物博——谈谈在VB6.0中如何使用资源文件进行编程”这一主题时,我们不仅触及了Visual Basic 6.0(简称VB6.0)这一经典开发环境的深层应用,还深入解析了资源文件在软件开发中的重要...

    Linux内核高级编程

    设备驱动编程是内核编程的重要实践领域。Linux支持广泛的硬件设备,每种设备都需要相应的驱动程序来与内核交互。书中会涉及字符设备、块设备驱动的编写,包括设备注册、中断处理、DMA(直接内存访问)等概念。此外,...

    机器人编程教程

    在机器人编程领域,掌握...掌握基础的编程知识,熟悉错误处理策略,理解语法解释过程,并通过实践不断磨练技能,你将能够创造出更加智能和高效的机器人解决方案。不断学习和探索,你将在机器人编程的世界里大放异彩。

    Lambda in Canvas – 谈谈函数式编程在前端开发中的应用

    在前端开发领域,Lambda表达式和函数式编程思想正逐渐成为主流,这主要得益于它们能够带来更简洁、可维护的代码。...在学习和实践中,开发者应不断探索如何将这些概念融入到日常工作中,以提升代码质量并优化开发流程。

    吴恩达deeplearning.ai项目第四个课程的编程实践(二)

    在这个编程实践中,我们将深入探索深度学习的两个热门应用:人脸识别和神经风格转换。这两个主题都是吴恩达在deeplearning.ai课程中的重要内容,通过实际项目,你可以深化对这些技术的理解并提升自己的编程技能。 ...

    C语言高级编程实例【rar】

    "C语言高级编程实例"这个资源旨在帮助开发者深入理解C语言的高级特性和用法,通过实践来提升编程技能。在这个压缩包中,可能包含了各种复杂的编程挑战,覆盖了C语言中的数据结构、算法、内存管理、预处理器、指针...

    Modbus 主站编程 OB83 OB86_OB83_Modbus主站编程_

    通过深入理解OB83和OB86的工作原理,结合实际的Modbus主站编程实践,可以有效地提高系统的可靠性和稳定性。在提供的文档“Modbus 主站编程 OB83 OB86.doc”中,应包含更详细的步骤和示例代码,帮助读者更好地理解和...

    和初学者谈谈如何学好单片机编程技术.doc

    然而,对于初学者来说,学习单片机并非易事,因为它不仅涉及硬件,还有复杂的软件编程。 单片机的学习难点在于“软件”层面,即编程。初学者往往对单片机如何通过简单的数据操作实现设备控制感到困惑。比如,如何...

    EVC高级编程及应用

    在本文中,我们将深入探讨“EVC高级编程及应用”这一主题,主要关注在基于ARM架构的Windows CE(wince)操作系统上使用Embedded Visual C++(EVC)进行软件开发的技术细节和实践案例。 首先,让我们理解Windows CE...

    socket编程实例

    总的来说,这个实例涵盖了网络编程的基础知识,通过实践可以帮助我们更好地理解和掌握多线程、Socket通信、文件操作和注册表编程等技能。对于想要深入学习网络编程的开发者来说,这是一个很好的起点。

    编程建议,多种资料,适合新手

    这些箴言通常源自于长期编程实践中积累的经验教训,它们可以引导我们形成良好的编程习惯,避免常见错误。例如,"写清晰易读的代码"强调了代码可维护性的重要性;"早测试,常测试"提醒我们在编程过程中要频繁进行单元...

    Lua编程技巧

    Lua是一种轻量级的脚本语言,以其简洁的语法、高效性能和易嵌入性而闻名。在"Lua编程技巧"这个主题中,我们将深入探讨几个关键知识点...通过深入学习和实践,你将能够充分利用Lua的这些特性,解决各种复杂的编程问题。

    图形编程 代码

    首先,让我们谈谈VC++ 6.0。这是一个由微软开发的集成开发环境(IDE),它主要用于编写使用C++编程语言的应用程序。尽管目前有更新的版本如Visual Studio,但VC++ 6.0因其简洁性和对低级编程的友好性,仍然在某些...

    VC++多线程/进程编程实例(2)(5个实例-附源代码)

    接下来,我们谈谈进程编程。进程是系统分配资源的基本单位,每个进程都有自己的内存空间,可以运行独立的程序。在VC++中,可以通过`CreateProcess`函数来创建新的进程。实例可能涉及进程间的通信(IPC),如管道、...

Global site tag (gtag.js) - Google Analytics