65.9K
CodeProject 正在变化。 阅读更多。
Home

成功程序员的基础实用编程技巧

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.90/5 (34投票s)

2018年2月12日

GPL3

12分钟阅读

viewsIcon

50190

所有程序员的实用主义清单

引言

创建优秀、成功的软件非常困难——极其困难。

因此,对于每一位程序员来说,了解、理解并应用基础的软件开发实用技巧至关重要——这些实用的建议和规则已被证明是有用的,并能帮助我们在最短的时间内创建出最好的软件。

在本文中,我试图收集一套我认为最相关的基础实用技巧。如果您是一位经验丰富的程序员,您可能对其中大部分/所有内容都很熟悉。如果您遗漏了某些建议,请通过评论与我们分享。

请注意

  • 本文中的实用技巧是关于设计和编写代码的。软件开发项目要取得成功,还有其他重要的方面(例如良好的用户界面、人际交往等),但这些超出了本文档的范围。
  • 以下实用技巧仅涵盖基础原则,旨在普遍适用。不包含针对特定编程环境(编程语言、库、工具和架构)的建议。

技巧分为三类

  • 通用指南
  • 数据设计
  • 编写代码(将在本文第二部分发布)

让我们开始吧。

通用指南

一切都应该尽可能简单,但又不能太简单! - 阿尔伯特·爱因斯坦

简单的工具和概念可以快速理解,易于使用,不易出错,并能提高我们的生产力。

我们都喜欢简单。简单使工作和生活更加愉快。

然而,我们必须意识到过度简化的危险,正如爱因斯坦美丽名言的结尾所示。

因此:保持简单,但不要过于简单!

许多名人都推崇简洁。以下是一些例子

引用

简洁是可靠性的先决条件。

简洁和清晰……决定成功与失败。

— 艾兹格·W·迪克斯特拉
荷兰计算机科学家;《Go To 语句被认为有害》的作者
引用

控制复杂性是计算机编程的本质。

— 布莱恩·W·克尼汉
计算机科学家和作家;Unix 的联合开发者
引用

简洁是终极的精致。

— 未知
引用

简洁能将普通变为非凡。

— 斯科特·亚当斯
《呆伯特》的创作者
引用

如果你不能简单地解释它,说明你理解得不够透彻。

— 阿尔伯特·爱因斯坦
物理学家;天才
引用

真理总是在简洁之中,而不是在事物的多重性和混乱之中。

— 艾萨克·牛顿
数学家;天文学家;神学家;作家和物理学家

如果注定要失败,那就快速失败!

大多数软件项目都会失败。这是一个令人 sad 且不可否认的事实。

如果一个项目被认为会失败,那么它应该尽快失败,以限制损失,并将时间和资源腾给其他(有望)不会失败的项目。

在此语境下的快速失败意味着问题应该尽早被检测到并得到处理。我们拖延解决问题的时​​间越长,浪费的时间、精力和资源就越多。随着时间的推移,累积损失会呈指数级增长。

例如,在设计阶段纠正一个设计缺陷既简单又便宜。但一旦软件投入生产并被许多人使用,修复错误通常会非常昂贵且令人沮丧。

引用

我们越早“失败”并越快学习,成功的几率就越大。尽早失败可以为您节省时间和金钱。

— 德米安·博尔巴
Adobe 产品经理
引用

快速测试,快速失败,快速调整。

— 汤姆·彼得斯
畅销书《追求卓越》的作者

追求“够好”,而非“完美”。然后发布!

创建完美的软件(没有 bug,所有功能都已完全实现,最佳用户界面,优秀的文档等)非常耗时且昂贵,除非我们处理的是一个非常小的项目。在大多数情况下,由于实际限制,实现完美是不可能的。

即使是软件行业的主要参与者,拥有最好的开发者和充足的预算,也无法编写出完美的软件。这就是为什么他们不断提供补丁和新版本。

因此,请按以下方式进行

  • 设定目标和优先级
  • 创建原型
  • 交付“够好”的软件
  • 持续改进(参见下一项)
Prototype arrow
Good enough arrow
Better arrow

原型

够好

更好

引用

您无法编写 100% 完美的代码。即使您做到了,6 个月后它也不会是完美的。

— 克里斯·蒙德
CodeProject 联合创始人
引用

在计算机简短的历史上,没有人编写过完美的软件。

— 安迪·亨特
作家;《实用的程序员》合著者
引用

现在交付 90% 的功能比永不交付 100% 的功能要好。

— 布莱恩·W·克尼汉
计算机科学家和作家;Unix 的联合开发者
引用

寻找完美解决方案通常会导致停滞和沮丧。坚持不懈,容忍不完美,追求改进,并致力于尽自己最大的努力,这些都是健康的,并且最有可能产生最佳结果。

— 阿尔伯特·埃利斯
心理治疗师

倾听用户的声音!

实践表明:

  • 软件开发人员无法预料到用户真正想要的一切。
  • 用户通常确切地知道他们想要什么,除非他们使用该软件一段时间。
  • 用户的满意度是软件成功的决定性因素。

我们想要满意的用户。因此,最好的方法是迭代式的方法,如下所示

Iterative software development
引用

在尝试了一些营销技巧并花费了大量时间后,他(乔尔·斯波尔斯基,Stack Exchange 联合创始人)总结说(5 年后):没有什么比改进你的产品更好。制作人们想要的好软件并不断改进它。与你的客户(用户)交谈并倾听。找出他们的需求。

— 书籍《程序员访谈录》
引用

……我们为每个主要产品功能构建原型。我们很早就与预发布用户和关键客户进行测试,并且绝对在实现之前进行。是的,我们确实经常“失败”,这很棒!这很棒,因为我们在过程中学到了很多东西,并最大程度地降低了长期的失败风险。最终,我们以富有同情心的方式,以创新的方式解决了客户的实际需求。

— 德米安·博尔巴
Adobe 产品经理
— 维基百科
引用

程序最重要的属性是它是否实现了用户的意图。

— 托尼·霍尔
计算机科学家;1980 年 ACM 图灵奖获得者

数据设计

在编写代码之前,仔细设计您的数据!

无论何时创建应用程序,都从仔细设计数据结构及其关系开始。在编写代码之前完成此操作。

设计良好的数据结构可以带来更简单、更易于维护的代码,减少 bug,提高性能,并降低内存消耗。

差异可能非常显著。

引用

一项又一项研究表明,最优秀的设计师产生的数据结构更快、更小、更简单、更清晰,并且付出的努力更少。优秀与平庸的方法之间的差异接近一个数量级。

— 弗雷德·布鲁克斯
书籍《没有银弹》
引用

给我看你的流程图(代码),隐藏你的表格(数据结构),我仍然会感到困惑。给我看你的表格(数据结构),我通常就不需要你的流程图(代码)了;它们会很明显。

— 弗雷德·布鲁克斯
书籍《人月神话》
引用

表征规则:将知识折叠成数据,这样程序逻辑就可以是愚蠢而健壮的。

数据主导一切。如果你选择了正确的数据结构并组织得当,算法几乎总是显而易见的。数据结构,而不是算法,是编程的核心。

— 《Unix 编程艺术》
引用

我非常提倡围绕数据设计代码,而不是反过来。

糟糕的程序员担心代码。好的程序员担心数据结构及其关系。

— 林纳斯·托瓦兹
Linux 的创造者
引用

如果你正确地处理了数据结构及其不变量,大部分代码就会自己写出来。

— 彼得·德意志
书籍《程序员访谈录》
Design data first

除非有充分的理由,否则使所有数据结构都不可变!

不可变数据结构更容易理解,更易于使用,并且不易出错,因为

  • 创建后,状态不再改变。没有状态转换,没有临时无效状态,也无需同步、锁定或防御性复制。
  • 不可变数据可以在并发/并行计算环境中自由共享——没有数据损坏、死锁或其他可能非常难以解决和修复的棘手问题的风险。
  • 基于不可变数据计算出的结果可以轻松缓存,以提高性能。

然而,不可变数据结构并非总是最佳选择。例如,为每一次更改克隆整个结构可能非常昂贵(在时间和空间上)。在某些情况下(例如游戏或 GUI 应用程序),可变数据结构更适合。

此外,如果两个对象需要直接相互引用,那么对象必须是可变的。例如,树中的父节点和子节点直接相互引用,互为好友(A 指向 B,B 指向 A)等。在这种情况下,如果必须保留不可变性,那么一种可能的解决方案是拥有一个额外的描述关系的数据结构,例如一组表示一对相关对象的元组。

将允许的值集限制为最小可能的值!

限制数据类型允许的值集

  • 记录并帮助理解数据类型

  • 消除因错误值而导致的误行为或严重故障的风险

  • 简化处理数据的代码

例如,考虑数据类型employeename字段。允许将任何字符串存储在name中,可能会发生以下情况

  • 长字符串可能导致缓冲区溢出(取决于编程语言)、内存耗尽或其他软件故障。

  • 在名称中无效的字符,但用作 JavaScript 和 SQL 中的有效符号(例如<>")可能会为 SQL 和/或脚本注入等攻击敞开大门。

  • 如果代码没有正确显式处理空字符串或null值,则可能导致 bug。

为避免这些风险,应限制name字段。例如,以下简单的正则表达式消除了上述所有问题

[a-zA-Z ]{1,70}

此正则表达式将名称限制为最多 70 个字符,至少需要一个字符,并且只允许字母和空格。但请注意,上述正则表达式过于简化,不适用于必须允许包含连字符、撇号等的名称的实际应用程序。有关更多信息,请参阅 Chad3F 在评论

保护数据免受无效值的影响(尤其是在处理来自外部源的数据时)通常被认为是编写安全软件最重要的规则。(例如,参见OWASP Top 10 关键 Web 应用程序漏洞前 10 名安全编码实践

数据输入表单中SQL 注入尝试的示例

SQL injection example

JavaScript 注入尝试的示例

Javascript injection attempt

大多数情况下,默认值应该是允许值集中最严格的。

通过选择最严格的可能值作为默认值,我们始终处于安全的一边。

更宽松的值需要明确说明(在代码中、配置文件中等)。

例如

  • 一个写入文件的函数,默认情况下不应覆盖现有文件。

  • 默认应启用所有编译器警告。

  • 如果在多用户应用程序中添加新用户,则默认应授予最低的权限。

最后一个例子表明,严格的默认值并不总是最佳选择。它们可能令人烦恼。如果该多用户应用程序只由一个人在其 PC 上使用,那么用户显然希望默认拥有完全的权限。因此,最佳默认值有时取决于几个因素。

GUI 中严格默认值的示例

Strict default value

避免数据冗余!

在不同位置存储可变数据的副本容易出错、劳动密集且成本高昂,因为

  • 在数据更改的情况下,存在未更新所有位置的风险——由于健忘、技术问题、安全问题等。这可能导致数据不一致和损坏,并最终导致严重的软件故障。

  • 有时需要实现和激活锁定/同步机制以进行每次数据修改(创建、更新和删除操作),以避免在数据修改过程中访问无效数据。实现这些机制可能非常棘手且容易出错。

  • 需要额外的内存来存储副本。

肤浅的例子:一位推销员错过了一个重要的约会。原因:他将约会输入了他的 PC 上的日程表,但忘记了与他手机上的另一个日程表同步数据。如果两个日程表的数据都存储在一个地方(例如云端),他就不会错过约会了。

考虑使用通用、标准化格式将数据存储在纯文本文件中!

使用文本文件作为存储介质有很多优点

  • 所有操作系统都完全支持文本文件。无需安装和配置数据库服务器等附加软件。

  • 文本文件可以由人类轻松读取和处理。这对于调试非常方便。

  • 它们也可以由许多第三方应用程序(用任何编程语言编写)和工具轻松读取和处理。例如,Unix 提供了许多有用的文本处理工具,如 grep、awk、sed 等。

  • 使用 JSON、XML 和 CSV 等标准格式可以使数据被许多现有应用程序查询、排序、过滤、搜索、打印和转换。例如,CSV 文件可以在电子表格应用程序中轻松使用。

然而,文本文件也有严重的局限性,尤其是在大数据的情况下。复杂的查询(带过滤和连接)、更新和删除操作、事务处理、数据加密和其他功能可能需要手动实现,并且可能非常低效。当涉及大数据时,将其存储在数据库中通常是唯一可行的选择。

此外,将数据存储为字符(而不是位)会消耗更多的空间和时间。因此,二进制数据有时是不可避免的。

使用文本表示图形的一个很好的例子是SVG(可缩放矢量图形)。

这是一个 SVG 文件的简单示例(SVG_example.svg

<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg"
    width="140"
    height="140" >

    <circle cx="70" cy="70" r="40" stroke="red" stroke-width="4" fill="yellow" />
</svg>

用(例如您的网络浏览器)打开文件会显示以下图像

SVG circle example
引用

编写处理文本流的程序,因为这是一个通用接口。

— 道格·麦克罗伊
Unix 管道的发明者
引用

Unix 传统强烈鼓励编写读取和写入简单、文本化、面向流、设备无关格式的程序。

— 埃里克·史蒂文·雷蒙德
《Unix 编程艺术》

编写代码

请继续阅读第二部分

© . All rights reserved.