protocol

Protocol ها شبیه به کلاسها هستند اما فقط شامل تعاریفی برای متدها و خواص (Property) می‌باشند. Protocol ها را می‌توان به عنوان پلاگین های کلاس‌ها در نظر گرفت. کلاسی که یک Protocol خاص را پیاده سازی می‌کند لازم است که کدهایی برای اجرا توسط اعضا و متدهای آن فراهم کند چون اعضا و متدهای Protocol هیچ کد اجرایی در بدنه خود ندارند. اجازه دهید که نحوه تعریف و استفاده از یک Protocol در کلاس را توضیح دهیم :

 1: protocol SampleProtocol
 2: {
 3:     func ShowMessage(message: String)
 4: }
 5: 
 6: class Sample : SampleProtocol
 7: {
 8:     func ShowMessage(message: String)
 9:     {
10:         print(message)
11:     }
12: }
13: 
14: 
15: var sample = Sample()
16: 
17: sample.ShowMessage(message: "Implemented the SampleProtocol protocol!")
Implemented the SampleProtocol protocol!

در خطوط 4-1 یک Protocol به نام SampleProtocol تعریف کرده‌ایم. بر طبق قراردادهای نامگذاری، Protocol ها به شیوه کوهان شتری نامگذاری می‌شوند. همچنین در تعریف آنها باید از کلمه کلیدی protocol استفاده شود. یک متد در داخل بدنه Protocol تعریف می‌کنیم (خط 3). به این نکته توجه کنید که متد تعریف شده فاقد بدنه است و در آخر آن باید از سیمیکولن استفاده شود.
وقتی که متد را در داخل Protocol تعریف می‌کنید، فقط لازم است که عنوان متد (نوع، نام و پارامترهای آن) را بنویسید. به این نکته نیز توجه کنید که متدها و خواص تعریف شده در داخل Protocol سطح دسترسی ندارند. چون باید همیشه هنگام اجرای کلاسها در دسترس باشند. برای پیاده سازی یک Protocol توسط یک کلاس از علامت دو نقطه (:) استفاده می‌شود. کلاسی که Protocol را اجرا می‌کند کدهای واقعی را برای اعضای آن فراهم می‌کند. همانطور که در مثال بالا می‌بینید کلاس Sample، متد ShowMessage() پروتکل SampleProtocol را اجرا و تغذیه می‌کند. برای روشن شدن کاربرد Protocol ها به مثال زیر توجه کنید:

 1: class CA
 2: {
 3:     var FullName : String
 4:     var Age      : Int
 5: 
 6:     init(fullname: String, age: Int)
 7:     {
 8:         self.FullName = fullname
 9:         self.Age = age
10:     }
11: }
12: 
13: class CB
14: {
15:     var FirstName  : String
16:     var LastName   : String
17:     var PersonsAge : Int
18: 
19:     init(firstname: String, lastname: String, personage: Int)
20:     {
21:         self.FirstName  = firstname
22:         self.LastName   = lastname
23:         self.PersonsAge = personage
24:     }
25: }
26: 
27: func PrintInfo(item: CA)
28: {
29:     print("Name: \(item.FullName), Age \(item.Age)")
30: }
31: 
32: var a = CA(fullname: "John Doe", age: 35)
33: 
34: PrintInfo(item: a)
Name: John Doe, Age 35

در کد بالا دو کلاس CA و CB تعریف شده‌اند، در کلاس CA دو فیلد به نام FullName و Age و در کلاس CB سه فیلد به نام های FirstName و LastName ،PersonsAge تعریف کرده‌ایم. در کلاس Program یک متد به نام ()PrintInfo داریم که یک پارامتر از نوع کلاس CA دارد. به شکل ساده در این متد مقدار فیلدهایشی ای که به این متد ارسال شده است چاپ می‌شود. در متد Main یک شیء از کلاس CA ساخته‌ایم و فیلدهای آن را مقدار دهی کرده‌ایم. سپس این شیء را به متد ()PrintInfo ارسال می‌کنیم. کلاس‌های CA و CB از نظر مفهومی شبیه یکدیگر هستند مثلاً کلاس CA فیلد FullName را برای نمایش نام و نام خانوادگی دارد ولی کلاس CB برای نمایش نام و نام خانوادگی دو فیلد جدا از هم به نام های FirstName و LastName را دارد. و همچنین یک فیلد برای نگهداری مقدار سن داریم که در کلاس CA نام آن Age و در کلاس CB نام آن PersonAge می‌باشد. مشکل اینجاست که اگر ما یک شیء از کلاس CA را به متد (()PrintInfo) ارسال کنیم از آنجایی که در داخل بدنه این متد فقط مقدار دو فیلد چاپ می‌شود اگر بخواهیم یک شیء از کلاس CB را به آن ارسال کنیم که دارای سه فیلد است با خطا مواجه می‌شویم (زیرا متد ()PrintInfo با ساختار کلاس CA سازگار است و فیلدهای CB را نمی‌شناسد). برای رفع این مشکل باید ساختار دو کلاس CA و CB را شبیه هم کنیم و این کار را با استفاده از protocol انجام می‌دهیم.

 1: protocol PersonInfo      
 2: {                    
 3:     func GetName() -> String
 4:     func GetAge () -> String
 5: }            
 6: 
 7: class CA: PersonInfo
 8: {
 9:     var FullName : String
10:     var Age      : Int
11: 
12:     init(fullname: String, age: Int)
13:     {
14:         self.FullName = fullname
15:         self.Age = age
16:     }
17: 
18:   func GetName() -> String { return FullName   }  
19:   func GetAge () -> String { return String(Age)}  
20: }
21: 
22: class CB: PersonInfo
23: {
24:     var FirstName  : String
25:     var LastName   : String
26:     var PersonsAge : Int
27: 
28:     init(firstname: String, lastname: String, personage: Int)
29:     {
30:         self.FirstName  = firstname
31:         self.LastName   = lastname
32:         self.PersonsAge = personage
33:     }
34:   
35:   func GetName() -> String { return FirstName + " " + LastName} 
36:   func GetAge () -> String { return String(PersonsAge)}    
37: }
38: 
39: func PrintInfo(item: PersonInfo)
40: {
41:   print("Name: \(item.GetName()), Age \(item.GetAge())")
42: }
43: 
44: var a = CA(fullname: "John Doe", age: 35)
45: var b = CB(firstname: "Jane", lastname: "Doe", personage: 33)
46: 
47: PrintInfo(item: a)
48: PrintInfo(item: b)
Name: John Doe, Age 35
Name: Jane Doe, Age 33

کد بالا را می‌توان به اینصورت توضیح داد که در خط 5-1 یک Protocol به نام PersonInfo تعریف و آن را در خطوط 4 و 22 توسط دو کلاس CA و CB پیاده سازی کرده‌ایم. چون این دو کلاس وظیفه دارند متدهای این Protocol را پیاده سازی کنند پس در خطوط 19-18 و 36-35 کدهای بدنه دو متد این Protocol را آن طور که می‌خواهیم، می‌نویسیم. در خط 39 متد ()PrintInfo را طوری دستکاری می‌کنیم که یک پارامتر از نوع Protocol دریافت کند. حال زمانی که دو شیء از دو کلاس CA و CB در دو خط 45 و 46 ایجاد می‌کنیم و آنها را در دو خط 47 و 48 به متد ()PrintInfo ارسال می‌کنیم، چونکه این دو کلاس Protocol PersonInfo را پیاده سازی کرده‌اند به طور صریح به Protocol تبدیل می‌شود. یعنی کلاسی که یک Protocol را پیاده سازی کند به طور صریح می‌تواند به Protocol تبدیل شود. حال بسته به اینکه شیء کدام کلاس به متد ()PrintInfo ارسال شده است، متد مربوط به آن کلاس فراخوانی شده و مقادیر فیلدها چاپ می‌شود. می‌توان چند Protocol را در کلاس اجرا کرد.

class Sample : SampleProtocol1, SampleProtocol2, SampleProtocol3
{
   //Implement all protocols
}

به مثال زیر توجه کنید :

 1: protocol FirstProtocol                     
 2: {                                                       
 3:     func FirstMessage(message :String) 
 4: }                                             
 5: 
 6: protocol SecondProtocol                    
 7: {                                                       
 8:     func SecondMessage(message :String)
 9: }                                             
10:                                                                 
11: class Sample : FirstProtocol , SecondProtocol                   
12: {                                                                
13:     func FirstMessage(message :String)                      
14:     {                                                            
15:         print(message)                              
16:     }
17:     
18:     func SecondMessage(message :String)                      
19:     {                                                            
20:         print(message)                              
21:     }
22: } 
23:      
24: var sample = Sample()                            
25:                                                          
26: sample.FirstMessage (message: "Implemented the FirstProtocol protocol!")
27: sample.SecondMessage(message: "Implemented the SecondProtocol protocol!")
Implemented the IFirstprotocol protocol!
Implemented the ISecondprotocol protocol!

درست است که می‌توان از چند Protocol در کلاس استفاده کرد ولی باید مطمئن شد که کلاس می‌تواند همه اعضای Protocol ها را تغذیه کند. همانطور که در کد بالا مشاهده می‌کنید دو Protocol به نام های FirstProtocol و SecondProtocol در خطوط 9-1 تعریف شده‌اند که به ترتیب دارای دو متد به نام های ()FirstMessage و ()SecondMessage می‌باشند. در خط 11 کلاس Sample این دو Protocol را پیاده سازی کرده است و درنتیجه همانطور که اشاره شد لازم است که کدهای بدنه دو متد موجود در این دو Protocol را تغذیه کند. که این کار در خطوط 21-13 انجام شده است. اگر یک کلاس از کلاس پایه ارث ببرد و در عین حال از Protocol ها هم استفاده کند، در این صورت باید نام کلاس پایه قبل از نام Protocol ها ذکر شود. به شکل زیر :

class Sample : BaseClass, SampleProtocol1, SampleProtocol2
{
}

می‌توان از یک Protocol یک متغیر ایجاد کرد :

var sample: SampleProtocol

ولی نمی‌توان از یک Protocol نمونه‌ای ایجاد کرد، چون Protocol ها دارای سازنده نیستند، مثلاً کد زیر اشتباه است :

var sample = SampleProtocol()

حال فرض کنید که ما یک متغیر از یک Protocol ایجاد کردیم. حال چه چیزی را می‌توانیم به این متغیر اختصاص دهیم و کاربرد آن چیست؟ بعد از ایجاد متغیر می‌توان یک شیء از کلاسی که Protocol را پیاده سازی کرده است به آن اختصاص دهیم :

var sample: SampleProtocol = SampleClass()

برای درک کاربرد آن نیز به مثال زیر توجه کنید :

 1: protocol BaseProtocol
 2: {
 3:     func BaseMethod()
 4: }
 5: 
 6: class Sample : BaseProtocol
 7: {
 8:     func BaseMethod()
 9:     {
10:         print("Hello World")
11:     }
12: }
13: 
14: var base: BaseProtocol = Sample()
15: base.BaseMethod()
Hello World

همانطور که در کد بالا مشاهده می‌کنید در خط 6 پروتکل BaseProtocol توسط کلاس Sample پیاده سازی شده است. در نتیجه در خط 14 می‌توانیم یک شیء از کلاس Sample را به این Protocol اختصاص دهیم. در خط 15 هم می‌توانیم با شیء ایجاد شده از این Protocol و سپس گذاشتن علامت نقطه به متد ()BaseMethod دسترسی یابیم. Protocol ها حتی می‌توانند از Protocol های دیگر با استفاده از علامت دو نقطه ارث بری کنند. به مثال زیر توجه کنید :

 1: protocol BaseProtocol
 2: {
 3:     func BaseMethod()
 4: }
 5: 
 6: protocol SampleProtocol : BaseProtocol
 7: {
 8:     func ShowMessage(_ message: String)
 9: }
10: 
11: class Sample : SampleProtocol
12: {
13:     func BaseMethod()
14:     {
15:         print("Method from base protocol!")
16:     }
17: 
18:     func ShowMessage(_ message: String)
19:     {
20:         print(message)
21:     }
22: }
23: 
24: var sample = Sample()
25: 
26: sample.ShowMessage("Implemented the SampleProtocol protocol!")
27: sample.BaseMethod()
Implemented the SampleProtocol protocol!
Method from base protocol!

همانطور که در خط 6 کد بالا مشاهده می‌کنید پروتکل SampleProtocol از پروتکل BaseProtocol ارث بری کرده است پس حتی اگر کلاس Sample فقط پروتکل SampleProtocol را پیاده سازی کند، لازم است که همه اعضای BaseProtocol را هم پیاده سازی کند چون SampleProtocol از آن ارث بری می‌کند.