[技術講座]ラッピングに関する一考察
次に、ライブラリ Service をコンポジション(委譲)を使ってラップする場合(静的な構造はコンポジション、実際の処理は委譲で行う)を見ていきます。「クライアント」がオリジナルのパッケージのクラスを知らなくてすむように、引数 Param、戻り値Result、例外 AppException の各クラスもラップする必要があります。なお、下図では Service 以外のクラスもコンポジションを使ってラップしていますが、ライブラリ Service とそれ以外のクラスで同じラッピング方法を使う必要はありません。
ソースコードは以下のようになります。
package delegate;
import original.*;
public class MyService
{
private Service delegate;
protected MyService(Service delegate) {
this.delegate = delegate;
}
public static MyService getInstance() {
return new MyService(Service.getInstance());
}
// 独自の引数、戻り値、例外を使用。
public MyResult service(MyParam param) throws MyAppException {
try {
Result result = delegate.service(param.getParam());
return (result == null) ? null : new MyResult(result);
} catch (AppException e) {
throw new MyAppException(e);
}
}
// 独自の引数を使用。戻り値は一般的な型。
public Object service2(MyParam param) {
return delegate.service2(param.getParam());
}
// 独自の戻り値を使用。引数は一般的な型。
public MyResult service3(Object param, String name) {
Result result = delegate.service3(param, name);
return (result == null) ? null : new MyResult(result);
}
// 引数、戻り値は一般的な型。
public Object service4(Object param, String name) {
return delegate.service4(param, name);
}
}
では、ライブラリをコンポジション(委譲)でラップした場合の特徴を見ていきましょう。まずはメリットから。
次にデメリットです。
クライアントが生成する引数クラス (Param) は継承を使ったほうが簡単かもしれませんね。
続いて、フレームワークのインタフェース Processor をコンポジション(委譲)を使ってラップする場合を見ていきます。「実装クラス」がオリジナルのパッケージのクラスを知らなくてすむように、引数 Param、戻り値 Result、例外 AppException の各クラスもラップする必要があります。
ソースコードは以下のようになります。
package delegate;
import original.*;
public class MyProcessorAdapter extends AbstractProcessor
{
private MyProcessor adaptee;
protected MyProcessorAdapter(MyProcessor adaptee) {
this.adaptee = adaptee;
}
public static MyProcessorAdapter create(String name) {
MyProcessorFactory factory = MyProcessorFactory.getInstance();
MyProcessor processor = factory.getMyProcessor(name);
return new MyProcessorAdapter(processor);
}
// 独自の引数、戻り値、例外を使用。
public Result process(Param param) throws AppException {
try {
MyResult result = adaptee.process(new MyParam(param));
return (result == null) ? null : result.getResult();
} catch (MyAppException e) {
throw e.getAppException();
}
}
// 独自の引数を使用。戻り値は一般的な型。
public Object process2(Param param) {
return adaptee.process2(new MyParam(param));
}
// 独自の戻り値を使用。引数は一般的な型。
public Result process3(Object param) {
MyResult result = adaptee.process3(param);
return (result == null) ? null : result.getResult();
}
// 引数、戻り値は一般的な型。
public Object process4(Object param) {
return adaptee.process4(param);
}
}
package delegate;
public interface MyProcessor
{
// 独自の引数、戻り値、例外を使用。
public MyResult process(MyParam param) throws MyAppException;
// 独自の引数を使用。戻り値は一般的な型。
public Object process2(MyParam param);
// 独自の戻り値を使用。引数は一般的な型。
public MyResult process3(Object param);
// 引数、戻り値は一般的な型。
public Object process4(Object param);
}
package delegate;
public abstract class MyAbstractProcessor implements MyProcessor
{
protected MyAbstractProcessor() {}
// デフォルトの実装(あれば)。
public MyResult process(MyParam param) throws MyAppException {
// デフォルトの実装。
}
}
package delegate.impl;
import delegate.*;
public class MyProcessorImpl extends MyAbstractProcessor
{
public MyProcessorImpl() {}
// 独自の引数、戻り値、例外を使用。
public MyResult process(MyParam param) throws MyAppException {
// 実装略。
}
// 独自の引数を使用。戻り値は一般的な型。
public Object process2(MyParam param) {
// 実装略。
}
// 独自の戻り値を使用。引数は一般的な型。
public MyResult process3(Object param) {
// 実装略。
}
// 引数、戻り値は一般的な型。
public Object process4(Object param) {
// 実装略。
}
}
では、フレームワークをコンポジション(委譲)でラップした場合の特徴を見ていきましょう。
次にデメリットです。
© 2003 OGIS-RI Co., Ltd. |
|