发布于 2026-01-06 5 阅读
0

项目前期规划

项目前期规划

为什么

我在几门不同的在线课程中担任导师,我注意到其中很多课程都有一个共同的主题,那就是培训中存在一个奇怪的“差距”。

有些课程是很好的入门选择。它们通常能很好地介绍“Web 101”的内容,例如 HTML、CSS 和 JavaScript 的基础知识,然后还会讲解一些关于 JavaScript 高级数据结构或基本 DOM 操作的内容。之后,它们会深入探讨框架和库之类的东西。

其他一些课程则内容全面,提供了丰富的资源和参考资料,从 HTML、CSS 和 JavaScript 的入门知识讲起,逐步深入到将它们结合起来构建小型项目,最终掌握这些框架,就像上面提到的那样。

但这两种方法虽然在信息量方面都很出色,却都缺少一些我认为至关重要的东西。我希望能够着手解决其中的一些问题。

什么

在我看来,目前缺失的一个重要环节是“开始像开发者一样思考”。我们教授了一些非常有价值的编程技能,而且无论采用哪种方法,技术资源都非常丰富。

但编写代码开发之间是有区别的

在我看来,编程就是坐在键盘前实际编写代码。这就是我们正在教授的内容。它不仅可以教,而且可以复制。我们可以引导大家完成相同的步骤,他们每次都能找到类似的方法。

开发工作略有不同。在我坐在编辑器前,戴上程序员的帽子之前,我应该已经戴上了开发者的帽子。当我拿到需求文档时,我的思绪应该已经在脑海中酝酿、涌现,思考解决问题的最佳策略。

程序员用代码思考,开发者用模式思考。而我觉得,正是我想要花些时间去研究的方向。

如何

作为一名开发人员,我需要考虑很多不同的事情。

  • 我需要时刻关注大局;
  • 我需要了解各个组成部分的运作方式;
  • 我需要不断思考我的项目所处理的数据(状态)

让我们来看一个具体的例子,一个大多数课程都会遇到的例子:用 HTML、CSS 和 JS 构建一个计算器。

一旦程序员看到这种情况,他们很可能会开始规划他们的 HTML,或者思考如何触发按钮事件,或者用代码思考。

一旦开发人员意识到这一点,虽然他们可能在某种程度上考虑代码,但他们更有可能思考构成整体的各个部分以及它们如何相互作用。

好的开发在于规划。上次我谈到了面向对象设计的三个基本原则:封装沟通延迟实例化,优秀的开发者会首先考虑这些方面:

  • 如何将各个部件封装起来,使它们互不干扰?
  • 如何让我的各个部件相互通信,以便每个部件都能做出适当的响应?
  • 如何让我的零件可以重复使用,并根据需要制造更多零件?

开发计算器。

一个简单的计算器

如果我们仔细分析,会发现它主要包含三个部分:

  • 显示器;
  • 键盘;
  • 用来盛放各个部件的容器。

作为一名开发者,我会考虑这些方面。我通常先将所有想法进行概括性的梳理,然后再逐步完善:

* Components of a calculator:
  * Display
    * Upper display showing entire 
      operation until equals?
    * Lower display showing current number
  * Keypad containing different types of keys
    * Numbers
    * Operators
    * Equals (still an operator, but
      special case?)
    * Special Keys 
      (C/AC/Backspace of some sort)
  * Container or Manager
    * Operations Stack
      Not necessarily a displayed
      component, but important.
Enter fullscreen mode Exit fullscreen mode

以上是对计算器各组成部分的概览,实际上,也就仅此而已了。仔细分析一下,其实并不复杂。但如果我们没有任何计划就贸然开始编写代码,很可能会很快陷入困境。

接下来,我们来看一下各个部件。上面的组件列表很好地体现了我们想要的封装方式——显示屏应该是独立的,键盘也应该是独立的,而计算器外壳则应该将它们全部封装起来。

下一步是考虑沟通问题。各个部分如何相互沟通?

这是在规划界面,在我看来,这是开发过程中最有趣的部分之一。我正在创建这个东西,我正在定义我们用来与它交流的“语言”,而且我可以在这里“列出”任何我喜欢的动词!

显示屏

显示屏本身并不复杂:它可能维护着自己的内部状态或显示逻辑,但这并不是我们现在应该考虑的重点。目前,我们应该思考的是,如何与显示屏通信,以及它需要向我们返回什么信息?例如,我们可能希望通过传入一个字符来更新显示屏。或者,我们可能希望显示屏清除部分或全部显示内容。让我们从简单的操作开始:

* Display interface:
  * update(string)
    display the given string
  * reset()
    clear the entire display
  * value
    maybe a way to get back the currently
    displayed value?
Enter fullscreen mode Exit fullscreen mode

我认为这样就可以先这样了。我们并非必须如此,如果以后想修改界面,也可以,但在规划阶段,这样可能比较合适。

接下来我们来看键盘。

键盘

考虑到键盘的通信,其实很简单:我们想知道某个键是否被按下,可能还想知道按键的类型和键值。用 HTML、CSS 和 JS 就能轻松实现这一点,我知道不少人都在考虑事件委托,让键盘容器监听子按键的点击事件……

你说得对。我们可以做到。很容易兴奋起来,也容易想得太远,这完全没问题。这意味着我们可能正在研究一个好的模式,因为这样更容易看清各个组成部分以及如何让它们运转起来!

不过,我们还是放慢速度吧。我们不知道键盘上会有哪些按键,而且,我们可能希望把这些行为封装起来。所以我们再想想:我们想如何与这个键盘交互?我们需要告诉它什么信息吗?键盘又如何回应我们?让我们从已知的信息开始:

* Keypad Interface
  * when a key is clicked, emit a notification.
    Indicate the type and value of the key.
Enter fullscreen mode Exit fullscreen mode

目前来看,接口可能是 JavaScript 事件 API,但我们应该保持开放的心态。

请注意,我们还没有编写任何代码。我们只是定义了组件,并开始讨论它们的通信路径。在容器中,我们的思路可能会有所不同,甚至会开始用代码来思考。

容器。

显示屏和键盘是同级组件。通常情况下,它们互不相干。设计得当的话,每个部分都能独立工作,无需另一个部分参与,但又能实现双向通信。

这一点很重要,因为容器充当着这些较小组件的管理器。它可以处理来自键盘的通信,并将这些通信信息通知显示屏。

管理器是这些组件接口的使用者。它利用已定义的路径来促进通信,并且可能提供自己的接口。目前它不会这样做,但将来可以。

不过,它确实包含了一个其他组件所不具备的组件。显示屏和键盘都没有真正的“状态”,因为它们实际上不需要跟踪任何数据。键盘会传递点击通知,但它不会保存该事件。显示屏会根据指令更新自身,但它很可能不会保存有关已接收数据的信息。

容器不仅管理组件,还管理计算器的状态。在上面的组件列表中,容器下唯一的条目是 `<constructor>` Operations Stack,它代表计算器的内部状态。

但现在与其概述容器提供的接口,不如思考容器如何处理内部通信:

* Container Interface
  * Keypad events:
    * update the Operations Stack as needed,
    * notify the Display as needed.
Enter fullscreen mode Exit fullscreen mode

就是这样——计算器基本上是一个单向应用程序。按下一个键,内部状态更新,然后我们告诉显示屏更新自身。

一些具体的应用场景或用户故事可能包括:

* User clicks '9','4','.','3','+'
  - at each click, the Operations Stack 
    (the state) is updated by appending;
  - and the Display is updated with each digit,
    or cleared when an operator is pressed.
* User then clicks '3','='
  - the Operations Stack should append the
    digit, and then collapse to a single 
    value for the equals;
  - the Display is updated with the final value.  
* User clicks "AC"
  - the Operations Stack should be emptied;
  - the display should be cleared.
Enter fullscreen mode Exit fullscreen mode

这样我们就能看到每个组成部分,以及它们如何封装自身的功能。我们还能看到这些部分如何通信(接口,从而实现交互。通过预先规划,从程序员转变开发,我们可以节省后续的时间和精力。

项目完成后进行重构、回顾和总结是很常见的做法,但同样重要的是,我们要预先对项目进行重构,有目的地进行设计和开发。

下次,我们将开始编写各个组成部分的代码,最终完成一个计算器项目。

文章来源:https://dev.to/parenttobias/pre-planning-your-project-4fd5