2024.12.10
“放置系”なのにサイバー攻撃を監視・検知、「統合ログ管理ツール」とは 最先端のログ管理体制を実現する方法
怠惰なエンジニアのためのコード生成(全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.12.10
メールのラリー回数でわかる「評価されない人」の特徴 職場での評価を下げる行動5選
2024.12.09
10点満点中7点の部下に言うべきこと 部下を育成できない上司の特徴トップ5
2024.12.09
国内の有名ホテルでは、マグロ丼がなんと1杯「24,000円」 「良いものをより安く」を追いすぎた日本にとって値上げが重要な理由
2023.03.21
民間宇宙開発で高まる「飛行機とロケットの衝突」の危機...どうやって回避する?
2024.12.10
職場であえて「不機嫌」を出したほうがいいタイプ NOと言えない人のための人間関係をラクにするヒント
2024.12.12
会議で発言しやすくなる「心理的安全性」を高めるには ファシリテーションがうまい人の3つの条件
2024.12.06
嫌いな相手の行動が気になって仕方ない… 臨床心理士が教える、人間関係のストレスを軽くする知恵
PR | 2024.11.26
なぜ電話営業はなくならない?その要因は「属人化」 通話内容をデータ化するZoomのクラウドサービス活用術
2024.12.11
大企業への転職前に感じた、「なんか違うかも」の違和感の正体 「親が喜ぶ」「モテそう」ではない、自分の判断基準を持つカギ
PR | 2024.11.22
「闇雲なAI導入」から脱却せよ Zoom・パーソル・THE GUILD幹部が語る、従業員と顧客体験を高めるAI戦略の要諦