Javaの「BeanUtils.copyProperties」は非常に便利な機能ですが
適切な利用が本当にされているでしょうか?
今回はEntityとDTOおよびFormのパターンについて語りたいと思います。
※DTOだけデザインパターンなのにこの名称で語られるのに違和感がありますが・・・
EntityとDTOおよびFormはBeanとして実装されるケースを見ます。
しかし、その理由が「BeanUtils.copyProperties」を利用したいだけになっていませんか?
例えばこんな実装
class EntityBean
{
private int value;
public int getValue(){ return value; }
public void setValue(int value){ this.value = value; }
}
class DtoBean
{
private int value;
public int getValue(){ return value; }
public void setValue(int value){ this.value = value; }
}
class FormBean
{
private int value;
public int getValue(){ return value; }
public void setValue(int value){ this.value = value; }
}
Entity、DTO、Formに同じフィールドを持てば「BeanUtils.copyProperties」を
利用することは確かにでき、便利だと思います。
しかし、よく考えてみてください。
Entity、DTO、Formは異なる実装を吸収するためにあるものではないでしょうか?
「BeanUtils.copyProperties」を利用したいがゆえに同じフィールドを持つことを
強制していないでしょうか? そうなってしまうと変化に強くもなく、データベースの仕様を
Entity、DTO、Formの3箇所を人力で運用し続けなければならなくなります。
#DBの仕様変更でフィールド名を変更した場合
Entityのvalue -> hogeに変更しました。
class EntityBean
{
private int hoge;
public int getHoge(){ return hoge; }
public void setHoge(int hoge){ this.hoge = hoge; }
}
class DtoBean
{
private int value;
public int getValue(){ return value; }
public void setValue(int value){ this.value = value; }
}
class FormBean
{
private int value;
public int getValue(){ return value; }
public void setValue(int value){ this.value = value; }
}
これって「BeanUtils.copyProperties」を使う限りビルドは通るし
それなりに動いてしまう可能性があります。DTOとForm作っている人に(もしかしたら自分)に
伝達して、修正してもらわなければなりません。動作を維持するならテストコードも必要になります。
ここまでのコストを支払う価値ってどこにありますか?
実装ミスってませんか?
Entity、DTO、Formの3箇所に同じフィールドを持つことを強制する(できる)のであれば
実装としては継承が適切です。
class EntityBean
{
private int hoge;
public int getHoge(){ return hoge; }
public void setHoge(int hoge){ this.hoge = hoge; }
}
class DtoBean extends EntityBean
{
}
class FormBean extends DtoBean
{
}
同じフィールドを持つことを担保できますし、変更があればコードレベルで
変わってくるので、ビルド時のエラーに気づけます。
継承しているので、プロパティを削りたければアップキャストすれば実現可能になり
「BeanUtils.copyProperties」を使う必要は全くありません。
でもEntity、DTO、Formの3箇所に同じフィールドを持つというのは、そもそもの目的を潰していませんか?
本当にやりたいことはこういうことのはず。
class EntityBean
{
// Entityでは日付を日付型で持つ
private DateTime date;
public DateTime getDate(){ return date; }
public void setDate(DateTime date){ this.date = date; }
}
class DtoBean
{
// DTOでは日付を文字列型で持つ
private String date;
public String getDate(){ return date; }
public void String(String date){ this.date = date; }
}
class FormBean
{
// Formでは日付を年月日別で持つ
private String year;
private String month;
private String day;
}
「Entity、DTO、Form」がそれぞれ異なる実装を持つ可能性があるからこそ
分けるのであって、同じ実装が前提になっていたら何の意味もないのです。
「BeanUtils.copyProperties」を使う限り、抽象度は低く何の備えにもなっていません。
そもそもですが、「Entity」と「Form」の変換は誰が担うべきなんでしょうか?
「BeanUtils.copyProperties」を使うことで責任をうやむやにしていませんか?
ちなみに上記の例に限って場合「DTO」って誰得の何の役割を持つのでしょうか?
私は不要だと考えます。DTOは複数のEntityと絡みがない限りは必要ないのです。
本当は「Entity」と「Form」の変換の役割に担う別の人が必要になります。
class EntityBean
{
// Entityでは日付を日付型で持つ
private DateTime date;
public DateTime getDate(){ return date; }
public void setDate(DateTime date){ this.date = date; }
}
class Converter
{
// 相互に変換するメソッドを用意してこいつが責任を負う
public static EntityBean convert(FormBean form) ... 実装は割愛
public static FormBean convert(EntityBean form) ... 実装は割愛
}
class FormBean
{
// Formでは日付を年月日別で持つ
private String year;
private String month;
private String day;
}
「Converter」の中で、「BeanUtils.copyProperties」を使うのであれば
抽象度は保たれていると思います。
Entity、DTO、Formの3箇所に同じフィールドを手動で作成してかつ
それを設計上の前提条件として「BeanUtils.copyProperties」を利用している
ケースはみなさんの周りにはないでしょうか?
本当にそれって正しいのか? 私は疑問に思います。
ご意見ご感想お待ちしております。