『駅すぱあと』新しい開発基盤の研究 for DevLove関西

56
『駅すぱあと』 新しい開発基盤の研究 株式会社ヴァル研究所 開発部 サービスプラットフォームチーム ソフトウェアエンジニア プロダクトチーフ 佐藤昭彦 for

Transcript of 『駅すぱあと』新しい開発基盤の研究 for DevLove関西

『駅すぱあと』新しい開発基盤の研究

株式会社ヴァル研究所 開発部 サービスプラットフォームチーム

ソフトウェアエンジニア プロダクトチーフ 佐藤昭彦

for

自己紹介• 佐藤昭彦(さとうあきひこ)

• 2005年 株式会社ヴァル研究所入社。ソフトウェアエンジニア。

• モバイル系Javaエンジニアを経て、現在駅すぱあと内部の改善プロジェクトでお仕事をしています

• プログラム言語大好き

※注意 この発表では現在研究中のものを紹介しています。『駅すぱあと』には”まだ”導入されていません。

もくじ1. C/C++開発環境の改善

• 利用中のツール類の紹介

2. 運賃計算とDSL

• 『駅すぱあと』が戦う運賃計算について

• DSLのススメ

開発環境の改善

利用中のツール類• ビルド構成管理

• 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”

開発者

カバレッジ計測

• Jenkins上で計測

• 毎日 AM8:00に1回レポート生成

• 計測にはgcovを使用

• gcovr + Cobertura 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も使っている。

開発環境まとめ・雑感• 自動化の効果は高い

• 単純にコストが減るし、何より単純作業のストレスがなくなるのがうれしいですね。

• もっとツールを使っていこう

• 手元で実行出来るだけでも便利ですし、何よりこういうのをいじるの楽しいですよね。

運賃計算とDSL

運賃計算?• 基本1

• 乗車した距離に応じて金額が決まる

• →「営業キロ」を求めて運賃表を引く

• 基本2

• 「複乗の禁止」

• → 同じ所を2度通ってはダメ

https://www.jr-odekake.net/railroad/ticket/guide/02.html

運賃表の例。時刻表やJRのサイトで確認できる。

運賃計算の例。京都→ユニバーサルシティまでの普通運賃を求める場合。

京都

大阪

西九条

42.8km

3.6km

通算 49.6km

ユニバーサルシティ

京都→ユニバーサルシティは 840円…?

3.2km

京都→ユニバーサルシティは通算49.6km。これを運賃表で引くと840円と出るが・・・

京都

大阪

西九条

42.8km

3.6km

通算 49.6km

ユニバーサルシティ

京都→ユニバーサルシティは 840円…?

3.2km

実はもっと安い切符で乗車できる。

●東京・大阪の電車特定区間の普通運賃表 東京・大阪の電車特定区間(次の図の範囲)のみをご利用になる場合は、「電車特定区間の普通運賃表」をご覧ください。

https://www.jr-odekake.net/railroad/ticket/guide/02.html

京都

大阪

西九条

42.8km

3.6km

通算 49.6km

ユニバーサルシティ

3.2km

京都→ユニバーサルシティは 800円!

「電車特定区間」のみの経路のため、割安の運賃表が利用できた。

もう一つ運賃計算の例。大阪→奈良だが、京都を経由してみる。

●東京・大阪の電車特定区間の普通運賃表 東京・大阪の電車特定区間(次の図の範囲)のみをご利用になる場合は、「電車特定区間の普通運賃表」をご覧ください。

https://www.jr-odekake.net/railroad/ticket/guide/02.html

京都から先は「電車特定区間」外なので、今回は当てはまらなそう。

大阪

京都

奈良

42.8km

41.7km

通算 84.5km

大阪→奈良は 1490円…?

大阪→京都→奈良は通算84.5km。これを運賃表で引くと1490円と出るが・・・

大阪

京都

奈良

42.8km

41.7km

通算 84.5km

大阪→奈良は 1490円…?

この場合ももっと安い切符で乗車できる。

●大都市近郊区間内のみをご利用になる場合の特例 下図のそれぞれの大都市近郊区間内のみを普通乗車券または回数乗車券でご利用になる場合は、実際にご乗車になる経路にかかわらず、最も安くなる経路で計算した運賃で乗車することができます。

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円の切符で乗車できることがわかった。

その他、運賃計算特例は全国に様々な種類がある。

運賃計算

非常に特例が多い

今は?• プログラムで頑張っている

• 運賃計算を行うC言語関数群の中に特例の数だけif文があるイメージ

• →関数設計を工夫しても保守性に限界がある

• →そこでDSL!

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

ご清聴ありがとうございました