層間型変換オブジェクト その1
先日言及した、二つの型のオブジェクトを変換する、「DxOのようなもの」だが、アウトラインがほぼ固まった。
- インタフェース
二種の型を互いに変換するのが目的なので、以下のようなインタフェースを用意する。
public interface IDataExchangewhere S : class where D : class { D Convert(S source); void Convert(S source, D dest); S Convert(D dest); void Convert(D dest, S source); }
- クラス
このインタフェースの型パラメタをオープンにしたままの実装クラスを用意する。汎用的な型の変換は全てこのクラス内に実装することになる。
public class DataExchange<S, D> : IDataExchange<S, D> where S : class where D : class { 〜 これから実装 〜 }
Seasar2のDXOは、相互に変換する型を指定したインタフェースを用意して、それをAOPのインターセプタ経由で駆動する、という非常にスマートな方法であり、そのまま拝借したかったのだが、今回はAOPを前提にはしたくなかったので、抽象度を上げる代わりの手段として、ジェネリックを使った。
従って、使う際には
IDataExchange<IFoo, IHoo> dxo = new DataExchange<IFoo, IHoo>();
このように、変換元と変換先の型を型パラメタとしてクローズしたクラスを使用する。
このクラスは、通常は、二つの型の全く同じ名前のプロパティだけを変換対象にするのだが、イリガルなケースとして、名前が違うプロパティを変換する手段として、以下のようにアノテーションで変換対象のプロパティをマッピングすることができるようにする。
[PropertyMapping(source="変換元プロパティ", destiny="変換先プロパティ", converter="明示的に使うコンバータがあれば指定")] public class DataExchangeFooToHoo : DataExchange<IFoo, IHoo> {}
派生クラスは通常は必要ない、しかし一番おいしい使い方である、DIコンテナによる自動生成の恩恵を受けるためには、クラスをプロジェクト配下に配置する必要があるので、実際には派生クラスは必然となろう。
- 追記:
>実際には派生クラスは必然となろう。
正しく型名を設定ファイルに書ければ、単なるコンポーネントとして書けるか。
として、インジェクション可能とするならば、派生の必要すらないな。
- 追記2:
変換オブジェクトの具象クラスだが、変換後の型で新たなインタンスを生成するメソッドがあるので、
D Convert(S source); S Convert(D dest);
本当は、以下のように型パラメタに明示的な制約"new()"を付加したかったのだが、
public class DataExchange<S, D> : IDataExchange<S, D> where S : class, new() where D : class, new() { }
こうすると、メソッド中に new S()とか、new D()とか書けるようになる代わりに、型パラメタにインタフェースが使用できなくなってしまう。痛し痒し。