这一部分就更重要了。项目术语这个部分必须要尽可能的把设计中涉及到的:
-
新引用的SDK/框架
-
项目之前没用过的语言
-
项目/公司内部工具,服务
-
产品本身的组件Component.
都过一遍,尤其是对一些刚刚进组的朋友,这对他们会有很大的帮助。很多刚刚入职的朋友初来乍到,可能也不太敢在研讨会上问问题,阅读没有项目术语的文档对他们可以说是直接劝退的。
作为一个往高级工程师方向努力的朋友们,扩大自己在组内影响力也是一个至关重要的点,如果你的设计文档可以对初级工程师/刚刚进组的朋友更友好,那么你已经成功了一半了。
很多在组里面待了很久的老鸟会懒得在产品本身的组件Component 解释太多,因为他们想当然的会觉得这是一个他自己每天都接触的组件没有必要解释。这其实是不太好的,因为你的文档不是给自己看的,而是给其他组员,甚至老板(老板很多情况下是不了解产品的技术细节的)。
比如你在新的项目中打算使用GraphQL这个查询语言和相应的框架。那么最好的做法是先在术语环节介绍一下:
- GraphQL -> 是一种针对图状数据进行查询特别有优势的查询语言
- GraphQL Query-> 一种类似于HTTP GET的GraphQL 请求,用来查询后端数据
- GraphQL Mutation-> 一种类似于HTTP POST 的GraphQL请求,用来修改后端数据
- GraphQL Subscription-> 一种建立在客户端和后端之间的长链接,用来监听后端数据变化请求,大部分GraphQL框架用websocket来实现
有了这上面的介绍,相信你在接下来设计细节说到Query/Mutation的时候就不会有人懵逼了。
技术挑战
这个环节就比较简单了,把该项目的技术难点都列举出来,但是有一个问题要切记:
不要贴源码!不要贴源码!不要贴源码!
很多朋友,包括在写博客的时候都是一言不合直接复制粘贴源码,这样的做法是非常让人讨厌的,说白了就是偷懒,连精炼一下源码,哪怕做一份伪代码加comment的功夫都不肯下。还是那句话,文档是写给别人看的,不是写给自己的。
这里我用KunMinX大哥的博客里面的伪代码做正面 例子,大家如果看到这一份安卓事件分发的源代码:
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
}
// If the event targets the accessibility focused view and this is it, start
// normal event dispatch. Maybe a descendant is what will handle the click.
if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) {
ev.setTargetAccessibilityFocus(false);
}
boolean handled = false;
if (onFilterTouchEventForSecurity(ev)) {
final int action = ev.getAction();
final int actionMasked = action & MotionEvent.ACTION_MASK;
// Handle an initial down.
if (actionMasked == MotionEvent.ACTION_DOWN) {
// Throw away all previous state when starting a new touch gesture.
// The framework may have dropped the up or cancel event for the previous gesture
// due to an app switch, ANR, or some other state change.
cancelAndClearTouchTargets(ev);
resetTouchState();
}
// Check for interception.
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
} else {
// There are no touch targets and this action is not an initial down
// so this view group continues to intercept touches.
intercepted = true;
}
// If intercepted, start normal event dispatch. Also if there is already
// a view that is handling the gesture, do normal event dispatch.
if (intercepted || mFirstTouchTarget != null) {
ev.setTargetAccessibilityFocus(false);
}
是不是瞬间没有任何兴趣了?请注意我还没有全部都复制上来,就只是一小段而已。
再看KunMinX大哥的精简版伪代码:
https://juejin.im/post/5d3140c951882565dd5a66ef
是不是瞬间就豁然开朗了。
写文档也是这样,如果我是审查者看到上来就贴项目内部的源代码的设计文档,对不起,我会直接打零分,贴源代码的设计文档,像极了初中的时候为了凑八百字而生硬的加排比句的作文一样,看起来很丰满,其实都是骨头没有肉。
当你自己想钻研技术的时候,一行行的研究源码是没毛病的,但是如果你是要分享给他人的时候,千万别直接复制粘贴。
完成要求
这个环节也不多做介绍,这是和公司/产品内部的需求有关系。比如你做结构的修改,做完之后是否会影响到原有的开发流程,如果有,是否会严重的影响,这些都是需要列出来的。
现有架构和建议架构
一提到架构,很多人都会觉得很虚,感觉无从下手。
其实在这方面,流程图和组件通信图都是很好的帮手。有时候可能自己会觉得无从写起,但是其实只要把流程图/组件通信图一画,其实就豁然开朗了。
这里我以我司的一个最近刚刚开源的移动开发框架 Amplify(aws.amazon.com/cn/amplify/)为例。
假如我在我的最新设计中提议使用这么一个新的框架,那么首先我得阐明这个框架是做什么的(PS:这是我自己总结的,和公司文案无关):
Amplify Mobile sdk 给客户端提供了一套离线应用解决方案,它包括了离线存储,和服务端数据增量更新,还有身份验证,日志发送等等移动端所需的功能。该框架以GraphQL语言为基础,通过WebSocket保持和服务器端的实时连接,还有基于时间戳的增量/全量更新保持客户端和服务端的数据一致。
好了,那你们组的高级工程师可能会问,那这个Amplify Mobile SDK内部是大概怎么实现离线还有和服务器端数据一致性的呢?
这个时候组件通信图就派上用场了。话不多说,先上图 (这里我们用 arcentry.com/app/ 来做示范,这个工具提供了很多AWS相关的服务组件图,比较好上手)。
同时,让我来以一个设计者的角度来说明这个架构图大概内容:
- 在Amplify Android SDK的架构设计上,每当用户在客户端进行数据操作(CRUD)的时候,Amplify都会通过Data 组件把用户本地的数据先进行修改(Model DataBase),在修改数据的同时,会把每一次CRUD操作进行序列化,存储在另一个Mutation数据库里面。
- Amplify Android SDK的Engine组件通过Observer模式,注册了一个数据源变化的观察者,如果有新的Mutation,Engine就会从Mutation数据库将Mutation取出并发送到API组件,API组件再将其封装成一个GraphQL的Mutation 请求发送至后端
- 图中的左边的区域为客户端,右边为后端
有了组件通信图,描述架构就变成了看图说话,小学四年级咱就学过了,非常轻松!
从以上的图和描述中,我们队友们就应该知道,数据存储在Sqlite 数据库内,同时保存了数据本身和对数据操作的序列化对象,并且他们也会有更多的问题,比如说
-
既然有Model数据库,我们怎么定义客户端的model,model长啥样,是Amplify有工具自动生成,还是必须我们手写?
-
既然是先写入数据库再和服务端更新,万一网络连接暂时不可用怎么办?Amplify怎么处理数据不一致?
这些都是文档阅读者在阅读完你写的简明易懂的架构简介之后会问的问题,是一个顺其自然的事情,当他们问到这里的时候,你应该感到高兴而不是紧张害怕,因为这说明大家把你的文档读进去了,而不是敷衍和不耐烦。能让阅读者和作者产生互动的技术文档,是好文档!
有了架构图,再加入一个流程图,就更棒了。
这里我会用 www.plantuml.com/ 作为师范工具来构建流程图。
还是以Amplify为例子。既然我们决定使用Amplify了,那使用Amplify前后我们的代码和架构会发生很大的变化么?
假如我们的产品是一款点餐的软件,我们的Model(数据模型)是一道一道的菜,同时菜本身可以修改相应的元数据,比如辣的程度,是否加入了配菜等等。每当我们把菜加入到购物车的时候,不同设备同一账号的软件的购物车应该出现相同的菜品。
在使用Amplify之前,我们都是手动存入自己设置好的数据库,然后马上发送给服务端,来更新购物车的。
在使用Amplify之后,我们不需要存进自己的数据库了,而是直接面向Amplify的Model编程
如果大家觉得对比还明显,咱再来一个一刀切式的对比,把两幅图放在一起,再使用中间切割的方式:
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy8xNjU5NTAzMS0yNjNhNWE5NDk1Mzk3NjYw?x-oss-process=imag
e/format,png)
通过上面这个对比图,阅读者可以很清晰的看到,在现有的设计中,我们完全没有修改Adapter和View之间的通信方式和流程顺序,仅仅是修改了Adapter和数据源的操作,从原来的Adapter修改本地数据库和发送网络请求两手一把抓,变成了现在仅需向Amplify SDK修改模型Model数据。