当有人制作出开源型软件后,我花了很长时间考虑如何才能使软件技术变得更好。
这是不可避免的事情:人们源源不断的在Stack
Overflow(关于编程问题的网站)请求帮助,Github(翻译网站)问题,涉及Slack(通信软件),在邮件和直接信息方面。幸运的是,你也见证了人们的成功,远超过你的想象力,人们做了许多了不起的事情,并且了解到他们被你帮助了,这是促使你去做它的强大动力。
因此,你会想知道是软件的什么特性造成人们的成功或失败?我怎么才能改善我的软件,怎么给人们更多地权利使他们取得成功?我可以清楚地表达各种指导原则,还是这仅仅是我的直觉,只能适用于个例。(思考和清楚地表达思想,这是两种不同的活动)像Dieter Ram的优秀的设计原则,对软件设计合适吗?
优秀的设计是具有创新性的。
优秀的设计使产品更具实用性。
优秀的设计是倍具审美感的。
优秀的设计使产品更易理解。
优秀的设计是低调的。
优秀的设计是诚实的。
优秀的设计是环境友好型的。
优秀的设计能贯彻到每个细节。
优秀的设计是经久不衰的。
优秀的设计是尽可能不繁琐的。
我过去尝试着谈论整体状况,像最有意思的小问题,确定并使工具上的偏差最小化或利用相关的技术。
即使你可以正确把握全局,这也不能保证你所设计的作品会完全成功。想法能被执行和想法本身关系很大,细节是导致想法不能执行的障碍。
整体状况是重要的——可能比我现在所写的更重要——但我不得不说这些顾全大局的意见有时是不是用的或者很能去应用。更糟的是,十分老套,像格言所道:做事应力求简单,但没有更简单。我们都想让事情变得更简单,但我们不知道为了实现这个目标我们会牺牲什么。
即使你可以正确把握全局,这也不能保证你所设计的作品会完全成功。想法能被执行和想法本身关系很大,细节是导致想法不能执行的障碍。
如果没有可以实际操作的顾全大局的意见,也许就会有更少的实用性建议。Green & Petre的“认知维度”框架定义了一套“探讨工具”来“提高演讲水平”关于“人造信息“的使用,例如代码。
抽象梯度
亲密关系的映射
一致性
传播性
错误倾向性
费力的心理活动
隐形依存度
不成熟的承诺
进步的评价
角色表达
二级符号
粘度
可见度
它不是完美无瑕的,没有框架,构想研究视觉编程环境,有时感觉对某些应用程序是特定的(鉴于可见度,即同时要看到所有的代码,当今有足够小的软件能全部在一个单一的屏幕上可见吗?也许模块化会更好?)我发现很难去处理一些一维或几维的实用性问题。(隐形依存度和角色表达表明了我认为的代码会做一些别的东西,除了他本身所能做的东西)不过,对于思考软件设计的“认知结果”来说,这是一个好的起点。
我不会定义一个通用的框架,但我要分享一些我自己的发现,这是一个好的时间就像去年我在D3 4.0.技术上进行事后理性分析一样。
我不想重新考虑D3的大局设计,我很高兴有像数据概念的加入,规模,布局分离了视觉展示,当然,这里有有意思的研究,但是没有我最近关注的焦点。
我将D3分成不同的模块,使其能够在更多的应用程序上被使用,为容易让他人去扩展,去开发跟多的乐趣,但我也在不断确定和解决API中大量的怪癖与缺陷。有些东西很容易被忽视 ,但我认为就是这些给人们带来真正的痛苦和限制人们做事。
我担心有时候变化是微不足道的,尤其是单独来看时,我希望能让你信服他们不是这样的。我担心,是因为我认为我们(也就是,编写软件的人)往往低估编程接口的实用性,而不考虑更客观的情况且更易测量的品质、功能性、性能及正确性。
这些品质起着很大作用,但可用性差有着确存的成本,问问那些努力破解诗人感到困惑的代码块的人或在他们弄调试程序时把他们拉走,我们需要做到更好的预估软件的可用性,把软件的可用性放在首位。
Mori Masahiro Design
Studio, LLC., CC-BY-3.0
你不能捡起一段代码并且能够感知它的重量或纹理在你的手中。代码是“人工信息”而不是物理和图形类似的东西。你与API的交流是通过操纵文本编辑器或命令行。
然而标准定义就是交互,由于人为因素的复杂度,因此我们应该评估代码,像任何工具,不仅仅是在是否执行计划的任务,还在是否容易能熟练运行,是否能有效和愉悦的被使用,我们应考虑代码功能的可见性,甚至是代码的美学性。它可以被人理解吗?它令人感到沮丧吗?它漂亮吗?
编程交接口是用户界面,用另一种方式说:程序员也是人,在主题为低估人类方面的设计上,又听到了公羊理论说:“对人民及人民所处的现实环境的冷漠是设计中的重要过失。”
这意味着好的文档不会为坏的设计找借口。你可以通过RTFM进行询问,但如果你认为他们已经读过全部东西而且记住了每个细节,那就太傻了。例,对软件的破译能力和调试能力在真正的世界中可能会更重要,形式必须与功能相互交流配合。
通过序言,这里有我对D3关于可用性方面做出的改变。但首先要对D3数据聚合课程感兴趣。
案例1.删除进入及附加键的魔力
D3代表数据驱动文件,数据是你想要的可视化的东西,文件指的是它的可视化表示。它被称作为文件是因为D3是基于网页的标准模型:文档对象模型。一个简单的页面看起来是这样的:
这恰好是一个HTML(超文本标记语言)文档包含一个SVG(可伸缩向量图形)元素,但你并不需要知道每个元素并掌握其概念,只要知道每个元素,例如<文本>…….<文本>一段文本,是一种分离的图形标志。元素被按照等级分组(<svg>包含<g>,其中包含文本,等等)因此你可以定位还可以使每个元素个性化。
一个相应的简单的数据集可能是这样的:
这个数据集是一个字符串(一个字符串是一个字符系类,尽管在这字符串有独立的字符)但数据库有你想要的各种结构如果你可以在JavaScript.(一种程序)中表示它。
因为每一个数组(每个字符串)的数据,我们需要一个文档中相应的<文本>元素,这是数据聚合的目的:一个简单转换文档的方法———添加、删除或修改,使其与数据对应。
数据聚合将数组作为输入的数据和文档元素的数组,并返回到3个选择:
进入选择代表“遗失”的元素(传入的数据),你可能需要创建和添加文档。
更新选择代表现存的元素(永存的数据),你可能需要修改(比如重新定位)。
退出选择代表“遗留”的元素(离开的数据)你可能需要把他们从文件中删除。
作。这就需要富有表现力,例:在进入和退出时使元素更加生动化。
数据聚合并不修改文档本身,它计算输入,更新和退出,然后逐一应用所需的操作。这就需要富有表现力,例:在进入和退出时使元素更加生动化。
你可以想象:数据聚合是你经常用的东西———但首次制造出可视化后,数据在不断变化,对D3总体的无用来说,这个特性的可用性是重要的,它看起来是这样的:
我在掩饰一些细节(像主要函数分配数据给每个元素)但我希望我的主旨被传达了,再加入数据库后,上述代码删除了现存的元素,更新元素并输入了附加元素。
在上述代码中有一个令人讨厌的可用性问题,这个问题我已经注意了,它是一个复制码,设置X属性进入和更新。
十分普遍进行进入和更新元素应用与操作,如果一个元素即将更新,(你不必从头创建它),你可能需要修改它以反映新的数据,这些修改也经常用于“进入”元素,因为他们必修反应新数据。
D3 2.0带来了一个改变来解决这类重复:附加“进入”选择现在将复制进入元素到“更新“选择处。因此,任何操作都会应用到“更新”选择在附加“进入”选择应用于进入和更新元素后,并且重复代码可能被消除:
这使得可用性更糟。
首先,没有任何迹象表明发生了什么(差劲的角色表达,也许是隐形依存度)大多数时候,选择,附加并选择新元素,确实在这是可行的,但也在默默的修改更新选择。
第二,代码取决于操作的顺序,如果操作输入之前更新选择应用。附加,它们仅影响更新点,如果在其后,则会对进入和更新均有影响。数据聚集的目标是消除这种复杂的逻辑,使更多的文档转化不用复杂分支与交互。代码看似简单,但内具有复杂性。
D3 4.0删除了“进入”和“附加”键的魔力。(实际上,D3 4.0删除了“进入”和完全正常选择的区别:现在只有一类选择)在原来的地方,一个新的选择,合并方法使进入和更新选择更统一:
这是消除重复代码而没有腐败行为的常见方法(选择,附加),此外,选择,合并的方法可以指导不熟悉的读者以便查找文档。
准则一 避免过量下载的意义。
我们可以从这次失败中学到什么?D3 3.x违反了公羊原则:好的设计让一个产品可理解。在认知维度上,它有很少的一致性因为在入口选择上表现不同,并且因此使用者不能拓展一般的选择来进入的理解。它的劣性角色表达因为后者表现不明显。并且有一个隐藏的依赖:操作文本选择必须附加后进行运行,尽管代码中的没有东西使这个需求显然。
D3 4.0避免了重复下载。与其添加功能到入口附件—即使一般情况下它是有用的-选择附加总是只在附加元素,如果你想要合并选择,你就需要一套新的方法。因此,是选择,合并。
案例2 删除逐个过度的魔力
过渡是为了使文档有生动化的可选择的界面。不是瞬时改变文档而是在一段时间内顺利插入文档从当前状态到理想目标。
转换可以是以不同方式,有时你想同步转型跨越多个选择。例如:转换一个轴,你必修重新同时定位行与标签。
对于像这个转换的一种说明方法:
(这里X是函数,如线性范围内,计算水平位置每秒的对应数值。)
又一个复制代码,转换线和文本元素是独立创建的,所以我们必须重复时间参数例如时间延迟和持续时间。
一个更加微妙的问题在于不能保证这种转化是同步的,第二种转化是在第一次之后创建的。所以它开始的时间稍晚一些,一毫秒的或两个可能不会看到的东西的差异,但它可能会在其他程序中。
D3 2.8引入了一个新特性,使同步异构能够这样转化:增加了逐个过渡的魔力———一个重复每个所选元素的方法———在创建回收中的新的转换会把握时机从周围过渡,你可能说:
像进入附加,这较差的可用性,它改变了现有的方法(逐个选择然后过渡)没有任何迹象,这种行为已经发生了改变,如果你在既的选择中创建了第二个转变,新的过渡并没有超越旧的过渡,你只能选择旧的过渡。
这个令人遗憾的例子目的是为了教学。另一种,较为清晰的方法(甚至D3 3.x)在选择使用过渡后同步转换:
在这里,过渡t在文档根通过选择它们来应用于行和文本元素。这是个优质的,但有限的方案,过渡仅适用于新的选择,而不是现存的选择。重选是可能发生的,但这是不必要的工作(尤其是对瞬时进入、更新和退出重回数据聚合的选择)。
D3 4.0删除了逐个过渡的魔力,目前在推行逐个选择。相反,选择。过渡的传递,从指定过渡把握时机到新的过渡。目前,当我们创建新选择时,我们可以实现同步化:
或者使用现存的选择时:
这项新设计使选择过渡这个行为备受争论。但一项新方法签名(一个具有相同名称不同论点的方法)是一种常见的设计模式,至少行为中的差异被定位到某个单点或者在调用点都是可行的。
准则二 避免模态行为
这是一条之前准则的扩展,避免重复下载,因为这是更严重的侵犯。这里,D3 2.8引入了矛盾选择。过渡,但行为引发器不是不同的类别。在其内部简单的被称为逐个过渡,这种设计有一个显著的结果是你可以不必编写逐个过渡的程序来改变行为代码。
如果你看到一个设置全球变量引发全球变化行为的代码,这不是件好事。
事后看来,这尤为突出。我在想什么?我是一个是失败的设计师么?坏想法为什么更有吸引力?有一个看似安慰人的理解:未来可以更容易辨识和拒绝他们。在这里,我回想起我曾经试图不采用新方法以减少复杂性,然而这是个明显的例子,引入新方法(或签名)比重复下载现有的东西更简单些。
案例三消除d3 转变(选择)的魔力
在多数现代工程语言中,有一个强有力的概念就是能规定重复使用的代码组的功能。通过某一个功能里的隐藏码,你可以在你任何需要的地方调用它,并且不用使用复制和粘贴。然而一些软件文库用重复使用代码(或者说延伸到表格形式)D3无法知道你是如何封装代码的,所以我建议就只使用一个功能。
既然选择和转换给出许多方法,例如对于设置选择类型和转变类型来说。你可以编写一个既可以在选择下运行也可以在转变下运行的程序,例如:
你可以通过设置选择来即刻设置正文的颜色为红色:
但是你可以在短时间内通过“使它变红”的转变将文件颜色逐渐变为红色。
这种方法被d3的内置部件像轴心,画笔还有变焦摄影所采用。
然而对这种方式的了解,即转变和选择不具备相同的APIs(应用程序接口),因此,并不是所有的代码都是不可知的。一些操作,例如像计算一个数据参与来更新轴心指针的操作,就需要选择功能了。
D3 2.8在这个案例中介绍了另一种被误导的观点:它超越了D3的转变,然后在文档根目录里形成了一个新的转换。如果从选择到D3的转变,则在内部完成了转换。每次回收时,D3 转换将会从成为一个新的转换在特定的选择里,否则它会仅形成这个特定选择(这个特点在 转换中也被添加为同样的错误,每个缺点都在上面讨论过了,当它要运行起来,一定是个特大错误!)
你应该从描述中推断出这个结论是个坏想法,但是让我们进一步从科学层面看,这里有上面“使它变红”的功能的同等的方法使一些编码(用s)受API和其他编码(用t)的限制而不是当文本转换时申请转换API:
转换的每个魔力都在于此:D3转换需要选择转换,其内部又是一个转换元回调。一个新的困惑即D3转换并不按常理出牌,并且关于文章和t是未知的类型这一点是迷惑的——既不是一个选择也不是一个转换——尽管在选择或转换上呼吁使用使变红十分方便。
D3 4.0 撤销了D3 转换。D3转换现在可以被用来在文档根目录里创造一个转换,像D3选择一样,为了从一个转换消除一个选择。向你平常做的一样用JavaScript来核实:运算符或者你更喜欢duck类型(动态语言程序设计风格)。
注意到除了去掉转换的魔力,D3逐个转换,新的makeitred功能阻止了转换,逐个完全转变允许你用D3 4.0的新转换写特定选择的编码,这是一个人为的例子,选择并没有被用到,并且t与语境具有一样的价值,因此它并不能降低到原来的原始定义:
但是那是我的观点,对一些特定选择编码的需求不应该要求一个用转换的完整的改写,每一个!Green&Petre称之为早产的献身。
准则三:支持吝啬
d3转换方法一直改善并且结合了两种操作。第一种是检查你是否在神奇的转换内部,召回,如果你是,第二种就是从一个选择中得到一个新的转换,然而后者可能已经使用选择,转换,所以结果d3转换一直将努力多做并多隐藏。
案例四:利用D3积极活动进行重复转换
D3转换是有限数列,通常来说,一个转换就是一个单级,从现有的文件形式中转换到得到的目标形式。然而,有时你想要一个更精心计划的数列,可以通过几个阶段:
(阶段性刺激的时候要谨慎!Heer & Robertson读取动态转换 .)
有时你甚至可能想要无限重复一个数列,在环形模型中上下起伏:
D3没有专有的无限转换序列的方法,但你可以创造一个新的转换,当一个旧的转换结束了听从转换开始或结束事件,这就是我曾经编写过最困惑编码:
主要有三步!我甚至犹豫想得到一个解释。我很犹豫,甚至尝试解释那些在我们早期案例中的“认知影响”。但是既然已经做到现在了,所以我会尽最大努力。
第一点,转换元援引滑动回调函数都在每个循环中迭代。这个滑动回调函数定义了一种自调用式重复结束,从而获得循环的变数。第一步,重复代表一个环绕因素的选择,转换的第一个阶段是开始运用选择。转换,从周围的转换中获得时机!第二步是创造运用转换,所以转换会在第一阶段结束的时候开始。这第二个阶段是指定重复循环。最后,每两个阶段性的转换数列结束后,会接着重复,重复并从新定义这个循环的转换。
而且,你注意到那个转换元了吗?带有一个参数的转换元确实不同于带有两个参数的转换元
我应该说说第四点了。
吁!
现在让我们比较一下D3 4.0
D3 4.0 介绍了D3的活跃成分,在特定元素中返还了积极转换。这个消除了从每一个循环中捕获局部变量的需求(循环变量),延伸到自我调用结束(重复函数功能)的需求,还有每个转换元的需求。真神奇!
准则四:不明确的解决办法不是解决办法
这是一个有效解决问题的案例,但是它太复杂以至于你不可能发现它并且你永远不会发现它,且永远不会记得它。我就是写了一堆的书也要用一下Google才行。
还有,它很丑。
案例五:在背景冻结时间。
在D3 3.x中,一个无限重复的转换里展示了有趣的行为,如果你长时间离开它并使标签保持开放的状态。我说“有趣”的意思是像这样的:
当这个标签被返回到前景这就会发生,它坚持尝试表现所有你错过的转换。如果一个标签在前景里,每秒规定数百的元素,在背景里则需要几个小时。那可能使数百万个转换!
当然这里没有管理这些转换的意义了:他们被安排在过去并且当他们一开始就结束了。但既然一个无限转换链从不中断它自己时,这些转换就一定要进行下去。
D3 4.0 通过改变时间的定义解决这个问题,转换不仅需要与绝对时间同化,转换主要是通过视野对目标跟踪。因此,当页面在前景的时候。D3 4.0 会在该已知的时间内运转,。当这个页面在背景里或返回到前景里时,它只是简单地运行仿佛什么事都没发生过。
准则五:质疑你的假设
有时一个设计的缺陷可能不会通过增加或改变一个单个的方法而被解决。相反,这里可能有一个潜在的需要再实验的假设-——例如时间是绝对的。
案例六:取消带有选择的转换,打断它
转换是经常由events开始的,例如,新的线上数据和用户互动的到来,因为转换不是瞬间就可以完成的—这需要一段过程—那就可以意味着多重转换就会去掌控到转换元的命运。为了避免这个,转换应有唯一性,允许新的转换代替旧的转换。
然而这样的独特性不应该是全面化,应允许多重同时转换,只要他们管理着不同的转换元。如果你快速的在下面堆放和排列的条码中进行转换,你能通过图表传递声音。
D3的转换是通过系统默认值来达到每个元素的独特性的,如果你需要更大的独特性,这个选择,妨碍被选择元素的积极转换。关于选择的问题,中断D3.3.x 也不能取消决定的被安排在特定的元素的转换。对于这个疏忽没有好的解释——在不同的设计缺陷中有可能会受责备,且他不能外部停止操作。因此,使得取消转换有点困难。(取而代之,先占的转换在开始时就自我结束了。)
D3 3.x建议的变通方案是创造一个中断后完全没有零推迟的转换:
那个是主要起作用的。但是你可以通过安排另一个转换来蒙蔽它。
第一个转变还没有活跃起来,他没有被打断,并且尽管第二次转换在第一次转换后,在随后它被中断之前又开始了第一次的转变。
在D3 4.0中, 选择中断两者中断了积极转变,如果真的这样的话就取消所有安排的转换,取消是更强的抢占:已安排的转换立刻会被摧毁,资源开放,并保证他们不会开始。
准则六:考虑到所有可能使用的模式
异步程序是非常困难的,因为操作的命令是很难预料的。虽然很难执行健全且确定性异步的APIs,当然用这些更加困难。设计者要对“之前的内容细节”负责。
案例七 命名参数
我将以一个简单的例子来结束。D3 4.0为了使代码更具有可读性和自我描述的特点,其包含少部分的语法改进。假如这个文章运用了D3.3.x:
你可能有一些问题:
•价值I意味着什么?
•价值0.3意味着什么?
•除了“elastic-out”,还有哪些简单的类型被支持?
•I可以使一个惯例简单的运行吗?
现在来和D3.4.0来对比一下:
1和0.3的意思现在挺明显清晰了,或者,至少你可以在应用程序界面参考处为弹性释放查找丰富的材料,其中包含这张图片:
此外,这不再有通过转变清除名字的硬编码,清除,这也是一种程序。D3.4.0仍然提供内部清除功能,同样,你也可以操纵你自己习惯的清除功能。
准则七:给出线索
有许多具有争论的程序很明显是不好的设计。人们不应该被期望记住精心制作的程序(我不能告诉你当提及2D时,有多少次我在查找与这个文章相关的元素。)
自开始,D3通过用方法链和单一元素getter-setter方法获得惠命名权利。但是,这仍有空间来提升。如果编码不能完全自我说明,至少它可以在文件中指给你正确的地方。
优质的软件的目的是什么?
它不仅仅可以快速正确计算出结果,并且它很简洁优雅。
人们拥有控制力但认知力有限。很多人为能力相互竞争,比如小孩。最重要的是,人们学习。我希望我展示的程序的例子可以影响人们理解编码,并通过学习做到精通。
但是用给的工具学习达到熟练,如果你可以对你所有学过的东西进行领悟并把它应用于其他领悟,其知识将更具有价值。那就是为什么比如说把D3用标准的文件做目标模型,而不是一个专门的说明。一个专门的说明大概会更有效率,但是将来当工具改变了,你获得专门知识的时间可能会被浪费。我不想你因为D3的利益学习D3。我想要你学习如何研究数据和有效的交流。
优质软件是可接近的,它可以被完全理解。在你没有理解任何事之前你不需要理解每件事。
优质软件是具有一致的,它让你得到你所学到的东西的一面,剩余的靠推断。它不是自我矛盾的,它是简洁不冗杂的。
优质软件可以解释它自己。
它能用来学习和探索,它有自我表现力和不易显露的魔力。
优质的软件可教授我们一些东西,它不仅自动运作一项 Bost显存的任务,而且提供洞察力或传授知识,比如一个好方法或者解决问题的好观点。
优质的软件是为人类。它在认知人类和他们生存的地方。它不期望通过详细描述和制度来被记住。它还可以预测出学习和解决问题方面的需求。
编译自:https://medium.com/@mbostock/what-makes-software-good-943557f8a488#.peifntbke
作者:Mike Bostock 译者:张恩凤 刘春明 校对:wendy