Swift “Protocol Oriented” Bir Dildir

Öncelikle Protocol nedir üzerine konuşmak gerek sanırım. Cocoa iOS uygulama geliştirme için kullandığımız framework’tür. Zamanla değişen, gelişen özelliklerini bir tarafa bırakırsak aslında Cocoa dilden bağımsızdır, Swift ve Objective-C ile yazmanız önemli değildir. Her iki durumda da aslında aynı framework üzerine uygulama geliştirmekteyiz. Bu yüzden syntax değişse de kullandığımız yapılar aynıdır.

Protocol kavramı da aynı şekilde hem Objective-C hem Swift ile kullanılan bir yapıdır. iOS method çağırma kavramı yerine nesnelerin mesajlaşması mantığını benimsemekte. Aslında bu mesajlaşma mantığının temelini protocol’lerle iletişim oluşturmaktadır. UIApplicationDelegate bu yapının anlaşılması için en temel kullanımdır aslında.  Uygulamanızın başlangıç noktası olan AppDelegate nesnesi UIApplicationDelegate protocol’ünü implement etmektedir ve bir uygulamayı çalıştırdığınız zaman ilk tetiklenen method application:didFinishLaunchingWithOptions methodudur.

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        return true
    }
}

Bu methodu tetikleyen iOS’un kendisidir ve mesajlaşma kavramına en temel örnek bu olabilir. iOS uygulamanın AppDelegate sınıfına application:didFinishLaunchingWithOptions üzerinden bir mesaj gönderir ve bu mesaja bir cevap bekler, return true aslında bu mesaja verilen bir cevap niteliğindedir ve mesajlaşma mantığıda bu iletişim yapısıdır.

Aynı kurguyu UITableView nesnesini kullanırken de görmekteyiz. UITableViewDataSource protokolü aslında UITableView  nesnesinin bulunduğu Controller’la UITableView arasındaki iletişimin kurallarını içermektedir. UITableView temel olarak aşağıdaki 2 protokol methoduna mesaj gönderir ve vereceğiniz cevaplara göre veriyi yönetir. Örnek bir kullanım aşağıdaki gibidir.

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return 10
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "cell")
    return cell!
}

Protocol kullanımını Cocoa framework içerisindeki bir çok nesnede kullanabilirsiniz. UITableViewDelegate, UITextFieldDelegate, CAAnimationDelegate, CLLocationManagerDelegate gibi gibi.

Peki neden Swift kendisini Object Oriented bir dil olarak değilde Protocol Oriented olarak nitelendirmektedir? Object Oriented dillerin en temel özelliği Inheritance’dır, yani nesneler birbirlerinde türetilerek kullanılırlar ve nesnelerin şablonları niteliğindeki Interface’ler bu yapının en temel taşıdır. “Composing Type” konusunda Inheritance’ın kısıtları üzerine konuşmuştuk. Protocol Oriented olmanın en temel gerekliliği nesnelerin tek bir interface’den türetilmesi yaklaşımıdır, bu yaklaşım is-a olarakta nitelendirilir. Yani bir nesne aslında başka bir nesnedir denir. Bu yapının her koşulda ihtiyacı karşılamasına en güzel örnek Penguen’dir 🙂 Penguen tür olarak kuştur, fakat diğer kuşlar gibi uçmaz. Tüm kuşlar hareket kabiliyeti olarak Yürür ve Uçar, fakat Penguenler Yüzer ve Yürür. Bu sebeple aşağıdaki şekilde hazırlanacak bir Kus sınıfı ile ne tür sorunlar yaşayacağımıza bakalım.

class Kus {
    func yuru
    func uc
}
class Penguen : Kus {
    func yuz
}

Yukarıda anlattığımız şekliyle Kus sınıfının yuru ve uc yetenekleri var. Penguen’i Kus’tan türetmeniz durumunda diğer bir yeteceği olan yuz’ü eklemeniz gerekiyor. Fakat bu durumda da beceremediği bir özellik olan uçmayı’da Penguen sınıfına eklemiş oluyoruz. Kus sınıfı içerisinden uçmayı çıkarsak bu durumda da diğer tüm kuşların ortak özelliği olan bir yeteneği çıkarmış oluyoruz ve bir anlamı kalmıyor.

Protocol Oriented olma yaklaşımı bu soruna çözümdür. is-a kavramı yerine has-a özelliği katmanızı sağlar. Yani bir nesne tam anlamıyla başka bir nesne olmak yerine, başka başka özelliklere sahip olabilir. Çünkü bir nesne tek bir Interface’den türetilebilecekken, birden çok Protokolü implement edebilir. Şimdi yapıyı buna nasıl dönüştüreceğimize bakalım.

Öncelikle protokollerimizi ekleyelim.

protocol Yuruyenler {
    func yuru()
}
protocol Ucanlar {
    func uc()
}
protocol Yuzenler {
    func yuz()
}

Şimdide Penguen sınıfımızı düzenleyelim.

class Penguen: Kus, Yuruyenler, Yuzenler{

}

Inheritance’dan farklı olarak “Protocol Oriented” yapısında bir nesnenin birden fazla protokol’ü impelement edebileceğini konuşmuştuk. Bu avantajla yukarıdaki Penguen nesnesi hem Kus sınıfından türemiş, hem de farklı protokolleri implement ederek gerekli yeteneklere sahip hale gelmiş oldu. Burada değinmek istediğim bir başka konu daha var. Bu şekildeki bir kullanımda Xcode sizi protokoller içerisindeki methodları eklemeniz için zorlayacak ve aşağıdaki şekilde hata verecektir.

Bir interface tanımlanırken methodların sadece tanımları yapılabilmektedir. Bu sebeple interface’den türeyen sınıflar içerisinde ilgili methodların implementasyonunun yapılması beklenmektedir. Yukarıdaki kullanım şekliyle aynı kural protokoller içinde geçerlidir. Şayet protokol methodlarının implementasyonuna zorlamak istemiyorsanız methodların önüne optional ifadesini ekleyebilirsiniz. Bu durumda Objective-C uyumluluğu gereği @objc kullanmanız gerekecektir.

@objc protocol Yuruyenler {
    @objc optional func yuru()
}

Veya protokol içerisinde yapılan method tanımına ilave olarak default bir method implementasyonu yapabilirsiniz. Bunu yapabilmek için bir Protocol Extension tanımlamanız gerekecektir. Bu da yine Protocol’ü Interface’den ayıran önemli özelliklerden biridir.

extension Yuruyenler {
    func yuru(){

    }
}
extension Yuzenler {
    func yuz(){

    }
}

 

Share on FacebookShare on Google+Tweet about this on TwitterShare on LinkedIn
Bu yazıyı beğendiyseniz daha fazla kişiye ulaşmasını sağlamak için paylaşabilirsiniz.

3 comments

Gayet başarılı bir yazı. Kaleminize sağlık…

Leave a reply