業務でJavaを使う際に気をつける事 Part2
序文
前回の記事の後、またJava + Spring Bootでバックエンドを担当する機会を得た。 8ヶ月Java + Spring Bootを使ってきたし、その案件のメンバーの中では実力派ではあったので意気揚々と火中の栗を拾いに行った。 この前口上からお察しなのだがボコボコだった。井の中の蛙大海を知らずとはまさにこの事。 控えめに言っても迷惑をかけてしまったなぁと思いつつ、次また同じ機会が巡ってきた時に同じ轍を踏まないよう学びを記録しておく。
- 序文
- 自分がハンドリングしている範囲ならば、念の為なんて理由でNull安全に書くな
- 安易にNullを戻り値とするな
- 「とりあえずLombok書いとくか〜」はやめろ
- Enumをもっと活用しよう
- 拡張for文なんて使ってる暇あるならStream APIも活用しよう
- どうしても定数定義クラスを作るというなら取り扱う範囲は限定しよう
- DTOとDBのカラム名が異なるならModelMapperをうまく活用しよう
- 細かなことでも統一感を持たせて書こう
- APIはコントローラから書くと全体的にスッキリ書ける(気がする)
- その他
自分がハンドリングしている範囲ならば、念の為なんて理由でNull安全に書くな
これすなわちただの怠慢である。
Nullが返ってくることなんて仕様上あり得ないのにif (Objects.isNull(hoge))
なんて書くのは無駄だし他の作業に対しても悪影響でしかない。
安易にNullを戻り値とするな
空のオブジェクトの方がいいのではないか?よく考えた方がいい。
「とりあえずLombok書いとくか〜」はやめろ
Lombokはボイラープレートを省略できるのでとてもありがたい反面、自動生成されるコードを意識しないと痛い目にあう。 Setter、Getterぐらいならまぁいいのかもしれないが、コンストラクタ関連のものは後述の件も含めて気をつけた方がいい。 自分は安易にAllArgsConstructorを付けがちだったが、RequiredArgsConstructorの方が適切かもしれないから見直した方がいい。
リクエストのDTOクラスは特に気をつけろ
リクエストDTOだとNoArgsConstructorじゃないとエラーが起きたりする。 インスタンス化する過程で引数なしでインスタンス化することがあるようだ。
Enumをもっと活用しよう
Enumには静的メソッドを定義できる。 例えば、引数の値から逆引きで特定のEnum要素を取り出すなんてこともできる。 これができるようになるとかなりコード量を削減できる。 Effective Javaを読めば書いているらしい。読んだけど鳥頭なので忘れていたようだ。
拡張for文なんて使ってる暇あるならStream APIも活用しよう
Java 8から導入のStream API。平たく言えば関数型言語っぽくCollectionを扱える。 Javaは手続き型言語なので読みづらいかな?と思って使わないようにしていた。 でも今時のJavaエンジニアはStream APIを使う事に慣れているので読みづらいなんて事はないようだ。 自分の知識が2013年あたりで止まっているから生じた誤解でしたね。 Stream APIだからって速度(実行?コンパイル?)がひどく低下することもないらしい。 Stream API出始めの頃だとそういう話が合った気もしたが、仮にそうだったとしても改善されたのであろう。
Stream APIを使えば記述量を拡張for文使った時より数割減らせるので、開発速度向上が期待できる。 先ほど出たEnumもStream APIで扱えるので組み合わせると尚良い。
どうしても定数定義クラスを作るというなら取り扱う範囲は限定しよう
Enumでは、例えばバリデーションのアノテーションで指定する値を定数としては扱えない。 自分の理解では定数をベタがきするのはよろしくないと思っているので、致し方なく定数定義クラスを作ってしまう。 せめて作るならその定数定義クラスが取り扱う範囲は限定して責任を明確化する方が良い。 Enumが果たしていた役割の一つがそれなので、同じことをしようという話です。
DTOとDBのカラム名が異なるならModelMapperをうまく活用しよう
JPAでカスタムクエリは使わないでSave(CreateやUpdate)する時はEntityインスタンスを引数にする。 なので、リクエストをDTO使ってインスタンス化した後、それをEntityインスタンスに変換してそれを引数にする事になる。 ModelMapperを使うとキャメルケース同士でもJacksonのObjectMapperのように簡単に変換ができる。 ただし、フィールド変数の名前が一致してなければいけない。
CoCで行きたいところではあるものの、DBのスキーマが固まっていない状況だと疎結合にしたくなる。 その結果、DTOのフィールド変数の名前がEntityクラスのそれと一致しなくなってしまう。 ModelMapperならばBean定義に規約から外れているものMapさせたり、とあるフィールドの値によってはMapしないなど設定できる。 柔軟にカスタマイズできるので、よく仕様を理解して活用すると良い。 Bean定義外にゴニョゴニョ処理書いてる場合はその処理はおかしいと思った方がいい。
細かなことでも統一感を持たせて書こう
規約がしっかりしていれば起きづらいけど、規約がないからって無法地帯にしても構わないなんて話ではない。 特に他所者としてプログラマとして入ったならば、郷にいれば剛に従えという話である。 どうしても、どうしてもそれは唾棄すべき書き方だとするならば、面倒臭がらずに説明して規約化していきましょう。
APIはコントローラから書くと全体的にスッキリ書ける(気がする)
テスト駆動開発と同じで、要件に近いところなのでサービスやリポジトリを考えるときの見通しが立てやすい。
その他
残りは愚痴っぽくなっちゃうので箇条書きでさらっと書いとく。