内容简介:关于ViewController讨论的最多的是它的肥胖和臃肿,即使使用传统的MVC模式,ViewController也可以写的很优雅,这无关乎设计模式,更多的是你对该模式理解有多深,你对于职责划分的认知是否足够清晰。ViewController也从很大程度上反应一个程序员的真实水平,初级程序员他的ViewController永远是臃肿的、肥胖的,什么功能都可以往里面塞,不同功能间缺乏清晰的界限。而一个优秀的程序员它的ViewController显得如此优雅,让你产生一种竟不能修改一笔一画的感觉。用户交互事件
关于ViewController讨论的最多的是它的肥胖和臃肿,即使使用传统的MVC模式,ViewController也可以写的很优雅,这无关乎设计模式,更多的是你对该模式理解有多深,你对于职责划分的认知是否足够清晰。ViewController也从很大程度上反应一个 程序员 的真实水平,初级程序员他的ViewController永远是臃肿的、肥胖的,什么功能都可以往里面塞,不同功能间缺乏清晰的界限。而一个优秀的程序员它的ViewController显得如此优雅,让你产生一种竟不能修改一笔一画的感觉。
ViewController职责
- UI 属性配置 和 布局
- 用户交互事件
- 用户交互事件处理和回调
用户交互事件处理: 通常会交给其他对象去处理 回调 : 可以根据具体的 设计模式 和应用场景交给 ViewController 或者其他对象处理
而通常我们在阅读别人 ViewController
代码的时候,我们关注的是什么?
- 控件属性配置在哪里?
- 用户交互的入口位置在哪里?
- 用户交互会产生什么样的结果?(回调在哪里?)
所以从这个角度来说,这三个功能一开始就应该是被分离的,需要有清晰明确的界限。因为谁都不希望自己在查找交互入口的时候 ,去阅读一堆控件冗长的控件配置代码, 更不愿意在一堆代码中去慢慢理清整个用户交互的流程。 我们通常只关心我当前最关注的东西,当看到一堆无关的代码时,第一反应就是我想注释掉它。
基于协议分离UI属性的配置
protocol MFViewConfigurer { var rootView: UIView { get } var contentViews: [UIView] { get } var contentViewsSettings: [() -> Void] { get } func addSubViews() func configureSubViewsProperty() func configureSubViewsLayouts() func initUI() } 复制代码
依赖这个协议就可以完成所有控件属性配置,然后通过extension protocol 大大减少重复代码,同时提高可读性
extension MFViewConfigurer { func addSubViews() { for element in contentViews { if let rootView = rootView as? UIStackView { rootView.addArrangedSubview(element) } else { rootView.addSubview(element) } } } func configureSubViewsProperty() { for element in contentViewsSettings { element() } } func configureSubViewsLayouts() { } func initUI() { addSubViews() configureSubViewsProperty() configureSubViewsLayouts() } } 复制代码
这里 我将控件的添加和控件的配置分成两个函数 addSubViews
和 configureSubViewsProperty
, 因为在我的眼里函数就应该遵循单一职责这个概念: addSubViews
: 明确告诉阅读者,我这个控制器包含哪些控件 configureSubViewsProperty
: 明确告诉阅读者,控件的所有属性配置都在这里,想要修改属性请阅读这个函数
来看一个实例:
override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. // 初始化 UI initUI() // 绑定用户交互事件 bindEvent() // 将ViewModel.value 绑定至控件 bindValueToUI() } // MARK: - UI configure // MARK: - UI extension MFWeatherViewController: MFViewConfigurer { var contentViews: [UIView] { return [scrollView, cancelButton] } var contentViewsSettings: [() -> Void] { return [{ self.view.backgroundColor = UIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.7) self.scrollView.hiddenSubViews(isHidden: false) }] } func configureSubViewsLayouts() { cancelButton.snp.makeConstraints { make in if #available(iOS 11, *) { make.top.equalTo(self.view.safeAreaLayoutGuide.snp.top) } else { make.top.equalTo(self.view.snp.top).offset(20) } make.left.equalTo(self.view).offset(20) make.height.width.equalTo(30) } scrollView.snp.makeConstraints { make in make.top.bottom.left.right.equalTo(self.view) } } } 而对于UIView 这套协议同样适用 ```Swift // MFWeatherSummaryView private override init(frame: CGRect) { super.init(frame: frame) initUI() } // MARK: - UI extension MFWeatherSummaryView: MFViewConfigurer { var rootView: UIView { return self } var contentViews: [UIView] { return [ cityLabel, weatherSummaryLabel, temperatureLabel, weatherSummaryImageView, ] } var contentViewsSettings: [() -> Void] { return [UIConfigure] } private func UIConfigure() { backgroundColor = UIColor.clear } public func configureSubViewsLayouts() { cityLabel.snp.makeConstraints { make in make.top.centerX.equalTo(self) make.bottom.equalTo(temperatureLabel.snp.top).offset(-10) } temperatureLabel.snp.makeConstraints { make in make.top.equalTo(cityLabel.snp.bottom).offset(10) make.right.equalTo(self.snp.centerX).offset(0) make.bottom.equalTo(self) } weatherSummaryImageView.snp.makeConstraints { make in make.left.equalTo(self.snp.centerX).offset(20) make.bottom.equalTo(temperatureLabel.snp.lastBaseline) make.top.equalTo(weatherSummaryLabel.snp.bottom).offset(5) make.height.equalTo(weatherSummaryImageView.snp.width).multipliedBy(61.0 / 69.0) } weatherSummaryLabel.snp.makeConstraints { make in make.top.equalTo(temperatureLabel).offset(20) make.centerX.equalTo(weatherSummaryImageView) make.bottom.equalTo(weatherSummaryImageView.snp.top).offset(-5) } } } 复制代码
由于我使用的是MVVM模式,所以 viewDidLoad
和MVC模式还是有些区别,如果是MVC可能就是这样
override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. // 初始化 UI initUI() // 用户交互事件入口 addEvents() } // MARK: callBack ...... 复制代码
由于MVC的回调模式很难统一,有Delegate, closure, notification 等等,所以回调通常会散落在控制器各个角落。最好加个 MARK
flag, 尽量收集在同一个区域中, 同时对于每个回调加上必要的注释:
- 由哪种操作触发
- 会导致什么后果
- 最终会留下哪里
所以从这个角度来说 UITableViewDataSource
和 UITableViewDelegate
完全是两种不一样的行为, 一个是 configure UI , 一个是 control behavior , 所以不要在把这两个东西写一块了, 真的很难看。
总结
基于职责对代码进行分割,这样会让你的代码变得更加优雅简洁,会大大减少一些万金油代码的出现,减少阅读代码的成本也是我们优化的一个方向,比较谁都不想因为混乱的代码影响自己的心情
以上所述就是小编给大家介绍的《如何构建优雅的ViewController》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- vueSSR: 从0到1构建vueSSR项目 --- 路由的构建
- 在 Android Studio 里使用构建分析器提升构建性能
- [译] 使用 React 和 ImmutableJS 构建一个拖放布局构建器
- 为 Envoy 构建控制面指南第4部分:构建的可扩展性
- 自动化构建工具 Gradle 4.5 RC1 发布,改进构建缓存
- 构建工具篇 - react 的 yarn eject 构建命令都做了什么
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Web应用漏洞侦测与防御
Mike Shema / 齐宁、庞建民、张铮、单征 / 机械工业出版社 / 2014-8-20 / 69.00
本书由国际知名网络安全专家亲笔撰写,全面讲解如何预防常见的网络攻击,包括HTML注入及跨站脚本攻击、跨站请求伪造攻击、SQL注入攻击及数据存储操纵、攻破身份认证模式、利用设计缺陷、利用平台弱点、攻击浏览器和隐私等, 全书共8章:第1章介绍HTML5的新增特性及使用和滥用HTML5的安全考虑;第2章展示了如何只通过浏览器和最基本的HTML知识就可以利用Web中最常见的漏洞;第3章详细讲解CSR......一起来看看 《Web应用漏洞侦测与防御》 这本书的介绍吧!