---
title: プロトコル指向に想う世界観 #__swift__
tags:  #swift愛好会 #swift #プロトコル指向  
author: [熊谷友宏](https://convert.docswell.com/user/es_kumagai)
site: [Docswell](https://www.docswell.com/)
thumbnail: https://bcdn.docswell.com/page/D7YG6D8N7M.jpg?width=480
description: 2016/03/29 の「Swift愛好会 #5」で発表したスライドです。ご覧上の注意的に、スライドのレベル感は『プロトコル拡張を理解し終えたばかりで積極的に使い始めたけれど、不可解な動きに出会い暗礁に乗り上げた人向け』です。  プロトコル拡張に行き詰った時の神頼みとして頼っていただければ幸いです。これからプロトコル拡張に挑戦しよう！という人は『おさらい』まで読んだらいったん、プロトコル拡張で好き好きに遊んでみると良いかもしれません。  ※ Docswell での公開に移行する直前の Slideshare での閲覧数は 3,387 でした。
published: March 29, 16
canonical: https://convert.docswell.com/s/es_kumagai/5N1JNL-swift-160329130847
---
# Page. 1

![Page Image](https://bcdn.docswell.com/page/D7YG6D8N7M.jpg)

Swift カジュアルプログラミング
プロトコル指向に想う世界観
2016.03.29
集まれSwift好き！Swift愛好会 #5
EZ-NET 熊⾕友宏
Swift 2.2
http://ez-net.jp/


# Page. 2

![Page Image](https://bcdn.docswell.com/page/VENGZ6QVJ8.jpg)

熊谷友宏
@es̲kumagai
EZ-NET http://ez-net.jp/
書籍 / 登壇
Xcode 5 徹底解説
MOSA
Xcode 5 の全機能を
徹底的に解説した本
OSX/iOS 系の歴史深い
有料会員制の勉強会
Xcode 7 でも役立つはず
法人会員も多数
紙版は絶版、電子書籍は販売中


# Page. 3

![Page Image](https://bcdn.docswell.com/page/Y79WZLDV73.jpg)

熊谷友宏
@es̲kumagai
EZ-NET http://ez-net.jp/
勉強会
横浜 iPhone 開発者勉強会
カジュアル Swift 勉強会
【 横浜・馬車道 】 #yidev
【 横浜・青葉台 】 #cswift
わいわい・ゆるく、iPhone 開発者の
みんなで楽しく過ごすのが目的の会
ゆるくみんなで Swift を語らえる場を
作りたくて始めた会
第２３回を 2016-05-07 に開催予定
第６回を 2016-04-02 に開催


# Page. 4

![Page Image](https://bcdn.docswell.com/page/G78N1X34JD.jpg)

熊谷友宏
@es̲kumagai
EZ-NET http://ez-net.jp/
iOS, OS X, Apple Watch アプリ
CodePiece
いつもの電卓
ソースコードを Twitter と
Gist に同時投稿できる。
計算式も見える電卓アプリ。
watchOS 1 対応
音で再配達ゴッド
EZ-NET IP Phone
簡単操作で
再配達の申し込み。
iPhone でひかり電話を使う。
自宅 LAN からの利用専用


# Page. 5

![Page Image](https://bcdn.docswell.com/page/L7LPQ8VVJR.jpg)

CodePiece for OS X
勉強会を楽しむアプリ
ソースコードを Twitter と Gist に同時投稿できる
勉強会で知見をみんなと共有したい時とかに便利！
できること
#̲̲swift̲̲


# Page. 6

![Page Image](https://bcdn.docswell.com/page/4EM416GNJW.jpg)

プロトコル指向ってなんだろう


# Page. 7

![Page Image](https://bcdn.docswell.com/page/PERWLPZ4J9.jpg)

プロトコル指向ってなんだろう
使うほどに見えなくなる
型
プロトコル
▶
プロトコルは分かる。
▶
プロトコル拡張って？
▶
型に実装するのと違う？
▶
型も拡張できるけど
何が違うの？
▶
プロトコル拡張って
ときどき変な動きする？
▶
既定の実装に何か違和感
▶
プロトコル拡張って
そもそも何？
型の拡張
プロトコル
拡張


# Page. 8

![Page Image](https://bcdn.docswell.com/page/P7XPW39ZJX.jpg)

そこで


# Page. 9

![Page Image](https://bcdn.docswell.com/page/37KDVYPVED.jpg)

スピリチュアル


# Page. 10

![Page Image](https://bcdn.docswell.com/page/LJ3RM94QE5.jpg)

見えない世界からアプローチしたら
見えなかった世界が見えてくる
… かも？


# Page. 11

![Page Image](https://bcdn.docswell.com/page/8JDY6GQWJG.jpg)

あの世とこの世をつなぐもの


# Page. 12

![Page Image](https://bcdn.docswell.com/page/VEP6Y3LX78.jpg)

プロトコル
Protocol


# Page. 13

![Page Image](https://bcdn.docswell.com/page/27V3G4Q3JQ.jpg)

おさらい


# Page. 14

![Page Image](https://bcdn.docswell.com/page/5JG381WYEL.jpg)

プロトコル
定義
▶ 性質を決める
▶ 性質を表現するための概念を規定する
protocol Movable {
}
func moved(x x: Int, y: Int) -&gt; Self
func movedHorizontal(x: Int) -&gt; Self
func movedVertical(y: Int) -&gt; Self


# Page. 15

![Page Image](https://bcdn.docswell.com/page/47Q59D36EP.jpg)

プロトコル
適用
▶ 概念を型で実現する … 実装
▶ その性質を持つことが約束される
struct Location : Movable {
func moved(x x:Int, y:Int) -&gt; Location {
return movedHorizontal(x).movedVertical(y)
}
func movedHorizontal(x:Int) -&gt; Location {
return Location(x: self.x + x, y: self.y)
}
}
func movedVertical(y:Int) -&gt; Location {
return Location(x: self.x, y: self.y + y)
}


# Page. 16

![Page Image](https://bcdn.docswell.com/page/KE49XZ1MJ1.jpg)

プロトコル
実現
▶ 型は性質の通りに振る舞える
▶ 必ず、振る舞える
var location = Location(x: 0, y: 0)
location = location.movedHorizontal(10)
location = location.movedVertical(20)
location = location.moved(x: 4, y: 8)


# Page. 17

![Page Image](https://bcdn.docswell.com/page/L71KNRGYEG.jpg)

&gt;&gt; 概念だけで説明できるものに注目


# Page. 18

![Page Image](https://bcdn.docswell.com/page/G7WD41K1E2.jpg)

プロトコル
概念だけで説明できるもの
▶ プロトコルの概念だけで説明できる
▶ 適用する型ごとに変わるものではない
protocol Movable {
func moved(x x: Int, y: Int) -&gt; Self
func movedHorizontal(x: Int) -&gt; Self
func movedVertical(y: Int) -&gt; Self
}


# Page. 19

![Page Image](https://bcdn.docswell.com/page/4JZG2PZX73.jpg)

プロトコル拡張


# Page. 20

![Page Image](https://bcdn.docswell.com/page/YE6YVMZPJV.jpg)

プロトコル拡張
新概念を作る
▶ プロトコルの概念だけで説明する
▶ 既存の概念から新しい概念を作る … 実装？
protocol Movable {
}
func moved(x x:Int, y:Int) -&gt; Self
func movedHorizontal(x:Int) -&gt; Self
func movedVertical(y:Int) -&gt; Self
extension Movable {
func moved(x x:Int, y:Int) -&gt; Self {
return movedHorizontal(x).movedVertical(y)
}
}


# Page. 21

![Page Image](https://bcdn.docswell.com/page/GE5GDZWPE4.jpg)

プロトコル拡張
型は最低限の概念だけを実現
▶ 最低限の概念を型に落とし込む
▶ プロトコル拡張で規定した概念が使える
struct Location : Movable {
func movedHorizontal(x:Int) -&gt; Location {
return Location(x: self.x + x, y: self.y)
}
func movedVertical(y:Int) -&gt; Location {
return Location(x: self.x, y: self.y + y)
}
}
// 既定の実装が使える
let location = Location().moved(x: 10, y: 10)


# Page. 22

![Page Image](https://bcdn.docswell.com/page/972Y8RQZ7R.jpg)

ジェネリック関数


# Page. 23

![Page Image](https://bcdn.docswell.com/page/DJYG6DWNJM.jpg)

ジェネリック関数
任意の型を受け取れる関数
▶ Movable に対応する型、みたいに指定
▶ プロトコルで規定した性質だけで組み立てる
func randomMove&lt;T:Movable&gt;(location: T, maxStep: Int) -&gt; T {
let deltaX = Int(arc4random_uniform(UInt32(maxStep)))
let deltaY = Int(arc4random_uniform(UInt32(maxStep)))
return location.moved(x: deltaX, y: deltaY)
}
// Movable に対応した Location 型を渡せる
var location = Location(x: 0, y: 0)
location = randomMove(location, maxStep: 100)


# Page. 24

![Page Image](https://bcdn.docswell.com/page/V7NGZ69VE8.jpg)

プロトコルの実例


# Page. 25

![Page Image](https://bcdn.docswell.com/page/YJ9WZL2VJ3.jpg)

CollectionType
複数の要素をまとめて扱う型の性質


# Page. 26

![Page Image](https://bcdn.docswell.com/page/GJ8N1X54ED.jpg)

CollectionType
型に適用
extension Location : CollectionType {
var startIndex: Int {
}
return 0
var endIndex: Int {
}
return 2
subscript (index: Int) -&gt; Int {
switch index {
case 0: return x
case 1: return y
default: fatalError()
}
}
}


# Page. 27

![Page Image](https://bcdn.docswell.com/page/LJLPQ8YVER.jpg)

CollectionType
配列のように振る舞う型になる
// 要素の数を取得できる
location.count
// 最初の要素 (x) と最後の要素 (y) を取得できる
location.first!
location.last!
// 要素を順次処理 (x -&gt; y) できる
for v in location {
}
// すべての要素 (x, y) をそれぞれ 2 倍にした配列を取得する
location.map { $0 * 2 }
// x, y で、小さい方の値や大きい方の値を取得できる
location.minElement()
location.maxElement()


# Page. 28

![Page Image](https://bcdn.docswell.com/page/47M416NNEW.jpg)

プロトコルは自作できる


# Page. 29

![Page Image](https://bcdn.docswell.com/page/P7RWLPD4E9.jpg)

やってみると
案外 むずかしい


# Page. 30

![Page Image](https://bcdn.docswell.com/page/PJXPW31ZEX.jpg)

プロトコル自作の難しさ
インターフェイスを規定する
protocol PlayerType {
var content: Music? { get }
var canPlay: Bool { get }
func play() throws
}


# Page. 31

![Page Image](https://bcdn.docswell.com/page/3JKDVY2V7D.jpg)

プロトコル自作の難しさ
型にプロトコルを適用する
final class MusicPlayer : PlayerType {
var content: Music?
var canPlay: Bool {
}
return content != nil
func play() throws {
guard canPlay else {
throw PlayerError.NotReady
}
try AVAudioPlayer(data: content!.data).play()
}
}


# Page. 32

![Page Image](https://bcdn.docswell.com/page/LE3RM9YQ75.jpg)

プロトコル自作の難しさ
型にプロトコルを適用したときの動き
ちゃんと動く
let player = MusicPlayer(content: Music(&quot;SomeMusic.mp3&quot;))
if player.canPlay {
try player.play()
}
再生 !


# Page. 33

![Page Image](https://bcdn.docswell.com/page/8EDY6G5WEG.jpg)

プロトコル自作の難しさ
プロトコル拡張を使うとき


# Page. 34

![Page Image](https://bcdn.docswell.com/page/V7P6Y3GXJ8.jpg)

プロトコル自作の難しさ
プロトコルの概念だけで作られた部分
final class MusicPlayer : PlayerType {
var content: Music?
型に依存しない
var canPlay: Bool {
}
return content != nil
func play() throws {
guard canPlay else {
throw PlayerError.NotReady
}
}
}
try AVAudioPlayer(data: content!.data).play()


# Page. 35

![Page Image](https://bcdn.docswell.com/page/2JV3G463EQ.jpg)

プロトコル自作の難しさ
プロトコル拡張で規定する
extension PlayerType {
}
var canPlay: Bool {
return content != nil
}
final class MusicPlayer : PlayerType {
var content: Music?
プロトコル拡張
に移行
func play() throws {
guard canPlay else {
throw PlayerError.NotReady
}
try AVAudioPlayer(data: content!.data).play()
}
}


# Page. 36

![Page Image](https://bcdn.docswell.com/page/5EG381NY7L.jpg)

プロトコル自作の難しさ
プロトコルの概念だけで作られた部分
final class MusicPlayer : PlayerType {
var content: Music?
型に依存しない
func play() throws {
guard canPlay else {
throw PlayerError.NotReady
}
try AVAudioPlayer(data: content!.data).play()
}
}


# Page. 37

![Page Image](https://bcdn.docswell.com/page/4JQ59DQ67P.jpg)

プロトコル自作の難しさ
プロトコル拡張で規定する
extension PlayerType {
var canPlay: Bool {
return content != nil
}
func play() throws {
guard canPlay else {
throw PlayerError.NotReady
}
}
try AVAudioPlayer(data: content!.data).play()
}
final class MusicPlayer : PlayerType {
var content: Music?
}
プロトコル拡張
に移行


# Page. 38

![Page Image](https://bcdn.docswell.com/page/K749XZNME1.jpg)

プロトコル自作の難しさ
プロトコル拡張で規定したときの動き
ちゃんと動く
let player = MusicPlayer(content: Music(&quot;SomeMusic.mp3&quot;))
if player.canPlay {
try player.play()
}
再生 !


# Page. 39

![Page Image](https://bcdn.docswell.com/page/LJ1KNR5Y7G.jpg)

プロトコル自作の難しさ
拡張とは別に独自実装するとき


# Page. 40

![Page Image](https://bcdn.docswell.com/page/GJWD415172.jpg)

プロトコル自作の難しさ
別の型では独自に実装する
final class SilentSoundPlayer : PlayerType {
let content: Music? = nil
var canPlay: Bool {
return true
}
func play() throws {
guard canPlay else {
throw PlayerError.NotReady
}
try AVAudioPlayer(data: NullSound().data).play()
}
}


# Page. 41

![Page Image](https://bcdn.docswell.com/page/4EZG2PNXJ3.jpg)

プロトコル自作の難しさ
別の型では独自に実装したときの動き
それぞれ期待通りに動く
let player1 = MusicPlayer(content: Music(&quot;SomeMusic.mp3&quot;))
let player2 = SilentSoundPlayer()
if player1.canPlay {
}
try player1.play()
再生 !
if player2.canPlay {
}
try player2.play()
再生 !


# Page. 42

![Page Image](https://bcdn.docswell.com/page/Y76YVM8PEV.jpg)

プロトコル自作の難しさ
別の型では独自に実装したときの動き
ジェネリック関数で共通化してみる
// どんな PlayerType にも対応した再生関数を用意
func start&lt;T:PlayerType&gt;(player: T) throws {
if player.canPlay {
try player.play()
}
}
// 用意した関数で再生する
let player1 = MusicPlayer(content: Music(&quot;SomeMusic.mp3&quot;))
let player2 = SilentSoundPlayer()
try start(player1)
try start(player2)
どちらも
再生 !


# Page. 43

![Page Image](https://bcdn.docswell.com/page/G75GDZ9P74.jpg)

プロトコル自作の難しさ
宣言しないで拡張できる…けれど


# Page. 44

![Page Image](https://bcdn.docswell.com/page/9J2Y8R2ZJR.jpg)

protocol PlayerType {
//
var content: Music? { get }
var canPlay: Bool { get }
func play() throws
}
extension PlayerType {
プロトコルから
宣言を消去
var canPlay: Bool {
return content != nil
}
func play() throws {
guard canPlay else {
throw PlayerError.NotReady
}
}
}
try AVAudioPlayer(data: content!.data).play()


# Page. 45

![Page Image](https://bcdn.docswell.com/page/DEYG6DYNEM.jpg)

プロトコル自作の難しさ
canPlay を宣言から省いたときの動き
こちらはちゃんと動く
if player1.canPlay {
}
try player1.play()
if player2.canPlay {
}
try player2.play()
再生 !
再生 !
こちらは動きがおかしくなる…？
try start(player1)
try start(player2)
player1 は 再生される
player2 は 再生されない


# Page. 46

![Page Image](https://bcdn.docswell.com/page/VJNGZ6PV78.jpg)

プロトコル自作の難しさ
さらにプロトコル拡張で共通化を図る…


# Page. 47

![Page Image](https://bcdn.docswell.com/page/YE9WZL3VE3.jpg)

protocol PlayerType {
//
var content: Music? { get }
var canPlay: Bool { get }
func play() throws
}
extension PlayerType {
var canPlay: Bool {
return content != nil
}
func play() throws {
guard canPlay else {
throw PlayerError.NotReady
}
content = nil 時の
無音再生に対応
let data = content?.data ?? NullSound().data
try AVAudioPlayer(data: data).play()
}
}


# Page. 48

![Page Image](https://bcdn.docswell.com/page/GE8N1XM47D.jpg)

final class SilentSoundPlayer : PlayerType {
let content: Music? = nil
var canPlay: Bool {
return true
}
プロトコル拡張で
対応したので消去
// func play() throws {
//
//
guard canPlay else {
//
throw PlayerError.NotReady
//
}
//
//
try AVAudioPlayer(data: NullSound().data).play()
// }
}


# Page. 49

![Page Image](https://bcdn.docswell.com/page/LELPQ83V7R.jpg)

プロトコル自作の難しさ
play をプロトコル拡張で共通化したときの動き
こちらも動きがおかしくなる…？
if player1.canPlay {
}
try player1.play()
if player2.canPlay {
}
try player2.play()
再生 !
canPlay は 成功
play 実行時に NotReady エラー
こちらの動きは従前通りのおかしさ
try start(player1)
try start(player2)
player1 は 再生される
player2 は 再生されない


# Page. 50

![Page Image](https://bcdn.docswell.com/page/4JM416DN7W.jpg)

いったい何が起こっているのか


# Page. 51

![Page Image](https://bcdn.docswell.com/page/PJRWLP4479.jpg)

見えない世界を垣間見たい
何か得体の知れない世界


# Page. 52

![Page Image](https://bcdn.docswell.com/page/PEXPW32Z7X.jpg)

そんなときに
得てして人が頼るもの


# Page. 53

![Page Image](https://bcdn.docswell.com/page/3EKDVYMVJD.jpg)

スピリチュアル


# Page. 54

![Page Image](https://bcdn.docswell.com/page/L73RM92QJ5.jpg)

スピリチュアルで
Swift の世界を観察


# Page. 55

![Page Image](https://bcdn.docswell.com/page/87DY6GZW7G.jpg)

プロトコル
型
〓
精神世界
〓
物質世界
人間界
霊界
型の世界
型に囚われない世界


# Page. 56

![Page Image](https://bcdn.docswell.com/page/VJP6Y3DXE8.jpg)

人間界
霊界
物質世界
明確に分離
精神世界
見えない
見える
人間
（インスタンス）
型
プロトコル


# Page. 57

![Page Image](https://bcdn.docswell.com/page/2EV3G4137Q.jpg)

人間界
霊界
物質世界
精神世界
干渉する
魂／霊
霊媒
（？？？）
人間
（インスタンス）
型
プロトコル


# Page. 58

![Page Image](https://bcdn.docswell.com/page/57G3812YJL.jpg)

Implementation を観察


# Page. 59

![Page Image](https://bcdn.docswell.com/page/4EQ59DP6JP.jpg)

Implementation を観察
型とプロトコルにおける実装
人間界
霊界
型
プロトコル
Implementation
extension Human {
}
func speak() {
…
}
Default Implementation
extension Speakable {
}
func speak() {
…
}


# Page. 60

![Page Image](https://bcdn.docswell.com/page/KJ49XZYM71.jpg)

Implementation を観察
何に実装されるのかに着目
人間界
霊界
物質世界
精神世界
型
Implementation
プロトコル
Default Implementation
func speak()
func speak()
“物体にしゃべり方を定義”
“魂にしゃべり方を定義”
… わかる気がする
… よくわからない


# Page. 61

![Page Image](https://bcdn.docswell.com/page/LE1KNR6YJG.jpg)

Implementation を観察
観測的な視点に着目
人間界
霊界
物質世界
精神世界
func speak()
見える
人間
（インスタンス）
func speak()
見えない


# Page. 62

![Page Image](https://bcdn.docswell.com/page/GEWD41W1J2.jpg)

見えてくること


# Page. 63

![Page Image](https://bcdn.docswell.com/page/47ZG2P5XE3.jpg)

Implementation を観察
ふたつの “同じ” 実装の 違い
人間界
霊界
型
プロトコル
func speak()
実体
✓ 見える
✓ しゃべり方を知っているから しゃべれる


# Page. 64

![Page Image](https://bcdn.docswell.com/page/YJ6YVM9P7V.jpg)

Implementation を観察
ふたつの “同じ” 実装の 違い
人間界
霊界
型
プロトコル
func speak()
実体
✓ 見えない
✓ なんかわからないけど しゃべれる


# Page. 65

![Page Image](https://bcdn.docswell.com/page/GJ5GDZNPJ4.jpg)

なんかわからないけど 出来る


# Page. 66

![Page Image](https://bcdn.docswell.com/page/LE3RM92675.jpg)

プロトコルは
本能


# Page. 67

![Page Image](https://bcdn.docswell.com/page/8EDY6GZMEG.jpg)

Implementation
ふたつの “同じ” 実装の 意味合い
人間界
霊界
型
Implementation
プロトコル
Default Implementation
func speak()
func speak()
“物体にしゃべり方を搭載”
“魂にしゃべり方を刻む”
… 後天的にできることを規定
… 先天的にできることを規定


# Page. 68

![Page Image](https://bcdn.docswell.com/page/V7P6Y3DQJ8.jpg)

2世界からのアプローチ


# Page. 69

![Page Image](https://bcdn.docswell.com/page/2JV3G41PEQ.jpg)

2世界からのアプローチ
プロトコル拡張とは別に独自実装
▶ プロトコル拡張でメソッドを定義
▶ 同じメソッドを型で独自に定義
// 型の定義
// プロトコルの定義
class Human : Speakable {
protocol Speakable {
func speak() {
…
}
}
extension Speakable {
}
func speak() {
…
}
}


# Page. 70

![Page Image](https://bcdn.docswell.com/page/5EG3812Q7L.jpg)

2世界からのアプローチ
プロトコル拡張とは別に独自実装
人間界
霊界
型
プロトコル
func speak()
こちらが実行される
実体
func speak()
こちらは実行されない
型の実装で上書きされた？


# Page. 71

![Page Image](https://bcdn.docswell.com/page/4JQ59DPW7P.jpg)

2世界からのアプローチ
それぞれの世界に存在する
人間界
霊界
物質世界
精神世界
func speak()
現実に存在している
func speak()
見えないだけで存在している
本能を発現
見えるものを実行
人間
本能は隠蔽される
魂／霊
本能が解放される
（インスタンス）
（言葉こそ意思疎通手段）
（？？？）
（言葉に依らない意思疎通）


# Page. 72

![Page Image](https://bcdn.docswell.com/page/K749XZY1E1.jpg)

精神世界からの干渉


# Page. 73

![Page Image](https://bcdn.docswell.com/page/LJ1KNR657G.jpg)

歩く


# Page. 74

![Page Image](https://bcdn.docswell.com/page/GJWD41WW72.jpg)

歩く
概要
▶ 足を使って移動する行為
▶ 片足を地面について重心を乗せ、
もう片足を進行方向へ運ぶ


# Page. 75

![Page Image](https://bcdn.docswell.com/page/4EZG2P52J3.jpg)

地面はどこにあるのか


# Page. 76

![Page Image](https://bcdn.docswell.com/page/Y76YVMV6EV.jpg)

歩く
地面はどこにあるのか
▶ 地面は物質世界にある
▶ 歩く概念は精神世界にも存在できる
▶ 足を地につけられるのは現実世界だけ
▶ 精神世界だけでは実現できない
精神世界が現実世界に干渉する必要性


# Page. 77

![Page Image](https://bcdn.docswell.com/page/G75GDZD574.jpg)

歩く
精神世界が現実世界に干渉
人間界
霊界
干渉する
物質世界
霊媒
精神世界
型
プロトコル
歩行を念ずる
魂／霊
人間
（インスタンス）
歩きを発現
（？？？）


# Page. 78

![Page Image](https://bcdn.docswell.com/page/9J2Y8R8GJR.jpg)

干渉する事柄をプロトコルで規定


# Page. 79

![Page Image](https://bcdn.docswell.com/page/DEYG6D6GEM.jpg)

歩く
干渉する事柄をプロトコルで規定
▶ プロトコルで物質世界への干渉を宣言
▶ 型で物質世界での動きを具現化
// 型の定義
// プロトコルの定義
class Human : Walkable {
protocol Walkable {
func walk() {
…
}
func walk()
}
extension Walkable {
}
具現化
}
宣言


# Page. 80

![Page Image](https://bcdn.docswell.com/page/VJNGZ6Z878.jpg)

歩く
2世界からのアプローチ
人間界
霊界
現実で
体現
物質世界
精神世界
func walk()
実際の歩行を定義
現実に
干渉
func walk()
概念のみを規定
歩行を念じる
見えるものを実行
人間
魂／霊
（インスタンス）
（？？？）


# Page. 81

![Page Image](https://bcdn.docswell.com/page/YE9WZLZXE3.jpg)

先天性と後天性


# Page. 82

![Page Image](https://bcdn.docswell.com/page/GE8N1X197D.jpg)

先天性と後天性
要約
▶ プロトコル拡張で規定したものは 先天的
▶ 型で規定したものは 後天的
// 人間には独自の遊泳を定義
// プロトコルの定義
class Human : Swimmable {
protocol Swimmable {
func swim() {
…
}
func swim()
}
}
extension Swimmable {
func swim() {
…
}
// 犬の遊泳は本能に従う
class Dog : Swimmable {
}
}


# Page. 83

![Page Image](https://bcdn.docswell.com/page/LELPQ8QM7R.jpg)

先天性と後天性
本能の発現
人間界
霊界
物質世界
現実に
干渉
精神世界
現実で
体現
func swim()
func swim() {
…
}
本能が発現
なぜか泳げる
遊泳を念じる
犬
魂／霊
（インスタンス）
（？？？）


# Page. 84

![Page Image](https://bcdn.docswell.com/page/4JM416167W.jpg)

先天性と後天性
体験による本能の置き換え
人間界
霊界
物質世界
現実で
体現
func swim()
現実に
干渉
精神世界
func swim()
func swim() {
…
}
本能は隠蔽
泳ぎは習得した
遊泳を念じる
人間
魂／霊
（インスタンス）
（？？？）


# Page. 85

![Page Image](https://bcdn.docswell.com/page/PJRWLPLN79.jpg)

型からの解放


# Page. 86

![Page Image](https://bcdn.docswell.com/page/PEXPW3W87X.jpg)

型からの解放
魂とは何者なのか
人間界
霊界
型
プロトコル
func speak()
これは
何者なのか
func speak()
本能を発現
見えるものを実行
人間
魂／霊
（インスタンス）
（？？？）


# Page. 87

![Page Image](https://bcdn.docswell.com/page/3EKDVYVLJD.jpg)

型からの解放
ヒントは ジェネリック関数
▶ プロトコルで引数の種類を特定する
▶ 実際の型に囚われない
func randomMove&lt;T:Movable&gt;(location: T, maxStep: Int) -&gt; T {
}
…
// Movable に対応した Location 型を渡せる
var location = Location(x: 0, y: 0)
location = randomMove(location, maxStep: 100)


# Page. 88

![Page Image](https://bcdn.docswell.com/page/L73RM9M6J5.jpg)

型に囚われない世界
精神世界


# Page. 89

![Page Image](https://bcdn.docswell.com/page/87DY6G6M7G.jpg)

型からの解放
ジェネリックによる昇華
人間界
霊界
物質世界
精神世界
func speak()
func speak()
ジェネリックを通して
霊体へと昇華
本能を発現
人間
魂／霊
（インスタンス）
（？？？）


# Page. 90

![Page Image](https://bcdn.docswell.com/page/VJP6Y3YQE8.jpg)

型からの解放
本能による昇華
人間界
霊界
func speak()
物質世界
func speak()
精神世界
本能を発現
func laugh()
なぜか笑える
本能を通して魂に昇華
人間
魂／霊
（インスタンス）
（？？？）


# Page. 91

![Page Image](https://bcdn.docswell.com/page/2EV3G4GP7Q.jpg)

プロトコルの世界観
見えない世界を垣間見たい
1.
プロトコルは本能
2.
物質世界と精神世界、２つのアプローチ
3.
精神世界から物質世界への干渉を宣言
4.
先天性と後天性という実装観点
5.
ジェネリックによる型からの解放


# Page. 92

![Page Image](https://bcdn.docswell.com/page/57G3818QJL.jpg)

これが …
プロトコルの世界 … !?


# Page. 93

![Page Image](https://bcdn.docswell.com/page/4EQ59D9WJP.jpg)

動作事例を解く


# Page. 94

![Page Image](https://bcdn.docswell.com/page/KJ49XZX171.jpg)

プロトコル自作の難しさ
canPlay を宣言から省いたときの動き


# Page. 95

![Page Image](https://bcdn.docswell.com/page/LE1KNRN5JG.jpg)

プロトコル自作の難しさ
canPlay を宣言から省いたときの動き
// このうちの player2 で不思議な動作が起こる
let player1 = MusicPlayer(content: Music(&quot;SomeMusic.mp3&quot;))
let player2 = SilentSoundPlayer()
// どんな PlayerType にも対応した再生関数を用意
func start&lt;T:PlayerType&gt;(player: T) throws {
if player.canPlay {
ジェネリック引数は
型に囚われない
〓
}
}
try player.play()
精神世界に解き放たれる


# Page. 96

![Page Image](https://bcdn.docswell.com/page/GEWD414WJ2.jpg)

protocol PlayerType {
//
var content: Music? { get }
var canPlay: Bool { get }
func play() throws
プロトコルから
宣言を消去
}
var canPlay: Bool {
return content != nil
}
〓
extension PlayerType {
物質世界に干渉しない
func play() throws {
guard canPlay else {
throw PlayerError.NotReady
}
}
}
try AVAudioPlayer(data: content!.data).play()


# Page. 97

![Page Image](https://bcdn.docswell.com/page/47ZG2P22E3.jpg)

プロトコル自作の難しさ
canPlay を宣言から省いたときの動き
こちらはちゃんと動く
if player1.canPlay {
}
try player1.play()
if player2.canPlay {
}
try player2.play()
再生 !
再生 !
こちらは動きがおかしくなる…？
try start(player1)
try start(player2)
player1 は 再生される
player2 は 再生されない


# Page. 98

![Page Image](https://bcdn.docswell.com/page/YJ6YV4667V.jpg)

プロトコル自作の難しさ
canPlay を宣言から省いたときの動き
人間界
霊界
型
プロトコル
var canPlay()
現実に存在している
発現しない
本能が発現
var canPlay
見えないだけで存在している
ジェネリック引数に渡す
canPlay を発動
（型から解き放たれる）
SilentSoundPlayer
魂／霊
本能が解放される
（インスタンス）
（ジェネリック）
（型に囚われない世界）


# Page. 99

![Page Image](https://bcdn.docswell.com/page/GJ5GDQ65J4.jpg)

プロトコル自作の難しさ
さらにプロトコル拡張で共通化を図る…


# Page. 100

![Page Image](https://bcdn.docswell.com/page/9E2Y8PMGER.jpg)

protocol PlayerType {
〓
func play() throws
物質世界に干渉しない
}
extension PlayerType {
var canPlay: Bool {
return content != nil
}
func play() throws {
guard canPlay else {
throw PlayerError.NotReady
}
本能側を調整
〓
//
var content: Music? { get }
var canPlay: Bool { get }
プロトコルに
宣言しない
content = nil 時の
無音再生に対応
let data = content?.data ?? NullSound().data
try AVAudioPlayer(data: data).play()
}
}


# Page. 101

![Page Image](https://bcdn.docswell.com/page/D7YG65XG7M.jpg)

final class SilentSoundPlayer : PlayerType {
let content: Music? = nil
var canPlay: Bool {
return true
}
プロトコル拡張で
対応したので消去
〓
// func play() throws {
//
//
guard canPlay else {
//
throw PlayerError.NotReady
本能に委ねる
//
}
//
//
try AVAudioPlayer(data: NullSound().data).play()
// }
}


# Page. 102

![Page Image](https://bcdn.docswell.com/page/VENGZN18J8.jpg)

プロトコル自作の難しさ
play をプロトコル拡張で共通化したときの動き
こちらも動きがおかしくなる…？
if player1.canPlay {
}
try player1.play()
if player2.canPlay {
}
try player2.play()
再生 !
canPlay は 成功
play 実行時に NotReady エラー
こちらの動きは従前通りのおかしさ
try start(player1)
try start(player2)
player1 は 再生される
player2 は 再生されない


# Page. 103

![Page Image](https://bcdn.docswell.com/page/Y79WZR5X73.jpg)

プロトコル自作の難しさ
canPlay を宣言から省いたときの動き
まずはこれから観察
if player2.canPlay {


# Page. 104

![Page Image](https://bcdn.docswell.com/page/G78N1WY9JD.jpg)

プロトコル自作の難しさ
play をプロトコル拡張で共通化したときの動き
人間界
霊界
実装が発現
型
var canPlay()
現実に存在している
見えるものを実行
型から直接実行
SilentSoundPlayer
（インスタンス）
プロトコル
発現しない
var canPlay
見えないだけで存在している


# Page. 105

![Page Image](https://bcdn.docswell.com/page/L7LPQN4MJR.jpg)

プロトコル自作の難しさ
canPlay を宣言から省いたときの動き
そしてこれを観察
try player2.play()


# Page. 106

![Page Image](https://bcdn.docswell.com/page/4EM41XK6JW.jpg)

プロトコル自作の難しさ
play をプロトコル拡張で共通化したときの動き
発現しない
人間界
霊界
var canPlay()
型
本能が発現
var canPlay
プロトコル
var play
canPlay を発動
見えないだけで存在している
なぜか play できる
型から直接実行
SilentSoundPlayer
（インスタンス）
本能が解放される
（型に囚われない世界）


# Page. 107

![Page Image](https://bcdn.docswell.com/page/PERWLNXNJ9.jpg)

スピリチュアルで
プロトコルの挙動を説明できた！


# Page. 108

![Page Image](https://bcdn.docswell.com/page/P7XPWN68JX.jpg)

ほかにも


# Page. 109

![Page Image](https://bcdn.docswell.com/page/37KDVN1LED.jpg)

ほかにも
データの保存場所は物質世界
人間界
霊界
型
プロトコル
var story:String
var story:String
データの入れ物は
物質世界に所属
読み書きするには
現実への干渉が不可欠
人間
魂／霊
（インスタンス）
（？？？）


# Page. 110

![Page Image](https://bcdn.docswell.com/page/LJ3RMVQ6E5.jpg)

ほかにも
インスタンスは物質世界に生成
人間界
霊界
型
プロトコル
init()
init()
インスタンスは
物質世界に生成
インスタンス生成には
現実への干渉が不可欠
インスタンス
魂／霊
（インスタンス）
（？？？）


# Page. 111

![Page Image](https://bcdn.docswell.com/page/8JDY689MJG.jpg)

以上
プロトコル指向に想う世界観


# Page. 112

![Page Image](https://bcdn.docswell.com/page/VEP6Y8ZQ78.jpg)

プロトコル指向に想う世界観
まとめ
1.
プロトコル志向ってなんだろう
✓ プロトコル、ジェネリック関数、プロトコル拡張
✓ 自作は案外難しい
2.
見えない世界を垣間見たい
✓ ２世界からのアプローチ
✓ 精神世界からの干渉
• 歩く、地面はどこにあるのか
• 干渉する事柄をプロトコルで規定
✓ 先天性と後天性
✓ 型からの解放
3.
動作事例を解く


