vault,uber,com,cn程序在哪儿找有一个验证码,让我输入到优步的程序里,可我找不到这个

* 本文所有图片和技术描述来源于Uber網站公开文档

Uber本身是基于一个简单的概念:点击按钮立即出行,而这个概念已从最初只用于呼叫高端商务车型发展到如今提供一系列多樣化产品每天在数百个城市协调数百万的出行安排。我们需要重新定义移动端架构来适应2017年之后Uber所面临的现实挑战

但是如何开始这个铨新的设计?我们决定就好像重新回到2009年Uber初创的时候什么都没有的时候!我们决定在不受限于现有的庞大代码库以及以前的设计方案前提下,对Rider App(乘客使用的Uber app)进行全新设计并从头开发这便带来大家今日所看到流畅无比的全新Uber App,并且在iOS和Android上使用了同一套移动架构设计下媔我们将详细讲述为何我们要创建这样的一个全新架构模式(我们称为Riblets),以及这样的架构是如何帮助我们达到目的的

尽管将乘客和可即时呼叫的运输工具连接在一起仍然是Uber的根本思路,我们的产品已经进化到某种非常庞大的程度以至于我们以前的移动端架构已经无法哏上其节奏。同时为了实现新功能在这几年中累积的技术负债(tech debt)和开发方面的挑战也越来越多。而很多原始设计并未考虑到的功能如拼车服务、预订服务以及推广活动所用的特殊交通工具类型等也大大增加了复杂性我们的出行模型变得越来越大,越来越复杂也越来樾难以测试。哪怕是进行微小的改动也很容易破坏掉其他部分让实验性尝试也充满着大量潜在的风险,以至于限制了我们未来发展的进喥为了保证Uber的高质量体验,我们需要找到一种方法去找回Uber最初的简洁设计但同时又能满足我们现在和未来发展的需求。

新的App不但要对鼡户简洁同时也要对Uber内部负责开发完善各种功能的工程师们保持简洁。因此我们决定了两大目标:保障基本出行体验的可靠性同时允許在产品的主要方向上进行全新甚至是激进的功能的实验。

从工程角度来说我们努力让Uber上核心出行体验的可靠性达到99.99%,这也就是说我们朂多每年只能有总共一小时的downtime每周最多一分钟,或者说每运行1万次只能失败1次。

要达到这个目的新的架构设计定义并实现了一套包括核心代码和可选代码的框架:

- 核心代码(core code): 要完成一次出行所必需运行的代码,例如注册、呼叫、完成或者取消出行要求对核心代码的任哬修改都必须经过严谨的审核过程

- 可选代码(optional code): 这部分的代码所经审核会少一些,并可以在任何时候动态关闭这就鼓励了代码层面的相互独竝,允许我们尝试新功能并在任何时候停止该功能而不担心影响到基本的乘车体验。

我们需要一个能支持上百个不同团队以及上千工程師快速开发高质量功能的平台并且能在之上不用担心影响到基本功能而创新。所以我们让这个新的架构能够跨平台兼容(架构概念兼容并非代码兼容),ios和android开发人员能在同样的体系下工作

一般来说,要在ios和Android上发布最优秀的产品通常会在架构设计、代码库设计、分析工具等多方面采用完全独立的解决方案然而Uber的新架构致力于在ios/android上使用同样的设计模式和实施手段。这就让我们能最大化从两个平台上所获取的经验从而避免同样的错误在不同平台犯两次。这样ios和android开发人员能更好地合作并同步开发新功能

尽管仍然有很多地方在ios和android上仍然需偠并且应该分别独立实现(例如UI和交互方面的实现),两者在平台体系上保持了一致的思路和设计包括:

要实现不同平台间统一的设计,我们的新移动架构需要在商业逻辑、视图逻辑、数据流以及路径之间具备清晰的组织这样才能达到降低复杂度,简化测试以及最终提高开发效率和可靠性的目的。我们在其他一些架构模式上做了大量创新来实现这样的目标

抱着上述两大目标,我们重新检查了以前的舊代码以便找出哪里可以提高并思索如何才能前进我们最初的代码遵循了MVC模式,同时我们也研究了其他模式特别是VIPER(), 在此基础上我们最終创建了Riblets。其中主要的创新在于路径选择由商业逻辑决定而不是视图逻辑如果你对MVC/VIPER不熟悉,可以先看看这些文章()

以前的Rider app是大约4年湔由几个工程师所开发。尽管在那个时候选用MVC模式是正确的但它在大规模开发时非常难以管理。当我们的开发团队扩大到几百个工程师時我们立刻注意到MVC无法和团队一起成长,问题尤其集中于两个问题:

首先成熟的MVC架构通常面临着大量view controller的问题。在Uber的实践中发出叫车請求的界面RequestViewController,最初只有300行代码在2016年时已超过3000行来处理大量的需求,包括商业逻辑数据处理,数据验证网络逻辑,路径逻辑等等实茬是难以阅读和修改。

其次MVC架构通常是在缺乏测试的同时进行着非常小心翼翼的更新。我们为了提供新功能而不断做着各种试验而这些试验归根到底就是一堆if-else语句。当一个类拥有大量函数时这些if-else语句就互相堆积在一起,使得相互逻辑非常难以理清更不要说进行有效嘚测试。另外当不同view controller之间整合共享的代码越来越多时,对某些部分进行修改就变成了一种非常危险的过程(想象一下修改并测试一大堆楿互嵌套的if-else语句的所有组合)而我们必须要通过实验来验证和添加新功能,所以这种无法扩展的架构是无法适应的

另一种方式:VIPER

当考慮MVC的替代方案时,我们对VIPER产生了极大的兴趣据称可以对iOS app提供极其干净的架构模式。相对于MVCVIPER具备几大优势。首先它提供了更多的抽象層。Presenter包括了将商业逻辑(business logic)和视图逻辑(view logic)联结在一起的Presentation Logic Interactor处理纯粹的数据操作和验证,包括对后端服务的调用来控制状态变化等如登录和请求絀行。最后Router发起状态切换,例如将用户从主界面(Home Screen)带到确认界面其次,Presenter和Interactor都是传统的object可以用简单的unit test测试。

但是我们也找到不少VIPER的缺点它是针对ios专门设计的,因此要做出一些让步才能用于Android另外它是依赖于视图逻辑驱动,换句话说它依赖于Interactor控制状态变化来实施的商业逻輯实际上始终要通过Presenter层从而仍然将商业逻辑暴露在别的层面。最后因为它的视图树和商业逻辑树紧密耦合,要实现一个只有商业逻辑戓者只有视图逻辑的模块会非常困难

尽管VIPER相对于MVC提供了显著的优势,它还是不能完全满足Uber对一个适应大规模开发并具备清晰的模块化平囼的需要所以我们回到设计阶段去研究我们如何才能实现一个架构包含了VIPER的优势,同时弥补它的缺点这样一来我们便设计了Riblets.


在我们新嘚架构模式中,所有的逻辑都划分成小块且可独立测试的模块每个模块只具备一个单一目的和责任。我们把这些模块成为Riblets而整个应用則建立在一个由Riblets构成的树状结构上。

在Riblets的概念上我们把功能任务分配到6个不同组件上以进一步抽象商业和视图逻辑

那么和前文提到的MVC/VIPER比,Riblets有什么具体差别呢首先在Riblets中,路径(Routing)选择由商业逻辑决定而非视图逻辑这也意味着程序是由信息和决策来驱动,而不是在表现层由界媔来驱动(举例来说就不是因为你点了某个按钮就从一个view controller跳到另一个view controller,这是视图交互逻辑驱动;而是因为在商业逻辑上你从一个状态切換到另一个状态例如从叫车状态切换到了出行进行中状态,因此才带来界面的切换)在Uber,并非每个商业逻辑都依赖于用户所看到的视圖(View)和直接把商业逻辑注入ViewController的MVC以及在Presenter层操作程序状态变化的VIPER不同,我们可以更有逻辑地在Riblets中清晰划分商业逻辑我们也把Riblets设计为不区分平囼的模式,以便于Android/iOS可以同步并行

让我们用图2的界面来解释每个组件的功能吧。图二是产品选择界面用于让用户选择uberX/uberBlack/uberPool等不同服务。

Componet获取並初始化一个Riblet的dependency这包括了各种服务(例如获得当前地址的服务,需要的网络服务或数据流以及其它任何需要从其它部分传下来的非Riblet对潒)。在上例中Product selection component获得从其他部分传来的city data stream,把它和合适的网络事件绑定(每个stream包括不同key在product

2. 状态切换逻辑,用于决定多个子节点(child riblets)的状态

* 對对应服务发出请求以启动某种行为例如发出叫车请求

* 决定要转换到哪种状态?比如根节点的Interactor (root interactor)注意到用户的认证token缺失它会发出请求给router偠求转换到欢迎界面

Product Selection Interactor 在初始化时会接受一个city data stream对象,其中数据包括了在该城市提供的服务价格信息,估计旅行时间和车辆类型等等这些信息都会被传送到Presenter中。如果用户点击从uberPool(拼车服务)换到uberX, Interactor就会从Presenter接收到这个信息然后会把相关数据重新发回到View让它可以展现uberX的车辆信息囷对应的搭乘时间。简单说Interactor会执行所有商业逻辑并把结果传送回View.

View创建并更新UI,包括初始化和布局各种UI组件处理用户交互,填充数据和動画等(一般来说View在ios中对应的就是一个ViewController)对于Product Selection,这包括了价格数据和车辆类型从View到Interactor,Presenter负责转换用户交互事件到Interactor中合适的行为例如点擊按钮选择产品等。

logic的Riblets一般只负责一个重要功能也就是把商业逻辑进行划分来驱动程序运行。这种Riblets构成了我们新架构模块化的根本

Riblets是洳何构筑应用程序的

Riblets构成了整个应用程序的树状结构,并经常需要互相通讯以更新状态或者将用户带入到下一阶段在我们讨论他们如何通讯钱,首先来理解Riblets中的数据流动

如果通讯是往下走到某个子节点,该接口一般会定义为一个delegate然后由子节点的Interactor来实现。Delegate一般只是为Riblet之間的同步直接通讯服务比如父节点和直接子节点之间。

特别是对于下行的通讯父节点可以将某个observable data stream暴露给子节点的Interactor。这样父节点的Interactor就可鉯直接把数据传送给子节点而不需要采用Delegate模式。实际上这才是我们建议首选的方式

通过对不同Riblets之间的数据交互做这样的架构设计,我們确保了正确的数据只会在正确时间出现在正确的界面因为Riblets又是基于商业逻辑来构建树状结构的程序,我们能够通过商业逻辑来决定正確的通讯路径(而不是依赖于视图逻辑)这对我们的商业模式来说非常有用,帮助我们达到了代码独立并防止开发过程过度复杂。

当峩们决定重新设计app时我们想要重新集中在依赖于app的可靠性的核心出行体验,以及建立未来开发模式的正确方向创建这个全新的架构对達到这两个目标是非常关键的。

如何提高核心出行体验的可靠性

Riblets对各部分负责的功能有非常清晰的划分,所以测试也是更加简单直接烸个Riblet都可独立测试。当我们提高了测试质量后也就在每次更新时对app的可靠性更有信心。同时因为每个Riblet只负责一个单一的责任这也就非瑺容易把Riblets和它们的依赖部分划分为核心模块和可选模块。然后通过对核心模块做更严格的审核我们便能对核心体验有更大信心。

同时我們也能够对核心流程做全局性的roll back以到达一个确信的工作状态。所有可选代码模块都在功能开关(feature flag)的控制下可以随时关闭开启。在最糟糕凊况下我们可以把所有可选模块全部关闭,只保持最基本的核心流程而因为我们对核心流程代码有极高的质量要求,我们可以确保核惢流程无论在何时都能正常运行

如何建立未来开发模式的正确方向

Riblets让我们极大的明确并分离了功能模块。这种对商业逻辑和视图逻辑的清晰分拆能帮助我们防止代码库的过度膨胀并保持其易于开发因为这个架构是无关具体平台的,ios和android开发者能轻松了解对方的开发情况從而互相学习,避免错误最终能一起推动uber的进步。同时功能试验也更加安全,不用担心影响到核心流程因为可选代码始终和核心代碼保持分离。我们便能通过插件模式开发新功能而不用担心它们会影响到uberX/uberPool的体验。

因为Riblets强化了抽象层和责任分离并清晰定义了数据交互清晰路径,持续开发变得非常轻松这个架构将为我们服务很多年。

我们的新架构为我们的未来铺好了道路这次的重写包括了完全重噺开发的乘客app代码库,重新打造轮子进行用户调查研究,案例分析A/B tests, 以及新功能的开发(例如feeds)。在此之上我们也想同时对全球进行發布让它尽快到达用户手上,所以我们也负责了全世界包括设计、功能实现、本地化、不同设备支持以及测试等多方面工作尽管发布已經结束,但基于新架构之上的工作才刚刚开始

在新架构之上的开发存在着大量的可能性----提高新的feeds体验,把同样架构扩展到司机方面以及UberEATS甚至版本发布系统。实际上我们也花了好几个月时间搭建原型以确定我们做出了正确的选择而现在我们自信我们获得了所需偠的架构,为未来大量的开发建设做好了准备!如果你对这样的工作感兴趣come be part of this story and improve the

这是一个关于Uber优步的故事,一个改變了你我现代出行方式的故事

我要回帖

更多关于 uber怎么用 的文章

 

随机推荐