Swiftのprotocolおぼえがき
「Swift実践入門」刊行記念 Tech Talks #swiftjn を聞きに行って、知らないこといっぱいありそうだったので、この本で学んだことをおぼえがきとして残しておきます。
本の裏にも書いてあることを引用しますが、「Swiftは簡潔な言語ですが、その言語仕様を理解し、正しく使うことはけっして容易ではありません。」という言葉、まさしくその通りって感じてます。
『Swift実践入門』の著者の石川さんも言ってましたが、
良い設計は誤った用法をコンパイルエラーにする #swiftjn
— かみぴーさんと呼ばれて2年半 (@kamip_net) 2017年2月24日
大事なことです。
せっかくSwiftで書くならSwiftらしいコードで書けるようになりたい。そのためのインプットとして「Swift実践入門」を教科書代わりに使ってみようと思います。
Swift実践入門 ── 直感的な文法と安全性を兼ね備えた言語 (WEB+DB PRESS plus)
- 作者: 石川洋資,西山勇世
- 出版社/メーカー: 技術評論社
- 発売日: 2017/02/07
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る
めんどくさがりやなので、14章から読み始めればOK問題ない、と思ってたけど、いざ読んでみるとしっかり理解していない箇所が結構ありそうなので、とりあえず第7章のprotocolから読みつつ、playgroundで書いたりして遊んでみます。
protocolの基本
ぼんやり知ってたことの復習。ストアドプロパティとかコンピューテッドプロパティという呼び方知らなかった。
protocol SomeProtocol{ var title:String { get } func someMethod(string: String) -> Void nonmutating func nonmutatingSomeMethod(string: String) -> Void mutating func mutatingSomeMethod(string: String) -> Void } struct SomeStruct1 : SomeProtocol { //ストアドプロパティ internal var title: String func someMethod(string: String) { // self.title = string //=> エラー } nonmutating func nonmutatingSomeMethod(string: String) -> Void{ // self.title = string //=> エラー } mutating func mutatingSomeMethod(string: String) -> Void{ self.title = string } } struct SomeStruct2 : SomeProtocol { //コンピューテッドプロパティ internal var title: String { return "default" } func someMethod(string: String) { // self.title = string //=>エラー } mutating func mutatingSomeMethod(string: String) -> Void{ // self.title = string //=>エラー print("nasi") } nonmutating func nonmutatingSomeMethod(string: String) -> Void{ // self.title = string //=>エラー } } class SomeClass : SomeProtocol { var title:String = "" func someMethod(string: String) -> Void{ self.title = string } func nonmutatingSomeMethod(string: String) -> Void{ self.title = string } func mutatingSomeMethod(string: String) -> Void{ self.title = string } } let struct1 = SomeStruct1.init(title: "aaa") struct1.title //struct1.title = "hoge" //#=>エラー //struct1.mutatingSomeMethod(string: "hoge") //#=> struct1をletで生成しているのでエラー struct1.title let struct2 = SomeStruct2.init() struct2.title //struct2.title = "hoge" //#=> コンピューテッドプロパティなのでエラー let someClass = SomeClass.init() someClass.title = "hoge"
associatedtype を使った抽象化
適当なサンプル出したくて消費税とかで書いてみたけど、実装が同じで意味なかった😢
protocol TaxProtocol { associatedtype AssociatedType var tax: AssociatedType { get } func calculate(price: Int) -> AssociatedType } // 実装からAssociatedTypeを自動で定義する方法 struct JapaneseTax: TaxProtocol { var tax: Float func calculate(price: Int) -> Float{ return floor(Float(price) + Float(price) * self.tax) } } let japaneseTax = JapaneseTax(tax: 0.08) japaneseTax.calculate(price: 20100) // typealiasを使ってAssociatedTypeを定義する方法 struct KoreanTax: TaxProtocol { typealias AssociatedType = Float var tax: AssociatedType func calculate(price: Int) -> AssociatedType{ return floor(Float(price) + Float(price) * self.tax) } } let koreanTax = KoreanTax(tax: 10.0) let taxPrice = koreanTax.calculate(price: 20100) // ネスト型AssociatedTypeを定義する方法(使いみちどんな時...?) struct ItarianTax: TaxProtocol { struct AssociatedType {} var tax: AssociatedType func calculate(price: Int) -> AssociatedType{ return AssociatedType() } }
protocol extension によるデフォルト実装 / extension where Self : を使った制約
Tackleって🎣の装備品?をまとめてタックルと言います。
KamipはFishingTackleとBloggerというprotocolを持っているけど、HogeFisherはFishingTackleだけ持っている例。これも適当な思いつきで読みながら進めたので、FishingTackleがwriteDiaryを持っている謎仕様ですが気にしない。HogeとかFooとかで書いたほうがよかったな..。
protocol FishingTackle { var rod: String { get } var reel: String { get } var line: String { get } var lure: String { get } } protocol Blogger { var blogUrl: String { get } } extension FishingTackle { var list: String { return [ "rod: \(self.rod)", "reel: \(self.reel)", "line: \(self.line)", "lure: \(self.lure)", ].joined(separator: "\n") } func printList() -> Void { print(self.list) } } extension FishingTackle where Self : Blogger { func writeDiary() -> Void{ // 日記を書く処理 } } struct Kamip : FishingTackle, Blogger { var rod: String var reel: String var line: String var lure: String var blogUrl: String } let kamip = Kamip( rod: "Daiwa presso-ltd ags ml", reel: "Daiwa 15luvias 2004", line: "SanyoNylon GT-R PINK-SELECTION 2lb", lure: "Jackall tearo 1.6g", blogUrl: "http://kamip.net/" ) print(kamip.list) kamip.printList() print(kamip.blogUrl) kamip.writeDiary() struct HogeFisher: FishingTackle { var rod: String var reel: String var line: String var lure: String } let hogeFisher = HogeFisher(rod: "", reel: "", line: "", lure: "") print(hogeFisher.list) hogeFisher.printList() //hogeFisher.writeDiary() //#=> Bloggerじゃないのでエラーになる
自分で書くと色々試せるのでとてもよい。
次は
標準ライブラリのプロトコルを使う設計が自然にできたら素敵そうだけど、そもそも、標準ライブラリのプロトコル何があるかInputしないと。
class
と struct
の違いも理解が足りなそうなので、次は第5章を読もう。ジェネリクスとかは怖いなー怖いなー。
参考
Swift実践入門 ── 直感的な文法と安全性を兼ね備えた言語 (WEB+DB PRESS plus)
- 作者: 石川洋資,西山勇世
- 出版社/メーカー: 技術評論社
- 発売日: 2017/02/07
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る