何でもDIすればいいってモノじゃない

また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のようなデータの入れ物が依存コンポーネントなのか?と言われれば微妙なのだがなー。