Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Protocol Extension

Protocol Extension

8889da6a67db3667b0694d993c9a962c?s=128

Yosuke Ishikawa

June 28, 2015
Tweet

Transcript

  1. Protocol Extension ishkawa 1 / 34

  2. 2 / 34

  3. Protocol Extension p r o t o c o l

    M y P r o t o c o l { v a r n a m e : S t r i n g { g e t } f u n c d o S o m e t h i n g ( ) } e x t e n s i o n M y P r o t o c o l { v a r n a m e : S t r i n g { r e t u r n " i s h k a w a " } f u n c d o S o m e t h i n g ( ) { } } 3 / 34
  4. Swift 1 の protocol 4 / 34

  5. Swift 1 の protocol インター フェー スを定義 p r o

    t o c o l D a t a S o u r c e { f u n c n u m b e r O f S e c t i o n s ( ) - > I n t f u n c n u m b e r O f I t e m s I n S e c t i o n ( s e c t i o n : I n t ) - > I n t } 5 / 34
  6. Swift 1 の protocol 適合する型で実装 c l a s s

    V i e w C o n t r o l l e r : U I V i e w C o n t r o l l e r , D a t a S o u r c e { f u n c n u m b e r O f S e c t i o n s ( ) - > I n t { r e t u r n 1 } f u n c n u m b e r O f I t e m s I n S e c t i o n ( s e c t i o n : I n t ) - > I n t { r e t u r n 1 } } 6 / 34
  7. Swift 1 の protocol @ o b j c ならo

    p t i o n a l を定義できる @ o b j c p r o t o c o l D a t a S o u r c e { o p t i o n a l f u n c n u m b e r O f S e c t i o n s ( ) - > I n t f u n c n u m b e r O f I t e m s I n S e c t i o n ( s e c t i o n : I n t ) - > I n t } 7 / 34
  8. Swift 1 の protocol o p t i o n

    a l は実装しなくても良い c l a s s V i e w C o n t r o l l e r : U I V i e w C o n t r o l l e r , D a t a S o u r c e { f u n c n u m b e r O f I t e m s I n S e c t i o n ( s e c t i o n : I n t ) - > I n t { r e t u r n 1 } } 8 / 34
  9. Swift 1 の protocol n u m b e r

    O f S e c t i o n s ( ) の実装の有無は呼び出し側でハンドル デフォルトの動作は利用側が用意する l e t d a t a S o u r c e : D a t a S o u r c e l e t n u m b e r O f S e c t i o n s = d a t a S o u r c e . n u m b e r O f S e c t i o n s ? ( ) ? ? 1 9 / 34
  10. Swift 2 の protocol 10 / 34

  11. Swift 2 の protocol インター フェー スを定義 p r o

    t o c o l D a t a S o u r c e { f u n c n u m b e r O f S e c t i o n s ( ) - > I n t f u n c n u m b e r O f I t e m s I n S e c t i o n ( s e c t i o n : I n t ) - > I n t } e x t e n s i o n でデフォルトの実装を定義 p r o t o c o l D a t a S o u r c e { f u n c n u m b e r O f S e c t i o n s ( ) - > I n t { r e t u r n 1 } } 11 / 34
  12. Swift 2 の protocol protocol extension で実装されているものは実装しなくても良い c l a

    s s V i e w C o n t r o l l e r : U I V i e w C o n t r o l l e r , D a t a S o u r c e { f u n c n u m b e r O f I t e m s I n S e c t i o n ( s e c t i o n : I n t ) - > I n t { r e t u r n 1 } } 12 / 34
  13. Swift 2 の protocol デフォルトの動作はprotocol extension が用意している 利用側は適合している型が実装しているか気にする必要はない l e

    t d a t a S o u r c e : D a t a S o u r c e l e t n u m b e r O f S e c t i o n s = d a t a S o u r c e . n u m b e r O f S e c t i o n s ( ) 13 / 34
  14. Swift 2 の protocol protocol にデフォルト実装を定義できるようになった デフォルト実装による設計の変化 @ o b

    j c にしなくてもo p t i o n a l ( のようなもの) を提供できる デフォルトの動作の定義を利用側からprotocol 側に移せる / / o p t i o n a l の場合( S w i f t 1 , 2 ) l e t n u m b e r O f S e c t i o n s = d a t a S o u r c e . n u m b e r O f S e c t i o n s ? ( ) ? ? 1 / / p r o t o c o l e x t e n s i o n の場合( S w i f t 2 ) l e t n u m b e r O f S e c t i o n s = d a t a S o u r c e . n u m b e r O f S e c t i o n s 14 / 34
  15. Swift 標準ライブラリの変化 15 / 34

  16. Swift 1 の map 16 / 34

  17. Swift 1 の map 定義 f u n c m

    a p < C : C o l l e c t i o n T y p e , T > ( s o u r c e : C , t r a n s f o r m : ( C . G e n e r a t o r . E l e m e n t ) - > T ) - > [ T ] 利用例 l e t n a m e s = [ " f o o " , " b a r " , " b a z " ] l e t c o u n t s = m a p ( n a m e s ) { n a m e i n c o u n t ( n a m e ) } 汎用的だがグロー バル関数はコー ド補完から探すのが面倒 17 / 34
  18. Swift 1 の map 型ごとにメソッドとしても定義されている e x t e n

    s i o n A r r a y { f u n c m a p < U > ( t r a n s f o r m : ( T ) - > U ) - > [ U ] } 利用例 l e t n a m e s = [ " f o o " , " b a r " , " b a z " ] l e t c o u n t s = n a m e s . m a p { n a m e i n c o u n t ( n a m e ) } 型ごとに定義... 18 / 34
  19. Swift 2 の map グローバル関数 → protocol extension 19 /

    34
  20. Swift 2 の map 定義 e x t e n

    s i o n C o l l e c t i o n T y p e { f u n c m a p < T > ( @ n o e s c a p e t r a n s f o r m : ( S e l f . G e n e r a t o r . E l e m e n t ) - > T ) - > [ T ] } 利用例 l e t c o u n t s : [ I n t ] = n a m e s . m a p { n a m e i n n a m e . c h a r a c t e r s . c o u n t } 型ごとに定義しなくてもメソッドとして利用できる 20 / 34
  21. map はどう変わったか 達成したい条件 C o l l e c t

    i o n T y p e に対して適用できるようにしたい メソッドとして提供したい Swift 1 グロー バル関数でC o l l e c t i o n T y p e への適用を提供 A r r a y など型ごとにメソッドを提供 Swift 2 C o l l e c t i o n T y p e のprotocol extension でメソッドを提供 21 / 34
  22. 型制約つき protocol extension 22 / 34

  23. 型制約つきグローバル関数 特定の条件を満たす型だけに実行できる関数もある SequenceType に適合している型C のうち、 要素となる C.Generator.Element がComparable に適合していればs o

    r t e d ( ) を 実行できる。 / / S w i f t 1 f u n c s o r t e d < C : S e q u e n c e T y p e w h e r e C . G e n e r a t o r . E l e m e n t : C o m p a r a b l e > ( s o u r c e : C ) - > [ C . G e n e r a t o r . E l e m e n t ] 23 / 34
  24. 型制約つき protocol extension 条件を満たす型のみprotocol extension を定義することもできる e x t e

    n s i o n S e q u e n c e T y p e w h e r e S e l f . G e n e r a t o r . E l e m e n t : C o m p a r a b l e { f u n c s o r t ( ) - > [ S e l f . G e n e r a t o r . E l e m e n t ] } 24 / 34
  25. Protocol Oriented Programming WWDC 2015 の"Protocol Oriented Programming in Swift"

    というトー クから理解したことをまとめます。 25 / 34
  26. 3 行で class にはいくつか問題がある protocol + struct で解決できる なるべくprotocol +

    struct を使おう 26 / 34
  27. 抽象クラスの継承の問題 何をoverride すればいいのかわからない ドキュメント以外から知ることは難しい / / 抽象クラス c l a

    s s O r d e r e d { f u n c p r e c e d e s ( o t h e r : O r d e r e d ) - > B o o l { f a t a l E r r o r ( " i m p l e m e n t m e ! " ) } } override が必要なものの抽象クラス側での実装を、f a t a l E r r o r に しておけばサブクラスでの実装が必要であることを知らせること はできる。 エラー はコンパイル時に知りたいがf a t a l E r r o r ( ) は実行時 27 / 34
  28. 抽象クラスの継承の問題 protocol では実装すべきものが明らか / / 抽象クラスをp r o t o

    c o l に置き換え p r o t o c o l O r d e r e d { f u n c p r e c e d e s ( o t h e r : O r d e r e d ) - > B o o l } 実装漏れはコンパイル時に知ることができる デフォルト実装はprotocol extension で定義できる 28 / 34
  29. 抽象化による型の損失 c l a s s O r d e

    r e d { f u n c p r e c e d e s ( o t h e r : O r d e r e d ) - > B o o l { f a t a l E r r o r ( ) } } c l a s s N u m b e r : O r d e r e d { o v e r r i d e f u n c p r e c e d e s ( o t h e r : O r d e r e d ) - > B o o l { l e t o t h e r N u m b e r = o t h e r a s ! N u m b e r . . . } } override したメソッドの型は変更できない Number のprecedes() に他のサブクラスが入る可能性もある つまり、 抽象的なコー ドを型安全に書けない 29 / 34
  30. 抽象化による型の損失 p r o t o c o l O

    r d e r e d { f u n c p r e c e d e s ( o t h e r : S e l f ) - > B o o l } s t r u c t N u m b e r : O r d e r e d { f u n c p r e c e d e s ( o t h e r : N u m b e r ) - > B o o l { . . . } } protocol ではSelf が利用できる precedes() の引数にはNumber 以外の型は入らない 30 / 34
  31. 抽象的かつ型安全なコードの他の例 p u b l i c p r o

    t o c o l R e q u e s t { t y p e a l i a s R e s p o n s e f u n c e x e c u t e ( R e s p o n s e - > V o i d ) } s t r u c t G e t R e p o s i t o r y R e q u e s t : R e q u e s t { t y p e a l i a s R e s p o n s e = R e p o s i t o r y } s t r u c t G e t U s e r R e q u e s t : R e q u e s t { t y p e a l i a s R e s p o n s e = U s e r } l e t r e p o s i t o r y R e q u e s t = G e t R e p o s i t o r y R e q u e s t ( ) r e p o s i t o r y R e q u e s t . e x e c u t e { r e s p o n s e i n / / r e s p o n s e はR e p o s i t o r y } l e t u s e r R e q u e s t = G e t U s e r R e q u e s t ( ) u s e r R e q u e s t . e x e c u t e { r e s p o n s e i n / / r e s p o n s e はU s e r } 31 / 34
  32. どちらを採用するべきか どちらもインター フェー スと実装の両方を提供し、 継承が可能と いう点では似ているが、 抽象的なコー ドにはプロトコルの方が向 いている。 抽象クラスは実装の要求が不明確

    抽象クラスでは抽象的なコー ドを型安全に書けない とはいえ、 プロトコルに弱点がないわけでもない デフォルト実装( クラスのsuper 相当) への参照ができない stored property を持つことができない 32 / 34
  33. まとめ プロトコルにデフォルト実装が定義できるようになった 標準ライブラリもそれを活かしたつくりに変わってきている 抽象クラスをプロトコルで置き換えるという流れがある 33 / 34

  34. 34 / 34