-
Notifications
You must be signed in to change notification settings - Fork 0
Swift_protocol
設計図を書くルールを決めよう!
知ってる人向け:他の言語で言うインターフェースに近い存在です
一般的にプロトコルというのは、データ通信を行うルールの事です。
ウェブ通信で使われる HTTP の P はプロトコルを意味しています。
なぜそんなルールがあるのでしょうか?
それは、いろいろな企業が勝手に通信機械を作っても、異なる通信機械間での通信を可能にするためです。
例えば、メールの送信ルールを以下のようにしましょう。
[SENDER]: "[ここに送信者を書く]"
[RECEIVER]: "[ここに受信者を書く]"
[MESSAGE]: "[ここに本文を書く]"
すると以下のような情報にすればメール通信が可能になるわけですね。
[SENDER]: "小林"
[RECEIVER]: "佐藤"
[MESSAGE]: "こんにちは!"
これで、どの企業も企業間で話し合うことなく、送信者の名前を見るには SENDER を見たらいいんだね、ってわかるわけです。
もしルールが決まって無ければ、SENDER が sender だったり、MESSAGE が BODY になったりと...個々が自由に作ってしまい、どれ見ればいいんだ!ってなってしまいますね...
そうなれば異なる企業間では通信ができなくなっていたことでしょう。
つまり、共通ルールであるプロトコルを作り、それにみんなが従うことで、わざわざ話し合いなどせずとも問題なく通信ができるようになったわけです。
さて、そんな便利なプロトコル。実は Swift にも存在しています。
とはいえ、別に通信のために存在してるわけではありません。
でもさっき言った、話し合わなくとも問題なく通信ができるってとこ、プログラミングでも便利だと思いませんか?
例えば、あなたはとあるソシャゲの開発者チームのひとり、 A さんです。
ここで、課金ユーザーと無課金ユーザーのクラスを作るとしましょう。
こいつらには、課金度に代わってコイン消費量とレア度が変わるクソみたいなガチャを実装します。
// ユーザークラス
class User {
let name: String
init(name: String) {
self.name = name
}
}
// 無課金ユーザー
class NoMoneyUser: User {
let cost = 10
func gacha() {
print("\(self.cost)コイン使用してガチャを引きます!")
print("レア!")
}
}
// 課金ユーザークラス
class MoneyUser: User {
let cost = 5
func gacha() {
print("\(self.cost)コイン使用してガチャを引きます!")
print("激レア!")
}
}
さて、このとき新しく重課金ユーザークラスを B さんが実装したようです。
// 重課金ユーザークラス
class HeavyMoneyUser: User {
let coin = 3
func gasha() {
print("\(self.coin)使用してガチャを引きます!")
print("超激レア!")
}
}
あらら、なんとスペルをミスってしまったようです。しかも cost
が coin
になっています...
しかし...もちろんこれでコンパイルエラーは出てくれません。バグる gasha
メソッドが実装されてしまいました。
A さん「じゃあ親クラスに gacha
メソッドを実装して override
して使わせればいいんじゃね?」
いいですね。やってみましょう。
// ユーザークラス
class User {
let name: String
init(name: String) {
self.name = name
}
func gacha() {
print("")
}
}
// 無課金ユーザー
class NoMoneyUser: User {
let cost = 10
override func gacha() {
print("\(self.cost)使用してガチャを引きます!")
print("レア!")
}
}
// 課金ユーザークラス
class MoneyUser: User {
let cost = 5
override func gacha() {
print("\(self.cost)使用してガチャを引きます!")
print("激レア!")
}
}
// 重課金ユーザークラス
class HeavyMoneyUser: User {
let coin = 3
func gasha() {
print("\(self.coin)使用してガチャを引きます!")
print("超激レア!")
}
}
あらら、ちゃんと B さんに親クラスの gacha
メソッドを override
してくれって話しましたか?
ちゃんと話さないと、override
してくれませんよ。
バグってしまいます...話し合いましょう...でも...いちいち仕様が変るたびに...話し合い...?
そんなんめんどいわ!
みんなで作ってても、話し合うことなくルールに則った実装ができれば安全ですね。
ではそれはどうすれば実現できるでしょうか?
答えは、 実装すべきメソッドを実装してなかったらコンパイルエラーで教えてくれ! です。
これをプロトコルで実現することができます。
さあ、プロトコルを使ってクラスを作るルールを決めてやりましょう。
User クラスではなく、GachaNeed プロトコルを作成しました。
ここに書いてあるメソッドやプロパティを実装しなかったらコンパイルエラーで教えてくれます。
protocol GachaNeed {
var cost: Int { get }
func gacha() -> void
}
プロパティに { get }
だのがついてますが、これは読み取りのみを可能にするものです。アクセス修飾子のときに話した private(set)
と一緒ですね。
これは継承と同じように使うことができます。
// 無課金ユーザー
class NoMoneyUser: GachaNeed {
let name: String
let cost = 10
init(name: String) {
self.name = name
}
override func gacha() {
print("\(self.cost)使用してガチャを引きます!")
print("レア!")
}
}
// 課金ユーザークラス
class MoneyUser: GachaNeed {
let name: String
let cost = 5
init(name: String) {
self.name = name
}
override func gacha() {
print("\(self.cost)使用してガチャを引きます!")
print("激レア!")
}
}
// 重課金ユーザークラス
class HeavyMoneyUser: GachaNeed {
let name: String
let coin = 3 // coinになっているのでエラーが出る
init(name: String) {
self.name = name
}
// gashaになってるのでエラーが出る!
func gasha() {
print("\(self.coin)使用してガチャを引きます!")
print("超激レア!")
}
}
これで B さんはルールに則っていないメソッドに気づくことができます!
一切話し合いの必要はありません!なんて便利なんだ!いやでも待ってくれ!
User
クラスの継承を止めたので、いちいち name
プロパティに書き込むイニシャライザを書かなければならない...
安心してください。継承 + プロトコルを組み合わせられます。
// ユーザークラス
class User {
let name: String
init(name: String) {
self.name = name
}
}
// 無課金ユーザー
class NoMoneyUser: User, GachaNeed {
let cost = 10
override func gacha() {
print("\(self.cost)使用してガチャを引きます!")
print("レア!")
}
}
// 課金ユーザークラス
class MoneyUser: User, GachaNeed {
let cost = 5
override func gacha() {
print("\(self.cost)使用してガチャを引きます!")
print("激レア!")
}
}
// 重課金ユーザークラス
class HeavyMoneyUser: GachaNeed {
let coin = 3 // coinになっているのでコンパイルエラーが出る
// gashaになってるのでコンパイルエラーが出る!
func gasha() {
print("\(self.coin)使用してガチャを引きます!")
print("超激レア!")
}
}
完璧ですね。ルールに則っているうえ、うまく使いまわせた美しいコードになりました!
- プロトコルはクラスの仕様書、ルール
- 仕様に則っていなけば、コンパイルエラーで教えてくれる
- 他のクラスの継承と合わせて使える