プロトコル

Swift は世界ではじめてのプロトコル指向である言語であると2015 年にWWDCで宣言されたようです。Javaにもプロトコルに似たインターフェースあるけどあれとは別扱いなんだろうか?

プロトコルとは、クラスや構造体、列挙型などに準拠させる決まりを作るものです。Javaで言うインターフェースがSwiftのプロトコルに近いものだと個人的に思っています。(そもそもあんま違いがわかっていないけど…)

プロトコル指向についてはiOSアプリ開発デザインパターン入門と言う本が参考になりました。

iOSアプリ開発デザインパターン入門

参考文献

プロトコルの使い方

protocol プロトコル名 {
  プロパティ宣言
  メソッド宣言
}

プロトコルはprotocolと言う予約語を用いて宣言します。そしてプロトコルを準拠するクラス、構造体、列挙型が実装する必要のあるプロパティとメソッドを宣言します。

具体的な例をみていきましょう!今回は生き物(Creature)というプロトコルを作ってみます。どんな生き物にも名前があり、大きさがあり、食べることができると想定してみました。

protocol Creature {
	var name : String { get }
	var size : Float { get set }
	func eat()
}

プロパティのnameとsizeに見慣れないものがついていると思います。

{ get }  {get set } 

こいつらは何者かと言うと、準拠される先のクラス、構造体、列挙型の中で値が書き換えられるか否かを示しています。

{ get }値の取得が可能
{get set } 値取得との書き換え可能

ここで勘違いしてしまうのが、{get}これはlet、{get set}これはvarと考えてしまうことです。私は間違って覚えていました…

またプロトコル内のプロパティはvarを用いて宣言をする必要があります。プロトコルではletを用いて定数を定義することはできません。プロトコルはあくまで必要なものを[宣言]するだけで、抽象的なものです。実装を行うのは準拠先のクラス、構造体、列挙型なのでそこでletを用いて定義を行います。

実際にclassに準拠させてみます。生き物のルールーを準拠させて人間を作ってみます。

class Human : Creature {
	let name
	var size
	func eat(){
	   print("うまい!!")
	}
        init(name: String, size: Int){
           self.name = name
           self.size = size
        }
}

HumanクラスはCreatureプロトコルに準拠しているので、Creatureプロトコルで定義したname, sizeに具体的な値、eat()に具体的な実装を行なっています。どれか1つでも実装をしなかった場合エラーになってしまします。

次にCreatureクラスを準拠した犬クラスを作ってみます

class Doc : Creature {
      let name
      let size
      func eat(){
	   print("うまい!!")
	}
        init(name: String, size: Int){
           self.name = name
           self.size = size
        }
}

eat関数の中身が人間も犬も同じ実装になっています。同じ実装を毎回書くのはめんどくさいですよね。Swiftには拡張と言う機能が含まれており、これを使うと準拠先のクラスなどで処理を記述しなくても良くなります。

拡張(Extension)

protocol Creature {
	var name : String { get }
	var size : Float { get set }
}
extension Creature{
    func eat(){
        print("うまい!!")
    }
}

このように記述をすることで、準拠先での実装が不要になります。

let harumi = Human("はるみ", 168)
print(harumi.name)
print(harumi.size)
eat()
let pochi = Doc("ポチ", 80)
pochi.eat()