[技術講座]ラッピングに関する一考察
ラッピングには継承を用いる方法と、コンポジション(委譲)を用いる方法がありますが、まずはライブラリ Service を継承を使ってラップする場合を見ていきたいと思います。「クライアント」がオリジナルのパッケージのクラスを知らなくてすむように、引数 Param、戻り値 Result、例外 AppException の各クラスもラップする必要があります。なお、下図では Service 以外のクラスも継承を使ってラップしていますが、ライブラリ Service とそれ以外のクラスで同じラッピング方法を使う必要はありません(以後の例でも同様)。
ソースコードは以下のようになります。
package extend;
import original.*;
public class MyService extends Service
{
protected MyService() {}
public static MyService getService() {
return new MyService();
}
// 独自の引数、戻り値、例外を使用。
public MyResult service(MyParam param) throws MyAppException {
try {
Result result = super.service(param);
return toMyResult(result);
} catch (MyAppException e) {
throw e;
} catch (AppException e) {
throw new SystemException(e);
}
}
// 独自の引数を使用。戻り値は一般的な型。
public Object service2(MyParam param) {
return super.service2(param);
}
// 独自の戻り値を使用。引数は一般的な型。
public MyResult service3x(Object param, String name) {
Result result = super.service3(param, name);
return toMyResult(result);
}
protected MyResult toMyResult(Result result) {
if (result == null || result instanceof MyResult) {
return (MyResult)result;
} else {
return new MyResult(result);
}
}
}
では、ライブラリを継承でラップした場合の特徴を見ていきましょう。まずはメリットから。
次にデメリットです。
なお、MyService の service()、service2() は引数の指定が異なるため、「オーバライド」ではなく「オーバロード」になります。
続いて、フレームワークのインタフェース Processor を継承を使ってラップする場合を見ていきます。「実装クラス」がオリジナルのパッケージのクラスを知らなくてすむように、引数 Param、戻り値 Result、例外 AppException の各クラスもラップする必要があります。
ソースコードは以下のようになります。
package extend;
import original.Processor;
public interface MyProcessor extends Processor
{
// 独自の引数、戻り値、例外を使用。
public MyResult process(MyParam param) throws MyAppException;
// 独自の引数を使用。戻り値は一般的な型。
public Object process2(MyParam param);
// 独自の戻り値を使用。引数は一般的な型。
public MyResult process3x(Object param);
}
package extend;
import original.*;
public abstract class MyAbstractProcessor extends AbstractProcessor
implements MyProcessor
{
protected MyAbstractProcessor() {}
// 独自の引数、戻り値、例外を使用。
public final Result process(Param param) throws AppException {
return process((MyParam)param);
}
// デフォルトの実装(あれば)。
public MyResult process(MyParam param) throws MyAppException {
// デフォルトの実装。
}
// 独自の引数を使用。戻り値は一般的な型。
public final Object process2(Param param) {
return process2((MyParam)param);
}
// 独自の戻り値を使用。引数は一般的な型。
public final Result process3(Object param) {
return process3x(param);
}
}
package extend.impl;
import extend.*;
public class MyProcessorImpl extends MyAbstractProcessor
{
public MyProcessorImpl() {}
// 独自の引数、戻り値、例外を使用。
public MyResult process(MyParam param) throws MyAppException {
// 実装略。
}
// 独自の引数を使用。戻り値は一般的な型。
public Object process2(MyParam param) {
// 実装略。
}
// 独自の戻り値を使用。引数は一般的な型。
public MyResult process3x(Object param) {
// 実装略。
}
// 引数、戻り値は一般的な型。
public Object process4(Object param) {
// 実装略。
}
}
では、フレームワークを継承でラップした場合の特徴を見ていきましょう。まずはメリットから。
次にデメリットです。
© 2003 OGIS-RI Co., Ltd. |
|