2024.10.10
将来は卵1パックの価格が2倍に? 多くの日本人が知らない世界の新潮流、「動物福祉」とは
怠惰なエンジニアのためのコード生成(全1記事)
リンクをコピー
記事をブックマーク
高橋一平氏(以下、高橋):では、怠惰なエンジニアのためのコード生成ということで、リクルートマーケティングパートナーズの高橋が発表させていただきます。
さっそくですが、みなさんコードを書くのは好きでしょうか? 僕はあんまり好きではありません。コードを書いていて、ロジックを書くのはおもしろいですが、ボイラープレートや定型文的なコードなどをどうしても書かなければならないことは多いと思います。
こうしたボイラープレートを何度も書くのはしんどい。共通化、設計とかで解決しようとしても限界があるということで、コード生成をして解決しようというのが今回のお話です。
本発表でお話することですが、直接的なコード生成の話。ソースコードをファイルで書き出すという話と、マクロなどを使ったメタプログラミングによって先ほどの問題を解決しようという2つのお話をさせていただきます。
まず、コード生成をするにあたっての前提条件ですが、きちんと設計がなされている、クラスや関数の役割が明確になっていることが前提となってきます。
ルールがないような煩雑なめちゃくちゃなものに関してはコード生成することはできません。それに加えて、副作用がなかったりだとかDIをしっかり使っていたりだとか、テストのしやすいプログラミングになっていることが、まずコード生成よりも先にやらなければならないことだと考えています。
さっそく、コード生成の具体的な方法について話していきます。まずはパーサを書きましょうということなんですが、パーサというのは、構造化されている対象に対してだったら必ず書けます。
例えばXMLであったり、JSONであったり、Protocol Buffersだったり、SQLであったり、はたまた誰かが作ったExcelのフォーマットだったりとか、いろいろあると思いますが、構造化されていれば必ずパースはできる。
開発言語にも依存しない方法なので汎用的にも使えます。例えばSwiftで開発してます、だけどパーサはHaskellで書いてみてSwiftのコードを生成しますとか。そういう使い方でもいいと。けっこう汎用性の効く方法なので、困ったらパーサを書こうということですね。
パーサを使った具体的なコード生成の方法について説明していきます。スキーマ情報を利用したコードの生成の方法ですね。
まずスキーマをまるごとダンプして、それをプログラムで扱いやすいかたちへパースする。
このときにSQLの構文すべてを網羅したようなパースを書かなければならないわけではなくて、ダンプしたものがどういったかたちになるのかに基づいて、ある程度限定したかたちでパースを書いて作るくらいで十分だと思います。
そのパースしたものからDAO関連のソースコードを生成できます。例えばデータモデルやDAO、あとDAOのテストコード、またデータモデルのファクトリなど。そういったものに関してスキーマの情報をもとに自動生成することができて、実際に我々のサービスでもこういったことをやっています。
続いてメタプログラミングについてです。メタプログラミングというのはプログラムを扱うようなプログラミング手法のことで、プログラムを入力したり出力したりするようなプログラミング手法のことです。
メタプログラミングをすることによってなにが嬉しいかと言うと、構文木だとか型の情報にアクセスすることができます。ということはメタプログラミングを利用することで、利用しないようなプログラミングと比べてプログラムの表現力が高まるということが挙げられます。
具体的にメタプログラミングを使ってどういったことができるのかをみていきたいと思います。我々のプロダクトでもこういうことをやっているんですけれども、テストコードのテンプレートの生成です。
ユースケースだとかそういったもののテストのコードの生成だと考えてもらえればいいんですが、例えばテストしたい対象のコードを文字列として読み込む。そこからマクロなどを使って構文木の情報を持ってくる。
そうすると何ができるかと言うと、パースされた構文木の情報がわかるので、モックすべき関数がどんなものかがわかる。モックすべき関数がわかれば何ができるかと言ったら、そこからテストコードのテンプレートを生成することができると。
前提条件としてきちんとクラスの役割が明確であるだとか、テストができる状態になっているというのはここに関連することで、ユースケースとか、書き方が統一されていれば比較的メタプログラミングによってテストコードを作るみたいなことはやりやすいんですけれども。これが人によってバラバラとか、そういった感じで作られていると、なかなかこういう生成は難しくなってきます。
続きまして、ダミーデータを簡単に作る方法ですね。テストを書いていて1番しんどいことは何かと言うと、たぶんダミーデータを作るのがとてもしんどいことだと思います。
巨大なクラスですと1つのユースケースにいくつものモデルが関連していたりして、何種類ものオブジェクトのダミーデータを作らなければならない。テスト用のダミーのデータを作らなければならないということで、これを手でやろうとするととてもしんどいと。
これまで話していたように、ユースケースのパースをしたりとか、メタプログラミングを使ってクラスごとにダミーデータのファクトリを作るっていう解決方法もあります。僕たちももともとそういうクラスごとのダミーデータを生成するという方法でこれを解決していたんですけれども。
クラスごとにファクトリを作らないといけないということで、手でやるとしんどい。生成するにしても、生成したファイルはけっこう膨大なのでメンテナンスのコストが大きくなってしまう。加えて、モデルの書き方がちょっと変わったりすると全体的に影響が出たりすることもあります。
では、これを楽に解決する方法はないのかということで、辛くない解決方法。どんな型のインスタンスだろうが作れる汎用的なファクトリというものを定義できればこれを解決できると考えました。
具体的にどうやってそれを実現するかと言うと、あらゆる型ということなので、あらゆる型が実際どんなかたちをしているのかを一般化することができれば、どんな型のインスタンスでも作れるファクトリというものが作れます。そこで登場するのが直和型と直積型の考え方です。
直和型というのはA+Bで、AかBのどちらかを取るような型というふうに定義します。直積型というのはA×Bということで、AとBの2つの要素を持つような型。
直和型というのはEitherとか聞いたことあると思いますが、そういったものです。直積型というのはTupleとかですね。そういったデータ構造だと思っていただければと思います。
こういったかたちにいろんな型は書き換えることができます。
例えばAnimalという抽象クラスがあって、それを継承するDog、Catっていうクラスがあったとします。AnimalはDogかCatのどちらかなので、DogとCatの直和であると。
Catというのは、nameというStringの要素とageというIntの要素を持っている直積であると定義して、直和と直積だけで記述することができるます。
これを使って、構造に関しては全部直和と直積のかたちで言葉は定義できるので汎用的なファクトリを作ることができる。実際にどのように実装するかと言うと、ジェネリックプログラミングライブラリを使うという方法がたぶん1番一般的だと思っています。
Scalaだったらshapelessっていうライブラリなんですけど、そのあたりを使って作るのが一般的です。もう1つはメタプログラミングを使った方法ですね。自分で自力で実装するという方法があります。
我々のプロダクトではScalaを使っていて、実際にこのようなどんな型のダミーデータでも作れるファクトリというものを実装したんですけれども、それがこの例ですね。
1番上の行で、Dogというクラスを定義しました。その真下でTestObject [Dog] というふうに書いて、型引数を渡しています。これによってDogのダミーデータが生み出されました。
ここで注目してほしいのは、TestObjectというものを書いている段階ではDog型のダミーデータをどのように作るかを、実はTestObjectに与えていません。
これが実際に中では直積と直和のかたちに置き換えて、そこからまた戻してというようなことをやって作っているんですけれども。このようにしてイントロの作り方、Stringの作り方を個別に与えるだけでどんなインスタンスでも作れるようなダミーデータのファクトリを作ることができました。
今回はお話ししませんが、Stateモナドを使うことによって、これは固定のダミーデータなんですけど、ものよって違うダミーデータを作ることも可能になってきます。
ということで、まとめです。コード生成を使って怠惰にプログラミングをしましょう。メタプログラミングとかジェネリックプログラミングみたいなものを使っていくといいんですけれども。
こうしたことは言語に依存するところがあると思うので、メタプログラミングやあまり強くない言語を使っている場合にはパーサを使ってコード生成をすることでなんとかすることができます。
以上になります。ご静聴ありがとうございました。
(会場拍手)
司会者:高橋さん、ありがとうございました。ご質問がある方は挙手お願いいたします。ないですか? じゃあ僕から。僕、実はAndroidエンジニアなんですが、テストとかも書かなきゃいけなくてすごくしんどい気持ちにはなっています。そこで、実際にこれを導入してどれくらい楽になったのかなというのは気になります。
高橋:とくに効果が大きかったのは、汎用ファクトリです。本当に最初の状態ってファクトリすら定義されていなくて、テストを1個書くごとにダミーデータを上からガーっと書いて。1つのテスト書くのに1,000行書く、みたいなことがあったりして大変でした。
ダミーデータを作って、さらにテンプレートの生成みたいなものを組み合わせることによって正常系のテストを1個書くくらいだったら、自分で手で書かなきゃいけないのは数十行とかそのくらいで済むようになりました。生産性の点に関してはだいぶよくなったのかなと思います。
司会者:ありがとうございます。それでは、これで終了とさせていただきます。高橋さん、改めてありがとうございました。
(会場拍手)
2024.11.13
週3日働いて年収2,000万稼ぐ元印刷屋のおじさん 好きなことだけして楽に稼ぐ3つのパターン
2024.11.11
自分の「本質的な才能」が見つかる一番簡単な質問 他者から「すごい」と思われても意外と気づかないのが才能
2024.11.13
“退職者が出た時の会社の対応”を従業員は見ている 離職防止策の前に見つめ直したい、部下との向き合い方
2024.11.12
自分の人生にプラスに働く「イライラ」は才能 自分の強みや才能につながる“良いイライラ”を見分けるポイント
2023.03.21
民間宇宙開発で高まる「飛行機とロケットの衝突」の危機...どうやって回避する?
2024.11.11
気づいたら借金、倒産して身ぐるみを剥がされる経営者 起業に「立派な動機」を求められる恐ろしさ
2024.11.11
「退職代行」を使われた管理職の本音と葛藤 メディアで話題、利用者が右肩上がり…企業が置かれている現状とは
2024.11.18
20名の会社でGoogleの採用を真似するのはもったいない 人手不足の時代における「脱能力主義」のヒント
2024.11.12
先週まで元気だったのに、突然辞める「びっくり退職」 退職代行サービスの影響も?上司と部下の“すれ違い”が起きる原因
2024.11.14
よってたかってハイリスクのビジネスモデルに仕立て上げるステークホルダー 「社会的理由」が求められる時代の起業戦略
2024.11.13
週3日働いて年収2,000万稼ぐ元印刷屋のおじさん 好きなことだけして楽に稼ぐ3つのパターン
2024.11.11
自分の「本質的な才能」が見つかる一番簡単な質問 他者から「すごい」と思われても意外と気づかないのが才能
2024.11.13
“退職者が出た時の会社の対応”を従業員は見ている 離職防止策の前に見つめ直したい、部下との向き合い方
2024.11.12
自分の人生にプラスに働く「イライラ」は才能 自分の強みや才能につながる“良いイライラ”を見分けるポイント
2023.03.21
民間宇宙開発で高まる「飛行機とロケットの衝突」の危機...どうやって回避する?
2024.11.11
気づいたら借金、倒産して身ぐるみを剥がされる経営者 起業に「立派な動機」を求められる恐ろしさ
2024.11.11
「退職代行」を使われた管理職の本音と葛藤 メディアで話題、利用者が右肩上がり…企業が置かれている現状とは
2024.11.18
20名の会社でGoogleの採用を真似するのはもったいない 人手不足の時代における「脱能力主義」のヒント
2024.11.12
先週まで元気だったのに、突然辞める「びっくり退職」 退職代行サービスの影響も?上司と部下の“すれ違い”が起きる原因
2024.11.14
よってたかってハイリスクのビジネスモデルに仕立て上げるステークホルダー 「社会的理由」が求められる時代の起業戦略