1. 开篇
1. 什么是设计模式?
在我们实际开发中通常会碰到一些特定问题,而这些问题可能会反复出现,设计模式的出现就是针对某一特定问题,提出的某一解决方案.
因为设计模式并不提供具体的代码实现,所以在各种语言之间设计模式都有通用性.
例如,面向对象设计模式通过类和对象来表述其中的关系和相互作用.
设计模式可以分成三个大类:
结构模式(Structural design pattern): 主要关注于如何将类和对象组合成大的结构.例如,MVC,MVVM,外观模式(Facade).
行为模式(Behavioral design pattern): 主要关注对象之间的通讯问题.例如,代理模式(Delegation),策略模式(Strategy),观察者模式(Observer).
创建模式(Creational design pattern): 主要关注于怎样将类的实例化抽象出来.例如,建造者模式(Builder),单例模式(Singleton), 原型模式(Prototype).
2. 设计模式使用的优缺点
优点:
设计模式可以用特定的方式去表述问题的解决方案,减少了开发者因为不同语言所产生的沟通成本.
合理的使用设计模式有利于提高代码的可维护性.
设计模式的通用性原则,可以使我们快速的应用到别的语言.
缺点:
设计模式是用来解决特定场景下的问题,过度使用会使代码的可维护性变得很差.
虽然设计模式有着通用性,但并不是所有的设计模式都是这样,也需要针对特定的语言去选择合理的设计模式.
3. 这个系列会涉及到的设计模式
基本的设计模式. MVC、代理模式(Delegation Pattern)、策略模式(Strategy Pattern)、单例模式(Singleton Pattern)、备忘录模式(Memento Pattern)、观察者模式(Observer Pattern)、建造者模式(Builder Pattern)
不常用的设计模式. MVVM、工厂模式(Factory Pattern)、适配器模式(Adapter Pattern)、迭代器模式(Iterator Pattern)、原型模式(Prototype Pattern)、状态模式(State Pattern)、多播代理模式(Multicast Delegate Pattern)、外观模式(Facade Pattern)
高级一点的设计模式. 享元模式(Flyweight Pattern)、中介者模式(Mediator Pattern)、组合模式(Composite Pattern)、命令模式(Command Pattern)、职责链模式(Chain of Responsibility)、装饰者模式(Decorator Pattern)
具体的细节请看后面分解.
4. 表述设计模式的工具 - 类图
大家都知道统一建模语言UML,它主要用来建模,为软件开发提供可视化的支持.当然了我们不需要关心这么多.我们只需要使用UML的类图来描述设计模式.
1.类图的描述
类
上图表示创建了一个Dog对象.
继承
空心箭头表示继承,我们一般使用Is a来表述继承
SheepDog is a Dog
上图表示SheepDog是Dog的子类.
属性
用实心箭头表示属性的指向.
Farmer有一条Dog.
我们可以用1 ... * 表示一对多的关系,上图表示Farmer有多条Dog.
这样就可以清楚的表示,Farmer有一条SheepDog,而SheepDog继承自Dog.
在名字前面加<
空心箭头加虚线表示遵守了某一个协议.
Farmer遵守了PetOwning这个协议.
Dog实现了这个协议.
上图就是综合以上的一个完整的类图.
思考下再看图:
交通工具有两个子类,一个是汽车,另一个是一个或多个轮子的交通工具.
教授是老师并且遵守了人这个协议.
类图到这里就说完啦,是不是很简单呢!下面开始正文
2. 基本的设计模式 - MVC
1.MVC概述
作为Cocoa框架的核心的MVC,顾名思义将所有的类分为三种类型:模型(Models)、视图(Views)、控制器(Controllers),用类图来解释就很形象生动.
MVC模式
模型(Models): 负责保存数据,通常是结构体或者类.
视图(Views): 负责展示屏幕上的元素和空间,通常是UIView的子类.
控制器(Controllers): 模型和视图之间的协调者,通常是UIViewController的子类.
三者之间的关系:
控制器负责管理模型和视图,一个控制器可以对应有多个模型和视图.控制器强引用着模型和视图,模型和控制器的交互通过属性观察器,视图跟控制器的交互通过IBActions.
因为控制器通常负责特定的业务逻辑,所以MVC的重用性并不是那么好.
Swift tips: 属性观察器
// 属性观察器 var s = "whatever" { // s必须是变量 willSet { // s变量被设置新值之前调用 print("willSet: ", newValue) // 新的值 } didSet { // 接收新值之后被调用 print("didSet: ", oldValue) // 旧的值 } } s = "hello" // willSet: hello // didSet: whatever // 注意点: willSet和didSet两种情况下不会调用: 初始化的时候,在didSet中改变变量的值的时候
2.MVC的使用
1.模型中的代码,包括四个属性:
public struct Address { public var street: String public var city: String public var state: String public var zipCode: String }
2.视图中的代码:
public final class AddressView: UIView { @IBOutlet weak var streetTextField: UITextField! @IBOutlet weak var cityTextField: UITextField! @IBOutlet weak var stateTextField: UITextField! @IBOutlet weak var zipCodeTextField: UITextField @IBOutlet weak var addressLabel: UILabel! }
注意:用AddressView替代了控制器中的view,好处是控制器只负责处理业务逻辑.
3.控制器中的代码:
class AddressViewController: UIViewController { // MARK: - Properties public var address: Address? { didSet { // 属性观察器 updateViewFromAddress() } } public var addressView: AddressView! { // 关联view guard isViewLoaded else { return nil } return view as! AddressView } // MARK: - View Lifecycle public override func viewDidLoad() { super.viewDidLoad() } private func updateViewFromAddress() { if let address = address { // 可选类型解包 addressView.addressLabel.text = "street: "+address.street+"/ncity: "+address.city+"/nstate: "+address.state+"/nzipCode: "+address.zipCode }else { addressView.addressLabel.text = "地址为空" } } // MARK: - Actions @IBAction public func updateAddressFromView(_ sender: AnyObject) { guard let street = addressView.streetTextField.text, street.count > 0, let city = addressView.cityTextField.text, city.count > 0, let state = addressView.stateTextField.text, state.count > 0, let zipCode = addressView.zipCodeTextField.text, zipCode.count > 0 else { return } address = Address(street: street, city: city, state: state, zipCode: zipCode) } @IBAction func clearAddressFromView(_ sender: Any) { address = nil } }
4. StroyBoard中控制器对应的视图
5. 总结
控制器中的view交给视图去管理,控制器只负责处理视图的IBAction和自己的业务逻辑.在控制器中通过模型的属性观察器去监听模型的改变,从而更新视图.
3. 基本的设计模式 - 代理
1. 代理概述
代理最通俗的形容就是:叫别人帮我们做事.
代理模式有三部分:
协议实现对象: 就是帮我们做事的人,由代理对象去调用协议方法.为了避免循环引用,代理属性需要用weak去修饰.
代理协议: 定义了一些需要实现的方法,这些方法可以是可选的.
协议遵守对象: 也就是我们,由协议遵守对象去实现协议方法.
代理和MVC一样被大量应用于Cocoa框架,最常见的就是tableView的代理和数据源方法了.
2. 代理的使用
1.协议实现方:
class MenuViewController: UIViewController { weak var delegate: MenuViewControllerDelegate? @IBOutlet weak var tableView: UITableView! { didSet { tableView.dataSource = self tableView.delegate = self } } let items = ["ITEMS 1", "ITEMS 2", "ITEMS 3", "ITEMS 4", "ITEMS 5", "ITEMS 6", "ITEMS 7", "ITEMS 8"] override func viewDidLoad() { super.viewDidLoad() } } extension MenuViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return items.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) cell.textLabel?.text = items[indexPath.row] return cell } } extension MenuViewController: UITableViewDelegate { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { delegate?.menuViewController(self, didSelectItemAtIndex: indexPath.row, chooseItem: items[indexPath.row]) // 可选链式调用,当有一个方法的返回值为nil,会导致整个链式调用失败 delegate?.didSelectItemAtIndex?(indexPath.row) self.navigationController?.popViewController(animated: true) } }
2.协议:
@objc protocol MenuViewControllerDelegate: class { // 限制协议实现的类型 // 必须实现的方法 func menuViewController(_ menuViewController: MenuViewController, didSelectItemAtIndex index: Int, chooseItem item: String) @objc optional func didSelectItemAtIndex(_ index: Int) // 可选方法 }
3.协议遵守方:
class ViewController: UIViewController { @IBOutlet weak var chooseLabel: UILabel! override func viewDidLoad() { super.viewDidLoad() } override func prepare(for segue: UIStoryboardSegue, sender: Any?) { guard let viewController = segue.destination as? MenuViewController else { return } viewController.delegate = self; } } extension ViewController: MenuViewControllerDelegate { func menuViewController(_ menuViewController: MenuViewController, didSelectItemAtIndex index: Int, chooseItem item: String) { chooseLabel.text = item } func didSelectItemAtIndex(_ index: Int) { print(index) } }
4.总结
本篇主要讲了三个内容,如何用类图直观的表达设计模式,MVC模式的使用,代理模式的使用.
参考:
The Swift Programming Language (Swift 4.1)
作者:Dariel
链接:https://www.jianshu.com/p/76ee83c53dec