개체를 나누는 것은:
MVC의 간단한 예제는 다음과 같다.
import UIKit
struct Person { // Model
let firstName: String
let lastName: String
}
class GreetingViewController : UIViewController { // View + Controller
var person: Person!
let showGreetingButton = UIButton()
let greetingLabel = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
self.showGreetingButton.addTarget(self, action: "didTapButton:", forControlEvents: .TouchUpInside)
}
func didTapButton(button: UIButton) {
let greeting = "Hello" + " " + self.person.firstName + " " + self.person.lastName
self.greetingLabel.text = greeting
}
// layout code goes here
}
// Assembling of MVC
let model = Person(firstName: "David", lastName: "Blaine")
let view = GreetingViewController()
view.person = model
MVC는 View와 Controller를 각각 테스트하기 어렵다. View와 Controller가 강하게 결합되어 있어서(viewDidLoad
, didTapButton
) 따로 분리해서 테스트하기 어렵다.
The interactions between the View and the Controller aren’t really testable with Unit Tests
MVC 패턴은 다른 패턴에 비해 코드가 적게 들기 때문에 개발 속도면에서는 좋은 선택이 될 수 있다.
import UIKit
struct Person { // Model
let firstName: String
let lastName: String
}
protocol GreetingViewModelProtocol: class {
var greeting: String? { get }
var greetingDidChange: ((GreetingViewModelProtocol) -> ())? { get set } // function to call when greeting did change
init(person: Person)
func showGreeting()
}
class GreetingViewModel : GreetingViewModelProtocol {
let person: Person
var greeting: String? {
didSet {
self.greetingDidChange?(self)
}
}
var greetingDidChange: ((GreetingViewModelProtocol) -> ())?
required init(person: Person) {
self.person = person
}
func showGreeting() {
self.greeting = "Hello" + " " + self.person.firstName + " " + self.person.lastName
}
}
class GreetingViewController : UIViewController {
var viewModel: GreetingViewModelProtocol! {
didSet {
self.viewModel.greetingDidChange = { [unowned self] viewModel in
self.greetingLabel.text = viewModel.greeting
}
}
}
let showGreetingButton = UIButton()
let greetingLabel = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
self.showGreetingButton.addTarget(self.viewModel, action: "showGreeting", forControlEvents: .TouchUpInside)
}
// layout code goes here
}
// Assembling of MVVM
let model = Person(firstName: "David", lastName: "Blaine")
let viewModel = GreetingViewModel(person: model)
let view = GreetingViewController()
view.viewModel = viewModel
MVVM 패턴은 코드를 유지보수하는 데 굉장히 편리하다.
또한, ReactiveCocoa, RxSwift or PromiseKit와 같은 functional reactive programming과 함께 사용할 때 그 진가를 발휘한다.
위 MVVM 패턴의 예제에서 ViewModel은 Dependency Inversion Principle 의존성 역전 원칙에 따라 (view model) protocol 타입에 의존하도록 설계했다.
Dependency Inversion Principle은 객체 지향 원칙(SOLID) 중 하나로, 의존 관계가 역전되도록 설계함을 의미하는데, 변하기 쉬운 것보다 변하기 어려운 것에 의존하도록 설계하라는 원칙이다.
변하기 어려운 것은 추상화된 것을 의미한다(class보다는 interface, protocol, abstract).