原文: How to Solve the Parking Lot Challenge in JavaScript

你听说过停车场挑战吗? 如果没有,那让我来简单解释一下吧。

停车场挑战要求编写一个管理停车场的 class。

在本教程中,我们将用 JavaScript 来实现这一挑战。为了让停车场更有趣一点,我们将创建一个小小的 React App,用以可视化我们的代码。

让我们开始吧。🎉

挑战要求

为实现挑战,你需要使用 JavaScript 实现一个类。 该类应包含以模拟停车场工作的变量和方法。 下面是详细要求:

  • 我们需要创建具有指定大小(停车位数量)的停车场
  • 我们认为所有车辆都是相同的,所以我们不需要对车辆进行区分
  • 我们的类提供了一种在停车场停放新车的方法
  • 我们的类提供了一种移除已停放车辆的方法
  • 我们的类提供了一种获取停车场大小(车位总数)的方法

停车场挑战解决方案

首先,让我们看一下停车场类的逻辑。

停车场自身的逻辑很简单,所以可能对于你们大多数人来说没有任何难度————特别当你已具备一些 OOP 和基于类的编程经验时。

使用class实现停车场挑战

我将会先展示实现代码,再对于代码实现进行简短的解释。

class ParkingLot {
  slots = [];

  constructor(parkingSize) {
    this.slots = new Array(parkingSize).fill(null);
  }

  park(carId) {
    console.log(`Parking car: ${carId}`);
    if (this.slots.every((slot) => slot !== null)) {
      return false;
    }

    for (let i = 0; i <= this.slots.length; i++) {
      const slot = this.slots[i];

      if (slot === null) {
        this.slots[i] = carId;
        return true;
      }
    }
  }

  remove(carId) {
    console.log(`Leaving car: ${carId}`);
    if (this.slots.every((slot) => slot !== carId)) {
      return false;
    }

    for (let i = 0; i <= this.slots.length; i++) {
      const slot = this.slots[i];

      if (slot === carId) {
        this.slots[i] = null;
        return true;
      }
    }
  }

  getSlots() {
    console.log(`Parking slots: ${this.slots}`);
    return this.slots;
  }

  getSize() {
    console.log(`Parking size is: ${this.slots.length}`);
    return this.slots.length;
  }

  getAvailable() {
    const availableSlots = this.slots.filter((s) => s === null).length;
    console.log(`Available parking slots: ${availableSlots}`);
    return availableSlots;
  }

  isFull() {
    return this.getAvailable() === 0;
  }
}

export default ParkingLot;

让我们从头开始:首先,我们的停车场class有一个slots属性,这个属性是一个数组,用于存储有关停车位的信息(不考虑停车位是否已被占用)。

slots属性之后,我们创建了constructor方法,constructor将在每次创建停车场类的实例时都会执行。在constructor中,我们使用parkingSize的输入数值参数来创建一个长度等于该数字的空数组。

严格来说,这个数组并不是空数组,因为我们用为 null 的值对它进行了初始化。这意味着在构造函数执行结束后,我们将会得到一个填充了 null 值的数组,而数组的具体大小则取决于我们传入构造方法的值。

举个例子,如果我们执行下面的代码:

const parking = new ParkingLot(5);

我们将会得到以下结果:

[null, null, null, null, null] // lenght = 5

instead of [] // 空数组,长度为 0

看完了停车场类的构造函数,我们接下来再来看看剩下的方法。

park()—— 是实现停车功能的方法。该方法遍历了 slots 数组,检查是否有空闲位置(即数组内仍然等于 null 的位置),并在这些空闲位置中添加汽车。

carId代表汽车,我们以它为标识符,表示在某个地点有一辆车。请注意,如果 slots 中没有空闲位置,则停车失败,此方法返回 false,如果停车成功,则返回 true。

getSlots() - 是一个辅助方法,用于返回存储停车位的数组。

remove() – 这个方法实现了从停车场移除汽车的功能,它也可以重置 slots 数组。

💡到目前为止,可能你已经注意到了,几乎在每种情况下,当我们需要操作 slots 数组时,我们都需要遍历整个数组,以便访问数组内的元素。

不同的编程语言提供了不同的数据结构和方式,但它们的主要思想也总是相同的:当你需要对数据做一些操作时,你需要以某种方式遍历它们。

为了将汽车从停车场中移走,我们使用前文中的标识符。我们会在 slots 数组中寻找带有标识符的车辆,在找到对应车辆后。将它“移出”停车场。我们只需将该 slots 数组再次设置为 null 即可执行移除操作。

现在你大概就会明白,为何我们使用 null 来初始化停车场数组。

该方法也会根据是否成功移除车辆返回布尔值。

当创建一些 UI 界面以展示变化,我们需要使用这些方法的返回值。比如,将汽车添加到停车场时(park 方法),也会有相同的返回值机制。

getSize() – 我们用来检查停车场大小的另一个辅助方法。

getAvailable() - 这个方法可以显示我们目前有多少空闲的停车位。

isFull() – 这个方法告诉我们停车场是否已满,即没有更多可用的车位。

如何构建 React App

停车场应用程序 - 主页面
停车场应用程序 - 主页面

有趣的部分快开始啦🕺。

我们将创建一个可交互的 app,将我们已实现的代码停车场可视化。

我们的 app 将提供基本的 UI 控件,允许(假想的)操作员使用停车场软件。为了使视觉效果更令人赏心悦目,我们将尝试为 app 的基本功能设置动画。

一起来看看,是如何实现的吧!📺

Demo

点击在线 demo 链接,提前尝鲜体验吧🥪。

项目源码

这里是停车场 app 的代码仓库

让我来简要介绍一下技术选项的 whatwhy

这个 app 是使用 vite 构建的,因为我最近一直在体验它,并且对于它的速度和性能都十分满意。

尽管 vite 还处于开发阶段,但如果我要开始开发一个新项目,并且可以选择构建工具的话,我会选择vite

这并不是说我反对它的老大哥 CRA。 相反,我已经用 creact react app 搭建了很多 app,并且我仍在我的一些项目中使用它。 这是因为 vite 更快,更符合我当前的诉求。

💡请记住,对于项目的技术选型应该取决于特定项目的特定需求。毕竟,软件开发不存在银弹,这始终是一个需求和优先级的问题。

React App 项目结构

App 项目结构
App 项目结构

停车场 app 的代码结构十分简洁。可以看到,在根目录,我们有两个文件夹 - assetssrcassets 文件夹包含了 app 使用的静态资源(在本例中,它是一张汽车图像)。 src 则包含了 app 的源码文件。

让我们更深入地看看 src 文件夹吧。

src 内部,有下列文件夹:

  • components - 包含 app 中使用的所有 React 组件源码

  • lib - 包含停车场类,负责 app 的主要逻辑

  • pages 包含两个子目录,分别用于 app 的两个页面 – Landing 页面和 Main 页面

  • utils 包含一个辅助方法,用于生成将停车位表示为 busy 时使用的虚构汽车牌照

  • 还有几个文件,其中大部分都与应用程序的入口程序有关。favicon 文件除外,它的作用你应该有所接触,如果之前没有接触过,那么你可以在浏览器的选项卡上看到它😉

带有 favicon 的浏览器选项卡
带有 favicon 的浏览器选项卡

App 页面

如前所述,app 的主页面(main pages,也称为 mian screens)称为Landing页面Main页面。 这些页面由 React 组件构成,它们是欢迎页面所有内容的骨架。在欢迎页中,你可以选择选择最初的登录位置,并且选择停车场的车位数量。

欢迎页
欢迎页

再点击欢迎页的 submit 按钮后,你会跳转到主页面。在主页面,你可以作为操作者对停车场进行管理。

主页
主页

App 的功能

停车场 app 提供了最基础的停车场管理功能。当用户选择了他们想要的车位数量(最多可选择 20 个车位)后,用户将回到主页面,并可以看到所有的空闲停车位。

用户可以通过 PARK! 按钮停放车辆,对应车位将在车辆停泊后显示为繁忙状态并且展示对应车辆的登记号码。操作者可以通过点击繁忙状态的车位来取消车位的停放车辆,即移除停放车辆。

💡红色汽车移动的简单动画只是为了视觉效果,对停车场的运作方式没有任何实际影响。

我使用 CSS modules 为 app 设置样式。为了让移动设备的体验更好,我还优化了移动端的样式。

行动起来,快试一试吧🧪

总结

在写作这篇文章时,我一开始只打算描述停车场类本身——仅仅出于科普目的,展示如何通过 JavaScript 实现一个类。

但后来我觉得,仅仅实现一个类有点无聊🥱。 因为我想创造一些更有趣💃🏻,更游戏化的东西🕹️。

所以我开发出了这款类似迷你游戏的应用程序🎮。

在创建它时,我 5 岁的女儿 🧒🏻 一看到它就想玩玩看,并且玩得特别开心!

或许这对于成年人来说过于幼稚,但是对于小朋友却刚刚好😀。

我希望这个游戏一般的实现方案可以吸引你的注意力,以便你可以更好得记忆它背后的知识📖。

感谢阅读!🙏