-
Notifications
You must be signed in to change notification settings - Fork 0
/
读-Design-Patterns-by-Tutorials-笔记-MVC.html
344 lines (277 loc) · 21.7 KB
/
读-Design-Patterns-by-Tutorials-笔记-MVC.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
<!DOCTYPE html>
<html>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/gitalk@1/dist/gitalk.css">
<script src="https://cdn.jsdelivr.net/npm/gitalk@1/dist/gitalk.min.js"></script>
<head>
<!-- Document Settings -->
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<!-- Page Meta -->
<title>读《Design Patterns by Tutorials》笔记——MVC</title>
<meta name="description" content="Freelf's Blog" />
<!-- Mobile Meta -->
<meta name="HandheldFriendly" content="True" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- Brand icon -->
<link rel="shortcut icon" href="/assets/images/favicon.ico" >
<!-- Styles'n'Scripts -->
<link rel="stylesheet" type="text/css" href="/assets/css/screen.css" />
<link rel="stylesheet" type="text/css" href="//fonts.googleapis.com/css?family=Merriweather:300,700,700italic,300italic|Open+Sans:700,400" />
<link rel="stylesheet" type="text/css" href="/assets/css/syntax.css" />
<!-- highlight.js -->
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.3.0/styles/default.min.css">
<style>.hljs { background: none; }</style>
<!-- Ghost outputs important style and meta data with this tag -->
<link rel="canonical" href="//%E8%AF%BB-Design-Patterns-by-Tutorials-%E7%AC%94%E8%AE%B0-MVC" />
<meta name="referrer" content="origin" />
<link rel="next" href="/page2/" />
<meta property="og:site_name" content="面向自由编程" />
<meta property="og:type" content="website" />
<meta property="og:title" content="读《Design Patterns by Tutorials》笔记——MVC" />
<meta property="og:description" content="Freelf's Blog" />
<meta property="og:url" content="//%E8%AF%BB-Design-Patterns-by-Tutorials-%E7%AC%94%E8%AE%B0-MVC" />
<meta property="og:image" content="/assets/images/cover1.jpg" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="读《Design Patterns by Tutorials》笔记——MVC" />
<meta name="twitter:description" content="Freelf's Blog" />
<meta name="twitter:url" content="//%E8%AF%BB-Design-Patterns-by-Tutorials-%E7%AC%94%E8%AE%B0-MVC" />
<meta name="twitter:image:src" content="/assets/images/cover1.jpg" />
<script type="application/ld+json">
{
"@context": "http://schema.org",
"@type": "Website",
"publisher": "面向自由编程",
"name": "读《Design Patterns by Tutorials》笔记——MVC",
"url": "//%E8%AF%BB-Design-Patterns-by-Tutorials-%E7%AC%94%E8%AE%B0-MVC",
"image": "/assets/images/cover1.jpg",
"description": "Freelf's Blog"
}
</script>
<meta name="generator" content="Jekyll 3.0.0" />
<link rel="alternate" type="application/rss+xml" title="面向自由编程" href="/feed.xml" />
</head>
<body class="home-template nav-closed">
<!-- The blog navigation links -->
<div class="nav">
<h3 class="nav-title">Menu</h3>
<a href="#" class="nav-close">
<span class="hidden">Close</span>
</a>
<ul>
<li class="nav-home " role="presentation"><a href="/">Home</a></li>
<li class="nav-about " role="presentation"><a href="/about">About</a></li>
</ul>
<a class="subscribe-button icon-feed" href="/feed.xml">Subscribe</a>
</div>
<span class="nav-cover"></span>
<div class="site-wrapper">
<!-- All the main content gets inserted here, index.hbs, post.hbs, etc -->
<!-- default -->
<!-- The comment above "< default" means - insert everything in this file into -->
<!-- the [body] of the default.hbs template, which contains our header/footer. -->
<!-- Everything inside the #post tags pulls data fom the post -->
<!-- #post -->
<header class="main-header post-head no-cover">
<nav class="main-nav clearfix">
<a class="back-button icon-arrow-left" href="/">Home</a>
<a class="menu-button icon-menu" href="#"><span class="word">Menu</span></a>
</nav>
</header>
<main class="content" role="main">
<article class="post">
<header class="post-header">
<h1 class="post-title">读《Design Patterns by Tutorials》笔记——MVC</h1>
<section class="post-meta">
<!-- <a href='/'>Freelf</a> -->
<!-- <a href='/author/Freelf'>Freelf</a> -->
<time class="post-date"
datetime="2018-08-16">16 Aug 2018</time>
<!-- [[tags prefix=" on "]] -->
<!-- on -->
<a href='/tag/读书笔记'>读书笔记</a>
</section>
</header>
<section class="post-content">
<p>MVC 设计模式把对象分为三个不同的类型: Models,Views和 Controllers。
UML 图表示如下:
<img src="https://nightwish.oss-cn-beijing.aliyuncs.com/15341242390937.jpg" alt="" />
<!-- more -->
MVC 是在 iOS 编程中是非常常见的,因为 Apple 在 UIKit 中大量选用了这种设计模式。</p>
<ul>
<li><strong>Models</strong>保持应用数据,通常为 structs 或者简单的 classes。</li>
<li><strong>Views</strong>在屏幕上显示看的见的元素和 controls,通常为<code class="highlighter-rouge">UIView</code>的子类。</li>
<li><strong>Controllers</strong>在 models 和 views 中间协调,通常为<code class="highlighter-rouge">UIViewController</code>的子类。</li>
</ul>
<p>Controllers 允许对它的 model 和 view 强引用,所以它可以直接操作model 和 view。Controllers 可以有不止一个 model 或者 view。
相反的,models 和 views 不应该保持他们所属 controller 的强引用。这会导致一个循环引用。
作为替代,通过属性监听,models 和它们的 controller 进行通信。views 通过<code class="highlighter-rouge">IBAction</code>和 controller 进行通信。
这可以让你在几个 controllers 中可以复用 models 和 views。</p>
<blockquote>
<p>注意:Views 可能有一个弱引用 delegate 指向拥有它的 controller。比如,一个 <code class="highlighter-rouge">UITableView</code>可能持有一个拥有它的 view controller 弱引用作为它的 <code class="highlighter-rouge">delegate</code> 或者 <code class="highlighter-rouge">dataSource</code>。然而,table view 不用明确知道这是拥有它的 view controller。
Controllers 非常难以复用,因为它们的逻辑是非常具体的描述它们所做的任务。因此,MVC 不尝试去复用 Controllers。</p>
<h2 id="什么时候使用-mvc">什么时候使用 MVC</h2>
<p>一般我们一开始做一个 app 时,我们使用 MVC。
接下来,我们可能使用除了 MVC 额外的设计模式,但是没关系,在需要时我们再引入更多的设计模式。</p>
<h2 id="playground-example">Playground example</h2>
<p>设计模式可以分为三类</p>
<ul>
<li>结构类型:描述一个大的子系统如何组合对象。</li>
<li>表现类型:描述对象如何和其他对象联系。</li>
<li>创建类型:创建对象。</li>
</ul>
</blockquote>
<p>很明显,MVC 是一个结构类型的模式。
接下来,我们看一下如何组合对象,来构成一个地址屏幕 app。
model:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// MARK: - Address</span>
<span class="kd">public</span> <span class="kd">struct</span> <span class="kt">Address</span> <span class="p">{</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">street</span><span class="p">:</span> <span class="kt">String</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">city</span><span class="p">:</span> <span class="kt">String</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">state</span><span class="p">:</span> <span class="kt">String</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">zipCode</span><span class="p">:</span> <span class="kt">String</span>
<span class="p">}</span>
</code></pre></div></div>
<p>view:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// MARK: - AddressView</span>
<span class="kd">public</span> <span class="kd">final</span> <span class="kd">class</span> <span class="kt">AddressView</span><span class="p">:</span> <span class="kt">UIView</span> <span class="p">{</span>
<span class="kd">@IBOutlet</span> <span class="kd">public</span> <span class="k">var</span> <span class="nv">streetTextField</span><span class="p">:</span> <span class="kt">UITextField</span><span class="o">!</span>
<span class="kd">@IBOutlet</span> <span class="kd">public</span> <span class="k">var</span> <span class="nv">cityTextField</span><span class="p">:</span> <span class="kt">UITextField</span><span class="o">!</span>
<span class="kd">@IBOutlet</span> <span class="kd">public</span> <span class="k">var</span> <span class="nv">stateTextField</span><span class="p">:</span> <span class="kt">UITextField</span><span class="o">!</span>
<span class="kd">@IBOutlet</span> <span class="kd">public</span> <span class="k">var</span> <span class="nv">zipCodeTextField</span><span class="p">:</span> <span class="kt">UITextField</span><span class="o">!</span>
<span class="p">}</span>
</code></pre></div></div>
<p>controller:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// MARK: - AddressViewController</span>
<span class="kd">public</span> <span class="kd">final</span> <span class="kd">class</span> <span class="kt">AddressViewController</span><span class="p">:</span> <span class="kt">UIViewController</span> <span class="p">{</span>
<span class="c1">// MARK: - Properties</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">address</span><span class="p">:</span> <span class="kt">Address</span><span class="p">?</span> <span class="p">{</span>
<span class="k">didSet</span> <span class="p">{</span>
<span class="nf">updateViewFromAddress</span><span class="p">()</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">addressView</span><span class="p">:</span> <span class="kt">AddressView</span><span class="o">!</span> <span class="p">{</span>
<span class="k">guard</span> <span class="n">isViewLoaded</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="kc">nil</span> <span class="p">}</span>
<span class="k">return</span> <span class="n">view</span> <span class="k">as!</span> <span class="kt">AddressView</span>
<span class="p">}</span>
<span class="c1">// MARK: - View Lifecycle</span>
<span class="kd">public</span> <span class="k">override</span> <span class="kd">func</span> <span class="nf">viewDidLoad</span><span class="p">()</span> <span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="nf">viewDidLoad</span><span class="p">()</span>
<span class="nf">updateViewFromAddress</span><span class="p">()</span>
<span class="p">}</span>
<span class="kd">private</span> <span class="kd">func</span> <span class="nf">updateViewFromAddress</span><span class="p">()</span> <span class="p">{</span>
<span class="k">guard</span> <span class="k">let</span> <span class="nv">addressView</span> <span class="o">=</span> <span class="n">addressView</span><span class="p">,</span>
<span class="k">let</span> <span class="nv">address</span> <span class="o">=</span> <span class="n">address</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="p">}</span>
<span class="n">addressView</span><span class="o">.</span><span class="n">streetTextField</span><span class="o">.</span><span class="n">text</span> <span class="o">=</span> <span class="n">address</span><span class="o">.</span><span class="n">street</span>
<span class="n">addressView</span><span class="o">.</span><span class="n">cityTextField</span><span class="o">.</span><span class="n">text</span> <span class="o">=</span> <span class="n">address</span><span class="o">.</span><span class="n">city</span>
<span class="n">addressView</span><span class="o">.</span><span class="n">stateTextField</span><span class="o">.</span><span class="n">text</span> <span class="o">=</span> <span class="n">address</span><span class="o">.</span><span class="n">state</span>
<span class="n">addressView</span><span class="o">.</span><span class="n">zipCodeTextField</span><span class="o">.</span><span class="n">text</span> <span class="o">=</span> <span class="n">address</span><span class="o">.</span><span class="n">zipCode</span>
<span class="p">}</span>
<span class="c1">// MARK: - Actions</span>
<span class="kd">@IBAction</span> <span class="kd">public</span> <span class="kd">func</span> <span class="nf">updateAddressFromView</span><span class="p">(</span><span class="n">_</span> <span class="nv">sender</span><span class="p">:</span> <span class="kt">AnyObject</span><span class="p">)</span> <span class="p">{</span>
<span class="k">guard</span> <span class="k">let</span> <span class="nv">street</span> <span class="o">=</span> <span class="n">addressView</span><span class="o">.</span><span class="n">streetTextField</span><span class="o">.</span><span class="n">text</span><span class="p">,</span> <span class="n">street</span><span class="o">.</span><span class="n">count</span> <span class="o">></span> <span class="mi">0</span><span class="p">,</span>
<span class="k">let</span> <span class="nv">city</span> <span class="o">=</span> <span class="n">addressView</span><span class="o">.</span><span class="n">cityTextField</span><span class="o">.</span><span class="n">text</span><span class="p">,</span> <span class="n">city</span><span class="o">.</span><span class="n">count</span> <span class="o">></span> <span class="mi">0</span><span class="p">,</span>
<span class="k">let</span> <span class="nv">state</span> <span class="o">=</span> <span class="n">addressView</span><span class="o">.</span><span class="n">stateTextField</span><span class="o">.</span><span class="n">text</span><span class="p">,</span> <span class="n">state</span><span class="o">.</span><span class="n">count</span> <span class="o">></span> <span class="mi">0</span><span class="p">,</span>
<span class="k">let</span> <span class="nv">zipCode</span> <span class="o">=</span> <span class="n">addressView</span><span class="o">.</span><span class="n">zipCodeTextField</span><span class="o">.</span><span class="n">text</span><span class="p">,</span> <span class="n">zipCode</span><span class="o">.</span><span class="n">count</span> <span class="o">></span> <span class="mi">0</span>
<span class="k">else</span> <span class="p">{</span>
<span class="c1">// TO-DO: show an error message, handle the error, etc</span>
<span class="k">return</span>
<span class="p">}</span>
<span class="n">address</span> <span class="o">=</span> <span class="kt">Address</span><span class="p">(</span><span class="nv">street</span><span class="p">:</span> <span class="n">street</span><span class="p">,</span> <span class="nv">city</span><span class="p">:</span> <span class="n">city</span><span class="p">,</span>
<span class="nv">state</span><span class="p">:</span> <span class="n">state</span><span class="p">,</span> <span class="nv">zipCode</span><span class="p">:</span> <span class="n">zipCode</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>我们在,<code class="highlighter-rouge">address</code>属性变化时去更新 <code class="highlighter-rouge">view</code>,并且在 <code class="highlighter-rouge">view</code>有交互时通过<code class="highlighter-rouge"> IBAction</code>去更新 <code class="highlighter-rouge">model</code>。
上面只是给出了一个简单的例子来表示 MVC 是如何工作的。我们可以看到 controller 如何持有 model 和 views,并且通过 controller 交互。</p>
<h2 id="使用时应该注意的地方">使用时应该注意的地方</h2>
<p>MVC 是一个好的开始,但是它有局限性。不是每一个对象都能很好的归类于 model,view ,controller 的范围中。因此,只是用 MVC 的应用在 controllers 中有很多逻辑,导致 view controller 越来越大。
为了解决这个问题,需要在需要时引入其他的设计模式。</p>
<h2 id="教程项目">教程项目</h2>
<p>通过这一整章,我们会做一个应用叫做:Rabble Wabble(就是一个类似背单词的 app)。这一小篇的效果如下:
<img src="https://nightwish.oss-cn-beijing.aliyuncs.com/MVC.gif" alt="MVC" />
功能类似一个背单词的 app,点击空白处显示答案,点正确,正确数加1,点错误错误数加1。很简单吧,利用这个功能,我们可以了解下 MVC 各个模块通信方式。通过下面的学习,我们将逐步完善这个应用。</p>
</section>
<footer class="post-footer">
<!-- Everything inside the #author tags pulls data from the author -->
<!-- #author-->
<figure class="author-image">
<a class="img" href="https://github.com/zhangdongpo"
style="background-image: url(/assets/images/freelf.jpg)"><span
class="hidden">Freelf's Picture</span></a>
</figure>
<section class="author">
<h4><a href="/author/Freelf">Freelf</a></h4>
<p> iOS Developer</p>
<div class="author-meta">
<span class="author-location icon-location">
Beijing, China</span>
<span class="author-link icon-link"><a href="https://freelf.me"> freelf.me</a></span>
</div>
</section>
<!-- /author -->
<section class="share">
<h4>Share this post</h4>
<a class="icon-twitter"
href="http://twitter.com/share?text=读《Design Patterns by Tutorials》笔记——MVC&url=%E8%AF%BB-Design-Patterns-by-Tutorials-%E7%AC%94%E8%AE%B0-MVC"
onclick="window.open(this.href, 'twitter-share', 'width=550,height=235');return false;">
<span class="hidden">Twitter</span>
</a>
<a class="icon-facebook"
href="https://www.facebook.com/sharer/sharer.php?u=%E8%AF%BB-Design-Patterns-by-Tutorials-%E7%AC%94%E8%AE%B0-MVC"
onclick="window.open(this.href, 'facebook-share','width=580,height=296');return false;">
<span class="hidden">Facebook</span>
</a>
<a class="icon-google-plus"
href="https://plus.google.com/share?url=%E8%AF%BB-Design-Patterns-by-Tutorials-%E7%AC%94%E8%AE%B0-MVC"
onclick="window.open(this.href, 'google-plus-share', 'width=490,height=530');return false;">
<span class="hidden">Google+</span>
</a>
</section>
<!-- Add Disqus Comments -->
<div id="gitalk-container"></div>
<script src="/assets/js/md5.min.js"></script>
<script>
const gitalk = new Gitalk({
clientID: 'b62d799fc3a6b1dd7de9',
clientSecret: 'd5a315aa0855dd6a00cdb4025b88059c1b0c585f',
repo: 'blog',
owner: 'zhangdongpo',
admin: 'zhangdongpo',
id: md5(location.pathname), // Ensure uniqueness and length less than 50
distractionFreeMode: false // Facebook-like distraction free mode
})
gitalk.render('gitalk-container')
</script>
</footer>
</article>
</main>
<!-- /post -->
<!-- The tiny footer at the very bottom -->
<footer class="site-footer clearfix">
<section class="copyright"><a href="/">面向自由编程</a> © 2024</section>
<section class="poweredby">Proudly published with <a href="https://jekyllrb.com/">Jekyll</a> using <a href="https://github.com/jekyller/jasper">Jasper</a></section>
</footer>
</div>
<!-- highlight.js -->
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.3.0/highlight.min.js"></script>
<script>hljs.initHighlightingOnLoad();</script>
<!-- jQuery needs to come before `` so that jQuery can be used in code injection -->
<script type="text/javascript" src="//code.jquery.com/jquery-1.12.0.min.js"></script>
<!-- Ghost outputs important scripts and data with this tag -->
<!-- -->
<!-- Add Google Analytics -->
<!-- Google Analytics Tracking code -->
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', '', 'auto');
ga('send', 'pageview');
</script>
<!-- Fitvids makes video embeds responsive and awesome -->
<script type="text/javascript" src="/assets/js/jquery.fitvids.js"></script>
<!-- The main JavaScript file for Casper -->
<script type="text/javascript" src="/assets/js/index.js"></script>
</body>
</html>