iOS 架构设计之冗余性思考

作者 : 董朝

做客户端开发的同学都知道业务需求千变万化,你永远不知道他未来会变化成什么样子。而作为开发人员也绝对不喜欢需求变更。本来说好的,都快做完了,产品经理变卦了:咱们不这样搞了,你看我七十二变。

我们极不情愿跟着这纷繁复杂的变化屁股后面,而疲于奔命。天下没有那么容易偷的懒,若不想应对这变化。就需要走到变化前面。技术往前走一步,领先于业务,而不是被业务赶着屁股走。这就需要冗余性思考,在做业务开发的时候,作为Coder就需要往后思考,业务可能会怎么变,当前程序结构是否足够灵活,方便调整。当业务需求调整的时候,能够以最小的代价来满足。而这就是『冗余性』思考。

针对变化进行设计而不只是针对需求。

这里说是『冗余』,是因为开发测多想了几步,为未来可能的变化也做了铺垫。与一些同学坚持的程序设计满足当前需求,不进行过度设计的理念多少有些不符。而我认为进满足当前需求进行设计是传统软件开发的路数,在互联网软件开发过程中,已经不适用。互联网软件产品,尤其是终端上的,版本迭代频繁,功能更新迅疾。已经很难有稳定的需求存在,变化是其本质特征。于是要求我们在开发的时候,就需要针对这种变化做出一定的设计。

探寻变化的规律

设计机制而不是仅仅满足需求

机制与策略分离

我们首先要说一个观点就是机制与策略分离。我们希望设计的是一整套能够满足上述要求的协议,其次才是实现,最后才是在我们的APP中的具体应用。这也是我这一年来的一个非常重要的总结。并且在逐渐开源出来的一些库中也体现着这个设计。具体说一下,所谓机制即是抽象出来的规则,比如:

f(x)=x^2 x属于R

所谓策略即是在具体场景中的应用,比如当x=2的时候:

f(2)=4 x=2

面对需求,我们需要首先透过表层的的东西,深入一步探讨一下在需求之下在变化的东西。举个例子来讲,我们都有处理过TableView相关的需求。

在版本1.0的时候,产品告诉你我们要做Feed。展示样式吗有目前是这个样子的:

当没有深入思考的时候。我没有区分什么机制那些变化的规律,什么是需求产品的当前的诉求。然后就开始动手了。写UITableViewCell的子类,往上面加Label和UIImageView,一起都是固定的。然后加载数据展示。。。。。

而1.0没发布几天,产品说咱们的Feed展示样式太简单了。现在我们需要支持链接跳转,Coders你们看UI给到的样式是这样的:

于是我们又开始增加新的UITableViewCell的子类。。。。。版本1.1发布了。

后面还有版本1.2,1.3,1.4……终于有一天忍无可忍

这个时候于是就有了重构,我们开始抽离Feed的公共部分放在基类里面,通过类继承等技术手段来控制工程的复杂度,同时提高开发效率。这个时候,我们反问一句:

这些重构的工作是否可以提前到1.0版本的时候开始做呢?

这里当然会有两方观点,

一方认为:代码腐败是伴随着项目推进必然产生的现象,何必实现多花费劳工去提前设计,兵来将挡水来土掩就好了。

另外一方认为:我们坚信事物背后都有一定的规律可以依循,虽然代码腐败是必然现象,但是依旧可以通过一定的技术手段来极大的减速这个过程。 ***

先说一点,这里没有对错之分。要是争对错了,估计又是一场骂战。只能说我更倾向于认同第二种观点。在项目设计初期,我们可以通过去预判功能走向,来进行软件设计。透过表面UI和业务逻辑,去看背后的底层变化逻辑。而这也正是我一直比较看好的:机制与策略分离比较好的应用。

首先透过表层的业务逻辑,去深度思考业务或者功能背后的底层变化规律。真对这些规律设计类库,然后再在当前的业务中选取适当的策略进行应用。再次强调一个理念:

针对变化进行设计而不仅仅是需求

几种真对变化进行设计的Case

说完干巴巴的理念的东西,我们来看一些大家喜闻乐见的具体的应用。

Talk is cheap, show me your code.

增加Client端的动态性或增加CS之间交互性

一般我们认为客户端开发很像是固体,给人的感觉是很硬的,一旦发版之后很多修改将变得异常困难。尤其是iOS的客户端,由于AppStore审核的限制,发版也相对难一点。一个很“硬”的东西,不如一个很“软”的东西更容易应对变化。这种变化有可能是业务需求,有可能是软件的BUG,有可能是系统突发危机…..

金以刚折,水以柔成。 ——晋·葛洪《抱朴子·广譬》

所以为了应对这些变化,需要让我们的Client变得像水:容万物而不折。我们就需要给其相应的能力。这里有两种策略:

增加CS之间交互性,通过Server下发指令来操作客户端

增加Client端的动态性,比如JSPatch,还有OCScript这类的方案。

让客户端变得像是一个可以接受指令的机器人,能够在服务器的配合下,灵活应对各种需求和突发状况。

动态性

先说动态性,这一块是业界一直在探索的东西,远的有Hybird等基于WebView的方案,进的有RubyMotion和ReactNative,还有阿里团队的Wexx,都是非常优秀的尝试。尤其是ReactNative和Wexx,最近一段时间很多团队将其引入到了技术栈,让自己的客户端可以支持动态发版,也就是常说的插件化。

去年下半年滴滴和手Q团队也分别抛出了两个非常惊艳的方案,一个是滴滴的DynamicCocoa,一个是手Q的OCScript。在编译器级别进行修改,通过下发中间语言,在客户端上跑对应语言的虚拟机来解释执行对应的语言。让客户端变得像是一个容器,可以承载下发的业务逻辑。

如果我们单纯的从业务需求的角度去思考,可能这些方案很难会出来。这些方案背后的大神们,也是深入思考之后,把这些机制性的方案设计并构建出来的。把动态性的支持做到如此的深入,也是极牛逼的事情。非常期待他们开源。

交互式客户端

交互式的客户端这种策略,更像是一个缩水版的“动态性”策略。我们在客户端中预置一些能够响应的指令。在需要的时候,通过服务器下发具体的指令,来触发执行客户端对应的逻辑。让客户端能够被动的应对一些业务需求和突发状况。下面我们说几个具体的应用:

命令式缓存

在我们构建了服务器与客户端命令式交互的架构(服务器向客户端发送指令,执行特定的动作)之后,我们做了一些有意思的事情。就比如命令式缓存:服务器向客户端下发文件缓存指令,客户端收到指令后进行文件缓存操作。

这套简单的系统构建起来之后,我们在这上面做了很多有意思的事情。

利用预缓存减小安装包体积

界面开发中往往用到很多的图片资源,尤其是PNG格式的资源,虽然可以进行压缩,但是某些尺寸比较大的背景资源图片还是可能会达到上百KB。这对于一个ipa包来说,也是举足轻重了。

最开始,可能为了包体积,我们牺牲了用户体验。对于这样的图片使用网络资源。这样就很难保证用户跳转到界面的时候,能够第一时间看到正确的背景图片。甚至有些网络差的时候,图片压根就下载不下来。

于是,我们做了这样的设计:

2.1版本将引入A,B,C三张大体积的图片

在2.1版本没有发布之前,就向全量客户端下发预缓存指令:缓存A,B,C三张图片。

然后发布2.1版本,用户升级之后进入对应页面时,将直接从本地缓存中读取对应的图片进行展示。

这样即兼顾了用户体验也减小了包体积。

利用预缓存减少CDN压力

在活动运营的时候,尤其是重大的节假日,一旦活动发布之后的段时间内,用户访问量激增,将会导致CDN过载。大量用户初次访问页面,大量下载文件资源。这个时候,就完全可以在活动之前,下发预缓存指令,来缓解CDN过载的问题。

染色日志上报

在我们客户端支持的指令列表中,日志上报也是一条非常有意思的指令。当客户端发生问题的时候,我们最开始的时候是手足无措。后来有了Crash上报,但也只是能够上报Crash这种极端情况。有很多没有Crash的情况,可能只是用户某个用法不当,或者产品逻辑本身有问题的时候,就比较难以追踪。这个时候我们就需要去查看生产环境上的日志。这个是在服务器开发中是比较常见的策略。

于是我们就构建了这样的一条指令,当指令被触发的时候,会将用户本地打的生产Log使用非对称加密之后上传到制定的地址。这种指令是和用户挂钩的,我们是根据UID针对单一用户下发指令。

然后我就可以,通过管理端拿到对应的Log,解密后查看。来看生产日志中可能存在的问题。

构建可运营的客户端

可运营的概念来自于别人的文章。意思是让自己的客户端能够支撑运营人员的需求,动态的更改展示的内容,甚至是功能点。虽然我们不进行发版,但是可以支持内容的动态替换,支持功能点的动态上线下线。甚至当某些功能上线之后,能够动态的增加其对应的保障性功能(例如日志,数据上报这样的保障性功能)。这里要说一下数据部分。

对于数据上报功能的设计,重点需要考虑扩展性,因为对于数据种类的需求无法一次就确定,往往随着产品的迭代会增加新的数据需求。这时候如果能够通过不修改上报模块,只在目标监控点增加采数据集代码就能完成新增需求,这就说明上报系统已经具有不错的扩展性了。当然这只是在客户端增加了数据上报的基础设施,真正运营的工作还要靠团队建立一个强大的大数据分析平台了,数据上报之后其实只是一堆数字垃圾而已,而真正赋予这些数字垃圾信息化价值的过程是分析。试想作为产品经理的你每天一到公司都会收到关于产品的数据邮件,可以看到新鲜的数据报表,曲折的线图,跃动的燃烧图等,这些都将为你的工作和负责的产品提供非常有价值的支撑,你应该感谢有这么好的运营团队。

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏互扯程序

千万级规模高性能、高并发的网络架构经验分享

现在是资源共享的时代,同样也是知识分享的时代,如果你觉得本文能学到知识,请把知识与别人分享。

1606
来自专栏SDNLAB

Jaguar项目 FAQ

自我们发起JAGUAR(捷豹)这个开源项目以来,受到了很多业界同仁的关注,关于一些常见的问题我们通过这篇文章进行一个比较全面的回答,希望能够解决大家的一些疑惑。...

702
来自专栏Java职业技术分享

千万级规模高性能、高并发的网络架构经验分享

在开始谈我对架构本质的理解之前,先谈谈对今天技术沙龙主题的个人见解,千万级规模的网站感觉数量级是非常大的,对这个数量级我们战略上 要重 视 它 , 战术上又 要...

1100
来自专栏SAP最佳业务实践

SAP最佳业务实践:生产订单拆分-工具生产(236)-1业务概览

image.png 用途 此业务情景将允许您使用按库存生产 (MTS, Make-to-stock) 的生产订单处理来生产工具。工具产品是匿名制造的,并作为...

3476
来自专栏大数据和云计算技术

实用调度工具Airflow

引言 前面写过一篇文章《端午搬砖:聊聊调度云服务》,主要讲云服务的。如果企业也业务上云,可以优先选用这些服务,减少工作量。 而在传统企业内部,数据集成是基础,更...

6326
来自专栏云飞学编程

想学习Python爬虫,但是找不到电子书或者不知道找什么资料

第一部分介绍用Python 编程所必须了解的基本概念,包括matplotlib、NumPy 和Pygal 等强大的Python 库和工具介绍,以及列表、字典、i...

1944
来自专栏Java学习网

为什么开源可以提高程序员的编程技能?

为什么开源可以提高程序员的编程技能? 我已经写了很多年的软件。最近我意识到,我越涉及(致力于,结合于等)开源技术,我写出来的代码就更好。这不由地让我疑惑起来:难...

2759
来自专栏前端小栈

每日阅读2017.5.7-12

git commit message 是什么? 用git的人都知道,但是估计很多人不会在意它,利用好它,对公司,对项目,对你都会有很好的辅助作用:

922
来自专栏JAVA高级架构

架构思想

关于什么是架构,一种比较通俗的说法是 “最高层次的规划,难以改变的决定”,这些规划和决定奠定了事物未来发展的方向和最终的蓝图。 从这个意义上说,人生规划也是一种...

3255
来自专栏大前端开发

前端开发者的智能硬件之路

前一段时间,收到了一个面试邀请的邮件。面试找工作,我是没这个打算的,但是,从这封邮件中,我一眼就看到了一个让我非常感兴趣的字眼,让我对这家公司的产品有了想进一步...

863

扫码关注云+社区