2024.12.19
システムの穴を運用でカバーしようとしてミス多発… バグが大量発生、決算が合わない状態から業務効率化を実現するまで
リンクをコピー
記事をブックマーク
渋川よしき氏:よろしくお願いします。それでは「Goの構造体とタグを極める」を発表します。
渋川と申します。株式会社本田技術研究所や株式会社ディー・エヌ・エーなどで働いて、今はフューチャー株式会社で仕事をしています。妻と子どもがいます。いろいろと本を書いたりしています。週末はインラインスケートを子どもたちとやっています。
これが今まで書いてきたGoの本です。『Real World HTTP』は韓国語バージョンも買えます。もう1ヶ国語を今翻訳中です。
当社では技術ブログにけっこう力を入れていて、最近勉強会で発表すると「『Go』で検索すると出てくる会社の人だ」と言われたりもします。ぜひいろいろチェックしてもらえるといいかなと思います。最近はGo 1.16のリリースに合わせて、「1.16でこういう更新がありました」みたいな内容を集中的に連載しています。
どんな技術ネタを検索しても必ず15年くらい前のIBMの「IBM DeveloperWorks」が引っかかるのがちょっといいなと思っていたので、いろいろなネタを掲載している当社のテックブログもIBMみたいにしていきたいなと個人的には思っています。
ということで、セッション発表に進みます。
アジェンダです。まず、Goの文化とタグについて紹介します。次に、タグをどうやって処理するかを説明していきます。そのあとに実装例の紹介をして、あとはタグの活用方法としてCode Generationの説明も軽くしようと思っています。Genericsの話もちょっと足したので、まとめで発表します。
まずGoの文化とタグですね。
ふだんからGoを仕事で使っている方も多いと思うんですが、巨大なシステムを開発するときになにも作戦なしでいくのはやはり厳しくて、なにかしらの方法を決めて開発していると思います。
多くのプログラミング言語は、そういう巨大なシステムをメンテナンス可能にしつつ、開発するためのメカニズムをいろいろと模索しながら提供してきました。オブジェクト指向もそうですよね。最近だと、状態を持たないとか、冪等性とか、いろいろな感じで状態を管理しやすくすることで、大きなコードになってもわかりやすくしたり、バグを出にくくしたりしています。
エンタープライズだと、今までJavaがけっこう使われてきているんですが、Javaの場合は、Dependency Injectionだったり、リフレクションだったりを駆使した巨大なフレームワークを作って、フレームワークが処理の流れを完全に制御して、一部分のロジックをプログラマが開発するみたいな流れです。
Goはだいぶやり方が違っていて、タグ付きの構造体みたいなものを使って、短いコード片の中にたくさんの宣言を入れて、裏でいろいろなロジックを走らせるのが特徴かなと思います。
図で説明したのがこれです。エンタープライズのアプリケーションで、けっこうたくさんのロジックを書かなきゃいけないときに、Goの場合はそのアプリケーションコードからそれぞれの複雑なロジックを呼び出します。アプリケーション固有のロジックは、呼び出し側にシンプルに集めるところがGoらしいコードかなと思います。
Goをやるときには「Go流でやりましょう」みたいにみなさんけっこう言うんですが、ほかの言語から取り入れたフレームワークはあまり流行っていないのかなと思います。
Javaなどよくあるフレームワークだと、フレームワークが全体を制御して、ホットスポットと呼ばれるアプリケーションのコードだけを部分的に差し込んでいく構造になっています。
僕の同僚がGoを評したコメントがおもしろくて、Javaユーザーから見ると、「Goというのは、なんかmain functionがあるぞ」と思うそうです。Javaにもあるんですが、main functionや起動からの流れは完全に隠されているので、それに比べるとGoはmainから順番に処理を追っかけていけるので、そこがわかりやすさにつながっていて、人気にもつながっているのかなと思います。
Javaのフレームワークでアプリケーションを作る場合、クラスはいっぱい作るんですが、結局誰がインスタンス作っているのかわからん、みたいなことがあります。
Goではメインの処理の中で、短い関数呼び出しで多くのロジックを駆動するんですが、そのときに使われているのがタグです。
タグは構造体の中に入れていくんですが、「このフィールドはこういう処理を裏で実行しますよ」みたいな宣言を書くことで、関数呼び出しのところだけ見るとシンプルだけれども、裏でいっぱいのことをやっていますよ、みたいなことができます。
例えばあるWebサービスで、リクエストが来ました。そしたら、まずそのリクエストを構造体にマッピングします。そのあとに、その構造体のままいくか、コピーを作るか選択して、今度はその構造体に対してValidationをします。Validationが通ったら今度はDB Accessです。DB Accessも構造体をsqlxなどのライブラリに渡して、SQL文を生成して実行して、返ってきた行オブジェクトもO/Rマッピングします。
これも構造体のタグを使って構造体にマップして、最後それをまたencoding/jsonなどのライブラリでJSONにシリアライズする感じで、けっこう複雑な、それでいて繰り返し使われるロジックは、このタグのマッピングでけっこうシンプルに使われています。
ざっと僕が使ったことがある、タグを使うライブラリをリストアップしてみました。例えば環境変数を構造体にマッピングしますとか、コマンドライン引数を構造体にマッピングしますとかですね。ほかにもJSONやTOMLなど、シリアライズやデシリアライズのライブラリもありますし、バリデーションもデータベースアクセスもタグを使えます。gocloud.devはDynamoDBやFirestoreへのマッピングを提供しますし、MongoDBや構造体間のコピーなど、本当に多種多様のタグを使うライブラリがあります。
ライブラリなしでGoのコードを書くと、コードの実装量がすごく増えるので、Goの開発の生産性を支えているのは、このあたりのタグを処理するライブラリかなと思います。
このタグの処理はすごく活用されているとは思うんですが、自分で実装する方法って、ドキュメントなどを探しても世の中あまりないなと実感したので、今回自分でいろいろと調べた内容を発表することにしました。
ライブラリなどを世間に公開するときは、やっぱりGoらしいコードほどみなさんに喜んで使ってもらえると思うんですが、タグを自分で処理する方法がわかれば、そのGoらしさを維持しつつ、Goのパワーをさらに引き出しやすくなるんじゃないかなと思いました。
Goのタグ処理ですが、タグ処理専用のライブラリは存在していなくて、比較的ふだんの業務で使わないようなreflectパッケージやgo/parserパッケージを使うことになります。reflectパッケージは、たまにデバッグ出力などで使う人もいるかもしれませんが、タグ処理だとまた独特な使い方をするので、そのあたりを本セッションの中では紹介していきたいと思っています。
どのようにタグを処理していくかですね。まずタグ処理の基本なんですが、プロセスを3つに分けて説明していきます。
まず構造体の型を取得します。それぞれの構造体にはフィールドがあって、NumFieldでフィールドの数が取れます。t.Field()というメソッドを呼ぶと、そのフィールドの状態のインスタンスが取れます。フィールドの構造体には、Tagというメンバーがいて、それに対してGet()というメソッドを呼ぶとタグの情報が取得できます。
基本的にはスライドの下に書いたように、タグを取ってきてNumField分でForループを回して情報を取ってくれば、Tagの一覧は簡単に出せます。ただ、実用的なコードだと、構造体のフィールドにさらに構造体がいる場合があって、それをネストして処理するのがけっこう必要になります。
これが再帰をするバージョンですね。構造体の型の情報を取ってきて、NumField分でForループを回すんですが、Anonymousは埋め込みですね。embeddingのときと、あとはフィールドの型がStructのときにはさらに再帰で呼び出します。
次は2ステップ目です。さっきは構造体の型を使っていたんですが、今度は型ではなくて、値からフィールドの値を取り出します。タグ処理の場合は型の処理と値の処理、インスタンスからデータを取ってくるという2種類あるので、パッとコードを見るとけっこうわかりにくいところですね。
フィールドの実際の値を取ってきて、タグにその値をどう加工するとか、どう処理するとかの文字列が書いてあるので、それを使ってその値に対していろいろと処理を加えるのがステップ2ですね。
フィールドのValue型にはInterface()というメソッドがあって、いつものAny型的に使われているinterface型が取れるので、あとは型アサーションを使って、いつもどおりに変数に入れられます。
これがそれですね。さっきはreflect.ValueOfのあとにType()というメソッドを呼んでいたんですが、これはType()を呼ばないでその実体のElem()メソッドを呼んで値を取ってきてます。それから、値側のNumFieldと値側のフィールドから値を取ってきて、いろいろと処理を回します。
次はタグ処理の基本のステップ3です。さっきはフィールドから値を取り出すというところ説明したんですが、タグ処理の場合だと逆に構造体のインスタンスに値を入れるということがよく行われます。
例えばJSONの文字列から構造体のフィールドに入れていくとか、データベースの行オブジェクトから構造体にしていくとかですね。その場合、値のSet()やSetSring()などのreflect.Value型には値を更新するみたいなメソッドがあるので、そのあたりを使って更新していきます。
ただ、これを実際やったことがある人もいると思うんですが、入力の値の型とか格納先の値の型とかあって、しかもポインタ型がいたりもして、いろいろな組み合わせがあるので、実際にやってみると超巨大なswitch文ができあがります。
ここまで3つの処理1・2・3を紹介したんですが、どのタグ処理のコードを書く場合でも、この3つの組み合わせで実用的なライブラリを作れるかなと思います。
構造体があったときに、その値に対して処理するのが2番と3番ですね。値を取り出して値を割り当てます。構造体のインスタンスの型情報を取ってきてメソッドを呼ぶのがタグ情報の取得です。
タグの処理のパターンは4通りあると思います。この中には世間にあるライブラリを使っていないパターンもあるんですが、きっとこういう処理もあるんだろうなというところで4通りのパターンにしています。これまでいくつかタグを使うライブラリを紹介してきたんですが、すべてこの4つのパターンに含まれます。
まずよくあるEncodeですね。json.Encode()みたいなものです。これはまずタグを処理して、タグの情報を取ってきます。その情報をもとにインスタンスから値を取り出します。取り出した値はmapにとりあえず入れてみたり、文字列化してみたり、取り出してきた値をもとにバリデーションのロジックを実行したりします。
次がDecodeですね。これもjson.Decode()と同じなのでDecodeと名付けています。これは構造体のインスタンスに値を設定します。例えばjson.Encode()はなにもない場合、フィールド名をもとにしてJSONのキーがオブジェクトのキーと一致するとみなして値を取ってきます。JSONのキーとフィールドの名前が違うときもマッチングさせて入れたりというところで、タグの情報を使っています。
出すときは無視するフィールドもあったりしますが、Decodeは名前のマッチングぐらいですかね。このタグの文字列を分析した結果を構造体に割り当てます。
次はPipingと名付けてますが、さっきの値を取り出す処理と割り当てる処理を一緒にします。同時に行う感じですね。ライブラリとして見たことがあるのは構造体間のコピーなんですが、同一インスタンスで値の正規化をするとかもきっとあると思いますし、構造体に値がなくてゼロ値だったときにデフォルト値を設定するライブラリもありなんじゃないかなと思っています。
これ(Zipping)は実装しているライブラリはあんまりなさそうかな。構造体同士の比較をする感じで考えました。テストコードを書いていて、構造体をインスタンスがリターンで返ってくるときに、「実はこのフィールドを無視してテストコードで比較したいんだけど」みたいなときが絶対あると思います。そういうところのライブラリを作るんだったら、それぞれのインスタンスから値を取り出してmapかなにかに入れておいて、そのmapを比較するみたいなライブラリがありなんじゃないかなと思います。
(次回へつづく)
2024.12.20
日本の約10倍がん患者が殺到し、病院はキャパオーバー ジャパンハートが描く医療の未来と、カンボジアに新病院を作る理由
2024.12.19
12万通りの「資格の組み合わせ」の中で厳選された60の項目 532の資格を持つ林雄次氏の新刊『資格のかけ算』の見所
2024.12.16
32歳で成績最下位から1年でトップ営業になれた理由 売るテクニックよりも大事な「あり方」
2023.03.21
民間宇宙開発で高まる「飛行機とロケットの衝突」の危機...どうやって回避する?
PR | 2024.12.20
モンスター化したExcelが、ある日突然崩壊 昭和のガス工事会社を生まれ変わらせた、起死回生のノーコード活用術
2024.12.12
会議で発言しやすくなる「心理的安全性」を高めるには ファシリテーションがうまい人の3つの条件
2024.12.18
「社長以外みんな儲かる給与設計」にした理由 経営者たちが語る、優秀な人材集め・会社を発展させるためのヒント
2024.12.17
面接で「後輩を指導できなさそう」と思われる人の伝え方 歳を重ねるほど重視される経験の「ノウハウ化」
2024.12.13
ファシリテーターは「しゃべらないほうがいい」理由 入山章栄氏が語る、心理的安全性の高い場を作るポイント
2024.12.10
メールのラリー回数でわかる「評価されない人」の特徴 職場での評価を下げる行動5選
Climbers Startup JAPAN EXPO 2024 - 秋 -
2024.11.20 - 2024.11.21
『主体的なキャリア形成』を考える~資格のかけ算について〜
2024.12.07 - 2024.12.07
Startup CTO of the year 2024
2024.11.19 - 2024.11.19
社員の力を引き出す経営戦略〜ひとり一人が自ら成長する組織づくり〜
2024.11.20 - 2024.11.20
「確率思考」で未来を見通す 事業を成功に導く意思決定 ~エビデンス・ベースド・マーケティング思考の調査分析で事業に有効な予測手法とは~
2024.11.05 - 2024.11.05