-
Notifications
You must be signed in to change notification settings - Fork 0
/
16750692932878.html
445 lines (324 loc) · 23.2 KB
/
16750692932878.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=0.8,minimum-scale=0.8, maximum-scale=0.8,user-scalable=no,viewport-fit=cover">
<title>
模块化与解耦 - 宋明的博客
</title>
<link href="atom.xml" rel="alternate" title="宋明的博客" type="application/atom+xml">
<link rel="stylesheet" href="asset/css/style.min.css">
<link rel="stylesheet" href="asset/css/doc.css">
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/[email protected]/css/font-awesome.min.css">
<!-- Global site tag (gtag.js) - Google Analytics -->
<!-- 百度分析 -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lib/L2Dwidget.min.js"></script>
<script src="asset/app.js"></script>
</head>
<body style="overflow-x: hidden;">
<section class="hero">
<div class="hero-head">
<nav class="navbar" role="navigation" aria-label="main navigation">
<div class="container">
<div class="navbar-brand">
<a target="self" class="navbar-item " href="index.html">Home</a>
<a target="_self" class="navbar-item " href="archives.html">Archives</a>
<a role="button" id="navbarSNSRssSwitchBtn" class="navbar-burger burger" aria-label="menu" aria-expanded="false" data-target="navbarSNSRssButtons">
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</a>
</div>
<div id="navbarSNSRssButtons" class="navbar-menu">
<div class="navbar-start">
</div>
<div class="navbar-end">
<div class="navbar-item">
<!--buttons start-->
<div class="buttons">
<a href="mailto: [email protected]" target="_blank" title="email">
<span class="icon is-large has-text-grey-darker">
<svg class="svg-inline--fa fa-email fa-w-14 fa-lg" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1208" width="200" height="200"><path fill="currentColor" d="M935.335233 153.62202h-846.666656a84.666666 84.666666 0 0 0-84.666666 84.666666v550.333327a84.666666 84.666666 0 0 0 84.666666 84.666665h846.666656a84.666666 84.666666 0 0 0 84.666666-84.666665v-550.333327a84.666666 84.666666 0 0 0-84.666666-84.666666z m-27.293711 213.952665L557.558216 549.672927a94.993177 94.993177 0 0 1-87.065555 0.197555l-354.612218-182.202664a42.333333 42.333333 0 0 1 38.698311-75.308177l354.606573 182.202664a10.196689 10.196689 0 0 0 9.341556-0.022577l350.477662-182.089776a42.333333 42.333333 0 1 1 39.034155 75.127555z" fill="#2c2c2c" p-id="1209"></path></svg>
</span>
</a>
<a href="atom.xml" target="_blank" title="RSS">
<span class="icon is-large has-text-black-bis">
<svg class="svg-inline--fa fa-rss fa-w-14 fa-lg" aria-hidden="true" focusable="false" data-prefix="fas" data-icon="rss" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" data-fa-i2svg=""><path fill="currentColor" d="M128.081 415.959c0 35.369-28.672 64.041-64.041 64.041S0 451.328 0 415.959s28.672-64.041 64.041-64.041 64.04 28.673 64.04 64.041zm175.66 47.25c-8.354-154.6-132.185-278.587-286.95-286.95C7.656 175.765 0 183.105 0 192.253v48.069c0 8.415 6.49 15.472 14.887 16.018 111.832 7.284 201.473 96.702 208.772 208.772.547 8.397 7.604 14.887 16.018 14.887h48.069c9.149.001 16.489-7.655 15.995-16.79zm144.249.288C439.596 229.677 251.465 40.445 16.503 32.01 7.473 31.686 0 38.981 0 48.016v48.068c0 8.625 6.835 15.645 15.453 15.999 191.179 7.839 344.627 161.316 352.465 352.465.353 8.618 7.373 15.453 15.999 15.453h48.068c9.034-.001 16.329-7.474 16.005-16.504z"></path></svg><!-- <i class="fas fa-rss fa-lg"></i> -->
</span>
</a>
</div>
<!--buttons end-->
</div>
</div>
</div>
</div>
</nav>
</div>
<div class="hero-body ct-body"></div>
</section>
<section class="ct-body">
<div class="container">
<div class="columns is-variable bd-klmn-columns is-4">
<div class="column is-two-thirds">
<div class="post-body single-content">
<div class="card-image">
<figure class="random-img">
</figure>
</div>
<h1 class="title">
模块化与解耦
</h1>
<div class="media">
<figure class="media-left">
<p class="image is-48x48">
<img class="is-rounded" src="">
</p>
</figure>
<div class="media-content">
<div class="content">
<p style="line-height: 30px; font-size: 12px;">
<a href="http://apolla.cc">宋明</a>
<span style="color: #ccc;">|</span>
<span class="date"><i class="fa fa-calendar-check-o" aria-hidden="true"></i> 2023/01/30</span>
<span class="tran-posted-in">posted in</span>
<span class="posted-in"><a href='%E7%BB%84%E4%BB%B6%E5%8C%96.html'><i class="fa fa-folder" aria-hidden="true"></i> 组件化</a></span>
</p>
</div>
</div>
</div>
</div>
<article class="markdown-body single-content">
<h2><a id="%E7%AE%80%E8%BF%B0" class="anchor" aria-hidden="true"><span class="octicon octicon-link"></span></a>简述</h2>
<pre class="line-numbers"><code class="language-plain_text">本文主要讲述了在iOS开发过程中,模块化工程架构的一种组织方式,本文主要讲述基于cocoapods来做模块化的方案,详细讲述了iOS开发怎么进行模块划分的内容,主要会在以下方面做阐述:
○ 为什么要做模块化
○ 模块设计原则
○ 模块化开发有哪些优点和缺点
○ 解耦与通信
</code></pre>
<h2><a id="1%E4%B8%BA%E4%BB%80%E4%B9%88%E8%A6%81%E5%81%9A%E6%A8%A1%E5%9D%97%E5%8C%96%EF%BC%9F" class="anchor" aria-hidden="true"><span class="octicon octicon-link"></span></a>1.为什么要做模块化?</h2>
<p>我们都知道最基本的代码设计原则:“Don’t repeat yourself!”,每一个工程都会有自己的架构,即使你是刚入门的开发者,写几天代码也会发现要把一些常用到的重复代码单独拿出来放在一个叫common的地方,实现代码复用。这样看来每个开发者其实都或多或少的做过架构方面的事情,每个团队至少有1~2个人在做这样的事情。</p>
<p>说道app代码架构,记得Samurai的开发者郭虹宇在群里说过这段精辟的话,引用一下:</p>
<p>一派是说app开发并不需要什么狗P架构,第二派说我们有自己NB的架构,第三派说只要模块化够好,每个模块应该有自己的架构。</p>
<p>这三个观点的出发点,我觉得也比较好理解,第一种应该是一些个人开发者,个人能力很强,经常一个人很快搞出来一个app,他的映像中不需要弄太多的框框框住自己,但是其实他也是有一套自己的架构的。第二派应该是一些公司或者大公司,有一套NB的架构对于团队的意义就比较大了,可以保证稳定迭代,保证规范和持久可维护性。第三派应该是BAT这样的有很多BU的超级公司,或者一些先进的开源开发者们,模块化能够更好的实现跨app的代码和功能的复用, 能够更好的共享资源,避免重复造轮子。</p>
<p>那么为什么要做模块化?已经很明显了,模块化的代码框架最屌,不信,看看苹果的框架怎么做的,你就明白了。</p>
<h2><a id="2%E6%A8%A1%E5%9D%97%E8%AE%BE%E8%AE%A1%E5%8E%9F%E5%88%99" class="anchor" aria-hidden="true"><span class="octicon octicon-link"></span></a>2. 模块设计原则</h2>
<p>既然模块化最屌,那怎么才能做好project的模块化拆分呢,哪些代码应该被放到一个模块?这里分享一些我的经验。</p>
<blockquote>
<p>越底层的模块,应该越稳定,越抽象,越具有高复用度。</p>
</blockquote>
<p>这一点,目测大家应该比较认同,越是底层的SDK,就应该越稳定,稳定的最直观表现就是API很久都不用变化,所有的变化因子不要暴露出来,避免传递给依赖它的模块。但是要做到设计一套API很久都不用改变,那么就需要设计的时候能越抽象, 即需要我们抽象总结的能力。</p>
<p>稳定性 还有一个特点就是会传递,比如 B 模块依赖了 A 模块,如果 B 模块很稳定,但是 A 模块不稳定,那么B模块也会变的不稳定了,因此下一个原则:</p>
<blockquote>
<p>不要让稳定的模块依赖不稳定的模块, 减少依赖</p>
</blockquote>
<p>既然上面说最好不要依赖,但是我发现我的 B 模块的确依赖了 A 模块里面不可或缺的代码怎么办? 假设依赖的代码段为 x , 现在来看x的特性, 如果X是一个可能高复用的代码段,那么无妨把x从 A 模块里面拿出来,单做成一个模块 X, 那么 B 模块依赖 X 模块就好了;灵一种情况,x是一个方法或函数,而且不太适合单做成一个模块,所以那就在B模块里面拷贝一份 x 代码就ok了,因为这样可以保证模块的 稳定性 和 自完备性.</p>
<p>如果上面两种方法都不太合适,我们会在后面解耦里面讲到如何解耦</p>
<blockquote>
<p>提升模块的复用度,自完备性有时候要优于代码复用</p>
</blockquote>
<p>什么是自完备性,就是尽可能的依赖少的模块来达到代码可复用。</p>
<p>举个例子,我有个模块 Utils 里面放了大量的category工具方法等,在日常UI产品开发中,依赖这个Utils会很方便,但是我现在要写一个比较基础的模块,应该就要求复用度更高一些,这个时候需要用到Utils里面的几个方法,那这个时候还适合直接依赖Utils吗,当然不合适了,这与我们上面的设计原则相悖了啊,因此我们这时候为了这个模块的自完备性,就可以重新实现下这几个方法,而不是依赖Utils模块</p>
<blockquote>
<p>每个模块只做好一件事情,不要让Common出现</p>
</blockquote>
<p>模块化结构是让工程结构更清晰,每个模块都只做一件事情,都有自己的一个命名,这样这个模块才能良性发展, 但是这个名字千万不要再叫Common了,试想下你有没有做过这样的事情:“哎呀,这块代码放哪都不太合适,放Common吧”, 日久以后,这个Common就变成了毒瘤,大家都依赖它,还一堆不相关的代码,这个Common模块就是我们设计原则第一点的反面教材: “非常不稳定,大量依赖,全是耦合,整个模块无法复用到其他app”, 所以删掉工程里面的Common吧,再遇到不知道放哪的代码,就要好好思考模块的设计,再不行如果具有可复用性就单建一个模块吧,为什么不可以呢?</p>
<p>按照你架构的层数从上到下依赖,不要出现下层模块依赖上层模块的现象</p>
<p>业务模块之间也尽量不要耦合</p>
<h2><a id="3%E6%A8%A1%E5%9D%97%E5%8C%96%E5%BC%80%E5%8F%91%E6%9C%89%E5%93%AA%E4%BA%9B%E4%BC%98%E7%82%B9%E5%92%8C%E7%BC%BA%E7%82%B9" class="anchor" aria-hidden="true"><span class="octicon octicon-link"></span></a>3. 模块化开发有哪些优点和缺点</h2>
<pre class="line-numbers"><code class="language-plain_text">优点: 1、不只提高了代码的复用度,还可以实现真正的功能复用,比如同样的功能模块如果实现了自完备性,可以在多个app中复用 2、业务隔离,跨团队开发代码控制和版本风险控制的实现 3、模块化对代码的封装性、合理性都有一定的要求,提升开发同学的设计能力。
缺点,模块化当然也有它的缺点: 1、入门门槛较高,新手入门需要的成本也更高 2、工具的使用成本,团队间和模块间的配合成本升高,开发效率短期会降低。
但是从长期的影响来说,带来的好处远大于坏处的,因此模块化仍然是最佳的架构选择。
</code></pre>
<h2><a id="4%E8%A7%A3%E8%80%A6%E4%B8%8E%E9%80%9A%E4%BF%A1" class="anchor" aria-hidden="true"><span class="octicon octicon-link"></span></a>4. 解耦与通信</h2>
<pre class="line-numbers"><code class="language-plain_text">我先说说为什么要解耦吧,模块化并不是说你把工程的代码拆分成 50 个 pod 或者framework就算完事了,要实现模块之间真正的解耦才算真正的模块化,否则如果模块之间还都是互相调用代码,循环依赖,那么和原本放文件夹里面没啥两样。那么什么是模块间的解耦呢?
模块解耦的目标就是, 在基于模块设计原则上, 让模块之间没有循环依赖, 让业务模块之间解除依赖。
</code></pre>
<h3><a id="4-1%E5%85%AC%E5%85%B1%E6%A8%A1%E5%9D%97%E4%B8%8B%E6%B2%89" class="anchor" aria-hidden="true"><span class="octicon octicon-link"></span></a>4.1 公共模块下沉</h3>
<pre class="line-numbers"><code class="language-plain_text">这块其实还是讲的模块设计,一个工程的架构可能会分为很多层,然而在开发的过程中,很容易有人不注意让应该处于较底层的模块依赖了上层的模块,这种情况下应该对模块的设计进行改造实现单向依赖。
比如一个常见的普遍的例子: 一个公共的WebView模块,里面可能有WebViewController的基类,然后还有JSBridge的服务,如果设计的时候没有注意,很容易在开发过程中,这个模块被塞入大量的其他业务代码,依赖了一大堆业务模块,因为经常注册JSBridge服务需要跟业务耦合。
这个时候怎么做呢,首先我们要思考WebView模块的定位,从更全局的角度思考,每个app的架构应该都需要这样一个模块,那么我们完全可以把这个模块单独拎出来下沉为基础模块,这个时候的解耦就需要你对WebView模块做出一些设计,添加一些注册型Api,修改JSBridge的服务为可以通过注册的方式添加逻辑,这样来实现与业务解耦,业务完全可以把与自己业务相关的代码放在自己的模块里面,然后通过你设计的Api注册到WebView模块中。
</code></pre>
<h3><a id="4-2%E9%9D%A2%E5%90%91%E6%8E%A5%E5%8F%A3%E8%B0%83%E7%94%A8" class="anchor" aria-hidden="true"><span class="octicon octicon-link"></span></a>4.2 面向接口调用</h3>
<pre class="line-numbers"><code class="language-plain_text">虽然说公共模块可以通过架构设计来避免耦合业务,但是业务模块之间还是会有耦合的啊,而且这种情况是最多的,比如页面跳转啊,数据传递啊,这些情况前面的方法已经不够用了。那如何解耦不同业务模块之间的代码调用呢?
那就是面向接口调用,我们知道只要直接引用代码,就会有依赖,比如:
</code></pre>
<pre class="line-numbers"><code class="language-plain_text"> // A 模块
- (void)getSomeDataFromB {
B.getSomeData();
}
// B 模块
- (void)getSomeData {
return self.data;
}
</code></pre>
<pre class="line-numbers"><code class="language-plain_text">那么我们可以实现一个 getSomeDataFromB 的接口,让 A 只依赖这个接口,而 B 来实现这个接口,这样就实现了 A 与 B 的解耦。
</code></pre>
<pre class="line-numbers"><code class="language-plain_text"> // 接口
@protocol BService <NSObject>
- (void)getSomeData;
@end
// A 模块, 只依赖接口
- (void)getSomeDataFromB {
id b = findService(@protocol(BService));
b.getSomeData;
}
// B 模块,实现BService接口
@interface B : NSObject <BService>
- (void)getSomeData {
return self.data;
}
@end
</code></pre>
<pre class="line-numbers"><code class="language-plain_text">这样就可以实现了即满足了模块之间调用,也实现了解耦
优点:
1、接口类似代码,可以非常灵活的定义函数和回调等。
缺点:
1、接口定义文件需要放在一个模块以供依赖,但是这个模块不回贡献代码,所以还好。
2、使用较为麻烦,每各调用都需要定义一个service,并实现, 对于一些具有普适性规律的场景不太合适,比如页面统一跳转
</code></pre>
<h3><a id="4-3%E9%9D%A2%E5%90%91%E5%8D%8F%E8%AE%AE%E8%B0%83%E7%94%A8" class="anchor" aria-hidden="true"><span class="octicon octicon-link"></span></a>4.3 面向协议调用</h3>
<pre class="line-numbers"><code class="language-plain_text">面向接口调用的缺点导致并不能满足所有的需求,也解耦的不够彻底,那么终极手段就是通过定义一套协议来实现模块间的通信,协议现成的,那就是URL跳转协议,基本满足需要,简单易上手,基本上现在很多的App架构里面都会有“统一跳转” 这一套东西的,这个不光是对模块解耦有帮助,对于统一化运营都是有极好的帮助的,比如app里面的任何页面,或者任何操作都是通过一个URL来唤起的话,这样是不是就把各个复杂的业务之间解耦了呢,通信都使用URL.
</code></pre>
<h2><a id="5%E6%BA%90%E7%A0%81%E6%8E%A8%E8%8D%90" class="anchor" aria-hidden="true"><span class="octicon octicon-link"></span></a>5. 源码推荐</h2>
<pre class="line-numbers"><code class="language-plain_text">说了这么多,也要放点干货吧,下面给出2个库的介绍,对你模块化的进程希望有帮助。
</code></pre>
<ul>
<li>1、 JLRoutes 是一个URL跳转协议支持的库,跟我想要的简直很契合,强烈推荐。</li>
<li>2、 我自己写的一个解耦框架 AppLord. 简单介绍一下几个概念
<ul>
<li>Module 是负责管理启动模块的工具,可以帮助你把AppDelegate里面一坨初始化的代码分别放到不同的module里面去</li>
<li>Service 就是对上面 4.2 中面向接口解耦方式的一种封装</li>
<li>Task, 全局的后台任务管理器,有时候一些不知道放哪的任务执行可以塞进去</li>
</ul>
</li>
</ul>
</article>
<div class="comments-wrap">
<div class="share-comments">
<script src="https://utteranc.es/client.js"
repo="Apolla/gtalk"
issue-term="title"
theme="github-dark"
crossorigin="anonymous"
id="github-comment"
async>
</script>
</div>
</div><!-- end comments wrap -->
</div>
<div class="column">
<div class="card">
<header class="card-header">
<p class="card-header-title">
<i class="fa fa-commenting" aria-hidden="true"></i>
<span class="tran-notice">Notice</span>
</p>
</header>
<div class="card-content site-notice">
<div class="content">
</div>
</div>
</div>
<div class="card">
<header class="card-header">
<p class="card-header-title">
<i class="fa fa-folder-open" aria-hidden="true"></i>
<span class="tran-site-categories">Categories</span>
</p>
</header>
<div class="card-content site-categories">
<div class="content">
<ul>
<li><a href="%E7%BB%84%E4%BB%B6%E5%8C%96.html">组件化</a>
</li>
<li><a href="%E7%A2%8E%E7%89%87%E8%8A%9D%E5%A3%AB%E6%94%B6%E8%97%8F.html">碎片芝士收藏</a>
</li>
<li><a href="%E7%9B%B4%E6%92%AD.html">直播</a>
</li>
<li><a href="coreBluetooth.html">coreBluetooth</a>
</li>
<li><a href="%E4%B8%80%E9%98%85%E9%98%85%E8%AF%BB.html">一阅阅读</a>
</li>
<li><a href="SwiftUI.html">SwiftUI</a>
</li>
<li><a href="%E8%91%B5%E8%8A%B1%E5%AE%9D%E5%85%B8.html">葵花宝典</a>
</li>
</ul>
</div>
</div>
</div>
<div class="card">
<header class="card-header">
<p class="card-header-title">
<i class="fa fa-tags" aria-hidden="true"></i>
<span class="tran-site-tags">Tags</span>
</p>
</header>
<div class="card-content site-tags">
<div class="content">
<div class="tags">
</div>
</div>
</div>
</div>
</div>
</div><!-- end columns -->
</div><!-- end container -->
</section>
<footer class="footer">
<div id="plt"></div>
<div class="content has-text-centered">
<p>
Copyright © 2019
<span id="tran-author" class="tran-author">Author: </span><a target="_blank" href="http://apolla.cc">宋明</a>,
<span class="tran-theme">Theme: </span><a target="_blank" href="https://github.com/AlanAlbert/atheme">Atheme</a> (Based on BulmaCSS).
</p>
</div>
</footer>
<script src="asset/prism.js"></script>
<script type="text/javascript">
var imgApi = "https://source.unsplash.com/random/1024x";
var imgContainers = document.getElementsByClassName('random-img');
for (var i = 0; i <= imgContainers.length - 1; i++) {
// https://picsum.photos/1024/
var img = document.createElement('img');
img.src = imgApi + (400 + i);
imgContainers[i].appendChild(img);
}
</script>
<script type="text/javascript">
var modelJson = "asset/plt/model.json";
var pluginRootPath = 'asset/plt';
var pluginModelPath = 'asset/plt';
var config = {
pluginRootPath: pluginRootPath,
pluginJsPath: "lib/",
pluginModelPath: pluginModelPath,
tagMode:false,
debug:false,
model: {
jsonPath: modelJson, // xxx.model.json 的路径
},
display: {
width: 325, // canvas的宽度
height: 300, // canvas的高度
position: 'right', // 显示位置:左或右
hOffset: -75, // canvas水平偏移
vOffset: 0, // canvas垂直偏移
},
dialog:{
enable: true
},
mobile: {
show: false, // 是否在移动设备上显示
},
react: {
opacity: 1, // 透明度
},
log: false,
};
L2Dwidget.init(config);
</script>
</body>
</html>