Protocol คือชุดของข้อกำหนด (คุณสมบัติและเมธอด) ที่ชนิดที่รับโปรโตคอลไปใช้ต้องปฏิบัติตาม ใน Swift โปรโตคอลเป็นองค์ประกอบหลักของ Protocol-Oriented Programming (POP) ซึ่งช่วยให้เกิดพหุสัณฐานโดยไม่ต้องสืบทอด ตามข้อมูลจาก Swift.org (2025) โปรโตคอลถูกใช้ใน 85% ของชนิดในไลบรารีมาตรฐานของ Swift ใน Objective-C นั้น @protocol มีบทบาทคล้ายกัน แต่มีข้อจำกัด — สำหรับคลาสเท่านั้น
ประเด็นสำคัญ
Protocol ใน Swift คืออินเทอร์เฟซนามธรรมที่กำหนดข้อกำหนดซึ่งชนิดต้องปฏิบัติตาม โปรโตคอลสามารถกำหนดคุณสมบัติ (พร้อม getter/setter), เมธอด (ของอินสแตนซ์และของชนิด), ตัวเริ่มต้น และ subscript ต่างจากคลาส โปรโตคอลสามารถรับไปใช้โดยโครงสร้าง การแจงนับ และคลาส — ให้ความยืดหยุ่นที่ไม่มีในภาษาที่ใช้คลาสล้วนๆ
protocol Drawable {
var boundingRect: CGRect { get }
mutating func draw(in context: CGContext)
}
struct Circle: Drawable {
var center: CGPoint
var radius: CGFloat
var boundingRect: CGRect {
CGRect(x: center.x - radius, y: center.y - radius,
width: radius * 2, height: radius * 2)
}
func draw(in context: CGContext) {
context.addEllipse(in: boundingRect)
}
}โครงสร้าง Circle รับโปรโตคอล Drawable ไปใช้ โดยให้คุณสมบัติ boundingRect และเมธอด draw(in:) ต้องขอบคุณโปรโตคอล ทำให้รูปร่างใดๆ ที่ใช้งาน Drawable สามารถจัดการได้อย่างเป็นเอกภาพ — นี่คือพหุสัณฐานโดยไม่ต้องสืบทอดจากคลาสพื้นฐานร่วม
Protocol-Oriented Programming (POP) คือกระบวนทัศน์ที่ Apple นำเสนอใน WWDC 2015 เป็นทางเลือกแทนการสืบทอดแบบคลาส ใน POP โปรโตคอลเป็นเครื่องมือหลักในการทำให้เป็นนามธรรม และส่วนขยายโปรโตคอลให้การใช้งานเริ่มต้น ซึ่งแก้ปัญหา «การสืบทอดรูปเพชร» และช่วยให้สามารถขยายชนิดที่มีอยู่ได้โดยไม่ต้องแก้ไขซอร์สโค้ด
| ด้าน | POP (เชิงโปรโตคอล) | OOP (เชิงคลาส) |
|---|---|---|
| หน่วยนามธรรม | Protocol | คลาสพื้นฐาน |
| กลไกการใช้ซ้ำ | Protocol extension | การสืบทอด |
| ชนิดค่า | รองรับ (struct) | เฉพาะชนิดอ้างอิง |
| การรับไปใช้หลายครั้ง | Protocol composition | การสืบทอดหลายทาง (ไม่มีใน Swift) |
| ความเสี่ยงการเชื่อมโยง | ต่ำ (การเชื่อมโยงหลวม) | สูง (ลำดับชั้นแข็งเกร็ง) |
ตามบล็อก Apple Swift (2025) โครงสร้างคิดเป็น 70% ของชนิดในแอปพลิเคชัน Swift สมัยใหม่ POP เป็นเหตุผลหลักของการเปลี่ยนแปลงนี้: โปรโตคอลช่วยให้โครงสร้างได้รับพฤติกรรมพหุสัณฐานโดยไม่ต้องเปลี่ยนไปใช้คลาส
โปรโตคอล Swift อยู่ตรงกลางระหว่างอินเทอร์เฟซ Java และคลาสนามธรรม C++ สามารถมีการใช้งานได้ (ผ่านส่วนขยาย) แต่ไม่สามารถมีสถานะ (คุณสมบัติที่เก็บไว้) มาดูความแตกต่างระหว่างสามภาษากัน
| ลักษณะ | Swift Protocol | Java Interface | C++ Abstract Class |
|---|---|---|---|
| การใช้งานเมธอด | ได้ (extension) | ได้ (default methods) | ได้ |
| คุณสมบัติที่เก็บไว้ | ไม่ได้ | ไม่ได้ | ได้ |
| ชนิดค่า | ได้ | ไม่ได้ | ไม่ได้ |
| การใช้งานหลายทาง | ได้ | ได้ | ได้ |
| ตัวเริ่มต้น | ได้ (ข้อกำหนด) | ไม่ได้ | ได้ |
ความแตกต่างหลัก: Swift Protocol สามารถกำหนดตัวเริ่มต้นและ subscript ซึ่งไม่มีใน Java Interface อย่างไรก็ตาม โปรโตคอลไม่สามารถเก็บสถานะได้ — นี่ยังคงเป็นความรับผิดชอบของชนิดที่รับโปรโตคอลไปใช้
Associated Type (ชนิดเชื่อมโยง) คือชนิดทั่วไปภายในโปรโตคอลที่ถูกทำให้เป็นรูปธรรมโดยชนิดที่รับไปใช้ ซึ่งช่วยให้สร้างโปรโตคอลที่ปลอดภัยชนิดได้โดยไม่ต้องระบุชนิดข้อมูลที่เป็นรูปธรรม ชนิดเชื่อมโยงทำงานควบคู่กับเจเนอริกของ Swift
protocol Container {
associatedtype Item
var count: Int { get }
mutating func append(_ item: Item)
subscript(i: Int) -> Item { get }
}
struct IntStack: Container {
var items: [Int] = []
var count: Int { items.count }
typealias Item = Int
mutating func append(_ item: Int) {
items.append(item)
}
subscript(i: Int) -> Int {
items[i]
}
}
func sum<C: Container>(_ container: C) -> C.Item where C.Item: Numeric {
// เนื้อความของฟังก์ชัน
}Container คือโปรโตคอลที่มี associatedtype Item IntStack นำไปใช้โดยระบุ typealias Item = Int ฟังก์ชันเจเนอริก sum ใช้ where clause เพื่อทำงานเฉพาะกับคอนเทนเนอร์ตัวเลข ชนิดเชื่อมโยงทำให้โปรโตคอลเป็นทั่วไปโดยไม่สูญเสียความปลอดภัยของชนิด
Protocol composition — การรวมหลายโปรโตคอลผ่านตัวดำเนินการ & ชนิดที่สอดคล้องกับองค์ประกอบต้องใช้งานโปรโตคอลที่รวมกันทั้งหมด ซึ่งแทนที่การสืบทอดหลายทางที่ไม่มีใน Swift และช่วยให้ระบุข้อกำหนดของพารามิเตอร์ฟังก์ชันได้อย่างแม่นยำ
protocol Named { var name: String { get } }
protocol Aged { var age: Int { get } }
struct Person: Named, Aged {
let name: String
let age: Int
}
func greet(_ entity: Named & Aged) {
print("สวัสดี, \(entity.name), อายุ \(entity.age)!")
}
// Protocol extension — การใช้งานเริ่มต้น
extension Named {
func introduce() {
print("ฉันชื่อ \(name)")
}
}Protocol extension ให้การใช้งานเมธอดเริ่มต้น ชนิดที่รับโปรโตคอลไปใช้สามารถแทนที่เมธอดของส่วนขยายได้ — ในกรณีนั้น การใช้งานของตัวเองจะถูกเรียก นี่คือกลไกที่ Swift ใช้ในการimplement เมธอดที่เป็นทางเลือกโดยไม่ต้องใช้ @objc
@protocol ใน Objective-C เป็นรุ่นก่อนของ Swift Protocol แต่มีข้อจำกัดที่สำคัญ โปรโตคอล Objective-C ใช้ได้เฉพาะกับคลาสเท่านั้น (ไม่ใช่โครงสร้างหรือการแจงนับ) และใช้การจัดส่งแบบไดนามิกผ่านการส่งข้อความ เมธอดสามารถเป็น @required (ค่าเริ่มต้น) หรือ @optional
@protocol Loggable
@required
- (void)logMessage: (NSString *)message;
@optional
- (NSString *)logPrefix;
@end
@interface ConsoleLogger : NSObject
@end
@implementation ConsoleLogger
- (void)logMessage: (NSString *)message {
NSLog(@"[LOG] %@", message);
}
@endต่างจาก Swift โปรโตคอล Objective-C ไม่รองรับชนิดเชื่อมโยง เจเนอริก ส่วนขยายโปรโตคอล หรือชนิดค่า โปรโตคอลเหล่านี้ยังคงเป็นกลไกนามธรรมเชิงคลาส ในขณะที่ Swift Protocol เป็นเครื่องมือพหุสัณฐานที่เต็มเปี่ยมสำหรับทุกชนิด
คำถามที่พบบ่อย
Protocol ไม่สามารถเก็บสถานะ (คุณสมบัติที่เก็บไว้) — ได้เพียงข้อกำหนดคุณสมบัติเท่านั้น คลาสนามธรรมสามารถมีฟิลด์ข้อมูลได้ โปรโตคอลสามารถรับไปใช้โดยโครงสร้างและการแจงนับ ในขณะที่คลาสนามธรรมสามารถเป็นคลาสย่อยได้โดยคลาสเท่านั้น ใน Swift โปรโตคอลเป็นเครื่องมือนามธรรมหลัก คลาสถูกใช้น้อยกว่า
POP คือกระบวนทัศน์ที่โปรโตคอลและส่วนขยายโปรโตคอลแทนที่ลำดับชั้นการสืบทอดที่ลึก แทนที่จะใช้คลาสพื้นฐานที่คลาสย่อยทั้งหมดสืบทอด POP ใช้องค์ประกอบของโปรโตคอลกับการใช้งานเริ่มต้นผ่านส่วนขยาย ซึ่งลดการเชื่อมโยงและปรับปรุงการนำโค้ดกลับมาใช้ใหม่
ได้ โปรโตคอล Swift รองรับการสืบทอด protocol SerializableDrawable: Drawable, Codable — โปรโตคอลที่รวมข้อกำหนดของ Drawable และ Codable ชนิดที่รับ SerializableDrawable ไปใช้ต้องปฏิบัติตามข้อกำหนดของโปรโตคอลทั้งหมดในลำดับชั้น ซึ่งแตกต่างจากการสืบทอดคลาส — โปรโตคอลไม่มีบรรพบุรุษร่วมกัน
some (ชนิดทึบแสง) รับประกันว่าฟังก์ชันคืนชนิดเฉพาะที่สอดคล้องกับโปรโตคอล any (ชนิดอัตถิภาวนิยม) อนุญาตให้เก็บชนิดใดๆ ที่สอดคล้องกับโปรโตคอล some ใช้เพื่อรักษาเอกลักษณ์ของชนิด any ใช้สำหรับคอลเลกชันที่ต่างชนิดกัน some ปรากฏใน Swift 5.1 any ปรากฏใน Swift 5.7
ใช้ is สำหรับตรวจสอบและ as? สำหรับการแปลง: if let drawable = object as? Drawable { drawable.draw(in: ctx) } ใน Objective-C ใช้ conformsToProtocol: Swift รองรับการตรวจสอบ is สำหรับโปรโตคอลที่ไม่มีชนิดเชื่อมโยงด้วย
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม