またSAStrutsネタ。
S2Container先生が何でもDIしてくれるからって、SessionスコープやApplicationスコープのインスタンスをむやみやたらにロジックやらサービスにインジェクトすると後悔するかも、という話。
SAStrutsでセッションにデータを格納したい時は、SessionスコープのDTOを作り、そこにデータを保存する。
@Component(instance=InstanceType.SESSION) public class SessionDto implements Serializable { public String value; }
DTOはアクションにDIして使う。
public class MyAction { @Binding protected SessionDto sessionDto; // SeasarがDIする public String index() { String val = this.sessionDto.value; } }
ところで、SMART Deployではserviceクラスのライフサイクルはデフォルトでprototypeスコープとなるので、sessionスコープのインスタンスをインジェクト可能。
public class HogeServiceImpl implments HogeService { public SessionDto sessionDto; public void exec() { this.sessionDto.value = "ほげ"; } }
しかし、サービスの中でDTOの状態を変更するのはおすすめしない。状態変更の処理がサービスに隠蔽され、アクションのコードから追えなくなってしまう。
public class MyAction { @Binding protected SessionDto sessionDto; @Binding protected HogeService hogeService; public String view() { this.sessionDto.value = "ふが"; this.hogeService.exec(); // アクションからはsessionDtoが操作されてるように見えない System.out.println(this.sessionDto.value); // "ほげ"と表示 } }
アクションのコードを見ただけでは、SessionDtoのvalueがほげに変わるのが分からない。何処で値が変化するか分からないのはグローバル変数の問題点と同じだ。
ライフサイクルが長く、状態変更可能なインスタンスをむやみやたら色んなコンポーネントにインジェクトすると酷い目に遭うかもしれない。状態を変更可能なインスタンスをサービスで処理させたい場合、状態変更があるならば引数で渡す、参照のみならばSeasarにインジェクトさせても良い、などとポリシーを決めた方がいいかもね。
ただ、そもそもDIパターンというのは依存性の注入。DTOのようなデータの入れ物が依存コンポーネントなのか?と言われれば微妙なのだがなー。