『駅すぱあと』新しい開発基盤の研究 for DevLove関西
Transcript of 『駅すぱあと』新しい開発基盤の研究 for DevLove関西
自己紹介• 佐藤昭彦(さとうあきひこ)
• 2005年 株式会社ヴァル研究所入社。ソフトウェアエンジニア。
• モバイル系Javaエンジニアを経て、現在駅すぱあと内部の改善プロジェクトでお仕事をしています
• プログラム言語大好き
利用中のツール類• ビルド構成管理
• CMake
• コード管理
• Git/GitHub
• CI(Jenkins プラグイン)
• GitHub pull request builder plugin
• Cobertura Plugin
• メトリクス計測
• metrix++
• ドキュメント生成
• Doxygen
• Graphviz
• 静的解析(lint)
• cpplint.py
• cppcheck
• コード整形/コンパート
• clang-format
• clang-modernize
GitHub/Jenkins連携
pull request
(polling)
pullbuild/test
結果通知
GitHub Jenkins+ “GitHub pull request
builder plugin”
開発者
pre-pushスクリプト• GitHubへpushする前にソースチェックスクリプトを実行。違反していればgit pushが失敗する。
• コードフォーマットがかかっているか→ clang-format
• コーディング規約は守られているか(静的解析)→ cpplint.py, cppcheck
• メトリクス計測結果は最低基準を満たしているか→ metrix++
clang-modernize• C++は2011年に大きな改定があった(C++11)
• → 多くのテクニック・イディオムがレガシー化してしまった
• clang-modernizeは、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
clang-modernizeをかけた例。型推論やforeach構文、新APIも使っている。
開発環境まとめ・雑感• 自動化の効果は高い
• 単純にコストが減るし、何より単純作業のストレスがなくなるのがうれしいですね。
• もっとツールを使っていこう
• 手元で実行出来るだけでも便利ですし、何よりこういうのをいじるの楽しいですよね。
京都
大阪
西九条
42.8km
3.6km
通算 49.6km
ユニバーサルシティ
京都→ユニバーサルシティは 840円…?
3.2km
京都→ユニバーサルシティは通算49.6km。これを運賃表で引くと840円と出るが・・・
●東京・大阪の電車特定区間の普通運賃表 東京・大阪の電車特定区間(次の図の範囲)のみをご利用になる場合は、「電車特定区間の普通運賃表」をご覧ください。
https://www.jr-odekake.net/railroad/ticket/guide/02.html
●東京・大阪の電車特定区間の普通運賃表 東京・大阪の電車特定区間(次の図の範囲)のみをご利用になる場合は、「電車特定区間の普通運賃表」をご覧ください。
https://www.jr-odekake.net/railroad/ticket/guide/02.html
京都から先は「電車特定区間」外なので、今回は当てはまらなそう。
●大都市近郊区間内のみをご利用になる場合の特例 下図のそれぞれの大都市近郊区間内のみを普通乗車券または回数乗車券でご利用になる場合は、実際にご乗車になる経路にかかわらず、最も安くなる経路で計算した運賃で乗車することができます。
https://www.jr-odekake.net/railroad/ticket/guide/02b.html
大阪
京都
奈良
42.8km
41.7km
通算 84.5km
大阪
京橋
放出
4.2km
3.2km
通算 47.0km
久宝寺9.2km
奈良30.4km
大阪→奈良は 840円…?
最短経路は通算47.0km。これを運賃表で引くと840円と出るが・・・
大阪
京都
奈良
42.8km
41.7km
通算 84.5km
大阪
京橋
放出
4.2km
3.2km
通算 47.0km
久宝寺9.2km
奈良30.4km
大阪→奈良は 840円…?
まだ安くなる。
●東京・大阪の電車特定区間の普通運賃表 東京・大阪の電車特定区間(次の図の範囲)のみをご利用になる場合は、「電車特定区間の普通運賃表」をご覧ください。
https://www.jr-odekake.net/railroad/ticket/guide/02.html
最短経路は「電車特定区間」の範囲に収まっている。「大都市近郊区間」特例が適用された結果、電車特定区間外を乗車したのに特例の対象となった!
大阪
京都
奈良
42.8km
41.7km
通算 84.5km
大阪
京橋
放出
4.2km
3.2km
通算 47.0km
久宝寺9.2km
奈良30.4km
大阪→奈良は 800円!
結果、遠回りの経路であるが、800円の切符で乗車できることがわかった。
DSL?
• Domain Specific Language:ドメイン特化言語
• ある特定領域(ドメイン)の問題を解決するのに特化した言語のこと(⇔汎用言語)
• 例)HTML, CSS, 正規表現, RSpec
ドメイン特化言語 パターンで学ぶDSLのベストプラクティス46項目 http://www.amazon.co.jp/dp/4864010471
DSLについてはマーチン・ファウラーのこの本が詳しいです。
DSLをなぜ使うのか• 開発者の生産性の向上
• 運賃計算特例を一から十までC言語で書くのはちょっと大変そうだな・・・
• ドメインエキスパートとのコミュニケーション改善
• 鉄道用語でそのまま書ける言語があったら、「鉄」の人にもチェックしてもらえるかも・・・。
DSLをなぜ使わないのか• プロジェクトで使用する言語が増える
• C++だけでも大変なのに、さらに鉄道言語を覚えるの・・・?
• → 習得コストはかかるけど、汎用言語とは量が違う
• DSL自体のメンテナンスコスト
• その巨大なオレオレ言語、だれがメンテしてくの・・・?
• → そもそも巨大にしてはいけない。表現力を限定することが重要 (チューリング完全になんてしてはいけない!)
※チューリング完全:どんなアルゴリズムでも 記述できる計算能力を持った状態のこと
DSLの導入• まずやること
• かっこいい文法を考える
• セマンティックモデルを作成する
• → 問題解決方法を「モデル化」する
• モデルを組み上げるためのDSLを作成する解決したい問題をモデル化することがDSL作りの肝となる。
運賃計算のモデル化• 「京都→ユニバーサルシティ」の例:
1.普通の運賃表で金額を求める(間違い)
2.電車特定区間の運賃表が使えると判断
3.電車特定区間の運賃表を使って金額を求める(正解)
→金額を求める前に、運賃表を決める必要がある先ほどの例について考えると、運賃計算の処理順(もちろん、運賃計算全体のごく一部ですが)が見えてくる・・・
運賃計算のモデル化• 「大阪→京都→奈良」の例:
1.電車特定区間の運賃表が使えないと判断(間違い)
2.大都市近郊区間特例で経路を変更
3.電車特定区間の運賃表が使えると判断(正解)
4.運賃表を使って金額を求める
→ 運賃表を決める前に、経路を決める必要がある
モデルの選択
if then
if then
if then
プロダクションルールシステム
main.c util.c
main.h util.h
app
依存ネットワーク
Y Y 20Y N 10N Y 5N N 0
デシジョンテーブル
マーチン・ファウラーの本に載っている例の一部を紹介。使えそうなものを検討する。
モデルの選択
• 条件の組み合わせを表の形で整理したもの
• {通常期,セール期}、{ゲスト会員,登録会員}、{クーポン有,クーポン無}の組み合わせで割引率が変わる…など
Y Y 20Y N 10N Y 5N N 0
デシジョンテーブル
モデルの選択• タスクの依存関係を有向グラフにしたもの
• 「トポロジカルソート」で適切な実行順序を決められる
• 必要最低限のタスクを絞り込みたいときにも
main.c util.c
main.h util.h
app
依存ネットワーク
モデルの選択
• 条件と処理の組み合わせでタスクを記述したもの
• イベントドリブン的な処理に向いている
• →今回はこれが使えそう
if then
if then
if then
プロダクションルールシステム
運賃計算のモデル化
「if-then」で表現される一連のルールとして表現できた
…
大阪近郊区間のみの利用 最も安くなる経路に補正ルール1
宝塚方面から乗車大阪折り返しで尼崎通過の列車に乗車 「尼崎⇔大阪」間を除外ルール2
近江塩津→山科を 琵琶湖線を使用して通過
「近江塩津→山科」区間を 湖西線に振り替え
ルール3
運賃計算のモデル化ルール
- ルールの適用条件 : 条件 - 経路の加工処理 : アクション+ ルール適用(:経路) : void
<< abstract >> 条件
+ ルールの対象?(:経路) : bool
<< abstract >> アクション
+ 経路の加工(:経路) : void
特定エリアのみの利用
あるパターンを含む
最短経路に変更
指定区間を除外
指定区間を置換実装方法は様々あるが、試しにルールをクラスにしてみる。
DSLの作成
• 「モデル」が適切にクラス化出来ていれば、言語としてのDSLは薄いファサード(窓口)としての役割しか持っていない
• 今回の場合、DSLの仕事は適用条件とアクションのインスタンスを記述に従って組み上げ、ルールのインスタンスを生成することのみ
DSLの実装方法• 外部DSL:コア言語外に作るDSL
• 例)コア言語でパーサーを用意する
• 独自言語, JSON, YAML, XML, Lua, mruby, …
• 例)コア言語のコードを生成する
• $ ruby codegen.rb > generated.c
DSLの実装方法
• 内部DSL:コア言語内に作るDSL
• 例)関数のネストでモデル構造を表現 Rule(If(…), Then(…))
• 例)メソッドチェーンでモデル構造を表現 Rule().If(…).Then(…)
def_rule "大都市近郊区間内のみをご利用になる場合の特例(大阪)" do |rule| rule.add_condition do |route| area("大都市近郊区間(大阪)").contains?(route) end rule.add_action do |route| from, to = route.fromto route.replace(area("大都市近郊区間(大阪)").shortest_path(from, to)) end end
def_rule "分岐駅を通過する列車に乗車する場合の特例(尼崎―大阪)" do |rule| rule.add_condition do |route| route.contains?(path("尼崎分岐駅通過特例パターン")) end … end …
DSL言語作成の注意点• 表現力を限定する
• 「このDSLで何でも書けるように・・・」はNG
• 一般的な作法に倣う
• 開発者がJavaに慣れているなら、コメントは「//」で始まるべき
• 専門用語を使うことを恐れない
• 専門用語があることでドメイン内のコミュニケーションは豊かになる
運賃計算とDSLまとめ・雑感• 運賃計算は難しい
• 特例のかたまりです。難しいからこそ、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