第4章 逆引きカタログ その他
Authors:Agata Toshitaka
ユーティリティクラスはデザインパターンではありませんが、良く使われる基本的な定石なのであえてここでご紹介いたします。
ユーティリティクラスは、staticな共通の処理のメソッドを集めたクラスです。
同じ処理が2箇所以上で出てきた場合は、ユーティリティクラスにまとめることができる可能性があります。
ユーティリティクラスを使うと共通処理を1箇所にまとめることができますので、再利用性の向上ばかりでなく、あとから修正が発生してもユーティリティクラスの修正だけですみ、保守性が向上するというメリットがあります。
ユーティリティクラスを作るのは簡単です。
各クラスに点在する共通処理を、ユーティリティクラスのstaticメソッドに移動させるだけで、ユーティリティクラスの一丁あがりです。
ユーティリティクラスに移動した処理はどこからでも呼び出すことができます(図1〜2)。
◎第1段階:ユーティリティークラス未使用
それではより具体的な例を見てみましょう。
まずは、ユーティリティクラスを使用しない状態のコードから見ていきます(図3)。
このサンプルWebアプリケーションでは、管理者が商品の価格の設定、変更処理を扱っています。
SetPriceServletクラス(
リスト1)は販売価格を新規に設定するサーブレットです。
また、UpdatePriceServletクラス(
リスト2)は販売価格を更新するサーブレットです。
リスト1 SetPriceServlet.java
public class SetPriceServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
String priceStr = req.getParameter("price");
| @ |
priceStr = priceStr.replaceAll(",", "");
priceStr = priceStr.replaceAll("\\\\", "");
int price = Integer.parseInt(priceStr);
|
}
}
リスト2 UpdatePriceServlet.java
public class UpdatePriceServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
String priceStr = req.getParameter("update_price");
| @ |
priceStr = priceStr.replaceAll(",", "");
priceStr = priceStr.replaceAll("\\\\", "");
int price = Integer.parseInt(priceStr);
|
}
}
Web画面では商品の金額を「¥1,000」のように¥記号とカンマを付けて入力します。
システム側では、入力された金額文字列から、¥記号とカンマを取り除いて、金額として扱う必要があります。
ユーティリティクラスを使わずに素直に書くと、金額文字列の変換処理が
リスト1-@、
リスト2-@のように重複してしまいます。
システムが大きくなり金額を入力する画面が増えると、重複箇所はさらに増えていくことでしょう。
そこで「金額文字列」→「数値」の変換処理をユーティリティクラスに移動させます。
| priceStr.replaceAll("¥¥¥¥","")ってどういう意味? |
StringクラスのreplaceAll()メソッドでは、第1引数で指定した正規表現にマッチした文字列を、第2引数で指定した文字列に置換します。
「¥¥¥¥」はJavaの文字列としては「¥¥」という意味になりますね。
さらに正規表現では「¥」が特別な意味を持ちますので、「¥¥」が¥記号を意味します。
結果的にこのコードは「¥記号を空文字に変換しなさい」という意味になります。
|
◎第2段階:ユーティリティークラスの適用
次にユーティリティクラス使用後のサンプルです(図4〜5)。
文字列や数値を変換するユーティリティクラスとして、ConvertUtilクラス(
リスト3)を作成しています。
リスト3 ConvertUtil.java
public class ConvertUtil {
| @ |
private ConvertUtil() { }
|
| A |
public static int toPrice(String PriceStr) {
priceStr = priceStr.replaceAll(",", "");
priceStr = priceStr.replaceAll("\\\\\\", "");
int price = Integer.ParseInt(priceStr);
return price;
}
|
}
先ほどの重複コードは、
リスト3-AのtoPrice()メソッドとして1箇所にまとめています。
金額文字列を数値に変換する処理ですので、toPrice()メソッドの引数は金額文字列(String)で、戻り値は金額の数値(int)になります。
各サーブレットでは、
リスト4-@、
リスト5-@のようにConvertUtilのtoPrice()メソッドを呼び出すだけになりました。
リスト4 SetPriceServlet.java(ユーティリティークラス版)
public class SetPriceServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
String priceStr = req.getParameter("price");
int price = ConvertUtil.toPrice(priceStr); …@
}
}
リスト5 UpdatePriceServlet.java(ユーティリティークラス版)
public class UpdatePriceServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
String priceStr = req.getParameter("update_price");
int price = ConvertUtil.toPrice(priceStr); …@
}
}
このようにたった3行の処理ですがユーティリティクラスに移動することで、保守性・再利用性が高いコードになりました。
今後金額を入力する画面が増えても、変換処理はtoPrice()メソッドで処理できそうです。
ユーティリティクラスは基本的にすべてのメソッドがstaticになりますので、インスタンス化する必要がありません。
そこで
リスト3-@のようにコンストラクタをprivateにして、インスタンス化を禁止しています。
◎ユーティリティクラスの対象となるもの
共通処理ならばなんでもユーティリティクラスにすればよいわけではありません。
本来ならオブジェクトにしたほうがよいものをユーティリティクラスにすると、オブジェクト指向のメリットを生かせない、融通の利かないシステムになってしまいます。
ではどのような処理をユーティリティクラスにすればよいのでしょうか?
まず考えられるのは「状態を持たない処理」です。
具体例であげている変換処理は、「変換前のデータ」を渡すと「変換後のデータ」が手に入ればよく、状態を持つ必要はありませんでした。
このように、状態を保持し続ける必要がない処理はユーティリティクラス化に向いています。
また、単なる計算や関数などもユーティリティクラス向きです。
java.lang.Mathクラスはこの典型であるといえます。
◎業務固有のユーティリティクラス
ユーティリティクラスはすべてのアプリケーションで共通に使用できる汎用的なものと、特定の業務アプリケーション固有の共通処理を行うものが考えられます。
特定業務固有のユーティリティクラスは「アプリ名+Util(s)」という名前になることが多いようです。
例えば「DP」いうプロジェクトなら「DPUtils」といった具合です。
◎Jakarta Commonsはユーティリティクラスの宝庫
JakartaプロジェクトのCommonsサブプロジェクト(
http://jakarta.apache.org/commons/)はユーティリティクラスの宝庫です。
Javaの標準APIにはない便利なメソッドを持つユーティリティクラスが数多く用意されています。
品質も高いのでこれらをうまく使わない手はありません。
自分でユーティリティークラスを作る前に、「こんなユーティリティークラスないかな?」という気持ちで、まずはCommonsをチェックしてみるのもよいでしょう。