LoginSignup
8
5

More than 5 years have passed since last update.

BeanUtils.copyPropertiesを利用することは思考停止していないか?

Last updated at Posted at 2019-01-27

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」を利用している
ケースはみなさんの周りにはないでしょうか?

本当にそれって正しいのか? 私は疑問に思います。
ご意見ご感想お待ちしております。

8
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
5