Protocol-Oriented Programming trong Swift (Phần 1)

(If you are looking for English version, here you go: POP English)

Lập trình hướng đối tượng (OOP – Object Oriented Programming): Ok Done.

oop-meme.jpg

Lập trình hướng Protocol (POP – Protocol Oriented Programming): Dafuq ????

d52f9ca95731da7aab0b7c704df9154b32d08d4f6d87b25a848ed2df9386451e

// Protocol: từ này mình không biết dịch thế nào cho hợp lý : giao thức, giao tác, … nghe nó không tròn ý lắm, nên mình xin đặt tên theo nguyên bản. (Protocol này tương tự như Interface của Java và C#)

Thuật ngữ Protocol-Oriented Programming (POP) được giới thiệu lần đầu tại hội nghị lập trình viên thường niên của Apple – WWDC 2015, với bản cập nhật Swift 2.0. Tại đây, Apple đã gọi ngôn ngữ của mình (Swift) là POP, chứ không phải là OOP như một số ngôn ngữ bậc cao khác : Java, Python, C#, C++,…

—> Protocol-Oriented Programming là cái quái gì ?

1. What the Hell is POP?

Nếu bạn đã làm quen với OOP rồi thì khi so sánh với POP, bạn sẽ thấy nhiều điểm tương đồng. Hãy nghĩ một chút về OOP, khi bạn giải quyết vấn đề bằng OOP, bạn sẽ suy nghĩ hướng giải quyết bài toán thông qua các Class và Object (instance của Class). Với POP, bạn thay vì suy nghĩ dưới góc độ của Class, bạn sẽ chuyển sang tập trung vào các Protocol ( lý do sẽ được giải thích sau). Xu hướng lập trình Swift hiện đại là cố gắng sử dụng POP kết hợp với Struct, thay vì sử dụng Class. Protocol linh hoạt hơn Class; instance của Struct được cấp phát trên Stack, còn instance của Class được cấp phát trên Heap nên phương pháp này thực thi tốt hơn. Tặng bạn câu note của Apple tại WWDC:

“THINK ABOUT PROTOCOL FIRST”

–> POP là phương pháp lập trình thiết kế và làm việc ưu tiên với các Protocol, thay vì Class như OOP.

2. Dude, Why we need to use POP?

POP được đưa ra để thay thế OOP, hiển nhiên POP phải có ưu điểm gì đó vượt trội hơn, hay nói cách khác OOP phải có điểm hạn chế gì thì Apple mới quyết định thay thế nó chứ.

Những việc mà Class làm được, thì Struct và Enum đều có thể làm được, như: tính đóng gói (encapsulation), đa hình (polymorp), kế thừa (inheritance),…

==> OOP làm được gì, thì POP cũng có thể làm được việc đó.

Nói về điểm yếu của Class và OOP, hãy thử xem xét một số ví dụ sau:

a. Implicit Sharing Data (Chia sẻ dữ liệu ngầm)

Trong ngôn ngữ Swift, Class là Reference Type, còn Struct là Values Type. Việc chúng ta truyền instance của Class (Object), thực chất là chúng ta truyền Reference (tham chiếu) của nó. Giả sử mình có một Class thể hiện họ nhà Chim sau:

Screen Shot 2016-08-24 at 11.26.00 AM.png

Mình tạo 1 instance của Bird, và tạo thêm một reference mới của nó, thông qua 2 biến “myParrot” và “hisParrot”. Thử xem reference tác động như thế nào nhé:

Screen Shot 2016-08-24 at 11.30.02 AM.png

Kết quả:

Screen Shot 2016-08-24 at 11.34.39 AM.png

==> Khi hisParrot thay đổi “name” của nó, thì đồng thời myParrot cũng thay đổi theo, do cả 2 cùng tham chiếu đến 1 địa chỉ. Vấn đề này gọi là “Implicit Sharing Data”.

Implicit Sharing Data quan trọng và cần được chú ý đối với các bài toán đa tham chiếu, và đa luồng. Trong xử lý đa luồng, bài toán khó nhằn bậc nhất chính là phải xử lý đồng thời (concurrency). Implicit Sharing Data rất dễ gây ra các lỗi như giá trị nhảy lung tung (do nhiều luồng cùng thay đổi), Deadlock (do việc chờ đợi quyền thay đổi vùng tham chiếu),…65068231.jpg

b. Inheritance problem (vấn đề về tính kế thừa):

Screen Shot 2016-08-22 at 2.45.02 PM.png

Ta có Class cha là Bird, và các subclass kế thừa từ nó là Parrot và Eagle. Ok, các class con này không có vấn đề gì hết, thế nhưng giả sử ta có trường hợp sau:

Screen Shot 2016-08-22 at 2.48.29 PM.png

Bây giờ ta có một class con khác, là class Penguin, Penguin rõ ràng cũng là một loài chim, thế nhưng nó lại không có lông vũ, và cũng không thể bay.

123.png

c. Classes Relationship Problem (vấn đề về mối quan hệ giữa Các Class):

Giả sử mình có một Class như sau:

Screen Shot 2016-08-24 at 2.08.07 PM.png

Screen Shot 2016-08-24 at 2.12.59 PM.png

Class Number và Class Text là 2 class con kế thừa từ Class này, tuy nhiên khi mình override lại hàm isGreaterThan từ class cha của nó, một vấn đề nảy sinh: mình muốn so sánh property value của nó, để quyết định kết quả trả lại, thế nhưng, vì tham số mình truyền vào có kiểu là kiểu của Class cha (AbstractComparation), nên mình buộc phải ép kiểu để so sánh. Điều này khá bất tiện, như bạn thấy, trong hình mình có 2 class con, mình phải ép kiểu 2 lần, nếu sau này mở rộng ra hàng chục hàng trăm class con, thì có nghĩa mình cũng phải ép kiểu hàng chục hàng trăm lần.

Apple gọi vấn đề này là Type Relationship Lost.

Ở trên mình đã trình bày 3 vấn đề về điểm yếu khi sử dụng Class (OOP) trong Swift. Tại phần sau mình sẽ đưa ra cách giải quyết những vấn đề trên bằng POP và Struct.

Hết phần 1. Link phần 2