Post on 28-May-2015
description
『駅すぱあと』新しい開発基盤の研究
株式会社ヴァル研究所 R&D Centre ソフトウェアエンジニア プロダクトチーフ 佐藤昭彦
※注意 この発表では現在研究中のものを紹介しています。『駅すぱあと』には”まだ”導入されていません。
自己紹介• 佐藤昭彦(さとうあきひこ)
• 2005年 株式会社ヴァル研究所入社
• モバイル系のJavaエンジニアを経て現在C++でお仕事してます
• プログラム言語大好き
もくじ• C/C++開発環境の改善
• 調査したツール類の紹介
• 運賃計算とDSL
• 『駅すぱあと』が戦う運賃計算について
• DSLのススメ
開発環境の改善
開発環境の改善• 自動化(CI環境の整備)
• GitHubとJenkinsを連携させてPull Request単位でビルド&テスト
• デイリーでカバレッジ計測。gcovのレポートをJenkins上で管理
• その他ツール類の調査
• 便利ツールの導入検討
GitHub/Jenkins連携
pull request
(polling)
pullbuild/test
結果通知
GitHub Jenkins+ “GitHub pull request builder plugin”
開発者
GitHub/Jenkins連携
!右下の赤丸のように、ビルド・テストの成否をGitHub上に表示してくれます。
導入による効果
• pull request対応コスト減
• pull requestされた内容について、ビルド・テストが通ることを手動で確認しなくてもよくなった
• 設計やロジックに注力してレビューが出来る
カバレッジ計測
• Jenkins上で計測
• 毎日 AM8:00に1回レポート生成
• 計測にはgcovを使用
• gcovr + Cobertura Pluginで可視化
!カバレッジの推移や現在の詳細な集計結果がレポートとして表示されます。
その他ツール• ビルド構成管理
• CMake
• ユニットテスト
• Google Test
• 静的解析(lint)
• cpplink.py
• cppcheck
• メトリクス計測
• metrix++
• ドキュメント生成
• Doxygen
• コード整形/コンパート
• clang-format
• clang-modernize『駅すぱあと』には未導入のもの、導入しないものもありますが、自分のプロジェクトではこれらを使って開発をしています。
clang-modernize
• C++は2011年に大きな改定があった(C++11)
• C++のコードを「C++11らしい」コードに変換
• 内部的にC++コンパイラ(clang)の技術を使っているので高精度
class Person { std::string name; ! public: explicit Person(std::string name) : name(std::move(name)) { } void greet(const Person &person) const; void greet(const std::vector<const Person *> &friends) const { for (const auto & friends_i : friends) { if (friends_i != nullptr) { greet(*friends_i); } } } };
AFTER
class Person { std::string name; ! public: explicit Person(const std::string &name) : name(name) { } void greet(const Person &person) const; void greet(const std::vector<const Person *> &friends) const { for (std::vector<const Person *>::const_iterator i = friends.begin(); i != friends.end(); ++i) { if (*i != NULL) { greet(**i); } } } };
BEFORE
!ムーブセマンティクス、型推論、range-based for、新キーワード(nullptr)など、C++11の新機能を使ってくれます。
開発環境まとめ・雑感• 自動化の効果は高い
• 単純にコストが減るし、何より単純作業のストレスがなくなるのがうれしいですね。
• もっとツールを使おう
• 手元で実行出来るだけでも楽しいし便利。
運賃計算とDSL
運賃計算?• 基本1
• 乗車した距離に応じて金額が決まる
• →「営業キロ」を求めて運賃表を引く
• 基本2
• 「複乗の禁止」
• → 同じ所を2度通ってはダメ
https://www.jr-odekake.net/railroad/ticket/guide/02.html
!運賃計算の実例を見てみます。高円寺から川崎まで、立川を経由する経路です。
高円寺
立川
川崎
21.4km
35.5km
本州3社内の幹線の普通運賃表
!高円寺→立川→川崎で通算56.9キロ。運賃表で引くと970円。 通算 56.9km
高円寺
立川
川崎
21.4km
35.5km高円寺→川崎は 970円…?
本州3社内の幹線の普通運賃表
通算 56.9km
高円寺
立川
川崎
21.4km
35.5km高円寺→川崎は 970円…?
本州3社内の幹線の普通運賃表
通算 56.9km!実はこの結果は間違い。
●東京・大阪の電車特定区間の普通運賃表 東京・大阪の電車特定区間(次の図の範囲)のみをご利用になる場合は、「電車特定区間の普通運賃表」をご覧ください。
https://www.jr-odekake.net/railroad/ticket/guide/02.html
!この図にある区間は、よく使われる路線のためもう少し割安の運賃表が設定されています。
高円寺
立川
川崎
21.4km
35.5km電車特定区間の普通運賃表
本州3社内の幹線の普通運賃表
通算 56.9km!通算距離56.9キロを「電車特定区間の普通運賃表」で引くと920円。
高円寺
立川
川崎
21.4km
35.5km電車特定区間の普通運賃表
高円寺→川崎は 920円…?
本州3社内の幹線の普通運賃表
通算 56.9km
高円寺
立川
川崎
21.4km
35.5km電車特定区間の普通運賃表
高円寺→川崎は 920円…?
本州3社内の幹線の普通運賃表
通算 56.9km!実はこの結果もまだ不十分。
●大都市近郊区間内のみをご利用になる場合の特例 下図のそれぞれの大都市近郊区間内のみを普通乗車券または回数乗車券でご利用になる場合は、実際にご乗車になる経路にかかわらず、最も安くなる経路で計算した運賃で乗車することができます。
https://www.jr-odekake.net/railroad/ticket/guide/02b.html
!この図にある区間は路線が入り組んでいて目的地までの経路が複数存在するため、常に最安になる経路で計算して良いという特例があります。
!乗車した「高円寺→立川→川崎」よりも「高円寺→新宿→品川→川崎」の経路のほうが安く計算できます。
高円寺
立川
川崎
21.4km
35.5km
高円寺
新宿
品川
5.8km
8.6km
通算 27.8km川崎
11.4km
高円寺→川崎は 470円!
通算 56.9km!ようやく正しい運賃が求まりました。
!紹介した以外にも、運賃計算の特例は無数にあります。
運賃計算
非常に特例が多い
今は?• プログラムで頑張っている
• 運賃計算を行うC言語関数群の中に特例の数だけif文があるイメージ
• →関数設計を工夫しても保守性に限界がある
• →そこでDSL!
DSL?
• Domain Specific Language:ドメイン特化言語
• ある特定領域(ドメイン)の問題を解決するのに特化した言語のこと(⇔汎用言語)
• 例)HTML, CSS, 正規表現, RSpec
ドメイン特化言語 パターンで学ぶDSLのベストプラクティス46項目 http://www.amazon.co.jp/dp/4864010471
!この本がDSLのバイブルです。非常にオススメです。
DSLをなぜ使うのか• 開発者の生産性の向上
• ビジネスロジックをより抽象度の高い言語で表現
• ドメインエキスパートとのコミュニケーション改善
• 問題の文脈に特化した言語なら、開発者以外でも理解しやすい
DSLをいつ使わないのか• プロジェクトで使用する言語が増える
• エンジニアの習得コストが上がる
• →但し、汎用言語の習得コストとは比べ物にならないほど低い
• DSL自体のメンテナンスコスト
• 独自言語を作れば、それをメンテナンスする人が必要
• →表現力を限定することが重要 (チューリング完全になんてしてはいけない!)
!「チューリング完全なDSL」はDSLのよくあるアンチパターンです。それは既に汎用言語です。解決したいドメインに集中し、小さい言語に保たれているのがいいDSLだと思います。
DSLの導入
• まずやること
• かっこいい文法を考える • セマンティックモデルを作成する
• → 問題解決方法を「モデル化」する
モデルの例
if then
main.c util.c
main.h util.h
app
if then
if then
Y Y 20Y N 10N Y 5N N 0
プロダクションルールシステム 依存ネットワーク デシジョンテーブル
!これら以外にも、マーチン・ファウラーの本に様々な例が詳しく紹介されています。
運賃計算のモデル化
「東京近郊区間」のみの利用である
最も安くなる経路で計算
「電車特定区間」のみの利用である
運賃表に「電車特定区間の普通運賃表」を使う
東京で折り返し、神田を通過する列車に乗る
「東京→神田→東京」 区間を除外
「if-then」で表現される一連のルールとして表現できる →今回はこれをクラスで実装してみる
…
!試しに「プロダクションルールシステム」で表現してみました。実際には、複雑な運賃計算を一つの単純なモデルで表現するのは難しいので、地域やルールの特性で再分割が必要でしょう。
モデルの実装
ConcreteCondition
Rule
<<abstract>> Condition
ConcreteAction<<abstract>> Action
ConcreteCondition
ConcreteAction
ConcreteCondition
ConcreteAction
1件の「ルール」は 「条件」と「アクション」の 組み合わせとして表現
!クラス化したものです。条件とアクションは種類が沢山あるのでabstractクラスを挟んで解決しています。
モデルの実装
ConcreteCondition
Rule
<<abstract>> Condition
ConcreteAction<<abstract>> Action
ConcreteCondition
ConcreteAction
ConcreteCondition
ConcreteAction
具体的な条件 “電車特定区間のみの乗車” ”区間A-Bを通っている”
…
具体的なアクション ”運賃表Xを使う”
“区間A-Bを除外する” …
言語としてのDSLの作成
• 「モデル」が適切にクラス化出来ていれば、言語としてのDSLは薄いファサード(窓口)としての役割しか持っていない
• 今回の場合、DSLの仕事は条件とアクションのインスタンスを記述に従って組み上げ、ルールのインスタンスを生成することのみ
!この後もスライド中に何度か出てきますが、問題のモデル化がDSL作りの中心です。モデルが しっかりしていれば、その上に用途に応じた複数のDSL言語を作るなんてことも可能です。
DSLの実装方法• 内部DSL:コア言語内に作るDSL
• 例)関数のネストでモデル構造を表現
• rule(condition(…), action(…))
• 例)メソッドチェーンでモデル構造を表現
• rule().condition(…).action(…).end()
DSLの作成rule “電車特定区間の普通運賃表(東京)” do condition “経路全体マッチ” do sections “電車特定区間(東京)” end action “運賃表変更” do pricetable “電車特定区間の普通運賃表” end end !rule “大都市近郊区間内のみをご利用になる場合の特例(東京)” do … end !…
!Rubyで内部DSLを定義した場合のイメージです。Rubyならメタプログラミングな手法を使えばもっとスマートに書けると思います。
DSLの実装方法• 外部DSL:コア言語外に作るDSL
• 例)コア言語でパーサーを用意する
• 独自言語, JSON, YAML, XML, Lua, mruby, …
• 例)コア言語のコードを生成する
• $ ruby codegen.rb > generated.c
DSL作成の注意点• 表現力を限定する
• 「このDSLで何でも書けるように・・・」はNG
• 一般的な作法に倣う
• 開発者がJavaに慣れているなら、コメントは「//」で始まるべき
• 専門用語を使うことを恐れない
• 専門用語があることでドメイン内のコミュニケーションは豊かになる
運賃計算とDSLまとめ・雑感
• 運賃計算は難しい
• 特例のかたまりです。
• DSLで大事なのは問題のモデル化と表現力の限定
• 解決したい問題を明確にしましょう。間違っても何でも書けるDSLを作っちゃダメです。
参考文献/URL• ドメイン特化言語 パターンで学ぶDSLのベストプラクティス46項目 • http://www.amazon.co.jp/dp/4864010471
• 進化するアーキテクチャーと新方式の設計: DSL の使用 - IBM • https://www.ibm.com/developerworks/jp/java/library/j-eaed13/
• きっぷのルール:JRおでかけネット • https://www.jr-odekake.net/railroad/ticket/guide/01.html
ご清聴ありがとうございました