본문 바로가기

개발/디자인 패턴

[iOS] 디자인패턴 - MVP

반응형

MVP

MVP는 Model - View - Presenter의 약자로 MVC에서 의존성 분리가 추가된 개념이다.

MVP 다이어그램

 

MVP 핵심요소

  • Model 
    • 일반 데이터 모델, DB 계층을 담당
  • View (= UIViewController)
    • UI 담당으로 UIKit을 사용. 
  • Presenter 
    • Model과 View의 중간다리로 이 둘을 관리.
    • 뷰의 이벤트에 반응하여 모델에서 데이터를 가져온다.
    • Presenter는 UIKit을 사용할 수 없다.

 

MVP 특징

  • View의 재사용성으로 테스트성이 좋다. (Testability)
  • 의존성 분리로 역할이 나뉘어 수정, 유지보수가 편리하다. 단, MVC 보다 코드의 양이 더 든다. (Easy of use)
  • Model과 View는 서로의 존재를 모른다. View는 UI만을 담당하며 비즈니스 로직을 담지 않는다. Presenter는 중재자로서 이들을 관리한다. (Distribution)

 

MVP 예제

먼저 구현해볼 View의 모습이다.

계산기 모습만 갖춘 아주 기본적인 산술 계산을 하는..! 뷰를 제작했다.

 

Model

import Foundation

struct Model {
    var number : Int
    var history : String
}

 

View

import UIKit

protocol ValueUpdate : class {
    func updateLabel(index: String)
    func updateHistory(index: String)
}

class View: UIViewController, UITextViewDelegate {
    @IBOutlet weak var historyTextView: UITextView!
    @IBOutlet weak var calculatorStackView: UIStackView!
    @IBOutlet weak var label: UILabel!
    
    @IBAction func buttonPressed(_ sender: UIButton){
        presenter!.tagAction(tag: sender.tag)
    }

	private var presenter : PresenterProtocol?
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        historyTextView.isEditable = false
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        initialize()
        historyTextView.delegate = self
    }
    
    private func initialize() {
        presenter = Presenter(view: self)
    }
}

extension View : ValueUpdate {
    func updateLabel(index : String) {
        label.text = index
    }
    func updateHistory(index: String) {
        historyTextView.text = index
    }
}


이번에도 프로토콜을 사용하여 Presenter과 연결을 해준다.
View에서 핵심은 Model의 존재를 모른다는 점이다.
화면을 구성할 UI를 구성한다.

 

Presenter

import Foundation 

class Presenter : PresenterProtocol {
    private let view : ValueUpdate
    private var model = Model(number: 0, history: "" )
    
    init(view: View) {
        self.view = view
    }
}

저는 버튼의 tag에 번호를 달아 UIButton을 구분할 수 있게 해두었다.

extension Presenter {
    func tagAction(tag: Int){
        switch tag {
        case 0...9:
            model.history.append(String(tag))
            view.updateLabel(index: model.history)
        case 10:
            model.history.append("+")
            view.updateLabel(index: model.history)
        case 11:
            model.history.append("-")
            view.updateLabel(index: model.history)
        case 12:
            model.history.append("*")
            view.updateLabel(index: model.history)
        case 13:
            model.history.append("/")
            view.updateLabel(index: model.history)
        case 14:
            model.history.append("%")
            view.updateLabel(index: model.history)
        case 15:
            model.history.append(".")
            view.updateLabel(index: model.history)
        case 16:
            model.history.removeLast()
            view.updateLabel(index: model.history)
        case 17:
            let expression = NSExpression(format: model.history)
            let result = expression.expressionValue(with: nil, context: nil) ?? 0
            model.number = result as! Int
            print("result = \(result)")
            view.updateHistory(index: String(model.number))
        default:
            fatalError("Unknown button tag")
        }
    }
}

 

 

반응형

'개발 > 디자인 패턴' 카테고리의 다른 글

[iOS] 디자인 패턴 - VIPER 화면 전환 예제  (0) 2020.03.08
[iOS] 디자인패턴 - VIPER  (4) 2020.02.24