MVP Application
MVC Phần lớn các ứng dụng iOS theo định hướng của Apple đều được xây dựng theo mô hình MVC. Dưới đây là mong muốn của Apple về mô hình này: Trong đó Controller đóng vai trò kết nối giữa View và Model do đó View và Model không biết lẫn nhau. Controller là thành phần khó sử dụng lại nhất và ...
MVC
Phần lớn các ứng dụng iOS theo định hướng của Apple đều được xây dựng theo mô hình MVC. Dưới đây là mong muốn của Apple về mô hình này:
Trong đó Controller đóng vai trò kết nối giữa View và Model do đó View và Model không biết lẫn nhau. Controller là thành phần khó sử dụng lại nhất và nó là nơi chúng ta lưu các code liên quan tới business. Mô hình này có nhược điểm là Controller đôi lúc trở nên quá lớn khi chứa toàn bộ code logic, đồng thời nó kiêm luôn vai trò quản lý vòng đời của View khiến View và Controller khó có thể tách rời. Dưới đây là thực tế của mô hình MVC của Apple:
Đặc điểm:
- Phân phối: View và Model được tách rời nhưng View và Controller bị liên kết chặt với nhau.
- Khả năng test: bạn chỉ có thể test được Model.
- Tính dễ dùng: số lượng code ít nhất trong số các mô hình, ngoài ra do tất cả mọi người đều biết về mô hình này nên nó dễ dàng bảo trì ngay cả với những lập trình viên ít kinh nghiệm.
Ngoài MVC chúng ta còn có các mô hình sau:
- MVP
- MVVM
- VIPER
Mỗi mô hình đều có những ưu và nhược điểm riêng, trong khuôn khổ bài viết này tôi sẽ trình bày về mô hình MVP.
MVP
Trong mô hình MVP UIViewController đóng vai trò là View chứ không phải là Presenter. Do đó tăng khả năng test, đổi lại thì việc phát triển ứng dụng sẽ mất thời gian hơn do chúng ta phải tạo các kết nối data và event thủ công.
Đặc điểm của mô hình MVP:
- Phân phối: phần lớn các trách nhiệm được chia đều giữa Presenter và Model, View có rất ít vai trò, chủ yếu để hiển thị dữ liệu.
- Khả năng test: chúng ta có thể test phần lớn các code logic do ít phụ thuộc vào View.
- Tính dễ dùng: số lượng code gấp 2 so với mô hình MVC truyền thống, tuy nhiên về mặt ý tưởng thì mô hình MVP rất rõ ràng.
Để hiểu rõ hơn về mô hình này, chúng ta sẽ làm một ứng dụng cho phép hiển thị danh sách các sản phẩm.
Tạo mới dự án
Chúng ta sẽ tạo một project Single View Application mới với các thông số như sau:
Storyboard
Để đơn giản chúng ta chỉ có một màn hình hiển thị danh sách sản phẩm, sử dụng UITableViewController
Model
Model của chúng ta là sản phẩm:
struct Product { var name: String var price: Double }
View
Phần View có nhiệm vụ thiết lập việc hiển thị danh sách các sản phẩm:
protocol ProductListView: class { func setProductList(products: [Product]) }
Presenter
Phần Presenter có nhiệm vụ liên kết giữa View và Model:
protocol ProductListViewPresenter: class { func showProductList() }
class ProductListPresenter: ProductListViewPresenter { unowned let view: ProductListView let products: [Product] required init(view: ProductListView, products: [Product]) { self.view = view self.products = products } func showProductList() { self.view.setProductList(self.products) } }
Controller
Controller chỉ đóng vai trò là View, liên kết với Presenter để hiển thị dữ liệu:
class ProductListViewController: UITableViewController, ProductListView { var presenter: ProductListViewPresenter! var productListDataSource: ProductListDataSource! override func viewDidLoad() { super.viewDidLoad() presenter.showProductList() } func setProductList(products: [Product]) { productListDataSource = ProductListDataSource() productListDataSource.products = products tableView.dataSource = productListDataSource tableView.reloadData() } }
Chúng ta sử dụng data source để đưa dữ liệu vào data table:
class ProductListDataSource: NSObject, UITableViewDataSource { var products: [Product]! func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return products.count } func numberOfSectionsInTableView(tableView: UITableView) -> Int { return 1 } func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCellWithIdentifier("ProductCell", forIndexPath: indexPath) let product = products[indexPath.row] cell.textLabel?.text = product.name cell.detailTextLabel?.text = String(product.price) return cell } }
Như vậy là việc triển khai mô hình MVP đã xong, việc cuối cùng là kết nối các thành phần với nhau, chúng ta sẽ thực hiện ở AppDelegate:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { // Override point for customization after application launch. let nc = self.window?.rootViewController as! UINavigationController let productListViewController = nc.topViewController as! ProductListViewController let products = [ Product(name: "Keyboard", price: 6), Product(name: "Mouse", price: 5) ] let productListPresenter = ProductListPresenter(view: productListViewController, products: products) productListViewController.presenter = productListPresenter return true }
Trong khuôn khổ bài viết này, tôi sử dụng mock data để demo, thực tế các bạn có thể dùng cơ sở dữ liệu như Core Data.
Chạy thử chương trình:
Như vậy là chúng ta đã hoàn thành việc tìm hiểu mô hình MVP, các bạn có thể hoàn thiện chương trình bằng cách thêm các tính năng thêm, sửa, xóa sản phẩm.
Với các bài toán nhỏ, yêu cầu thời gian triển khai nhanh thì mô hình MVC là phù hợp, tuy nhiên với bài toán lớn và yêu cầu có unit test thì các mô hình khác như MVP, VIPER sẽ hợp lý hơn. Hi vọng bài viết này sẽ giúp các bạn có thêm một hướng đi mới cho việc triển khai ứng dụng của mình.
Tham khảo
Source code