自定义组成要素:样式指南

多年来,Blockly 和 Blockly Games 团队积累了许多经验,这些经验适用于开发新块的开发者。下面列出了我们常犯的错误或其他人常犯的错误。

这些是我们使用 Blockly 的视觉样式学到的一般性经验,可能并不适用于所有用例或设计。还有其他解决方案。但此列表并未详尽列出用户可能会遇到的问题以及如何避免这些问题。每种情况都略有不同,并且各有利弊。

1. 条件语句与循环

对新用户来说,最难实现的是条件语句和循环。许多基于方块的环境会将这两种块归入同一个“控件”类别,其中两种块的形状和颜色相同。 这往往会导致用户感到沮丧,因为新用户会混淆这两个块。Blockly 建议将条件和循环移至单独的“Logic”和“Loops”类别,并为每个类别指定不同的颜色。这清楚地表明,虽然形状类似,但这些截然不同的概念却表现出不同的行为。

建议:将条件语句和循环分开。

2. 基于 1 的列表

初级程序员在第一次遇到基于 0 的列表时反应不佳。因此,Blockly 遵循 Lua 和 Lambda Moo 的先行原则,将列表和字符串索引从 1 开始。

对于 Blockly 的更高级用途,支持基于零的列表,以便更轻松地转换为文本。对于年龄较小的受众群体或新手受众群体,仍建议按 1 开头编入索引。

建议:第一个数字是第一个数字。

3. 用户输入

从用户处获取参数的方式有三种。下拉菜单的限制性最强,适用于简单的教程和练习。输入字段具有更大的自由度,适合进行更具创造性的活动。值块输入(通常使用影子块)提供计算值(例如随机生成器)的机会,而不仅仅是一个静态值。

建议:选择适合您用户的输入法。

4. 实时屏蔽图片

块文档应包含所引用块的图片。截取屏幕截图非常简单。但是,如果有 50 张此类图片,而应用被翻译成 50 种语言,那么突然可以保持 2500 张静态图片。然后,配色方案发生变化,还有 2,500 张图像需要更新。

为了摆脱这种维护工作的麻烦,Blockly Games 将所有屏幕截图都替换成了以只读模式运行的 Blockly 实例。结果看起来与照片相同,但保证是最新的。只读模式实现了国际化。

建议:如果支持多种语言,请使用只读模式。

5. 您的另一个

美国儿童(但有趣的是并非其他国家/地区)的反馈表明,左右两地之间存在严重的混淆。添加箭头解决了此问题。如果方向是相对于头像的,则箭头的样式很重要。当头像朝向相反的方向时,→ 直箭头或 ↱ 转向箭头会令人感到困惑。最有帮助的方法是 ⟳ 圆形箭头,即使转角小于箭头所指示的角度也是如此。

建议:尽可能在文本中补充 Unicode 图标。

6. 概要屏蔽

应尽可能采用更高级别的方法,即使这会降低执行性能或灵活性。请参考以下 Apps 脚本表达式:

SpreadsheetApp.getActiveSheet().getDataRange().getValues()

在保留所有潜在功能的 1:1 映射下,将使用四个块构建上述表达式。但 Blockly 的目标是更高级别,并提供一个封装整个表达式的块。目标是针对 95% 的情况进行优化,即使这使得剩余 5% 的情况更加困难。Blockly 并不是要取代基于文本的语言,而是旨在帮助用户快速上手,从而能够使用基于文本的语言。

建议:不要盲目地将整个 API 转换为块。

7. 可选返回值

基于文本的编程中的许多函数都会执行操作,然后返回一个值。该返回值不一定使用。例如,堆栈的 pop() 函数。可能会调用 Pop 来获取和移除最后一个元素,或者调用它仅移除返回值被忽略的最后一个元素。

var last = stack.pop();  // Get and remove last element.
stack.pop();  // Just remove last element.

基于块的语言通常不擅长忽略返回值。值块必须插入接受值的地方。有几种策略可以解决这个问题。

a) 针对问题进行引导。大多数基于块的语言在设计语言时都避免了这些情况。例如,Scratch 没有任何同时具有附带效应和返回值的块。

b) 提供两个版块。如果工具箱中的空间不构成问题,一种简单的解决方案是为每个此类块提供两个,一个有返回值,另一个不带返回值。其缺点是,这可能会导致包含大量几乎完全相同的块令人困惑的工具箱。

c) 更改一个块。使用下拉菜单、复选框或其他控件,让用户选择是否具有返回值。然后,该代码块会根据其选项改变形状。您可在 Blockly 的列表访问块中查看相关示例。

d) 吃掉值。App Inventor 的第一版创建了一个特殊的竖线块,以吃掉任何连接的值。用户不理解这个概念,第二版 App Inventor 移除了管道块,改为建议用户直接将值赋值给一次性变量。

建议:每种策略都有其优缺点,请选择适合您的用户的策略。

8. 生长积木

某些代码块可能需要不同数量的输入。例如,可对任意一组数字求和的加法块、具有任意一组 elseif 子句的 if/elseif/else 块,或具有任意数量的初始化元素的列表构造函数。我们提供了多种策略,每种策略都有自己的优缺点。

a) 最简单的方法是让用户使用较小的分块组成分块。例如,通过嵌套两个二数加法块来将三个数相加。另一个示例是仅提供 if/else 块,并让用户嵌套这些块以创建 elseif 条件。

这种方法的优势在于,它最初很简单(无论是对用户还是对开发者)。其缺点是,如果存在大量嵌套,代码就会变得非常繁琐,难以阅读和维护。

b) 另一种方法是动态扩展块,以便末尾始终有一个空闲输入。同样,如果最后有两个自由输入,则代码块会删除最后的输入。这是 App Inventor 的第一个版本使用的方法。

出于多种原因,App Inventor 的用户不喜欢自动增长的块。首先,总是有自由输入,而程序永远不会“完成”。其次,在堆栈中间插入元素会令人不快,因为这涉及断开修改下方所有元素的连接,然后重新连接这些元素。不过,如果顺序不重要,并且用户可以接受程序中的漏洞,这是非常方便的选择。

c) 为了解决此漏洞问题,有些开发者会向块添加 +/- 按钮,以便手动添加或移除输入。Open Roberta 使用两个此类按钮在底部添加或移除输入。其他开发者会在每行添加两个按钮,以便可以适应在堆栈中间进行的插入和删除操作。还有一些 API 会在每行添加两个向上/向下按钮,以便调整堆栈的重新排序。

此策略包含一系列选项,从每个块两个按钮,到每行四个按钮,不一而足。一方面是存在用户无法执行所需操作的风险,另一端是界面上满是按钮,看起来就像是星际飞船企业的桥梁。

d) 最灵活的方法是向块添加更改器气泡。该对象表示为一个按钮,用于打开该块的配置对话框。你可以随意添加、删除或重新排列元素。

这种方法的缺点是,更改器对于新手用户来说并不直观。引入赋值函数需要某种形式的指令。针对年幼儿童的基于块的应用不应使用更改器。不过,一旦掌握了这些信息,它们对高级用户来说非常宝贵。

建议:每种策略都有其优缺点,请选择适合您的用户的策略。

9. 简洁代码生成

高级 Blockly 用户应该能够查看生成的代码(JavaScript、Python、PHP、Lua、Dart 等),并立即识别他们编写的程序。这意味着需要采取额外的措施来确保此机器生成的代码可读。多余的括号、数字变量、压缩的空格和详细的代码模板都会妨碍生成精巧的代码。生成的代码应包含注释,并且应符合 Google 的样式指南

建议:以生成的代码为中心。向用户显示。

10. 语言依赖性

使用干净代码有个副作用是,Blockly 的行为在很大程度上取决于交叉编译语言的行为方式。最常见的输出语言是 JavaScript,但如果 Blockly 需要交叉编译为其他语言,则不应进行不合理的尝试,以保持这两种语言的确切行为。例如,在 JavaScript 中,空字符串为 false,而在 Lua 中则为 true。定义一种行为模式,让 Blockly 的代码无论采用哪种目标语言都能执行,会导致代码看起来像是来自 GWT 编译器的不可维护。

建议:Blockly 不是一种语言,请允许现有语言影响行为。