如何解决任意编程问题

ℹ️ 本文发布于请注意文中内容的时效性。 本文译自: 原文链接

作为开发者,我们每天都在解决问题,但真正关注解决问题本身的课程却并不常见。许多开发者专注于学习工具、语言和框架,却忽略了如何正确解决问题的方法。

如何定义“解决问题”?

解决问题是一个宽泛的概念,不同的人会有不同的理解。本文中,我们将解决问题定义为:

当你把抛锚的汽车送到 4S 店维修时,他们可能会决定通过更换坏掉的零件来修复损坏的车辆,或者提供给你购买一辆新车的选择。虽然这些选项看起来都是 “解决方案”,但只有第一个选项才是真正在处理问题,其他的选项都是在避免处理问题。

作为开发者,可能很多时候你不得不通过新旧替换来规避整个问题,但本文并不是要你替换问题或回避问题,而是重点关注在解决问题的技巧。

💡 译注:作为一个开发者,你可能会认为 “相比在老旧项目上的修修补补,
通过新旧替换的方式规避问题,这样做的成本不是更低吗?”。

确实,新的工具、框架和语言都会带来一些新的或更简单的解决问题的思路和方式。
而且新的东西不会有什么历史遗留的债务,从这个方面来看,这样做确实成本更低更高效。

但作者在这里表达的并不是说不可以规避问题,人性都是趋利避害的,
肯定会选择成本小收益高的方式。但这样真的能从根本上解决问题吗?

工具、框架和语言一直在源源不断的推陈出新,看起来每一个都是解决问题的好手,
但随着时间的推移,项目总会遇到这样、那样的问题,避无可避。

工具只能解决特定的问题,而真正需要解决的问题是无法被调包的。
所以我们需要的并不只是更新更好的工具,而是需要解决问题的思路。

当给你一组约束条件,并且必须遵循一些规则时,你就会想出一个符合所有约束条件且不违反规则的解决方案。作为程序员,我们通过编写一个程序,一组计算机指令来解决该问题。

编写代码与解决问题不同

任何花时间学习代码的人最终都会有能力进行编程。学习编码就像学习一种新的语言,它是指通过使用计算机能够理解的语言,为计算机创建执行指令的能力。而解决问题则是一种完全不同的技能,我们人类很擅长解决问题,我们通过解决一个又一个问题建立了我们周围的世界。

开发人员需要努力地将这两种技能联系起来。能够解决编程问题会使你更善于解决现实世界的问题,但如果你善于解决现实世界的问题,那么编程对你来说就是自然而然的事情。

为什么这方面的课程不多?

网络上有非常多的课程在教你如何使用工具,如何利用工具去搭建项目,却很少有课程教你如何解决问题。当然,对语言、工具和框架的理解是很重要的,但这并不能使你成为一个善于解决问题的人。仅仅了解如何使用修车工具并不意味着你就能够修车,同时了解解决问题的策略以及解决问题的思维方式才可能会让你能够修好一辆车。

教程的好处是让你接触尽可能多的问题和解决方案,以此来教你如何编程。但开发人员在没有找到问题的答案时往往会迷茫,这也是他们在论坛或社会媒体网站(如 Reddit 和 StackOverflow)发帖的原因。

在互联网普及之前,程序员必须真正为他们所处理的问题找到解决方案。那时,拥有解决问题的策略和思维方式是至关重要的,而现在的开发人员往往会忽视这一技能,因为他们可以直接用谷歌搜索解决方案。互联网的便利性使学习如何编程变得更加容易,你甚至不需要去学校学习,但也使这个行业充斥着许多会编码但没有解决问题思维的开发者。

解决问题时的常见错误

新手开发者经常做的是直接开始编写代码。他们在犯错的过程中不断地适应和修改代码,虽然这并不是一个糟糕的技能,但会绕很多弯路,甚至可能永远无法找到一个解决问题的方案。这种通过试错的蛮力方法也可能是有效的,尤其是对于原型项目和探索性项目来说是很适合的,但无论在任何时候,你最好还是要有一个计划。

这种“暴力破解法”并不是谁的错,现在的人们基本上是通过观看视频教程或博文,直接得到自己需要的确切答案。所以他们在解决问题时,都是在“复制”答案。你自己在解决问题的时候,可能也会做同样的事情。但通常发布教程视频的教员都是具有多年经验和接触过许多问题的人,他们能够很容易带领你找到解决方案,但为了缩短视频的时长,他们往往不会展示找到解决方案的过程。

观看教程不是坏事,我并不是说要阻止你们观看教程或用谷歌寻找解决方案。即使是专业开发人员也会做同样的事情。我想让你明白的是,在你进入编码环节之前,你应该投入一些时间和精力来学习如何解决问题。

到底如何解决问题呢?

解决问题是在求职面试的环节中需要测试的一项技能。当你去参加技术面试时,你可能会被测试某些具体工具的知识(通常是低级或入门级工作)或解决问题的能力(通常是中高级工作职位),这是因为专业的开发人员和工程师经常要面对一些不太具体的问题,这使他们走出了自己的舒适区。

有些解决方案甚至可能需要你创建自己的工具或系统。这就是为什么大的框架、工具和系统会在高层工作和科技公司中诞生。Facebook 推出了 React 和 GraphQL,谷歌开发人员创造了 Angular 和 Dart,微软给了我们 Typescript。这说明,当你在实际参与解决问题时,它可能会带来可以与他人共享的新工具或系统。

每个善于解决问题的人都有自己的一套方法,一个逐步的指南来找到解决方案,这就是你需要熟悉的东西。实际上,你的大脑早已经知道这些,但它找到解决方案的速度太快了,以至于你通常不会想到它是如何做到的。要解决问题,你需要放慢脚步,控制焦虑。

你可以使用一些解决问题的方法比如分析,研究和规划解决方案来解决问题。这需要时间和耐心。因此,让我们分解一下。

清楚了解问题

如果你对问题了解的不清楚,那么问题可能看起来就会很复杂。为了解决问题,首先要清楚地了解问题以及相关细节。在面试过程中,你需要在编写代码之前,仔细了解相关需求。在做自由职业时,你必须清楚地了解客户的要求,然后再提出解决方案。作为一个开发人员,你必须在实施解决方案之前了解任务的要求和验收标准。

如果你能用简单的语言向别人清晰地阐述一个问题,达到他们能够理解的程度,那么才能说明你了解了问题。你可以把你的理解写下来,画草图,做图表等都是很有帮助的。很多时候,其他开发者把我叫到他们的办公桌前,帮助他们解决一些问题,但仅仅通过向我描述问题,他们就想出了解决方案。

💡 作者在这里阐述的了解问题的思路和费曼学习法的思路是一样的,
如果你不能用通俗的语言来解释你了解到的事物,那就说明你还没有真正掌握它。

第二部分就是开发者都熟悉的小黄鸭调试法,把同事当作小黄鸭,
如果你体验过,你就会知道那种豁然开朗的感觉,太奇妙了。

如果你在这个过程中经常卡住,那会很影响对整个问题的理解。你可以尝试使用复述问题、画草图、分解问题等方法,这能迫使你的大脑抓住更多的细节,丰富你对问题的理解。你还可以通过向你提问的人来复述问题,或者对你收集的细节进行确认来验证自己对问题的理解程度。

首先制定计划

💡 凡事预则立,不预则废。

当你掌握了足够多的细节,且清楚地了解问题之后,就应该先制定一个解决问题的计划。这也是分析问题的一部分,你可以通过写注释、伪代码、画图表、流程图、更详细的草图等来完成。计划阶段的目的是要纵观全局,识别出所有的子问题、实施解决方案的步骤和问题的剩余部分。制定一个逐步解决问题的计划,并使用测试数据或假设来验证它。

在解决方案的实施过程中,你的计划可能会发生变化,或者在实施过程中解决方案被完全不同的东西所取代。但不要担心我们会在一个可能放弃的计划上浪费时间,做计划给了你完成整个解决方案的思路和想法,会让你更具有大局观,使以后更容易做出调整。

而且在计划阶段不可能预测到整个事情的结果,这就是为什么计划可能会改变或在你努力实施时就被推倒。但如果没有计划,你就是在掷骰子,希望自己能得到幸运。同时缺乏计划,也就很难让其他人也加入这个方案。

从熟悉的事情做起

如果计划正确,你就能够确定问题的许多部分,最好是先解决你熟悉的东西。这种方法可以帮助你保持解决问题的动力,并可能激发你对如何解决其余问题的新想法。

识别模式

有时侯,类似的问题已经被别人解决了。一个问题甚至可以与其他更简单的问题共享模式。这一步需要你具有良好的研究和经验,从而可以帮助你轻松地识别相似的问题。一旦你确定了这些模式,你其实就已经解决了这个问题。

先从限制最多者开始

如果你要找一个地方和你的同事一起吃午饭,最好的选择是先从有最多饮食限制和规则的人开始。一旦你解决了最多的约束,你就缩小了你的选择范围,使解决其他一切问题变得更加容易。

这就相当于购买新房子。你先列出你所有的限制条件;你能付多少钱,有多少间卧室和浴室,面积,预算,大小,等等。一旦你有了所有的细节,你就会缩小你的选择范围,使你更清楚地看到你能负担得起什么。如果不满意,你需要在哪些方面下工夫来改变你的选择,也会同样明显。

分割问题

有些问题不能用一个方案解决。如果你要解决数独或国际象棋游戏,试图为赢得游戏所需的所有步骤提出计划,这使得寻找解决方案成为一项复杂的任务。你应该总是把问题分成几块,解决所有的几块然后再解决整个问题。

简化问题

简化与划分问题不同。简化意味着改变约束或规则。在技术领域,它通常被称为寻找 MVP 解决方案。你几乎是减少了问题的范围,使问题变得更简单。一旦你有了较简单版本的问题的解决方案,它就为我们提供了足够的知识、经验和研究时间,以便以后解决更大版本的问题。

实验

有些时候,尝试事物和观察结果是解决问题的最好方法,但不能与输入一些代码并希望得到正确结果相混淆。实验是一个受控的过程,旨在探索未知的东西或收集信息。

也许有一个库可以帮助你解决问题,但你并不熟悉。也许你在期待一个结果,但得到的却是完全不同的东西。实验是一种特别好的方法来处理错误修复。一般来说,探索一个问题以尝试更多的了解它,或者测试不同的选项以比较哪个可以成为更好地解决方案,这是一个好方法。

回顾问题

有时,即使你收集了所有的细节,计划,并将问题分块,你也无法找到一个解决方案。如果你觉得自己越来越沮丧,最好是退一步,冷静下来,重新回顾问题。你不应该在影响你情绪的事情上打转。

一旦你感到沮丧,就往往会使事情复杂化,失败或给它选择一个不适合的解决方案。此时出去走走,深呼吸,重新解释这个问题,画出正在发生的事情,检查你对问题的理解。

解决问题应该是一件合乎逻辑的事情,而不是一件情绪化的事情。

练习

解决问题就是要让自己接触尽可能多的情况,并反复练习这些策略。随着时间的推移,它会成为你处理任何问题的第二天性和自然方式。无论大小,总是从计划开始,使用这里提到的其他策略,直到你有信心并准备好编码的解决方案。

总结

掌握解决问题的能力需要时间和实践。当涉及到编程时,理解基础知识、数据结构、算法、设计模式和事物的运作方式是更重要的知识,因为它们可以应用于任何编程语言和环境。

本文的灵感来自 Anton Spraul 的《像程序员一样思考》一书。