設計模式(Swift) - 1.MVC和代理

1. 開篇

1. 什麼是設計模式?

在咱們實際開發中一般會碰到一些特定問題,而這些問題可能會反覆出現,設計模式的出現就是針對某一特定問題,提出的某一解決方案. 由於設計模式並不提供具體的代碼實現,因此在各類語言之間設計模式都有通用性. 例如,面向對象設計模式經過類和對象來表述其中的關係和相互做用.git

設計模式能夠分紅三個大類:github

  1. 結構模式(Structural design pattern): 主要關注於如何將類和對象組合成大的結構.例如,MVC,MVVM,外觀模式(Facade).
  2. 行爲模式(Behavioral design pattern): 主要關注對象之間的通信問題.例如,代理模式(Delegation),策略模式(Strategy),觀察者模式(Observer).
  3. 建立模式(Creational design pattern): 主要關注於怎樣將類的實例化抽象出來.例如,建造者模式(Builder),單例模式(Singleton), 原型模式(Prototype).

2. 設計模式使用的優缺點

優勢:
  1. 設計模式能夠用特定的方式去表述問題的解決方案,減小了開發者由於不一樣語言所產生的溝通成本.
  2. 合理的使用設計模式有利於提升代碼的可維護性.
  3. 設計模式的通用性原則,可使咱們快速的應用到別的語言.
缺點:
  1. 設計模式是用來解決特定場景下的問題,過分使用會使代碼的可維護性變得不好.
  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的類圖來描述設計模式.swift

1.類圖的描述

類

上圖表示建立了一個Dog對象. 設計模式

繼承

空心箭頭表示繼承,咱們通常使用Is a來表述繼承app

SheepDog is a Dog

上圖表示SheepDog是Dog的子類. 框架

屬性

用實心箭頭表示屬性的指向.ide

Farmer有一條Dog.

咱們能夠用1 ... * 表示一對多的關係,上圖表示Farmer有多條Dog.工具

這樣就能夠清楚的表示,Farmer有一條SheepDog,而SheepDog繼承自Dog. ui

在名字前面加<< protocol>>表示協議

空心箭頭加虛線表示遵照了某一個協議.

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)

Objective-C編程之道

Design Patterns by Tutorials

相關文章
相關標籤/搜索