Ứng dụng Protocol vào việc xây dựng kiến trúc ứng dụng

180
Bài này mình viết nhằm mở rộng cho bài Swift 2: Protocol trước đó. Các bạn có thể nên xem trước về Protocol để tiếp tục theo dõi bài viết này.
Protocol đóng vai trò quan trọng trong việc ẩn đi logic của các đối tượng, giúp chúng “loose coupling” (một nguyên tắc quan trọng trong việc xây dựng kiến trúc ứng dụng). Từ dó chúng ta có thể dễ dàng bảo trì (maintain), mở rộng (extend) cũng như tái sử dụng.

Tôi không biết bạn là ai !! Nhưng tôi biết bạn có khả năng làm được cái tôi cần.

Để giúp mọi người hiểu nguyên lý trên mình sẽ lấy ví dụ mối quan hệ kinh điển là Chủ Nợ và Con Nợ.

1. Hướng đối tượng cơ bản: biết tuốt về nhau, cứ có đối tượng là moi hết ra xài


 

Đoạn code trên đại khái là ta sẽ viết cho 2 đối tượng có thể gọi đến nhau thông qua các method của chúng để vay và trả tiền. Tới đây chương trình không có lỗi và ta tự tin rằng mình đã có kiến thức tốt về hướng đối tượng.

2. Logic của class nào ra class nấy: tôi biết anh là ai và tôi biết anh có thể làm cái tôi muốn

Nếu chúng ta để ý sẽ thấy rằng hình như có gì đó không ổn …. Người mượn tiền thì biết rõ số tiền của người cho mượn và người cho mượn cũng thế. Giống kiểu bạn giật bóp tiền người ta rồi tự lấy tiền và khi nợ tới hạn người ta cũng đòi tiền theo cách mà bạn mượn tiền họ. Tới đây ta nói 2 đối tượng chủ nợ và con nợ biết quá nhiều về nhau hay nói 1 cách khác là dính chặt vào nhau (tight). Mà đó là điều mà ta nên né tránh trong việc xây dựng ứng dụng có kiến trúc tốt.
Thêm vào đó, cách viết trên ta giả sử nếu mà con nợ (Borrower) có thẻ tín dụng, tiền trong ngân hàng thì sao. Lúc đó ta phải update cả 2 object trên gần như toàn bộ logic cho vay tiền ra sao và trả tiền thế nào !!! Và điều này trong thực tế ta gặp rất nhìu, vd như mối quan hệ giữa đơn hàng (Order) và giỏ hàng (Cart) và Sản Phẩm hoặc Kho … Theo cách trên là cứ lấy hết ruột gan nhau ra mà xài (dù chúng có được encapsulation hay không).
Ta có thể thay đổi logic cho 2 đối tượng trên như sau:

 

Đoạn code sau phức tạp hơn khá nhiều nhưng đại khái lúc này ta nói rằng việc cho vay (của chủ nợ) và trả nợ (của con nợ) sẽ rõ ràng hơn. Đặc biệt ta không còn bị tình trạng mượn tiền kiểu côn đồ như cách đoạn code trước đó :). Logic cho việc lấy tiền ở đâu là việc riêng của cả 2 mà không muốn cho đối phương biết. Cách viết này tốt hơn cách đầu tiên nhưng vẫn chưa thể giải quyết vấn đề Lender – Borrower dính chặt vào nhau.

3. Tôi không biết bạn là ai nhưng tôi biết bạn có thể làm được cái tôi cần

Tới đây ta có thể giải quyết vấn đề trên bằng Protocol

 

Điều gì xảy ra nếu ra có class Worker nào đó mà muốn vay tiền ?? Nếu Worker là subclass của Person thì mọi thứ khá đơn giản, nhưng nếu nó đang là subclass của 1 class khác thì sao ?? Ta biết trong hướng đối tượng, 1 class không thể kế thừa từ 2 class. Vì có Protocol rồi thì mọi thứ sẽ đơn giản hơn nhiều:

 

Ta thấy rằng dù class Worker có nhìu logic của nó đến chừng nào thì việc mượn tiền chỉ cần adopt protocol BorrowerBehavior và viết chi tiết các method bên trong. Với Company cũng thế :).
Lúc này 2 đối tượng Lender – Borrower không còn kết dính nữa, chúng cũng chẳng biết ai là ai, con nợ và chủ nợ lúc này chỉ còn là “người có khả năng cho vay” và “người có thể sẽ phải vay tiền”. Từ đó ta có thể linh hoạt sử dụng các đối tượng hơn, không cứ phải là Lender hay Borrower nữa.

4. Tầm quan trọng của Protocol

Protocol đóng vai trò như một chất phân giải các đối tượng, giúp chúng độc lập với nhau. Vì vậy nó được xem như là nền tảng của các kiến trúc ứng dụng đương đại như: VIPER, Clean Architect, …
Protocol hầu như xuất hiện mọi nơi trong ứng dụng iOS, điển hình như: UITableView và UICollectionView chỉ làm nhiệm vụ layout các cell, scroll để thấy các cell tiếp theo. Nhưng chúng sẽ không tự giải quyết được có bao nhiêu item, section trong chúng, cũng như cell cho một vị trí cụ thế (indexPath) sẽ ra sao…. Những cái đó sẽ được “ủy thác” cho datasource.