Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
自分のライブラリを1年運用してみた

振り返りと知見
Otemachi.swift	x	Kyobashi.swift	#01

2017/08/08	Fumiya	Sakai
自己紹介と簡単な経歴など
✦ 今までの仕事履歴(本業)
石川県金沢市生まれ
前はサーバーサイドのプログラマ	※Rails&PHP使い
26歳〜32歳: Webプログラマ(PHP	&	Rubyがキャリア長い)
23歳〜25歳: Webデザイナー兼...
これまでに作ったもの(ネイティブアプリ)
①	簡易家計簿アプリ「Coffre」
②	ゲームアプリ「10秒虫食い算」
・カレンダーを自作しています
・シンプルなお小遣い帳感覚で支出管理できます
・全問正解者ほとんどいません…
・不定期ですがコラム...
カレンダーが好きでライブラリを作りました
日本の祝祭日を計算で出してくれる
・カレンダーアプリ等での活用を想定
・シルバーウィーク・ゴールデンウィークも対応
・ハッピーマンデー法の施行も対応
・春分の日・秋分の日にも対応
・過去の祝祭日もおおむ...
✦ CalculateCalendarLogicの歩み
カレンダー用ライブラリの歴史振り返り
最初の設計段階でBool型を返すだけのシンプルなもの&ライブラリ本体もStructファイルだけで完結させた。
★年月日から祝祭日を判定するだけの超シン...
✦ 投げ出さないために気をつけていたこと
ほったらかしにしないモチベーション
ちっちゃいライブラリなので言うてもすぐに修正できる
自動じゃなくって計算で判定しているから、追加されたら?
完全に自動にできているわけではないが、ユニットテスト等品質...
✦ 厳密に行うなら知識や法律の理解が結構大切
祝祭日を理解する上で必要な法律・知識
祝祭日の判定を日本語からSwiftのロジックに落とし込んでいく作業なので「やるやら」は初めにしっかり。
★祝祭日に関連する参考資料
・国民の祝日に関する法律(W...
public func judgeJapaneseHoliday(year: Int, month: Int, day: Int) -> Bool {
/// 国民の祝日に関する法律(こくみんのしゅくじつにかんするほうりつ)は、
/// 194...
✦ 春分・秋分は計算式を用いて算出する方針
上記のように日付が年によってずれる部分なのでシルバーウィーク同様に気がつきにくい部分なので注意。
★春分の日は(3/20	or	3/21)になり、秋分の日は(9/22	or	9/23)になる
祝祭日判...
//(1).9月15日(1966年から2002年まで)、(2).9月の第3月曜日(2003年から): 敬老の日
case (1966...2002, 9, 15, _):
return true
case (year, 9, 15...21, ...
✦ 人間がして確認が辛い&見落としやすいものを優先的に
複数の条件が重なっていたり特殊な計算を行うものは紛らわしいテストケースを用意しておくとベター。
★難易度に応じてテストコードの手厚さを決めていく
ユニットテストの優先度
目視だけではかなり...
✦ 特に難易度が高そう&紛らわしそうな部分は手厚くする
UIに組み込んだサンプルだけでなく最初は拙い感じでもコードだけでなく徐々に改善を定期的に加える。
★ここではシルバーウィークに関する例を見てみる(5連休になっているか)
ユニットテストのコ...
✦ 最初は辛い部分もあるけれども徐々に安定感が増していく
ユニットテストを入れて見ての振り返り
最初は自分の個人的な開発の範疇で行なっていたが、実務でUnitTestをする足がかりにできたのは収穫。
★考慮漏れによるバグの検知がしやすくなった
...
✦ ライブラリで得た知見が今も少しではあるが生きている
今回のまとめ
ご清聴ありがとうございました!またこのような機会があった際には是非ともよろしくお願い致します!
★最初は不完全でグダグダでもユニットテストは書ける範囲でも入れておく
後々にロ...
Upcoming SlideShare
Loading in …5
×

自分のライブラリを1年運用をして見た振り返りと知見

1,182 views

Published on

Otemachi.swift x Kyobashi.swift #01での登壇資料になります。
日本の祝祭日判定を計算ロジックだけで行うライブラリ、「CalculateCalendarLogic」を1年前から開発し今日まで運用した際のことをまとめました。

判定が難しい部分のロジック及びテストコードの組み立て方針などを中心に解説しています。

Github:
https://github.com/fumiyasac/handMadeCalendarAdvance

開発当時のQiita記事:
http://qiita.com/fumiyasac@github/items/33bfc07ad36dfffcdf8f

※今後と改善は継続していきますので、よろしくお願い致します。

Published in: Technology
  • Be the first to comment

  • Be the first to like this

自分のライブラリを1年運用をして見た振り返りと知見

  1. 1. 自分のライブラリを1年運用してみた 振り返りと知見 Otemachi.swift x Kyobashi.swift #01 2017/08/08 Fumiya Sakai
  2. 2. 自己紹介と簡単な経歴など ✦ 今までの仕事履歴(本業) 石川県金沢市生まれ 前はサーバーサイドのプログラマ ※Rails&PHP使い 26歳〜32歳: Webプログラマ(PHP & Rubyがキャリア長い) 23歳〜25歳: Webデザイナー兼ディレクター 本業はiOSアプリエンジニア@エバーセンス株式会社 趣味:シルバーアクセサリー集め・スイーツ作り・アプリ開発 これまでも女子向け・グルメ・エンタメ関連のお仕事が多かった Qiita : http://qiita.com/fumiyasac@github Github : https://github.com/fumiyasac ✦ 酒井文也(さかい ふみや) 東京(大塚)住まいの32歳 こんな格好を普段からしているので 遊び人に見られますがエンジニアです。 文系卒に思われますが 実は数学科で理系卒です。 めっちゃお酒好きそうに見えますが ビール苦手でお酒も超弱いです。 今でもたまにUIまわりとか触りたく なることがあったりなかったり 今年の6月から女性向けのアプリ つくってます@ever sense, Inc. 最近のはまっている食べ物は カボチャと担々麺と甘栗です。 最近はSwift以外ではRailsやLaravel・CakePHP・ReactNative
  3. 3. これまでに作ったもの(ネイティブアプリ) ① 簡易家計簿アプリ「Coffre」 ② ゲームアプリ「10秒虫食い算」 ・カレンダーを自作しています ・シンプルなお小遣い帳感覚で支出管理できます ・全問正解者ほとんどいません… ・不定期ですがコラムも書いています ・サーバーサイドはRuby on Railsを使用 http://www.coffre.me/ ・デザインにもこだわってみました(特にグラフ) ・実はちょっとバグがあります。 ・問題は今後追加予定(現在110問収録) 個人的にはなりますが、他にもアプリ・Webサービスなど開発中です(2017年も宜しくお願いします) ・サイト等は次回のアップデートで公開予定 http://blog.just1factory.net/services/284 ・若干の中毒性を含みます
  4. 4. カレンダーが好きでライブラリを作りました 日本の祝祭日を計算で出してくれる ・カレンダーアプリ等での活用を想定 ・シルバーウィーク・ゴールデンウィークも対応 ・ハッピーマンデー法の施行も対応 ・春分の日・秋分の日にも対応 ・過去の祝祭日もおおむね考慮はしている みなさまのStarや温かい応援のお言葉もありまして無事に1年間運用することができました!激しく感謝! CocoaPods & Carthage & 手動で導入可能 ・HTTP(HTTPS)通信は不要 ★CalculateCalendarLogic ver0.1.3 【2017年】Swift4系への対応〜テストケースの充実化・リファクタリング等これからも継続していきます。 ・Github: https://github.com/fumiyasac/handMadeCalendarAdvance ・実装解説: http://qiita.com/fumiyasac@github/items/33bfc07ad36dfffcdf8f ・Github: https://github.com/fumiyasac/handMadeCalendarOfSwift 新機能や運用保守は継続しています
  5. 5. ✦ CalculateCalendarLogicの歩み カレンダー用ライブラリの歴史振り返り 最初の設計段階でBool型を返すだけのシンプルなもの&ライブラリ本体もStructファイルだけで完結させた。 ★年月日から祝祭日を判定するだけの超シンプルなライブラリ 2016.05 : ライブラリの公開(ほぼβバージョン) ★自分ひとりだけで作れたものでは決してない ・幸いなことに開発の早い段階でPullRequestやIssueを頂けたのですぐに改善ができた ・最初に不完全ながらもテストケースを用意していたので心強かった 2016.09 : CocoaPods対応(Issue対応) 2016.10 : Swift3対応 2017.01 : macOSへのサポート 2017.08 : テストがあまったるい部分の改善&Swift4化 ・Cathage対応 ・Refactoring・UnitTest 裏側では判定開始年の問題などを検討する 対応の初動が遅れたがなんとか直せた… 少なくとも2000年以降のテストは完璧に… Swift4化は終了&テストの充実化を着手中
  6. 6. ✦ 投げ出さないために気をつけていたこと ほったらかしにしないモチベーション ちっちゃいライブラリなので言うてもすぐに修正できる 自動じゃなくって計算で判定しているから、追加されたら? 完全に自動にできているわけではないが、ユニットテスト等品質に関わりそうな部分は優先的に改善する。 ★中身のロジックに関しての取り組み全般 ・ドキュメントの点検や追記には注意する ・祝祭日についてのインプット(実際に図書館で調べる) ★Contributorからのレスや要望に関して 見落とすことが機会損失にもなるので要チェック ・実装するか否かはできるだけ早く決める(方針は設計の根幹) ・実装しなおした&おかしな点を見つけたらテストも加える 【よくある質問】 ・定期的に祝祭日の件に関しては目を通す ・テストケースの加筆と修正対応と調整 直近で言えば、 2016年8月11日 が祝祭日になった テストケース 2000年以降は、 担保できるように 制定から施行まで期間があるので対応可能 例)山の日:2014年制定 → 2016年施行
  7. 7. ✦ 厳密に行うなら知識や法律の理解が結構大切 祝祭日を理解する上で必要な法律・知識 祝祭日の判定を日本語からSwiftのロジックに落とし込んでいく作業なので「やるやら」は初めにしっかり。 ★祝祭日に関連する参考資料 ・国民の祝日に関する法律(Wikipedia) https://goo.gl/94h2kR ライブラリの判定開始については、1948年〜とした。 ・振替休日(Wikipedia) https://goo.gl/jfYtyr ・秋分(Wikipedia) https://goo.gl/HxXP6v ・春分(Wikipedia) https://goo.gl/1MYZTh 秋分の日・春分の日については計算で算出する。 振替休日・シルバーウィーク・ゴールデンウィーク・ ハッピーマンデー制度についてはもちろん考慮する。 該当の年月日からその日が祝祭日ないしは振替休日の 場合はtrue(Bool型)を返す形にする。 日本特有のものになるのでタイムゾーン関連の実装は ライブラリ内で行わないようにする。 ・ゴールデンウィーク(Wikipedia) https://goo.gl/uuUYhe ライブラリの実装時に決めたこと 【設計面】 【実装面】 ・シルバーウィーク(Wikipedia) https://goo.gl/BLTyZj 必要な知識や法律に関すること
  8. 8. public func judgeJapaneseHoliday(year: Int, month: Int, day: Int) -> Bool { /// 国民の祝日に関する法律(こくみんのしゅくじつにかんするほうりつ)は、 /// 1948年(昭和23年)7月20日に公布・即日施行された日本の法律である。通称祝日法。 /// See also: https://ja.wikipedia.org/wiki/国民の祝日に関する律 /// 「国民の祝日」が日曜日に当たるときは、その日後においてその日に最も近い「国民の祝日」でない日を休日とする /// 1973年の国民の祝日に関する法律が改正されたことにより制定 /// See also: https://ja.wikipedia.org/wiki/%E6%8C%AF%E6%9B%BF%E4%BC%91%E6%97%A5 /// (注意)春分の日・秋分の日は1948年以前も祝祭日であったが、このカレンダーロジックの基準は1948年〜を基準とするので考慮しない /// See also: https://ja.wikipedia.org/wiki/%E7%9A%87%E9%9C%8A%E7%A5%AD switch (year, month, day, weekday) { /// 例)海の日の場合 //(1).7月20日(1996年から2002年まで)、(2).7月の第3月曜日(2003年から): 海の日 case (1996...2002, 7, 20, _): return true //(2).7月の第3月曜日(2003年から): 海の日 case (year, 7, 15...21, .mon) where 2003 <= year: return true //7月21日: 海の日の振替休日 case (1996...2002, 7, 21, .mon): return true タプルでの条件分岐を活用し、条件指定を整理 ✦ N月の第M週となるロジック部分と振替休日になる部分の実装 第M週のロジックに関してはカレンダーで取りうる値の範囲が決まる&月曜日であることをチェックする。 ★振替休日&ハッピーマンデー制度の施行前&施行後の考慮してロジックにする 祝祭日判定でのポイント(ハッピーマンデー制度) 参考: 2000年〜 ・成人の日 → 1月第2月曜日 ・体育の日 → 10月第2月曜日 2003年〜 ・海の日 → 7月第3月曜日 ・敬老の日 → 9月第3月曜日
  9. 9. ✦ 春分・秋分は計算式を用いて算出する方針 上記のように日付が年によってずれる部分なのでシルバーウィーク同様に気がつきにくい部分なので注意。 ★春分の日は(3/20 or 3/21)になり、秋分の日は(9/22 or 9/23)になる 祝祭日判定でのポイント(春分・秋分) private enum SpringAutumn { /// 春分の日 case spring /// 秋分の日 case autumn var constant: Double { switch self { case .spring: return 20.69115 case .autumn: return 23.09000 } } /// 春分の日・秋分の日を計算する /// 参考:http://koyomi8.com/reki_doc/doc_0330.htm func calcDay(year: Int) -> Int { let x1: Double = Double(year - 2000) * 0.242194 let x2: Int = Int(Double(year - 2000) / 4) return Int(constant + x1 - Double(x2)) } } //3月20日 or 21日: 春分の日(計算値によって算出) case (year, 3, day, _) where PublicHolidaysLawYear < year && day == SpringAutumn.spring.calcDay(year: year): return true //春分の日の次が月曜日: 振替休日 case (year, 3, day, .mon) where year >= AlternateHolidaysLawYear && day == SpringAutumn.spring.calcDay(year: year) + 1: return true //9月22日 or 23日: 秋分の日(計算値によって算出) case (year, 9, day, _) where PublicHolidaysLawYear <= year && day == SpringAutumn.autumn.calcDay(year: year): return true //秋分の日の次が月曜日: 振替休日 case (year, 9, day, .mon) where year >= AlternateHolidaysLawYear && day == SpringAutumn.autumn.calcDay(year: year) + 1: return true 春分点・秋分点のずれを考慮 テストケース 2000年以降は、 担保できるように 計算ロジックに反映する
  10. 10. //(1).9月15日(1966年から2002年まで)、(2).9月の第3月曜日(2003年から): 敬老の日 case (1966...2002, 9, 15, _): return true case (year, 9, 15...21, .mon) where year > 2002: return true //9月16日: 敬老の日の振替休日 case (1973...2002, 9, 16, .mon): return true //9月22日 or 23日: 秋分の日(計算値によって算出) case (year, 9, day, _) where PublicHolidaysLawYear <= year && day == SpringAutumn.autumn.calcDay(year: year): return true //秋分の日の次が月曜日: 振替休日 case (year, 9, day, .mon) where year >= AlternateHolidaysLawYear && day == SpringAutumn.autumn.calcDay(year: year) + 1: return true //シルバーウィークの振替休日である(※現行の法律改正から変わらないと仮定した場合2009年から発生する) //See also: https://ja.wikipedia.org/wiki/シルバーウィーク case (_, 9, _, _) where oldPeopleDay(year: year) < day && day < SpringAutumn.autumn.calcDay(year: year) && getAlterHolidaySliverWeek(year: year) && year > 2008: return true 国民の祝日の判定 ✦ 9月に国民の祝日が重なって5連休になる場合のロジック実装 シルバーウィークの判定は敬老の日と秋分の日&国民の祝日になる条件が重なるので気がつきにくい部分。 ★敬老の日の2日後が秋分の日ならば間に挟まれた期間は国民の休日とする 祝祭日判定でのポイント(シルバーウィーク) 歴代シルバーウィーク: 2009年, 2015年に発生 次は「2026年」になります。 敬老の日が9月第3月曜になった影響 /** *� *�シルバーウィークの振替休日を判定する *�敬老の日の2日後が秋分の日ならば間に挟まれた期間は国民の休日とする * */ private func getAlterHolidaySliverWeek(year: Int) -> Bool { return oldPeopleDay(year: year) + 2 == SpringAutumn.autumn.calcDay(year: year) }
  11. 11. ✦ 人間がして確認が辛い&見落としやすいものを優先的に 複数の条件が重なっていたり特殊な計算を行うものは紛らわしいテストケースを用意しておくとベター。 ★難易度に応じてテストコードの手厚さを決めていく ユニットテストの優先度 目視だけではかなりつらい 頑張ればなんとかいける シルバーウィークに関する判定部分のテスト 春分の日・秋分の日に関する判定部分のテスト ゴールデンウィークとその振替休日に関する判定部分のテスト 突発的な祝祭日とハッピーマンデー制度に関する判定部分のテスト 日にちが決まっている祝祭日に関する判定部分のテスト サンプルアプリのUIだけでは難しい サンプルアプリのUIでも頑張れば
  12. 12. ✦ 特に難易度が高そう&紛らわしそうな部分は手厚くする UIに組み込んだサンプルだけでなく最初は拙い感じでもコードだけでなく徐々に改善を定期的に加える。 ★ここではシルバーウィークに関する例を見てみる(5連休になっているか) ユニットテストのコード例(XCTestでの記載例) /** * シルバーウィークの判定のテスト */ func testSilverWeek() { let test = CalculateCalendarLogic() let testCases: [(Int,Int,Int,Bool)] = [ // 2015年 (2015, 9, 19, false), (2015, 9, 20, false), (2015, 9, 21, true), (2015, 9, 22, true), (2015, 9, 23, true), (2015, 9, 24, false), // 2016年 (2016, 9, 19, true), (2016, 9, 20, false), (2016, 9, 21, false), (2016, 9, 22, true), (2016, 9, 23, false), (2016, 9, 24, false), ] testCases.forEach { year, month, day, expected in let result = test.judgeJapaneseHoliday(year: year, month: month, day: day) guard let weekday = Weekday(year: year, month: month, day: day) else { XCTFail() ; return } let message = "(year)年(month)月(day)日((weekday.longName)):(result)" if expected { XCTAssertTrue (result, message) } else { XCTAssertFalse(result, message) } } } 2015年:○, 2016年× シルバーウィークか否かの判定 紛らわしい判定をするテストケースがあるとGood! 少しずつ怪しいテストケースを追加 テストコード&ドキュメントも加筆する いきなりではなく徐々に改善: 現在から前後10年ぐらいは担保したい:
  13. 13. ✦ 最初は辛い部分もあるけれども徐々に安定感が増していく ユニットテストを入れて見ての振り返り 最初は自分の個人的な開発の範疇で行なっていたが、実務でUnitTestをする足がかりにできたのは収穫。 ★考慮漏れによるバグの検知がしやすくなった ★シルバーウィーク判定や春分・秋分の日判定が鬼 ★Swift2 → Swift3をはじめとするメジャーバージョンアップ対応 ★結果的にではあるが実務前の良い予習になった よかった: 目視だけのデバッグだと確実に見落としてしまう 考慮した: 複雑な計算式や特定のケースでしか起こり得ないものは目視はさすがに辛すぎる 変更点は少なけれどもバージョンアップに起因する書き換え作業等の際に重宝する 実際に今携わっているアプリも検索ロジックや内部データに関する部分のテストケースを作成した 判定ロジックはテストコード、UIくずれは目視で確認という切り分けができた この部分に関しては、個別にテストケースを作り紛らわしいものを追加する よかった: 幸いこのライブラリは書き換え箇所が少ないけど多い場合だとさすがに怖そう よかった: ぶっつけ本番にならなかったので、落ち着いて取り組むことができた(はず…)
  14. 14. ✦ ライブラリで得た知見が今も少しではあるが生きている 今回のまとめ ご清聴ありがとうございました!またこのような機会があった際には是非ともよろしくお願い致します! ★最初は不完全でグダグダでもユニットテストは書ける範囲でも入れておく 後々にロジックやテストケースを修正&改善をする際やバージョンアップ時にとても心強い武器になる ★実サンプルコードやドキュメントを充実させることもしっかりと 業務でのドキュメント作成やサンプルに関する説明をする良いプラクティスにもなった ★業務でもテストコードを書く必要が出た際もスムーズに取り組めた このライブラリで取り組んだ事が今の業務でも生きていることが多い ★自分ルール(今回守れなくってすみません…) 【良いアウトプットのために】 発表・登壇時はこの中のいずれか2つを 絶対に準備するルールを設けています!

×