Refactoring table view data source and delegate methods
Bài đăng này đã không được cập nhật trong 7 năm
Nếu bạn đã code ios với swift hay object c thì chắc hẳn bạn cũng nhận thấy những vấn đề với controller quá khổ của mình và làm cách nào để giảm bớt nó xuống. Điều tất nhiên, cách thông dụng nhất là sẽ di chuyển datasource và delegate ra khỏi viewController. Kết quả, cấu trúc rõ ràng hơn, controller ít code hơn, dễ bảo trì hơn. Trong bài này mình sẽ đưa ra một vài cách để refactor. Hi vọng bài viết sẽ giúp ích cho bạn. Có gì hãy comment bên dưới nhé.!
Do nothing
Bạn không phải làm gì cả. Bạn chỉ cần loại bỏ datasource và delegate method ra khỏi viewController
Hãy xem một ví dụ về danh sách creamFlavors
IceCreamListViewController dưới đây rất phổ biếnbiến. Nó thực hiện các method của UITableViewDataSource sau đây:
- numberOfSectionsInTableView(_
- tableView(_:numberOfRowsInSection:)
- tableView(_:cellForRowAtIndexPath:indexPath:)
IceCreamListViewController code sẽ như sau:
class IceCreamListViewController: UITableViewController
{
let dataStore = IceCreamStore()
// MARK: - View lifecycle
override func viewDidLoad()
{
super.viewDidLoad()
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int
{
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return dataStore.allFlavors().count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let flavor = dataStore.allFlavors()[indexPath.row]
let cell = tableView.dequeueReusableCellWithIdentifier("IceCreamListCell", forIndexPath: indexPath)
cell.textLabel?.text = flavor
return cell
}
}
Một IceCreamStore đơn giản bằng class Switf để lưu một số ice cream flavors. Class có một method là allFlavors() trả về toàn bộ flavors. Điều này giúp bạn không phải lưu flavors trong viewControllerviewController
IceCreamStore code như sau:
class IceCreamStore
{
private let flavors = ["Vanilla", "Chocolate", "Strawberry", "Coffee", "Cookies & Cream", "Rum Raisins", "Mint Chocolate Chip", "Peanut Butter Cup"]
func allFlavors() -> [String]
{
return flavors
}
}
Code không có sai cả. Nhưng vấn đề ở 3 method datasource UITableViewDataSource bên trong IceCreamListViewController. Nào hãy đá chúng ra khỏi tableviewController nào
Refactor method data source trong tableview
Có một kĩ thuật rất phổ biến mà chúng ta thường thấy là di chuyển tất cả các phương thức UITableViewDataSource vào trong class của chúng ta. Hãy theo dõi class IceCreamListDataSource sau với protocol UITableDataSource và implement 3 method. Chúng ta sẽ chuyển code chứ không làm thay đổi hành vi
class IceCreamListDataSource: NSObject, UITableViewDataSource
{
let dataStore = IceCreamStore()
// MARK: - Table view data source
func numberOfSectionsInTableView(tableView: UITableView) -> Int
{
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return dataStore.allFlavors().count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let flavor = dataStore.allFlavors()[indexPath.row]
let cell = tableView.dequeueReusableCellWithIdentifier("IceCreamListCell", forIndexPath: indexPath)
cell.textLabel?.text = flavor
return cell
}
}
Bây giờ hay di chuyển code và để lại viewController một "căn nhà" thoáng mát nào:
class IceCreamListViewController: UITableViewController
{
let dataSource = IceCreamListDataSource()
// MARK: - View lifecycle
override func viewDidLoad()
{
super.viewDidLoad()
tableView.dataSource = dataSource
}
}
Di chuyển các hàm của table view data source tới một Switf extension
Nếu mục tiêu là giảm mã trong viewController thì chúng ta chỉ cần di chuyển các phương thức data source ra khỏi viewController và cho vào Swift extension
class IceCreamListViewController: UITableViewController
{
// MARK: - View lifecycle
override func viewDidLoad()
{
super.viewDidLoad()
}
}
// MARK: - Table view data source
extension IceCreamListViewController: UITableViewDataSource {
var dataStore: IceCreamStore {
return IceCreamStore()
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int
{
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return dataStore.allFlavors().count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let flavor = dataStore.allFlavors()[indexPath.row]
let cell = tableView.dequeueReusableCellWithIdentifier("IceCreamListCell", forIndexPath: indexPath)
cell.textLabel?.text = flavor
return cell
}
}
Hãy nhìn lại, IceCreamListViewController rất là "empty" và phần extension trông giống hệt nhau. Việc thực hiện refactor không làm cho mã nguồn thay đổi.
Lưu ý rằng Swift extension không cho phép chúng ta định nghĩa thuộc tính "stored". Chúng ta phải sử dụng thuộc tính "computed" để trả về một instance của IceCreamStore() và nó sử dụng giống như được sử dụng trước đó.
Lợi ích của việc refactor
- Tái cấu trúc (refactoring)
- Các tiếp cận: "Do nothing" là dễ tiếp cận và sử dụng nhất, Nhưng viewController của bạn sẽ tăng kich thước và độ phức tạp theo thời gian. Hãy tưởng tượng điều gì xảy ra với một viewController lớn. Lương code sẽ là rất lớn.
- Trước khi chúng ta bỏ cách tiếp cận trên thì hãy nghĩ sâu hơn. Khi nào mà viewController của bạn tăng lên: Đó là logic về label, text_fields, picker và các custom views của bạn. Hay những logic.
- Chúng ta di chuyển chúng ra khỏi viewController thay vì xử lý logic trong viewController
- Và tôi nghĩ điều này là con đường đi đúng. Nguyên tắc, Mọi thức nên nằm ngoài viewController nếu là xử lý logic
- Tái sử dụng
- Dễ sử dụng
- Khả năng bảo trì cao
Kết luận.
Giải pháp của tôi là:
- Làm đơn giản view controller, di chuyển tất cả data source và delegate method vào bên trong viewController là rất tệ
- Đối với những view controller phức tạp, có thể tách riêng từng phần bằng Swift extension
Điều quan trọng và phải làm cho các viewController tối giản và gọn nhất.
Link tham khảo:
https://clean-swift.com/refactoring-table-view-data-source-and-delegate-methods/
All rights reserved