はじめに
多くのJavaプロジェクトにおいて、定数はコードの一部として重要な役割を果たしています。
しかし、これらの定数がハードコーディングされていると、変更が必要になった際コードの柔軟性が損なわれます。
この記事では、ハードコーディングされた定数をExcel関数に置き換えることで、どのようにして柔軟性を向上させたかをご紹介します。
背景
法定帳票のレイアウトをもとにしたExcelファイルを出力する機能を持つプロジェクトがありました。
このプロジェクトの一部コードでは、以下のように定数がコード内に直接書き込まれていました。
法定帳票に表示する名称を動的に取得しているのですが、特定の条件に応じてハードコーディングされた定数が使用されています。
if (param.getHogeType() == 1) {
xml.replaceValue(STR_TEMP1, STR_TEMP2); // STR_TEMP1: "XXXX" -> STR_TEMP2: "名称1"
}
今回は法改正による影響を受けたため、コードを修正する必要がありました。
問題点
このような現状では、帳票レイアウトの変更が生じる度にJavaコードの定数を修正する必要があります。
外的要因になりますが、リリース後要件が明確になることも多々あり、都度コード修正はできる限り避けることが望ましいと思っています。
さらに、コード修正が発生するとテストと修正モジュールの適用作業を行う必要があり、開発者に限らずユーザーまで負担が増します。
解決策
定数で管理していた表示名称をExcelファイルにあらかじめデータテーブルを埋め込み、関数で名称を取得することにしました。(※テーブルはフォントを背景色と同じ白にして見せないようにしています。)
これにより、Javaコードの修正を最小限に抑え、柔軟に対応できるようになりました。
パラメータ値 | 表示名称1 | 表示名称2 | 表示名称3 |
---|---|---|---|
0 | XXXX | XXXX | XXXX |
1 | XXXX | XXXX | XXXX |
2 | XXXX | XXXX | XXXX |
Excel関数の活用
ExcelのVLOOKUP関数を用いて、例えばパラメータ値(IDやコードなど)に応じた名称をテーブルから動的に取得しています。
これにより、定数をJavaコード内に保持する必要がなくなりました。
=VLOOKUP(IF($A$1="条件", 0, $A$1), 範囲指定, 列番号, FALSE)
enumを用いた定数管理
他のアプローチとして前述のJavaコード修正と変わりないですが、enumによる定数管理の方法もあります。
EnumMapを使用することで、Excel(セル位置)と表示名の関連づける実装も可能ですが、次のメリット・デメリットがあります。
メリット
- 可読性の向上: 既存のif分岐を除くことができ、各Enumがセルと表示名の関連を保持するため、コードがより直感的になります
- 一元的な管理: 定数とその関連情報を一箇所で管理でき、更新が容易です
デメリット
- 実装量の増加: enumの定義が複雑になり、定数や関連情報が増えると管理が煩雑になります
- 変更時の負担: Javaコードの変更が必要になるため、デプロイ作業が発生します
- ドキュメントとメンテナンス: 継続的な管理と、変更時のドキュメント更新が必要です
コード例
コードはEnumMapにした時の参考例になります。
public enum hogehoge {
TYPE0(0, createMap(
ExcelCellType.TITLE_DISP_NM_TYPE, DisplayName.STR_TITLE_TEMP0,
ExcelCellType.OUTPUT_DISP_NM_TYPE, DisplayName.STR_OUTPUT_TEMP0,
ExcelCellType.DISP_NM_TYPE, DisplayName.STR_TEMP0)),
TYPE1(1, createMap(
ExcelCellType.TITLE_DISP_NM_TYPE, DisplayName.STR_TITLE_TEMP1,
ExcelCellType.OUTPUT_DISP_NM_TYPE, DisplayName.STR_OUTPUT_TEMP1,
ExcelCellType.DISP_NM_TYPE, DisplayName.STR_TEMP1)),
TYPE2(2, createMap(
ExcelCellType.TITLE_DISP_NM_TYPE, DisplayName.STR_TITLE_TEMP2,
ExcelCellType.OUTPUT_DISP_NM_TYPE, DisplayName.STR_OUTPUT_TEMP2,
ExcelCellType.DISP_NM_TYPE, DisplayName.STR_TEMP2));
private final int code;
private final Map<ExcelCellType, DisplayName> valuesMap;
hogehoge(int code, Map<ExcelCellType, DisplayName> valuesMap) {
this.code = code;
this.valuesMap = valuesMap;
}
public static hogehoge fromCode(int code) {
// コードに基づいて適切なDispNmTypeを取得
return Arrays.stream(DispNmType.values())
.filter(type -> type.getCode() == code)
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("Invalid DispNmType code: " + code));
}
public void replaceValues(hoge xml) {
// 各Excelセルタイプに対応する表示名を置き換える
valuesMap.forEach((excelCellType, displayName) -> {
xml.replaceValue(excelCellType.getValue(), displayName.getValue());
});
}
private static Map<ExcelCellType, DisplayName> createMap(Object... keyValues) {
// ExcelCellTypeとDisplayNameのペアからマップを構築
EnumMap<ExcelCellType, DisplayName> map = new EnumMap<>(ExcelCellType.class);
for (int i = 0; i < keyValues.length; i += 2) {
ExcelCellType key = (ExcelCellType) keyValues[i];
DisplayName value = (DisplayName) keyValues[i + 1];
map.put(key, value);
}
return map;
}
コーディングの効率化
先ほど記載したenumで管理する場合もJavaコードの実装量が増加します。Excel関数の利点を活かし、コーディングの効率化を実現しました。
柔軟性の向上
変更が必要になった場合も、Excelファイルのデータテーブルを更新するだけで済み、Javaコードの修正は不要です。これにより、迅速かつ容易に対応できます。
メンテナンス性の向上
Excelファイルを使用することで、ノンプログラマーでも修正が可能です。一方で、Excel関数が壊れた場合やファイルが大きくなった場合、重くなるといった制約はあります。
他のアプローチ
マスタテーブルを作成し定数の値を参照する方法でハードコーディングを解消させるアプローチも取ることができます。
今回のプロジェクトではデータ量は少なかったため既存コードを活かしてExcel関数を使用しましたが、増える場合はデータベースなど利用することも検討できます。
おわりに
いかがだったでしょうか?
Excelの特性を最大限活かすことで、Javaコードの柔軟性を向上させた例をご紹介しました。
ほんの少しの工夫で既存のものを改善し、開発者の負担を軽減できます。
こういった法改正の影響を受けるプロジェクトが多いため、今後も変更容易性の高い設計を追求し、運用保守しやすいよう改善していきたいと考えています。
皆さんのプロジェクトでも同様の課題に直面している場合、このアプローチが参考になることを願っています。
開発者にとって、この記事が少しでも参考になれば幸いです。