各ケースのデータ選択には分岐より規約に基づいた処理を適用する
概要
分岐処理は、手軽に記述できるため、多くの場面で利用される。
しかし、あるデータの値によって、途中のデータを選択する処理を分岐を用いて
実装してしまうと、全てのケースをカバーしなければならないため、
新たなケースの追加や既存のケースの修正によって、プログラムを修正する必要がある。
もし、これら各ケースをプログラムでは同じ処理だと認識できれば、
ケースの追加やケースの修正を感知しないため、もっと保守性を向上できる。
今回はこれについて説明する。
詳細説明
今回は、会員のカテゴリ(VIP, 会員, 無料会員)があり、
これらのカテゴリ毎にメッセージを別々に表示したいとしよう。
まずは、改善の余地がある処理を提示する。
package sample; import resources.properties.Property; public class Main { public static void main(String[] args) { Property property = new Property("message"); String title, message; // ここで会員カテゴリを取得するとする. String category = getMemberCategory(); // ここでデータを選択する. if(category.equals("A")) { title = property.getString("vip.title"); message = property.getString("vip.message"); }else if(category.equals("B")) { title = property.getString("member.title"); message = property.getString("member.message"); }else if(category.equals("C")) { title = property.getString("normal.title"); message = property.getString("normal.message"); }else { throw new IllegalStateException("カテゴリ名が不正です"); } // データを用いた処理. System.out.println("title: " + title); System.out.println("message: " + message); } // コンパイラを通すために仮の値を作成. private static String getMemberCategory() { return "B"; } }
なお、property クラスは、message.properties からキーに対応した値を取得するクラスである。
以下の過去記事にその実装例がある。
プロパティファイルの使用方法 - Status Code 303 - See Other
コンストラクタでリソースファイル名を指定できる実装にしているが、基本的に動作は同じ。
そして、タイトルやメッセージを格納した message.properties はこちら。
vip.title = VIP会員様へ vip.message = 今お買い物頂くと 40% のポイントが溜まります。 member.title = 会員様へ member.message = 今お買い物頂くと 20% のポイントが溜まります! normal.title = 無料会員様へ normal.message = 正会員になれば、ポイントが溜まり、1p=1円でご利用できます!
タイトルとメッセージを外出しファイルにすることで、内容の変更は properties ファイルの
更新だけになり、プログラムを修正する必要がないようにしている。
しかし、もうちょっと保守性を向上できる。
今回、ネックとなっているのは分岐の部分だ。
分岐を用いた、このような実装は自然に映るかもしれないが、
会員カテゴリが追加、変更された場合は、以下を変更しなければならない。
- プログラム(if 文の分岐部分)
- リソースファイルの xxx.title, xxx.message キーの追加
本来、プログラムは処理を記述するものであるから、データ追加の影響は受けたくない。
タイトルとメッセージだけを追加すれば動作するようにしたい。
これを実現するには、下記のように message.properties のキー名を
カテゴリに対応したキー名にし、それを処理で取得するようにする。
今回の例では、次のようにする。
- [カテゴリ名] + ".title" の値をタイトルに表示する
- [カテゴリ名] + ".message" の値を本文に表示する
このように、リソースデータの記述方法を規約化してしまうことによって、
各ケースに対する処理をまとめてしまう。
package sample; import java.util.MissingResourceException; import resources.properties.Property; public class Main { public static void main(String[] args) { Property property = new Property("message"); String title, message; // ここで会員カテゴリを取得するとする. String category = getMemberCategory(); try { // ここでデータを選択する. title = property.getString(category + ".title"); message = property.getString(category + ".message"); } catch(MissingResourceException e) { throw new IllegalStateException("カテゴリ名が不正です"); } // データを用いた処理. System.out.println("title: " + title); System.out.println("message: " + message); } // コンパイラを通すために仮の値を作成. private static String getMemberCategory() { return "B"; } }
message.properties は以下のようにキー名を変更する。
# 存在するカテゴリ名 + .title, + .message をデータとして取得する A.title = VIP会員様へ A.message = 今お買い物頂くと 40% のポイントが溜まります。 B.title = 会員様へ B.message = 今お買い物頂くと 20% のポイントが溜まります! C.title = 無料会員様へ C.message = 正会員になれば、ポイントが溜まり、1p=1円でご利用できます!
このようにすれば、もし、カテゴリが追加されたとしても、message.properties に
新しいカテゴリ "D" に対する title と message を追加すれば、処理は変更しなくて良くなる。
# 存在するカテゴリ名 + .title, + .message をデータとして取得する A.title = VIP会員様へ A.message = 今お買い物頂くと 40% のポイントが溜まります。 B.title = 会員様へ B.message = 今お買い物頂くと 20% のポイントが溜まります! C.title = 無料会員様へ C.message = 正会員になれば、ポイントが溜まり、1p=1円でご利用できます! # 追加 D.title = お試し会員様へ D.message = お試し期間中は全サービスが使えますが、有効期間は1週間です!
その出力結果。(getMemberCategory() で "D" を返すように変更)
title: お試し会員様へ message: お試し期間中は全サービスが使えますが、有効期間は1週間です!
今回は、簡単のため、同一 properties ファイル内にデータを記述しているが、
カテゴリによって変更されるデータの数が多くなればなるほど、大量のデータで混沌とする。
この場合は、読み込むリソースファイル自体を変更する。
例えば カテゴリが "A" の場合、message_A.properties を読み込むようにする。
注意点として、この方法は各ケースの異なる部分が処理データだけだということ。
もし、各ケースの処理自体が異なっている場合は、単純にこの方法は適用できない。
もし、データ選択とそのデータの処理がコード的に分割できるのであれば、
まずそれを行ってから、上記変形を行う。
結論
データの値によって、処理されるデータを変更したい場合は、
リソースファイルに規約を適用した処理を記述することによって、
ケースの追加や変更にリソースファイルだけで対応できるようになる。
しかし、各ケースによって処理自体が異なってしまう場合は、単純に適用できない。
もし、これらからデータ選択部と処理部が分割できるなら、それをまず行う。