软件开发的科学与艺术--写好代码的十个秘决

技术、艺术与禅道《禅与计算机程序设计艺术》 / 陈光剑 来自二千五百年前的思想:庖丁解牛的故事 庖丁释刀对曰:“臣之所好者,道也,进乎技矣。始臣之解牛之时,所见无非牛者。三年之后,未尝见全牛也。方今之时,臣以神遇而不以目视,官知止而神欲行。依乎天理,批大郤,导大窾,因其固然,技经肯綮之未尝,而况大軱乎!良庖岁更刀,割也;族庖月更刀,折也。今臣之刀十九年矣,所解数千牛矣,而刀刃若新发于硎。彼节者有间,而刀刃者无厚;以无厚入有间,恢恢乎其于游刃必有余地矣... 阅读详情

    本文选自由微软公司华人专家编著的《软件开发的科学与艺术》一书。全书透彻解析了微软软件开发的思想与过程。

    双手互搏, 无坚不摧

    作为一个软件开发人员,必须测试自己的程序,使得代码做得更好,更加稳定。就我个人的经验来说,如果没有测试过代码,程序就不可能正确运行。

    另外,在同一组的开发人员之间做得很多的一件事就是:别人来对你的代码进行检查,反过来你对别人的代码进行检查,这个过程不仅是希望检查的人来发现你的代码中的问题,或是你去发现别人代码中的问题,更重要的是在向别人讲解你的代码的过程中,可以发现自己遗漏的地方和问题,理顺自己思路。

    下面这段程序是我在开发Exchange Server时写的一段代码,当时写完以后我没有测试它。因为这段代码实在是太简单了,只有几行代码:取文件的长度,如果出错就返回。于是我仅仅是编译通过后就将其提交(checkin)到实际产品中了。

    结果第二天早上当我到办公室的时候,发现我的三位上司都已经铁青着脸在那里等我了。原来,整个Exchange Server都运行不起来了!因为我的这段代码被加在了Exchange Server启动代码序列中,当Server启动时,由于我这段代码的错误,一启动就失败,导致了DOA(dead on arrival)。

//

// Get file size first

//

DWORD   dwFileSize = GetFileSize( hFile, NULL );

if ( dwFileSize = -1 ) {

  // what can we do ? keep silent

  ErrorTrace(0, "GetFileSize failed with %d", GetLastError());

  return;

}

    注:GetFileSize调用失败时将返回–1。

    这段代码的错误在于:if的判断条件写成了赋值,所以无论怎样都会出错,然后返回。

    其实改进的方法很简单:就是将-1移到前面。这样,如果你遗漏了一个“=”,编译时编译器就会发现错误。所以在if语句中,要把常量放在前面。

//

// Get file size first

//

DWORD   dwFileSize = GetFileSize( hFile, NULL );

if ( -1 == dwFileSize ) {

  // what can we do ? keep silent

  ErrorTrace(0, "GetFileSize failed with %d", GetLastError());

  return;

}

    这件事情给我的教训是很深刻的。

    见招拆招, 滴水不漏

    错误情况(Error Case)是指那些不易重现的错误。一定要对错误情况进行处理,免得程序崩溃。其中最常出现的情况如下。

    × 内存耗尽。不要认为100 B内存很小,不会出错。因为在系统运行时,尽管我们自己的程序申请的内存很少,但是不能保证别人的程序申请的内存也很少。在开发过程中就经常出现这样的情况:别人的程序申请的内存太多,导致我的程序由于内存申请不到而崩溃了。

    × 异常。在C++中经常会出现异常,要做好处理它的准备。用过MFC的用户都知道,如果出现异常,就必须处理它,否则程序随时会崩溃。

    × 网络中断。不要以为发送一个socket都会成功。尤其是用异步socket做一些网络软件,客户端将数据包发送到服务器端时很容易出错。由于是异步,所以尽管发送方是正确的,但过一段时间接收方就可能出错,而且这种异步错误是很难检测的。有些错误可以在调试过程中借助一些手段产生出来,有些错误则是不能重现的。在错误不能重现的情况下,我们就要做一些工具,迫使错误情况出现。

    另外,在编程的过程中还应该注意:

    × 在C++的对象的构造函数中不要做一些可能会失败的操作,如内存的操作,因为在构造函数中出错后是没有方法知道的。

    × 处理错误情况时,要释放分配到的资源;接口中应该清楚地定义程序的行为,如返回状态,异常的处理等,要让调用者清楚地知道接口的定义。

    × 千万不要忽略错误,造成程序崩溃或退出。

CWInfFile::CWInfFile() {

  m_plLines     = new TPtrList(); // ...

  m_plSections  = new TPtrList(); // ...

  m_ReadContext.posLine = m_plLines->end();

    . . .

}

    这段程序中最大的问题是MFC的new操作,如果失败了会产生异常。如果前一个new操作分配正常,而后面的new分配出错,则前一个变量的分配会导致内存的泄漏。解决的办法是:用一个try…catch语句来处理,如果出现异常,则删除变量,释放内存。

CWInfFile::CWInfFile() {

try {

  m_plLines     = new TPtrList(); // ...

  m_plSections  = new TPtrList(); // ...

  m_ReadContext.posLine = m_plLines->end();

    . . .

} catch ( . . . ) {

   if (m_plLines) delete m_plLines;

   if (m_plSections) delete m_plSections;

 }

}

    但是,这里面还有一个问题:由于构造函数是最先执行的,如果new分配出错,就会执行delete语句,而此时变量m_plLines和m_plSections还没有初始化,若对它们进行delete操作就会出错。所以我们应该在构造函数中将其初始化。

CWInfFile::CWInfFile() : m_plLines(NULL), m_plSections(NULL) {

try {

  m_plLines     = new TPtrList(); // ...

  m_plSections  = new TPtrList(); // ...

  m_ReadContext.posLine = m_plLines->end();

    . . .

} catch ( . . . ) {

   if (m_plLines) delete m_plLines;

   if (m_plSections) delete m_plSections;

 }

}

    请注意,这里如果用的不是MFC,则new操作出错时就会返回NULL,而不会抛出异常,那么第三个变量的初始化就会出错。

CWInfFile::CWInfFile() : m_plLines(NULL), m_plSections(NULL) {

try {

  m_plLines     = new TPtrList(); // ...

  m_plSections  = new TPtrList(); // ...

  m_ReadContext.posLine = m_plLines->end();

    . . .

} catch ( . . . ) {

   if (m_plLines) delete m_plLines;

   if (m_plSections) delete m_plSections;

 }

}

    下面讲的是在构造函数中不要使用一些会失败的操作。

Class foo {

private:

    CHAR* m_pszName;

    DWORD m_cbName;

public:

    foo(CHAR* pszName);

    CHAR* GetName()

    {return m_pszName;}

…

};


foo::foo(CHAR* pszName)

{

    m_pszName = (BYTE*) malloc(NAME_LEN);

    if (m_pszName == NULL) {

        return;

    }

    strcpy(m_pszName, pszName);

    m_cbName = strlen(pszName);

}

……

foo* pfoo = new foo(“MyName”);

if (pfoo) {

    CHAR c = *(pfoo->GetName());

}

    由于在构造函数中无法返回错误码,当我们使用new操作符创建一个foo对象时,会调用其构造函数,如果malloc函数出错,构造函数就直接返回,则pfoo就没有分配值,这样在后面的程序中对pfoo的访问就会出错。因此,在构造函数中不要用一些会失败的操作。

    在下面这个改动后的版本中,构造函数中不再包含失败的成分,而是增加了一个init函数来完成内存的分配操作。在使用中先调用new函数再调用init函数,检验返回值,判断是否执行正确,有错就将其返回。

Class foo {

private:

  CHAR* m_pszName;

  DWORD m_cbName;

public:

  foo();

  HRESULT

  Init(CHAR* pszName);

…

};

foo::foo()

{

  m_cbName = 0;

  m_pszName = NULL;

}


HRESULT foo::Init(CHAR* pszName)

{

  HRESULT hr = S_OK;

  if (pszName) {

    m_cbName = lstrlen(pszName);

    m_pszName = (CHAR*)malloc(m_cbName+1);

    if (m_pszName == NULL) {

      hr = E_OUTOFMEMORY;

      return hr;

    }

软件开发中的设计目标与平衡艺术 本文探讨了软件开发中的设计目标和平衡艺术。首先,平衡软件设计中的整体结构和细节是至关重要的,缺乏整体设计会导致不断权宜之计,而过度强调结构则可能导致资源的过度消耗和实施的延误。其次,软件开发是一个迭代和增量的过程,需要在分析、设计和实现之间保持适当的平衡和沟通。文章还强调了软件设计中的灵活性、可扩展性和可移植性的重要性,并提出了设计时应遵循的步骤和方法。... 阅读详情

相关推荐

项目管理-(补充)七种基本质量工具

七种基本质量工具,用于PDCA循环的架构内 解决与质量相关的问题,分为老七工具和新七工具。 1)老七种:刘英只点劣质茶(流程图、因果图、直方图、散点图、帕累托图、控制图、核查表)2)新七种:锯树联亲测动优(矩阵图、树形图、关联图、亲和图、过程决策程序图、活动网络图、优先矩阵)

weixin_42081167的博客 4604

软件开发的艺术与科学

因此,软件开发对于各行各业的发展以及社会的进步具有至关重要的作用。本文将探讨软件开发的重要性和现状,以及开发过程中涉及的关键环节和常见问题。随着新技术的不断涌现和新趋势的形成,软件开发将迎来更多的机遇和挑战。通过区块链技术,可以实现数据的安全存储和共享,提高软件系统的安全性。发布与维护:发布软件后,开发团队还需要对软件进行持续的维护和升级,以满足用户的需求变化。测试:测试团队需要对软件进行全面的测试,以确保软件的质量和稳定性。编码:编码是将设计转化为实际的软件代码,是软件开发的核心环节。

朱雀随云记的博客 872

cadence—virtuosoic617的使用技巧收集区

cadence使用

qq_33599939的博客 3万+

优快云推荐好书:《软件开发的科学与艺术》

《软件开发的科学与艺术》(书号:7-5053-7555-5/TP.4380),电子工业出版社出版,将于2002年4月20日正式发行。定价:38元。内 容 提 要本书凝聚了微软专家多年研究和工作中获得的宝贵经验,是特为每个关注软件开发的人而准备的一本书。书中,这些微软专家现身说法,提供了许多成功亦或失败的案例,并进行深刻中肯的剖析,为读者展现微软企业文化,以及微软的软件开发过程和项目管理等方面的内容

讲武堂-Jiangtao 4077

控制障碍函数(Control Barrier Function,CBF) 二、示例

使用一个经典简单的示例讲解CBF的推导

Lyajpunov的博客 6433

解决win11无法安装.net framework 3.5(包括.net2.0和3.0)错误代码:0x80070005

解决win11无法安装.net framework 3.5(包括.net2.0和3.0)错误代码:0x80070005

zeng699304的博客 2万+

《软件开发的科学与艺术》节选-写好代码的十个秘诀

    本文选自电子工业出版社2002年即将出版的由微软公司华人专家编著的《软件开发的科学与艺术》一书。全书透彻解析了微软软件开发的思想与过程。  双手互搏, 无坚不摧     作为一个软件开发人员,必须测试自己的程序,使得代码做得更好,更加稳定。就我个人的经验来说,如果没有测试过代码,程序就不可能正确运行。      另外,在同一组的开发人员之间做得很多的一件事就是:别人来对你的

讲武堂-Jiangtao 3024

STM32F103多通道PWM输出避坑指南:TIM1_CH1异常输出的解决方案

本文深入解析STM32F103多通道PWM输出中的TIM1_CH1异常问题,提供从时钟配置到GPIO复用的系统化解决方案。通过黄金法则配置流程和高级调试技巧,帮助开发者快速定位并解决PWM输出不稳定、占空比异常等问题,同时拓展多定时器协同和互补PWM等高级应用场景。

weixin_29194817的博客 117

软件开发的艺术与科学从需求到上线的完美旅程

从需求到上线的完美旅程,是一次艺术与科学不断交织、共鸣的实践。艺术赋予软件以灵魂和人性化的体验,使其能够精准地满足用户未被言明的渴望;科学则赋予软件以坚实的骨架和强健的体魄,使其能够在复杂的现实环境中稳定、高效地运行。唯有将创新的艺术思维与严谨的科学方法论融为一体,软件开发才能真正从一个纯粹的技术活动,升华为创造持久价值的完美旅程。

lxs_569190的博客 411

④ Django创建用户中心应用users,扩展用户信息

本章配套视频教程 Django创建用户中心应用users,扩展用户数据 在settings.py文件中配置项目 1、语言配置:LANGUAGE_CODE = 'zh-hans’ 2、时区配置:TIME_ZONE = 'Asia\Shanghai’ 3、模板路径配置:‘DIRS’: [os.path.join(BASE_DIR, ‘templates’)], 并创建templates文件

轻编程的博客 673

YOLOv9分割改进 ,YOLOv9分割改进主干网络为华为EfficientNet,助力涨点

EfficientNet论文中研究了卷积网络的缩放和,并证明对深度,宽度和分辨率复合缩放的重要性,因此精度和效率更好。为了阐述相关的原理,我们提出了简单有效的复合缩放方法,使得模型缩放具有一定设计准则,同时兼顾了模型的效率。我下文将 YOLOv9 分割模型改进主干网络为 EfficientNet 融合网络结构。论文地址代码地址下文都是手把手教程,跟着操作即可添加成功把环境配置好,数据集处理好,训练基本能成功,创作不易,请帮忙点一个爱心,关注我,带你不挂科!

在职AI算法工程师,擅长计算机视觉,YOLO目标检测、分割等,擅长web、pyqt界面可视化,好内容持续更新中,来这里跟大家一起学习,共同进步 938

软件开发的艺术与科学从需求到部署的全流程解析

从需求到部署的软件开发生命周期,是一场在科学与艺术之间寻求永恒平衡的实践。过分强调科学,可能导致流程僵化,扼杀创新;过分强调艺术,则可能带来项目的混乱和不可预测。最成功的团队和项目,往往是那些能够灵活运用科学方法论确保效率和可靠性,同时又能发挥艺术创造力以应对复杂性和实现卓越的团队。理解并欣赏这一者间的张力,是成为一名卓越软件开发者的关键。

andyzu2012的博客 326

uniapp - 详解APP端免费人脸识别+人脸核身+人脸对比+活体检测插件功能完整源码,uniAPP纯前端实现不依赖后端API接口或第三方付费人脸识别功能示例(仅兼容安卓app与苹果ios App)

uniapp免费人脸认证功能,uniapp APP开发活体人脸识别,UniApp人脸识别与活体检测,人脸核身uni-app插件 活体检测 人脸对比 人脸识别 人脸认证 身份证件,uni-app人脸核身,如何在app端实现免费人脸识别教程,人脸核身uni-app插件活体检测,uniapp安卓ios百度人脸识别、活体检测、人脸采集APP原生插件,详细实现人脸识别和活体检测功能,实时监测人脸是否进入区域及动作是否正确播报,提供提供实名认证、身份证识别、有没有免费的,api,活体检测,人脸信息采集、刷脸扫脸认证示例

🏆 前端领域优质创作者 4386

解锁代码注释之谜:掌握代码注释的艺术与科学,提升软件开发与团队协作的卓越实践

跟随猫头虎博主深入探索代码注释的迷人世界,我们将揭示软件开发领域中有效代码注释背后的关键角色和策略艺术。在穿越编程、编码实践和开发者协作的错综复杂的道路上,这一富有洞察力的探讨聚焦于在提升代码质量、软件可维护性和无缝团队沟通方面经常被忽视的注释的重要性。无论您是一名软件工程师、全栈开发者还是技术爱好者,加入我们,解码代码文档的神秘之谜,并利用有影响力的代码文档的潜力,将您的编码熟练程度、软件项目和开发团队的协同作战能力提升到一个新的高度。通过切实的见解、真实世界的轶事和务实的技巧,探索、学习并放大您的开发者

猫头虎技术团队:授渔优于赠鱼,兴趣引领智慧,探索之乐尤显珍贵。商务合作+:Libin9iOak ,万粉变现+:优快云WF,猫头虎承诺每年免费为100名C站创作者做账号流量诊断服务!全网搜:猫头虎技术团队,点击文章底部名片或直接私信我一切皆可谈,快找虎哥! 457

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

代码审查的艺术与科学

它需要有条不紊的方法、对代码用途的清晰理解以及对细节的关注。Linters 和 Formatters:像 ESLint 或 Prettier 这样的工具可以自动捕获并修复样式不一致的地方,从而减少审查中的手动开销。如果做得正确,它们不仅仅是开发过程中的一个步骤,而且是一种丰富的经验,可以提高整个团队的技能和凝聚力。除了冰冷的代码逻辑之外,代码审查还有更微妙、更巧妙的一面。要真正掌握代码审查,我们必须在审查代码的科学性和理解人类协作的艺术之间找到微妙的界限。它应该是干净的和模块化的,以便将来的可维护性。

我的博客,不一样的自我表达 139

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

编程,艺术 or 科学?

在一个微社区帖子里讨论这个话题:编程,艺术 or 科学?

时间文盲的记事板 1546

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

软件是什么?科学还是艺术,亦或工程?

    真正开始接触软件之前,一直对软件没有太大的概念 ,仅仅认为软件是程序,哪天我能写出像优化大师那样的软件就好了,后来学习了java半年之后,开始觉得 软件是个工程,特别像建筑工程,看到一本UML书中的开篇一句话,大致意思是程序只是盖狗窝,而软件则是在建筑一座大厦,我对软件的工程性的越来越有感觉。     之后,开始接触设计模式,那时候开始觉得软件是门艺术,而程序员,则是艺术的创造者,是创

文风一动 749

软件开发的艺术

相对于那些不善运用隐喻的人,那些使用隐喻来照亮自己的软件开发过程的人,他们对于编程的理解会更好,并且能够更快地写出更好的代码。 有专家说,软件开发是一门科学,是艺术,是一个过程,是在驾驶汽车等等。 文字写作隐喻软件开发,暗示其过程是一种代价昂贵的试错过程,而非仔细的规划和设计。 有人将软件开发比作耕作,“每次做一点”,最后添加到整个系统。实际上,应该对系统计划施肥,对细节设计蔬果...

aibaqian0241的博客 213

探索Haskell与生成艺术的奇妙世界

探索Haskell与生成艺术的奇妙世界 项目介绍 "Generative art using Haskell" 是一个由 quchen 和 fmthoma 共同开发的开源项目。该项目利用Haskell编程语言与Cairo图形库,探索二维几何与生成艺术的结合。通过这个项目,开发者们展示了如何使用函数式编程语言Haskell来创建令人惊叹的生成艺术作品。 项目技术分析 Haskel...

gitblog_00202的博客 638

John Carmack谈软件工程中艺术与科学

虽然我不是个游戏超级玩家,但却是因喜欢游戏而开始学习编程的(特别是图像渲染算法)。所以当我看到John Carmack在2012 Quake­Con上的发言时,我觉得自己该听听,学习游戏设计以及开发相关的知识。不过我所听到的是一个黑客在谈论他的最近感悟——软件工程事实上是一门社会科学。其中的大约10分钟,他涉及到了人性的各个方面,比如开发人员犯的错误,编程语言设计,静态分析,代码评审,开发人员培训...

永无止境,上下求索 711

《大学编译原理:语言翻译的艺术与科学》

在大学的计算机科学课程中,编译原理无疑是一门充满挑战与魅力的重要学科。它就像是一座连接高级编程语言和计算机硬件的桥梁,让程序员能够用人类易于理解的语言编写代码,而计算机则能高效地执行这些指令。一、编译原理的重要性编译原理是计算机科学的核心领域之一,它对于软件开发和计算机系统的性能有着至关重要的影响。首先,编译器能够将高级编程语言(如 C、C++、Java 等)翻译成低级机器语言,使得计算机可以直接执行程序。没有编译器,我们就无法将人类的创意和算法转化为计算机可执行的指令。

m0_52796585的博客 1221
上一篇: 使用正规表达式编写更好的 SQL
下一篇: 介绍 IOC
GoBest
博客等级 码龄22年 4粉丝 27原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值