これはC#ですが、アプリケーションのさまざまなレイヤーに異なるオブジェクトのセットが存在するすべてのOO言語に適用できます。
チーム内で、あるレイヤーのオブジェクトを別のレイヤーのオブジェクトにマッピングするときにコードを配置する場所についての規則を考え出そうとしています。例えば;データ転送オブジェクトをWCFサービスなどで使用されるサービスオブジェクトにマッピングする。私は次の方法が使用されているのを見てきましたが、コードを新しく見た人がどちらを好むか、およびこれらのどれが自然なC#イディオムに最も適しているかを判断しようとしています。
これらの例では、AutoMapperを使用してオブジェクトFooをオブジェクトBarにマッピングしますが、これは任意のマッピングフレームワークに適用されるか、オブジェクト初期化子を使用してFooから手動でBarを作成するだけです。これらのオブジェクトは両方とも同じデータを表しますが、それらは異なるアセンブリにあります。
1)Barのコンストラクタ
public class Bar
{
public Bar(Foo foo)
{
Mapper.Map(foo, this);
}
}
2)Barのファクトリーメソッド
public class Bar
{
public static Bar CreateFrom(Foo foo)
{
return Mapper.Map<Bar>(foo);
}
}
3)Fooを返すメソッドBar
public class Foo
{
public Bar GetBar()
{
return Mapper.Map<Bar>(this);
}
}
4)Fooの明示的な型変換演算子Barにキャストできます
public class Foo
{
public static explicit operator Bar(Foo foo)
{
return Mapper.Map<Bar>(foo);
}
}
5)ファクトリーパターンのいくつかの実装
public class BarFactory : IFactory<Bar>
{
public Bar Create(Foo foo)
{
return Mapper.Map<Bar>(foo);
}
}
6)必要なピースコードで実行するだけ
public void SomeMethod()
{
Foo foo = fooRepository.GetFoo();
//I need it to be bar now...
Bar bar = Mapper.Map<Bar>(foo);
}
免責事項:この例ではAutoMapperを使用していなかったと思います。ここで「AutoMapper」が「IMappingProvider」または「IObjectConverter」または何かを読んでいると想像してみてください
すべての場合において、私は#5を好みます。 1-2-3-4のサンプルコードでは、ドメインオブジェクトは彼に関連するDTOオブジェクトを知っている必要はないと思います。
あなたが持っているとしましょう
public class User
{
... some properties ...
}
public class UserThumbnail
{
... some properties ...
}
ドメインモデルオブジェクト(User)は、DTOオブジェクト(UserThumbnail)がユーザーのプロパティを使用していることを認識する必要はありません。これらのクラス間の結合を追加し、DTOオブジェクト(UserThumbnail)を作成する責任がドメインオブジェクト(ユーザー)。
したがって、ビジネスのユースケースで、ユーザーがUserThumbnailを作成できるように指定されていない場合は、作成しないでください。そのため、この場合はFactoryなどのクラスを使用する必要があります。DTOオブジェクトはドメインオブジェクトを汚染しないためです。その場合にファクトリが使用されると、ドメインオブジェクトはDTOオブジェクトが存在することさえ知りません。
#6の場合、AutoMapperを別のマッピングツールに変更する場合は、コードを検索する必要があります。ファクトリを使用した場合、すべてのAutoMapper呼び出しが存在することがわかります。
Mapper.Mapは2つのことを実行しています。インスタンスを作成し、プロパティをマップします。ファクトリパターンは、オブジェクト作成コードを分離する場合のソリューションです。
通常、私は#3を好みます。GetBarは、どこから来て何を取得しているのかが非常に明白ですが、この場合、クラスのマッピングで変換演算子をよく使用します。
しかし、マッピングはキャストしているようには見えません。オブジェクト(GetBarを使用する場所)に含まれていない新しいものを実際に作成し、それを変換するだけです(演算子バー)。
したがって、この場合はCreateFromを使用します。新しいマップオブジェクトを作成して、Fooに関連する他の何かを表していることは明らかです。
マッパークラスを作成し、データをあるタイプから別のタイプに手動でマップします。ええ、それは定型文ですが、完全に制御されており、それは単純であり、それは仕事をします。モデルオブジェクトにはWCFの戻り値の型に関する知識がないため、モデルアセンブリには含めないでください。
public class FooMapper
{
public Foo Map(Bar bar)
{
return new Foo
{
ValueOne = bar.ValueOne,
ValueTwo = bar.ValueTwo,
....
};
}
}