此前,58 同城的技术委员会执行主席沈剑在 OneAPM 的技术公开课上分享过一个主题,「好的架构不是设计出来的,而是演技出来的」。因为对很多创业公司而言,随着业务的发展,网站流量或者移动端用户都会经历不同的阶段。前期的时候,很难去设计一个百万或者千万级并发的架构,所以后期往往不断对架构进行更新迭代。
当然,在这里也推荐一篇文章《移动开发中的痛点》,本文是 infoQ 举办线下聚会的一次讨论整理,当时参与的讨论者包括唐巧、郭亮、郭虹宇、邓宇光、代码家等国内知名移动开发者。其中就谈及了 MVC 架构的痛点,以及 React Native 所代表的 Web 开发 Native App 技术,相信会对大家有所启发。移动精英开发社群的第8期,也是围绕「架构」这个话题进行讨论。本文系滴滴出行的刘佳达整理。
以下为讨论内容整理:
主持人韩兵:「架构」对开发者而言就是「高大上」,当然也不是每位 coder 都能搞定。对很多菜鸟而言就更陌生了,基本只能停留在理论层次。实践才能出真知,一个好的架构也是这样产生的。本次我们希望结合实际开发中遇到的问题,来聊聊移动端的架构设计。
李文杰:我们的项目划分成了:数据层、UI 层、逻辑层。
上海-李博:我们使用的是 MVVM(Model-View-ViewModel),然后使用了一些 React Native。
韩兵:MVC 这样的模式可以代替咱们移动端架构吗?
攻城狮:MVC、MVVM 只是一种设计模式而已。
Wallace:个人认为, MVC 这种经典结构已经满足不了现在的需求了。
Rory:MVC 这样的模式改进一些,可能会更适合移动端架构。从业务逻辑的角度看,架构应该服从业务逻辑。
王威特-明道-ios:基于团队、服务于团队的代码基础规范包含开发模式,代码规范等等。
Draven-ios:我认为,MVVM + React Native 能让开发变得轻松很多。
李文杰-上海数云-ios:特别希望有一套成熟的架构,UI 和逻辑都写好了。只要写写数据就可以用了。我针对的是超快速的简单需求 APP 开发,就像很多建站技术一样,「拖一拖」就可以完成。
攻城狮:功能模块化也是架构中很重要的一环。
小龙:万变不离其宗,天下武功出一家! 移动端各路设计不也是围绕着 MVC 控制显示数据,然后再做各种拆分吗?
露露:模块与模块之间,应该能快速拆分然后移植到新的项目中吧?
蜂鸟:用 React 基于响应式函数编程是可以简化实现过程,省很多代码。
刘玉娇-小邻小里-ios:问下大家网络层搭建用的是 delegate 还是 block ?我们后来用的,发现好像 delegate 会好一点。
Draven-ios:block 比较灵活。
王宇-陆金所-ios:但是 block 写的太随意了。
Draven-ios:delegate 看起来比较规范。
小龙:block 是方便,但别忘了它是一个结构体。
蜂鸟:昨天碰到一个问题,就是一个 App 有100多个人共同开发,怎么设计架构?才能让100个人协同开发不出问题?
A-困惑:一般用 block。
Icocos:之前有人讨论过,说用 MVP 比较好。
小龙:如果东西多了很重的,delegate 传的时候只是一个指针。
进击的小比克:一部分做网络层,一部分做业务逻辑,一部分做 UI。
王威特-明道-ios:delegate 书写规范,也可以避免循环引用的问题。block 书写流畅,但是会把网络的代码分散在各处,最终维护不一定好。建议在网络业务种的结构内用 delegate,反之block更方便。
露露:如果几个实例用一个 delegate,需要判断。
上海-李博:我觉得,进行工程性的管理,还是比较好。各做各的业务,淘宝就是采用这种模式。相当于每个业务维护一个工程。
潘卫杰:可以使用插座式的模块开发。分成多个小 Team ,负责相对独立的业务功能开发,每个 Team 配一个测试,通过了就提交,随时可以打包出成品。
韩兵:之前也有人说了,架构是不仅仅是为了方便业务,还有一个是为了我们 coder 快速开发。那架构解决了我们项目开发中的什么问题?
露露:现在 block 用的很多,但是怎么才算使用的好呢?
小龙:delegate 是指针,block 是结构体,一个传递的时候轻,但写着麻烦,一个写着方便传递的时候比较重。
潘卫杰:之前我们公司移动端的大项目就是「插座式」开发的,批量出各个行业的 App。
Rory:移动架构设计和选型根据我的经验应该遵守以下几个原则吧:
- 优先保证业务逻辑;
- 架构设计遵循简洁的原则;
- 架构的学习成本不要太高,需要符合当前技术团队内开发人员的能力范围;
- 架构是在开发过程中不断扩充和维护的,代码重构也是架构的核心之一。
刘玉娇:还有个问题,大家 VC 的分层是 base 然后下边分为 baselist 和 baseentity 然后往下继承吗?
上海-李博:我感觉上面说的继承这样下去会越来越痛苦
潘卫杰:前年的时候,行业内有一个说法:各个行业扁平化,去中心化,很多行业老板也都想有自己的电子商务平台。我们之前就给他们解决这个问题,但是我们又不能每家都出一个,就用这种方式出一个很多功能的,各家用的时候在后台自定义组合。
小龙:高内聚低耦合!
Noark9:以前公司做一个 Windows 上应用的觉得思路可以借鉴:我们有一个比较大的,我们称为 shell(壳程序),然后,壳程序提供了接口请求的框架,用户信息的接口,发送通知,以及一些公共的功能。然后,每个小功能,都作为一个动态库接入,壳程序负责加载下载下来的动态库,然后打开到界面上。
王宇-陆金所-ios:具体怎么和业务模块拆分方法呢?
韩兵:服务端都是重架构,一般移动端都偏轻架构。
王威特:是,继承的耦合性太重。用组合的方式更好,前几天的一篇文章就非常推荐阅读。
http://blog.yongfengzhang.com/cn/blog/write-code-that-is-easy-to-delete-not-easy-to/
Rory:理论上,架构不一定要多复杂。但是至少简明的可以区分组件、组件的逻辑、业务逻辑、数据层。让组件的逻辑和业务逻辑分离,减少代码耦合;方法的适量重载和重写;在项目的维护期,抽离项目特有的组件。
蜂鸟:具体到写代码,就是多用接口,定义个协议来实现后使用它。
小龙:面向协议就是看接口定义的怎么样了。可以把各行业一些特性归类为某一类属性,统一处理,最后用的时候换个皮,换套文案就可以。
蜂鸟:API 接口定义的好,说明模块分离,边界定义的很清楚。
小龙:面向协议的变成是不是给 ViewController 暴露的都是 id<某个协议> ?这种形式的,而不需要 import 具体的类?求解答。
你该休息了:Android 架构中热部署大家常采用的多吗 ?
李哲-京东:我重写的时候,就遵循的就是各个模块的拆分。将业务,功能,包括很小的通知名,颜色,缓存路径都搞成了宏定义。将网络,数据,业务,功能都剥离了出来。
王威特:宏定义写起来有些难堪,用 const string 不是更好吗?
韩兵:可以推荐一些架构设计比较好的来源代码
阿尘:个人感觉,要深入移动架构,必须了解企业应用架构。开源的完整 App 应该比较少。为什么要有架构?根本原因还是在于复杂性。头一次看到就五体投地,之前用 NimbusKit 搭架构,现在打算转向 YYKit 。简单的 App 是不需要架构的。
李哲:将一些业务的解决方法,使用模型思想去解决,尤其是那个微博列表的设计。要写易删除,而不易扩展的代码。
阿尘:个人感觉,合理的分层设计很关键。
哈哈:比如微信 SDK,集成了登录、分享、支付三个大的功能,不支持单独拆分使用,如果之前已经使用了友盟、shareSDK 等,只想接入微信支付功能,就会出现冲突问题。
阿尘:架构的构建是一个不断权衡的过程。
有一个道理:架构的构建是一个不断权衡的过程?这个有点不同意。
蜂鸟:我觉得架构之所以需要,就是要保持一个基本的稳定架构,别变来变去的。再变也是细节和模块功能的补充,跳不出已经设定好的架构。
韩兵:好的东西都不是不断改,不断优化的。
小龙:响应式编程思想:不需要考虑调用顺序,只需要知道考虑结果,类似于蝴蝶效应,产生一个事件,会影响很多东西,这些事件像流一样的传播出去,然后影响结果,借用面向对象的一句话,万物皆是流。
而函数式编程思想是把操作尽量写成一系列嵌套的函数或者方法调用。函数式编程特点:每个方法必须有返回值(本身对象),把函数或者 Block 当做参数,block 参数(需要操作的值)block 返回值(操作结果)。
代表:ReactiveCocoa。
李哲-京东:而一个好的架构也总是在业务不断变化中慢慢变好的。
小龙:链式编程思想:是将多个操作(多行代码)通过点号(.)链接在一起成为一句代码,使代码可读性好。a(1).b(2).c(3)
链式编程特点:方法的返回值是 block,block 必须有返回值(本身对象),block 参数(需要操作的值)
代表:masonry 框架。
蜂鸟:如果架构总是变来变去,恰恰证明了架构设计的不合理。
攻城狮:现在好多大公司有个技术平台部,把一些功能抽象出来模块化,这样业务线开发就简单很多,效率也高了。
阿尘:架构是一组决策的组合,做每个决策的过程都是一次取舍和权衡,这样理解吗?我觉得,实践出真知,从0开始打造一个复杂 App 的过程,会让你受益匪浅。
王威特:这回到主持人说的,架构是经验积累出来的,慢慢来。即使做了一个坏架构,以后和别人的对比下就收获颇丰了。
攻城狮:架构不是一下就有的,走过才知道捷径。
蜂鸟:同意你说的决策的取舍平衡,我的看法是这种决策取舍越少越好。好的架构应该能有空间适应这种业务的变化,预见到也许会有的改动,有点理想化了。
王威特:一个长期迭代的 App 做的就是架构和业务,架构能力也是慢慢磨出来的。如果在外包公司工作,更要做好架构,才能快速开发。
攻城狮:有些公司 iOS 页面跳转是统一的一个管理类来管理的,在座各位有这么做的吗?
进击的小比克:现在的项目既按功能模块分,又分业务逻辑、网络层、数据层,分得有点乱啊!和网络服务器框架一样。
高山:比如一个页面,需要 abcd 四个页面才能过来,现在一个 url ,很多前置条件都没有,可能导致错误。这就对架构解耦提出更高的要求。
王威特:最早 ABRouter ,你给 viewController 写个 category 就行了。这里说的是iOS,用 router 实现跳转,一个极大的极大的便利就是以后你的 App 需要实现从 safari 浏览器打开 App ,发现你都搞定了。
韩兵:好像咱们讨论组做服务端的人不多。什么是服务端架构?服务端涉及的东西太多。只有亲自设计过和处理过数量级比较大的系统,才可能会对架构有所体悟。要不然很可能是「纸上谈兵」。
Wallace:其实好的架构,无非是能在你现有的情况下,能解决实际中存在的问题。有些很牛逼的架构不一定适用于现在的具体场景。
韩兵:本身架构包含的知识就很多,不仅仅一门编程语言。
攻城狮:Java 比较成熟,但是提供的架构还有一些并不是很成熟。
柠檬:从头看了下,都是偏于理论,对于架构还是没什么印象,比如说 MVC,这也是最基本的。但是如何做到架构师级别?求指教。
柠檬:最近在整理一些知识,个人感觉,想能为架构师,就是把基本的东西包「打包」在一个项目中。
Wallace:其实写的时间长了,很自然的就能时刻考虑到「高内聚低耦合」的思维了。尤其是,写完一个模块后也可以锤炼自己。
韩兵:设计一个 App,首先应该考虑的就是它的架构。先把结构搭建起来,网络层封装好,第三方控件封装好,然后最快速度支撑起业务层。其实在实际开发中,没有那么多时间成本,还是需要优化一些东西。
丁建龙:模块化,最少的暴露参数和接口。
柠檬:目前所面临的代码质量问题,绝大部分不是技术问题,不是 coding 能力问题,是节奏把控,是质量观念的问题,如何平衡 scope 、quality、schedule,这不是自下而上的变革,是自上而下的思想改变,每一个技术 leader 都需要去权衡把控。
一切质量问题,本质上就是技术 leader 的问题,很多人可能会说,业务需求太多,工作量太大,跑得太快,这些都是事实,事实的背后是,我们技术有没有真正去理解业务,理解产品的思路和想法,理解业务的核心诉求和难点,大家一定可以平衡好这三方面的关系。
韩兵:大家的项目都是怎么搭建起来的?
柠檬:我们是第一版肯定是快速出原形,看用户的功能,两周一个迭代。成型后重构。
Rory:初期:简易架构,实现基础功能,组件;开发过程中:迭代丰富组件,抽离业务,形成模块化。像初期选型,我可以用诺远目前1.0->1.1的版本发展历程举个例子。后期维护扩容,可以用365日历的 Android 客户端举个例子。
韩兵:这个只有实践过,才可能会有一些感悟。说说大家 App 从无到有的构建过程。从这个过程中理解架构的形成和运作。个人觉得从这点入手,会容易理解一些。
柠檬:架构看功力,架构师的路很长,不断的被指责,改进的过程,这是漫长的过程。我现在的想法是把几乎所有项目都用到的东西,整理在一个项目下,以后独立开发可以直接运用。还可以把整个项目底层做一个基类。
Rory:目前互联网行业主要还是敏捷开发,所以初期选型的时候,其实架构细分显得不是很重要,倒是架构上为后期模块化,可扩展性的方面考虑更多。比如,小诺理财 Android 客户端,初期架构只做了网络层,数据解析,activity 基类封装,图片缓存,和一些常用的 UI 组件。
露露:我觉得你可以整合,但是如果用了某一个功能,能快速拆分。
Wallace:我曾经最开始工作的时候,以为把见过的东西都存下来,到时候用上,但其实都没用上。
Rory:整体上业务结构初期都是在 Activity 中处理的,没有做业务逻辑的抽离。从1.0-1.1的过程中,这边的 Android 技术团队进行了一些重构和业务抽离,构建基础。形成目前定制的 UI组件库(目前 UI 组件的逻辑仍然未进行细致抽离)、业务逻辑 control 单元(目前是依据业务模块的不同,抽离不同模块的 control。)这么做,主要的目的是为了后续新加入的技术,可以快速熟悉项目,了解业务。
韩兵:如果这个项目越做越大,咱们就暂定理财项目,是不是要考虑底层框架对现有业务的支持?如果团队越来越大,是不是还要考虑对n多个团队的支持?
Rory:理财和支付一样,对数据安全和通信安全比较重视。
阿尘:横向业务模块化,竖向低耦合,做到单向依赖。
Rory:曾经参与过,365日历 Android 客户端的后期维护。当时客户端项目到了后期,模块划分会越来越多,项目会变得越来越臃肿。这时候在 java 层面和 Android framework 层的优化其实更少。
多数组件这时候都已经很成熟,这时候需要集成性能测试,针对某一点上进行细节优化。架构上也是抽离出更多的库,进行细分支持。365日历其实是典型的 MVC 向 MVP 的过渡。当然,我离职的时候这个过渡还未完成。基本上雏形可以看到,UI 层和业务层已经进行深度划分,通过 control 控制数据传递和通信。
MVP,个人经验更适合移动端,移动端开发的主要难点是两个,业务逻辑和界面效果。
韩兵:所以移动端和服务端考虑的点也是不一样的!
Rory:把 UI 和业务逻辑区分,这样控制单元更有针对性,对原有业务或者UI的修改更有针对性,迭代过程中产生错误理解和错误逻辑导致 bug 的可能性也更小。
可能因为这样抽离后,新技术的加入,理解项目需要更长的时间。代码阅读性会随着项目的臃肿变得越来越差。所以必要的抽离就是保证代码的可读性。
过渡重构举个很简单的例子,一个 View 的 UI 逻辑非要强行适应整个项目所有的 View,创造 n 个构造,传入多个控制参数。那么逻辑理解起来是不是很困难?最简单的判定标准,代码的创造者如果隔一段时间都很难读得懂自己写的代码,算不算一种过度?
简单的总结下,架构应该随着项目发展不断发展,一成不变的架构是行不通的!
国内 ITOM 管理平台 OneAPM 致力于帮助企业用户提供全栈式的性能管理以及 IT 运维管理服务,通过一个探针就能够完成日志分析、安全防护、APM 基础组件监控、集成报警以及大数据分析等功能。想阅读更多优秀文章,请访问 OneAPM 官方技术博客
本文转自 OneAPM 官方博客