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

Protocol Extension

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.

Protocol Extension

Avatar for Yosuke Ishikawa

Yosuke Ishikawa

June 28, 2015
Tweet

More Decks by Yosuke Ishikawa

Other Decks in Programming

Transcript

  1. 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
  2. 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
  3. 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
  4. 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
  5. 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
  6. 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
  7. 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
  8. 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
  9. 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
  10. 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
  11. 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
  12. 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
  13. 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
  14. 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
  15. 型制約つきグローバル関数 特定の条件を満たす型だけに実行できる関数もある 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
  16. 型制約つき 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
  17. Protocol Oriented Programming WWDC 2015 の"Protocol Oriented Programming in Swift"

    というトー クから理解したことをまとめます。 25 / 34
  18. 抽象クラスの継承の問題 何を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
  19. 抽象クラスの継承の問題 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
  20. 抽象化による型の損失 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
  21. 抽象化による型の損失 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
  22. 抽象的かつ型安全なコードの他の例 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
  23. どちらを採用するべきか どちらもインター フェー スと実装の両方を提供し、 継承が可能と いう点では似ているが、 抽象的なコー ドにはプロトコルの方が向 いている。 抽象クラスは実装の要求が不明確

    抽象クラスでは抽象的なコー ドを型安全に書けない とはいえ、 プロトコルに弱点がないわけでもない デフォルト実装( クラスのsuper 相当) への参照ができない stored property を持つことができない 32 / 34