スレッドローカルは便利なクラスです。
いつでもどこでもスレッドセーフなグローバル変数を定義することができます。
ごく稀にですが、スレッドローカルを使わないと解決できない問題もあるでしょう。
しかし、思うのです。スレッドローカルを使うのは基本的に「負け」なのだと。
例えば
例えば、こんなコードがあったとします。
public class FooAction implements WebAction { // 入力フォームのバリデーションを行う。 @Override public void validate(Form form) throws ValidationException { if (form.get("name") == null) { throw new ValidationException("name is null"); } if (form.getInt("age") < 0) { throw new ValidationException("invalid age"); } } public void processRequest(Request request) { ... } }
さて、この仕様だと、ひとつでもエラーが発生した時点で、エラーとなるので、
残りのバリデーションは実行されません。ではどうすればよいでしょうか?
改善すると
手戻りは嫌なので、validateメソッドのシグネチャ等は変えたくない。
ではどうするか。
例えばこんなやり方があります。
public class ValidationErrorMsg { private static final ThreadLocal<List<String>> msgs = new ThreadLocal<List<String>>() { protected List<String> initialVaule() { ... }; }; public static void addValidationError(String msg) { msgs.get().add(msg); } public static List<String> getValidationErrors() { return msgs.get(); } } public class FooAction implements WebAction { // スレッドローカルで、エラーメッセージを保持する。 @Override public void validate(Form form) { if (form.get("name") == null) { ValidationErrorMsg.add("name is null\n"); } if (form.getInt("age") < 0) { ValidationErrorMsg.add("invalid age\n"); } } public void processRequest(Request request) { ... } }
で、フレームワーク側でValidationErrorMsg.get()の結果を処理する、というわけです。
個人的正解
しかしながら僕は、次のようにするのが、設計上の正解なのではないかと思います。
(これは実際に、ほとんどのバリデーションフレームワークが採用している方法でもあります)
public class ValidationContext { private final List<String> msgs = new ArrayList<List<String>>(); public void addValidationError(String msg) { msgs.add(msg); } public List<String> getValidationErrors() { return msgs; } } public class FooAction implements WebAction { // ValidationContext引数を追加 @Override public void validate(ValidationContext context, Form form) { if (form.get("name") == null) { context.addValidationError("name is null\n"); } if (form.getInt("age") < 0) { context.addValidationError("invalid age\n"); } } public void processRequest(Request request) { ... } }
引数にValidationContextクラスを追加しています。
このようにしておくことで、また何か機能追加があった時でも柔軟に対応できます。
何らかのフレームワークを構成するとき、最初の設計では不十分であり、
後から必要に応じて機能追加していくことが多いと思います。
そのとき、如何にスマートに対応していくか、という観点からすると、
将来の拡張を見越して、Context変数を設計の最初から用意しておくべきだと僕は思います。
その意味において、安直にスレッドローカルへ逃げてしまうのは、設計上の「負け」だと思うのです。
ちなみに今、r42fwでスレッドローカルを使っている箇所が2つだけあります。
だから僕も偉そうなこと言えないんですけどね…(-_-)