デザインパターン入門「Model-View-Controller」

最近、デザインパターンが増えてきたので、まとめていこうかなと思います。

記念すべき第1回は**MVC**です！！

MVCを調べてたら、何種類か出てきて困りましたが、自分が理解できて使えそうな形のものを紹介していきたいと思います。
なので、自分の知ってるのと違うという場合もありますが、コメントなどで教えてもらえたら勉強になりますのでぜひお願いします:angel_tone2:（笑）

今回の実装コードは、Swiftが個人的に好きなのでSwiftをつかいます:thumbsup:

# MVC構成要素の概要

```swift:Model
データの保持
データの管理
ビジネスロジック
```

```swift:View
modelの情報を表示する
```

```swift:Controller
ユーザーからの入力を受け取る
modelへ入力を伝える
```

iOSではViewControllerというクラスが`Controller`の役割をします
今回は、ViewControllerクラスに`View`も持たせました

# MVC構成要素の実装

## Model
```swift:MVCModel.swift
import Foundation

// データの変更を通知するためにdelegateを実装
protocol MVCModelDelegate {
	func didChange()
}

class MVCModel {
	
	public var count = 0;
	var delegate: MVCModelDelegate? = nil
	
	// ④データの変更用メソッドを定義
	func inc() {
		count += 1
		delegate?.didChange()
	}
	
	func dec() {
		count -= 1
		delegate?.didChange()
	}
	
}
```

## ViewController
```swift:MVCViewController.swift
import Foundation
import UIKit

class MVCViewController: UIViewController, MVCModelDelegate {
	
	/** Controller側の実装 **/

	let mvcModel = MVCModel()
	
	override func viewDidLoad() {
		super.viewDidLoad()
		// 初期値を表示するためにview側の表示更新を呼ぶ
		didChange()
		mvcModel.delegate = self
	}

	// ②ユーザーからの入力イベント
	@IBAction func inc(_ sender: Any) {
		// ③Modelのメソッドを呼ぶ
		mvcModel.inc()
	}
	
	@IBAction func dec(_ sender: Any) {
		mvcModel.dec()
	}

	/** View側の実装 **/

	@IBOutlet weak var countLabel: UILabel!
	
	// ⑤Modelからのdelegate
	func didChange() {
		countLabel.text = String(mvcModel.count)
	}

}
```

## 制御フロー

1. ユーザーが入力をする（incボタンをタップ）
2. `Controller`が入力を受け取る
3. 入力に対応した`Model`のメソッドを呼ぶ
4. `Model`のデータが変更された場合は、delegateが呼ばれる
5. `View`で表示を更新する

# 画面遷移

画面遷移がある場合の実装

- 遷移元（`MVCViewController.swift`）
- 遷移先（`MVCSubViewController.swift`）

## ViewController
```swift:MVCViewController.swift
extension MVCViewController {

	// ②ユーザーからの入力イベント
	@IBAction func display(_ sender: Any) {
		// SubViewController画面に遷移
		performSegue(withIdentifier: "presentMVCSubViewController", sender: self)
	}
	
	// ③画面遷移の際呼ばれる
	override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
		// ④遷移先のViewControllerにModelクラスを渡す
		if let destination = segue.destination as? MVCSubViewController {
			destination.mvcModel = mvcModel
		}
	}

}
```

## SubViewController
```swift:MVCSubViewController.swift
import Foundation
import UIKit

class MVCSubViewController: UIViewController {
	
	var mvcModel: MVCModel? = nil

	@IBOutlet weak var countLabel: UILabel!
	
	override func viewDidLoad() {
		super.viewDidLoad()
		// ⑤初期値を表示
		if let count = mvcModel?.count {
			countLabel.text = String(count)
		}
	}
	
}
```

## 制御フロー

1. ユーザーが入力をする（画面遷移ボタンをタップ）
2. `Controller(MVCViewController)`が入力を受け取る
3. `Controller(MVCSubViewController)`画面に遷移する
4. `Model`クラスの参照をMVCSubViewControllerクラスに渡す
5. `View(MVCSubViewController)`で渡された`Model`のデータを表示する

# まとめ

作ってみた感想になってしまいますが、メリットとデメリット:point_up:

## メリット

- 制御フローが`User → Controller → Model → View`とわかりやすくなっている
- 「わかりやすい = 作りやすい」と思う
- `Model`だけを渡すので、ViewController間でのデータの受け渡しが簡単

## デメリット

- `View`と`Controller`が含まれるのでViewControllerクラスが肥大化する
- `Model`もデータの種類ごとにうまく分けないと肥大化する
- `Model`が複数個できたときに、どの`Controller`で参照されているのかを追うのが大変

# 追記
## 2017.11.16
お互いに強参照していたため、メモリリークを起こす可能性があるとの指摘をいただきましたので修正をこちらに書かせていただきます。

```swift:MVCModel.swift
// データの変更を通知するためにdelegateを実装
protocol MVCModelDelegate: class { // ← class制約
    func didChange()
}

class MVCModel {

    public var count = 0;
    weak var delegate: MVCModelDelegate? = nil // ← 弱参照へ
```