キモいVisitorパターン

Jiemamyのコーディングをゴリゴリやっている。

Jiemamyは多くの複雑なモデルクラスを持っているのだが、その各モデルに便利メソッドを色々追加していると、カオスになってくる。そこで、Visitorパターンを使って便利メソッドを全てクラス化してみている。

/**
 * オブジェクトに対して実行する処理のインターフェイス。
 * @author daisuke
 * @param <R> 結果の型
 * @param <T> 処理対象のオブジェクトの型
 */
public interface Processor<T, R> {
  
  /**
   * オブジェクトに対する処理を実行する。
   * @param target 対象オブジェクト
   * @return 結果
   */
  R process(T target);
  
}

public class Model {
  private String str1;
  private String str2;
  
  // accessor省略
  
  /**
   * {@link Processor}による処理を実行する。
   * @param <R> 処理結果の型
   * @param <T> 処理対象モデルの型
   * @param processor 実行するプロセッサ
   * @return プロセス結果
   */
  @SuppressWarnings("unchecked")
  public <R, T extends Model>R process(Processor<T, R> processor) {
    return processor.process((T) this);
  }
}

このModelに以下のようなメソッドを追加していくと、キリがなく、見通しが悪くなってくる。

public String concat() {
  return str1 + str2;
}

そこで上記のようなprocessメソッドを用意しつつ、concatメソッドの代りに以下のようなProcessorを書く。

public class ConcatProcessor implements <Model, String> {
  public String process(Model model) {
    return model.getStr1() + model.getStr2();
  }
}

そすると、以下のように使える。

Model model = new Model();
model.setStr1("Miyamoto");
model.setStr2("Daisuke");
String result = model.process(new ConcatProcessor()); // "MiyamotoDaisuke"

こうする事により、モデルクラスはaccessorとprocessメソッドのみで作る事ができ、整理される。まぁこの時点で、Visitorに慣れていなければそれなりにキモいかもしれない。
しかし途中で問題にぶち当たった。チェック例外を投げたいメソッド(Processor)と、そうでないメソッド(Processor)があるのだ。

そこでこんなのを試してみた。

/**
 * (略)
 * @param <E> 処理が投げる可能性のある例外の型
 */
public interface Processor<T, R, E extends Exception> {
  
  R process(T target) throws E;
  
}

public class Model {
  private String str1;
  private String str2;
  
  // accessor省略
  
  @SuppressWarnings("unchecked")
  public <R, T extends Model, E extends Exception>R process(Processor<T, R, E> processor) thrwos E {
    return processor.process((T) this);
  }
}

で、例外を投げないProcessorと投げるProcessorは以下のように…。

/** 投げない */
public class ConcatProcessor implements <Model, String, RuntimeException> {
  public String process(Model model) {
    return model.getStr1() + model.getStr2();
  }
}

/** 投げる */
public class HogeConcatProcessor implements <Model, String, HogeException> {
  public String process(Model model) throws HogeException {
    if(model.getStr1() == null || model.getStr2() == null) {
      throw new HogeException();
    }
    return "Hoge"+ model.getStr1() + model.getStr2();
  }
}

クライアント側。

Model model = new Model();
model.setStr1("Miyamoto");
model.setStr2("Daisuke");
String result = model.process(new ConcatProcessor());
try {
  String resultHoge = model.process(new HogeConcatProcessor());
} catch (HogeException e) {
  // ...
}

どーーーだ!! ...一般的じゃないから可読性は悪いよなぁ><;