2018年5月4日金曜日

【グルメレポ】15年以上熟成された秘伝のソース(Java)


猫と一緒にガジェットライフ♪ムチャです。

以前関わったシステムの中で、15年以上前に作られて稼働し続けている、長い年月をかけて熟成されたそれは非常に味わい深いソースがありました。
独自フレームワークで元々の設計が良くない上に、設計書などもソースから逆コンパイルしたような代物、かつノーレビューで本番まで行ってしまっている感じでした。

長期間に渡り継ぎ足し(機能拡張)しながら腐敗熟成された味わいは、自分のプログラマ人生で出会った中で間違いなく最高の一品だったため、この感動をぜひ共有したいと思い、この記事を書きます。
この深い味わいをご賞味下さい。

注意事項

  • 本記事は【グルメレポート】です。特定の企業、団体、プロダクト、個人を中傷する意図はありません。純粋にソースの味わいのみをお楽しみください。
  • どんな酷いシステムにしろその時々の複雑な事情があり、関わった人たち全てが悪いわけではありません。まして長い間熟成されたシステムは当初関わっていた人や会社は既に離れていて別の会社に管理が移っていたりするため、もはや追求は不可能です。様々な事情が複雑に絡み合った濃厚な味わいをお楽しみください。
  • いろいろ分かっても追求はおやめください。取材不可の隠れた名店です。
  • 自分も所詮クソ雑魚エンジニアなので、ご指摘はご歓迎します。まさかりはご勘弁下さい。
  • より味わい深いソースの情報も歓迎です。

Hors-d'œuvre (オードブル)

1.無駄なインポート

Javaでは使用するパッケージ(ライブラリみたいなもの)はimport文で宣言しないといけませんが、最も基本的なjava.lang.*は書かなくても使えるようになっています。

しかしこれがすべてのソースファイルに書いてありました。
import java.lang.*;
書いてあったところで問題なく動くのですが、この時点ですでに不穏な空気を醸し出していたことは言うまでもありません。

2.インスタンスの二番だし

Foo foo = new Foo();
foo = bar.getFoo();
ここは二番だしで旨味を出したい」という感じでしょうか。
変数fooはFooインスタンスを生成して初期化してますが、直後に別の方法でFooインスタンスを取得して上書きしています。1行目で生成したインスタンスは即座にガベージコレクションの対象になります。

わりとよく見るコードです。おそらくC言語とかと同じように「メモリを割り当てないとその変数が使えない」みたいな勘違いをしているのではないかと思います。

変数は無意味に初期化するべきではありません。その辺のことは別の記事に書こうと思います。


Les soupe(スープ)

3.それ三項演算子の意味ある?

三項演算子とは、1つ目に条件を書き、それが真・偽のそれぞれで何を実行するかを1行で記述するものです。
(条件)? 条件が真の場合の値 : 条件が偽の場合の値

例えばある値がnullだった場合は0、それ以外はその値をそのまま使うといった使い方をします。
しかし、出てきたのがこれ。
boolean result = foo > 2 ? true : false;
うん・・・それboolean result = foo > 2;でいいよね・・・。

4.それif文の(ry

3.とほとんど同じですが。
if(foo > 2){
  return true;
}else{
  return false;
}
うん・・・そ(ry


5.お楽しみは最後まで

if文で、真のブロックのあと偽のブロックが延々と最後まで。
if(foo){
  //処理
}else{
  //以下メソッドの最後まで続く
  :
  :
  :
  :
}
無駄にブロック・インデントがあると読み手に無駄な労力をかけます。
if文のあとに処理が不要な場合、真のブロック内でreturnしてしまえばあとに続く部分はブロック・インデント不要になります。
if(foo){
  //処理
  return;
}
//偽の場合の処理



ポワソン(Les poisson)魚料理

6.StringBuilder(Buffer)の包み焼き

StringBuilder sb = new StringBuilder();
:
sb.append("aaa" + b);
うん、なんでStringBuilderなんてものが用意されているのかわかってるかな?(#^ω^)

※上記コードは以下と同じになります。(Java9以降は少し事情が違いますが)
sb.append(new StringBuilder().append("aaa").append(b).toString());


7.スクリプトレットのてんこ盛り

スクリプトレットとはJSP内に<% 〜 %>でくくってJavaのコードを直接書けるものです。
また<%! 〜 %>という宣言タグもあり、メソッドの定義なんかもできます。

これらがものすごく多かったです。
複雑な条件で出力の出し分けなどもすべてJSP上に実装されており、その判定のためのメソッドまで定義されている始末。
制御構造が複雑になるとHTMLと混ざってぐちゃぐちゃになり、もはやどんな画面になるか想像が大変です。

業務的なロジックは他で書いて、JSPには最後の結果だけ持ってきてほしいものです。
やるにしてもJSTLで最小限の制御構造のみに。分岐後の出力が大きい場合は別ファイルにしてインクルードした方がいいですね。


8.一つ一つ丹精込めたゲット

"1".equals(obj.getFoo().getBar())
|| "2".equals(obj.getFoo().getBar())
|| "3".equals(obj.getFoo().getBar())
毎回ゲッターを呼び出してオブジェクトを取得するやつ。
変数というものを知らんのか!
メソッド呼び出しでパフォーマンスも落ちるしとにかく可読性が下がるのでいいことありません。


9.レイヤの重ね煮、Mapを添えて

フレームワークの階層が多く、DTO(Data Transfer Object:別の層へデータを渡すためのオブジェクト)がMapでした。

Mapだと何でも放り込めて作るのは楽ですが、何が入っているのかが管理されていないと取り出すときに混乱します。キーのスペルミスがあっても動かしてみないと分かりません。

どの階層でどういうオブジェクトがどんなキーで入るのか、使うのかをまとめた資料なんかはもちろんありません。しかも層をまたぐ際に名称が変わったりするものもありました。カオスです。


10.秘伝の味は記録に残して継承

よくありますよね。削除や変更をコメントで残し続けるやつ。
もちろんバージョン管理システムは使ってます。
// ADD 2018.1.1 START
:
// ADD 2018.1.1 END
:
// MOD 2018.2.1 START
//元のコード
新しいコード
// MOD 2018.2.1 END
:
// DEL 2018 3.1 START
//消したコード
// DEL 2018.3.1 END
もうこれ絶対意味ないと思うんですがどうでしょう。
バージョン管理システムで履歴見たほうがいいですし、ただコードだけ残っててもなんで変更されたのかがわからないと意味が無い上に読みにくくなるだけです。

自分も初めてシステム開発をやったとき、プロジェクトの方針でこうしたのですが、次のプロジェクトからはやめました。

ヴィヤンド(Les viandes)肉料理

いよいよメインディッシュです。

11.変数名のまるごと姿焼き

一番長い変数名を調べたところ、75文字ありました。
ダミー文字に置き換えてますが、日本語文章をそのままローマ字にしている感じです。
clearXXXXAaaaaaaaBbbbbCcccccccDddddCccccccccccEeeeeeeeeeeCtlListValueStatus
もうそこまでするなら日本語でいいよ・・・
(Javaは日本語変数名も可能です)

メソッド名も長いので、一番長い文だとメソッド宣言の以下でした。
public XXXXAaaaaaaaBbbbbCcccccccDddddCccccccccccEeeeeeeeeeeFffffffCtlModel getXXXXAaaaaaaajyohoFffffffListyo(UserInformation argUserInformation,
                 XXXXAaaaaaaaBbbbbCcccccccDddddCccccccccccEeeeeeeeeeeFffffffCtlModel argAaaaaaaaBbbbbCcccccccDddddCccccccccccEeeeeeeeeeeFffffffCtl
                 ) throws RemoteException {
読むのも嫌になります。


12.超厚切りステーキ

今回のコースの目玉です。

9.で書いたとおりレイヤは複数あるのですが、大きく「EJB層」と「その前の層」とで分かれていました。
そしてEJB層を呼び出す際に必ず1つのクラスを経由することになっていました。呼び出す手順を統一させたかったのだとは思いますが・・・。
結果、そのクラスのソースコード行数はなんと

_人人人人_
> 5万行 <
 ̄Y^Y^Y ̄

5万ですよ。サイズをメモしてくるの忘れましたが、数十MBとかだったと思います。
もう開くだけでも大変。メソッド数は200以上でした。
15年の重みを感じました。

これより長いソースコードあるんでしょうか。見たことあるという方はご一報くださいw

デセール(Les dessert)デザート

お口直しに軽いのを。

13.全メソッドにthrows Throwable

public void func() throws Throwable
非チェック例外(RuntimExceptionとそのサブクラス)とか関係ありません。
なんでもスローし放題。
まあ独自フレームワークの方で最終的に例外ハンドリングされるのでいいんですが・・・


14.勝手にhead要素

EUでは原産地名称保護制度というのがあり、勝手にその土地の名産の名前を使ってはいけないのですが。

IE8→11になってレイアウト崩れて動かない画面を調査したところ、とあるフォーム名が「head」になっていて、document.head.xxxxと書いてるとこがhead要素を参照しててエラーに。

もちろんフォームなのでヘッダーと全く関係がありません。なぜその名前にした。

IE8だとdocument.headという記述ができないため、問題なく動いていました。


カフェ・ウ・テ(Cafe ou the)食後のコーヒー

15.ストリームからじっくり抽出したバイナリ

ファイルIOはBuffered〜を使うべしとどの本にも書いてあると思いますが。
FileInputStream in = new FileInputStream("foo.txt");
ByteArrayOutputStream out = new ByteArrayOutputStream();
int i;
while((i = in.read()) != 0){
  out.write(i);
}

Java8以降ならjava.nio.file.Filesクラスを使えば一発です。
byte b[] = Files.readAllBytes(Paths.get("foo.txt"));



と、い、う、わ、け、で

お楽しみいただけましたでしょうか。
一応現役のシステムであり、リファクタリングする方が危険なのでこのまま秘伝の味は受け継がれていくのでしょう。
でももう見たくありません。

みなさまの味わったことのあるソースもご紹介いただければ幸いです。

それではみなさまよきガジェットライフを(´∀`)ノ



▼こちらの記事もどうぞ

▼ブログを気に入っていただけたらRSS登録をお願いします!
▼ブログランキング参加中!応援よろしくお願いします。

スポンサーリンク