iOS架构师的必经之路
序⾔
为什么要关注架构设计?
因为假如你不关⼼架构,那么总有⼀天,需要在同⼀个庞⼤的类中调试若⼲复杂的事情,你会发现在这样的条件下,根本不可能在这个类中快速的到以及有效的修改任何bug.当然,把这样的⼀个类想象为⼀个整体是困难的,因此,有可能⼀些重要的细节总会在这个过程中会被忽略。
架构模式
⼀个整体的分层? 逻辑清晰? 还是清晰的分⼯? 对于架构模式来说并没有⼀个⾮常明确的定义, ⽐较抽象, 在于设计在于架构, 不管是⼩到类与类之间的交互, 还是不同的⼩模块, ⼩版块之间, 甚⾄于在不同的业务之间, 我们都可以去从架构的⽅⾯去理解, 去分析。
那么在iOS中常见的架构来说, 常见有就有这些: MVC架构, MVP架构, MVVM架构
当然这些架构都有⼀个共同特点: 解耦合
,
书籍欢迎⼤家接下来⼩编来浅谈⼀下这些架构模式,同时⼩编也欢迎⼤家加⼊⼩编的iOS交流551346706,⾥会提供架构的资料,书籍欢迎⼤家接下来⼩编来浅谈⼀下这些架构模式,同时⼩编也欢迎⼤家加⼊⼩编的iOS交流551346706,⾥会提供架构的资料
⼊驻!
MVC
什么是MVC?
MVC最早存在于桌⾯程序中的, M是指业务数据, V是指⽤户界⾯, C则是控制器. 在具体的业务场景中, C作为M和V之间的连接, 负责获取输⼊的业务数据, 然后将处理后的数据输出到界⾯上做相应展⽰, 另外, 在数据有所更新时, C还需要及时提交相应更新到界⾯展⽰. 在上述过程中, 因为M和V 之间是完全隔离的, 所以在业务场景切换时, 通常只需要替换相应的C, 复⽤已有的M和V便可快速搭建新的业务场景. MVC因其复⽤性, ⼤⼤提⾼了开发效率, 现已被⼴泛应⽤在各端开发中。
过去
MVC的过去
MVC的
在我们探讨Apple的MVC模式之前,我们来看下传统的MVC模式。
传统的MVC
在图⾥,View并没有任何界限,仅仅是简单的在Controller中呈现出Model的变化。想象⼀下,就像⽹页⼀样,在点击了跳转到某个其他页⾯的连接之后就会完全的重新加载页⾯。尽管在iOS平台上实现这这种MVC模式是没有任何难度的,但是它并不会为我们解决架构问题带来任何裨益。因为它本⾝也是,三个实体间相互都有通信,⽽且是紧密耦合的。这很显然会⼤⼤降低了三者的复⽤性,⽽这正是我们不愿意看到的。
然⽽传统的MVC架构不适⽤于现在的iOS开发
苹果推荐的MVC---愿景
Cocoa MVC
由于Controller是⼀个介于View 和 Model之间的协调器,所以View和Model之间没有任何直接的联系。Controller是⼀个最⼩可重⽤单元,这对我们来说是⼀个好消息,因为我们总要⼀个地⽅来写逻辑复杂度较⾼的代码,⽽这些代码⼜不适合放在Model中。理论上来讲,这种模式看起来⾮常直观,但你有没有感到哪⾥有⼀丝诡异?你甚⾄听说过,有⼈将MVC的缩写展开成(Massive View Controller),更有甚者,为View Controller减负也成为iOS开发者⾯临的⼀个重要话题。如果苹果继承并且对MVC模式有⼀些进展,所有这些为什么还会发⽣?
苹果推荐的MVC--
事实
苹果推荐的MVC--事实
Realistic Cocoa MVC
Cocoa的MVC模式驱使⼈们写出臃肿的视图控制器,因为它们经常被混杂到View的⽣命周期中,因此很难说View和ViewController是分离的。尽管仍可以将业务逻辑和数据转换到Model,但是⼤多数情况下当需要为View减负的时候我们却⽆能为⼒了,View的最⼤的任务就是向Controller传递⽤户动作事件。ViewController最终会承担⼀切代理和数据源的职责,还负责⼀些分发和取消⽹络请求以及⼀些其他的任务。
你可能会看见过很多次这样的代码:
1varuserCell = tableView.dequeueReusableCellWithIdentifier("identifier") as UserCell
这个cell,正是由View直接来调⽤Model,所以事实上MVC的原则已经违背了,但是这种情况是⼀直发⽣的甚⾄于⼈们不觉得这⾥有哪些不对。如果严格遵守MVC的话,你会把对cell的设置放在 Controller 中,不向View传递⼀个Model对象,这样就会⼤⼤增加Controller的体积。
直到进⾏单元测试的时候才会发现问题越来越明显。因为你的ViewController和View是紧密耦合的,对它们进⾏测试就显得很艰难--你得有⾜够的创造性来模拟View和它们的⽣命周期,在以这样的⽅式来写View Controller的同时,业务逻辑的代码也逐渐被分散到View的布局代码中去。我们看下⼀些简单的例⼦:
import UIKit
struct Person { // Model
let firstName: String
let lastName: String
}
class GreetingViewController : UIViewController { // View + Controller
varperson: Person!
let showGreetingButton = UIButton()
let greetingLabel = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
view ui框架self.showGreetingButton.addTarget(self, action: "didTapButton:", forControlEvents: .TouchUpInside)
}
func didTapButton(button: UIButton) {
let greeting = "Hello"+ " "+ self.person.firstName + " "+ self.person.lastName
< = greeting
}
// layout code goes here
}
// Assembling of MVC
let model = Person(firstName: "David", lastName: "Blaine")
let view = GreetingViewController()
view.person = model;
这段代码看起来可测试性并不强,我们可以把和greeting相关的都放到GreetingModel中然后分开测试,但是这样我们就⽆法通过直接调⽤在GreetingViewController中的UIView的⽅法(viewDidLoad和didTapButton⽅法)来测试页⾯的展⽰逻辑了,因为⼀旦调⽤则会使整个页⾯都变化,这对单元测试来讲并不是什么好消息。事实上,在单独⼀个模拟器中(⽐如iPhone 4S)加载并测试UIView并不能保证
在其他设备中也能正常⼯作,因此我建议在单元测试的Target的设置下移除"Host Application"项,并且不要在模拟器中测试你的应⽤。
以上所述,似乎Cocoa MVC 看起来是⼀个相当差的架构⽅案。我们来评估⼀下MVC⼀系列的特征:
任务均摊--View和Model确实是分开的,但是View和Controller却是紧密耦合的
任务均摊
可测试性--由于糟糕的分散性,只能对Model进⾏测试
可测试性
易⽤性--与其他⼏种模式相⽐最⼩的代码量。熟悉的⼈很多,因⽽即使对于经验不那么丰富的开发者来讲维护起来也较为容易。
易⽤性
如果你不想在架构选择上投⼊更多精⼒,那么Cocoa MVC⽆疑是最好的⽅案,⽽且你会发现⼀些其他维护成本较⾼的模式对于你所开发的⼩的应⽤是⼀个致命的打击。
MVP
MVC的缺点在于并没有区分业务逻辑和业务展⽰, 这对单元测试很不友好. MVP针对以上缺点做了优化, 它将业务逻辑和业务展⽰也做了⼀层隔离,对应的就变成了MVCP. M和V功能不变, 原来的C现在只负责布局, ⽽所有的逻辑全都转移到了P层。
MVP实现了Cocoa的MVC的愿景
Passive View variant of MVP
这看起来不正是苹果所提出的MVC⽅案吗?确实是的,这种模式的名字叫做MVC,但是,这就是说苹果的MVC实际上就是MVP了?不,并不是这样的。如果你仔细回忆⼀下,View是和Controller紧密耦合的,但是MVP的协调器Presenter并没有对ViewController的⽣命周期做任何改变,因此View可以很容易的被模拟出来。在Presenter中根本没有和布局有关的代码,但是它却负责更新View的数据和状态。就MVP⽽
⾔,UIViewController的⼦类实际上就是Views并不是Presenters。这点区别使得这种模式的可测试性得到了极⼤的提⾼,付出的代价是开发速度的⼀些降低,因为必须要做⼀些⼿动的数据和事件绑定,从下例中可以看出:
import UIKit
struct Person { // Model
let firstName: String
let lastName: String
}
protocol GreetingView: class {
func setGreeting(greeting: String)
}
protocol GreetingViewPresenter {
init(view: GreetingView, person: Person)
func showGreeting()
}
class GreetingPresenter : GreetingViewPresenter {
unowned let view: GreetingView
let person: Person
required init(view: GreetingView, person: Person) {
self.view = view
self.person = person
}
func showGreeting() {
let greeting = "Hello"+ " "+ self.person.firstName + " "+ self.person.lastName
self.view.setGreeting(greeting)
}
}
class GreetingViewController : UIViewController, GreetingView {
varpresenter: GreetingViewPresenter!
let showGreetingButton = UIButton()
let greetingLabel = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
self.showGreetingButton.addTarget(self, action: "didTapButton:", forControlEvents: .TouchUpInside) }
func didTapButton(button: UIButton) {
self.presenter.showGreeting()
}
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论