以下のようなDTOクラスに出会いました。
class PersonDTO {
private String firstName;
private String middleName;
private String lastName;
private String dob;
// some 50 fields more
private List<PersonDTO> persons;
//getters and setter
}
質問1:非常に多くのプロパティを持つこのような巨大なDTOクラスを持つことは良い習慣ですか?
質問2:独自のタイプのArrayListを持つDTOクラスを作成することは良い習慣ですか?それは循環参照を引き起こしませんか?
更新:
私はヘルスケア分野の製品で働いています。ユースケースは、システム内のすべてのユーザーのレポートを生成することです。ユーザーのリストに加えて、レポートには要約情報も表示する必要があります。 UIはJSON応答を想定しているため、データをシリアル化する必要があります。
以下は私が取り掛かっていた実際のDTOです。
メソッドはDAOクラスで作成され、UserDTOオブジェクトを返します。返されるこのUserDTOオブジェクトは、すべてのユーザーのリストと、合計医師、合計看護師などのいくつかの要約情報で構成されています。
class UserDTO{
private String userID; //This is unique
private String firstName;
private String middleName;
private String lastName;
private String dob;
private String userType; // value can be DT(for doctor), ST(for nurse),
private int totalDoctorCount; //Holds the total number of UserDTOs with userType value set to DT in the ArrayList of users.
private int totalNurseCount; //Holds the total number of UserDTOs with userType value set to ST in the ArrayList of users.
// there are some 40 more properties.
private List<UserDTO> users;
//In this class there are also properties like below...
private String primaryAddrStreetName;
private String primaryAddrCity;
private String primaryAddrState;
private String primaryAddrCountry;
private String primaryAddrZipcode;
private String billingAddrStreetName;
private String billingAddrCity;
private String billingAddrState;
private String billingAddrCountry;
private String billingAddrZipcode;
private String shippingAddrStreetName;
private String shippingAddrCity;
private String shippingAddrState;
private String shippingAddrCountry;
private String shippingAddrZipcode;
}
質問3:このDTO設計はそのユースケースに適していますか?そうでない場合、あなたは何を提案しますか?
質問4:ユーザーが複数のアドレスを持っています。しかし、アドレスの詳細はそれ自体のクラス(UserAddressDTOのようなもの)にあるべきではなく、UserDTOにUserAddressDTOの配列/ ArrayListを追加する必要がありますか?
質問5:また、このような種類のDTOがJVMメモリにどのように影響するかについても洞察を与えてください。レポートは何千ものレコードをフェッチします。
DTOの目標 は、プロセスまたはレイヤー間でデータを転送して、メソッド呼び出しの数を減らすことです。
したがって、そのような構造のフィールドの膨大なリストがあることは衝撃的ではありません。別の方法としては、たとえば ResultSet
(または--a(non-jaaコンテキストでは dataset )など)のような、より複雑な構造でDTOを実装します。異なるエンティティを1つのオブジェクトにグループ化します。その場合、不便なのは、そのようなオブジェクトの構築とその要素へのアクセスに時間がかかることです。これは、フラットな(そして醜いが効率的な)構造の場合です。
参考までに、 ここ DTOのいくつかの利点と欠点を見つけることができます。
PersonDTO
にPersonDTO
のリストがあることは、DTOが再帰的な構造(ツリーなど)を持っていることを意味します。したがって、これは1つのPerson
のフラットデータだけでなく、実際には1つのPerson
と関連するPersons
のグループです。データ構造は再帰的です。
これは循環参照を意味するものではありませんが、あなたの言う通り、追加の注意が必要です。それはすべて元のデータ構造に依存します:
また、同じ人物がDTOの異なるリストに複数回出現する可能性があることにも注意してください(例:2つの部門で働いている非常勤の従業員)。残念ながら、データ転送中に、オブジェクトが逆シリアル化されてネット経由で転送され、その後再びシリアル化されると、オブジェクトのID(Javaに同じ参照がある))が失われる可能性があります。同じデータをレプリケートする2つの異なるオブジェクトが作成されます。したがって、受信側では、さらに注意する必要があります。そのようなオブジェクトのIDを検出するか、想定するかを決定する必要があります。すべての人は常に異なるでしょう。
結論:この構造は問題ないかもしれませんが、元のドメインオブジェクトに関するいくつかの情報を確認する必要があります。
代替:より安全なDTO構造は、各個人にIDを与え、DTOをIDを持つ個人のコレクションとして編成することです。次に、PersonDTO
のリストをIDのリストで置き換えることができます。これは、すべてのタイプの人物グラフで機能し、アイデンティティ検出を必要としないため、はるかに柔軟です。
このDTO構造の目的が無関係なユーザーのリストを提供することだけである場合は、次の構造にする必要があります。
class UserDTO { // Only properties of a single user
private String userID;
private String firstName;
...
private String userType;
};
class UserListDTO { // Only properties of the list as a whole
private int totalDoctorCount;
...
private List<UserDTO> users;
};
ご覧のとおり、適切なDTO設計は、多くの場合、DTOクラスの乗算に関連しています。 この記事はこちら で公開されている欠点で確認できるように、これはよく知られているトピックです。
一意のDTOを拡張して2つのレイヤーを1つのクラスに結合することは、 単一の責任の原則 に準拠しないショートカットです。また、あいまいです。最上位のユーザーをグループに追加する必要があるかどうか。それから彼は統計に数えられますか?リストのユーザーは常に空のリストを持つ必要がありますか?彼らは彼ら自身の統計を提供しませんか?リストの1人のユーザーが彼のリストに予期しないユーザーを連れてきた場合はどうなりますか?これらすべての質問と追加の処理、1つの追加クラスを回避するために...
この場合、それは悪い設計です。
データ転送オブジェクト(DTO)は、プロセス間でデータを運ぶオブジェクトです。その使用の動機は、プロセス間の通信が通常、リモートインターフェース(Webサービスなど)を使用して行われることです。この場合、各呼び出しは高価な操作です。各呼び出しのコストの大部分はクライアントとサーバー間の往復時間に関連しているため、呼び出しの数を減らす1つの方法は、転送されたであろうデータを集約するオブジェクト(DTO)を使用することですいくつかの呼び出しによって、しかしそれは1つの呼び出しによってのみ提供されます
上記の定義によれば、DTOがプロセス間の呼び出しの数を減らすように対処されている場合、DTOのサイズについては議論できますが、その妥当性については議論できません。一方、DTOがコミュニケーション戦略に応答しない場合、おそらく回答はです。設計を確認してください。 DTOを利用しようとする人は誰でも、そのような怪物からデータを探索、分析、抽出するのに苦労します。
2番目の質問に関しては、何らかの理由で同じDTOがリストのどこかで参照されているか、DTOの内部リストのいずれかによって参照されている場合、循環参照が発生する可能性があります。等々。
編集後の更新
@Christopheはすでに私たち全員が(多かれ少なかれ)考えていることを伝えています。 UserDTOがレポートに必要なすべてのデータを保持している場合、提供した責任が多すぎます。
次の2つのオプションを検討してください。
他のDTOと独自のローカル属性で構成されるWebリソースReportDTOのモデリング。
サーバーへの呼び出しを増やします。 DTOの目的はこれとは逆であることはわかっていますが、怒ってはいけません。呼び出しを50に減らして1にするのはすばらしいことですが、1つのDTOを実装するリスクがあるので(DTO 1つですべてを持ち込み、暗闇の中でそれらをバインドします)。呼び出し回数を50から5に減らしても、大幅な改善です。
質問1:非常に多くのプロパティを持つこのような巨大なDTOクラスを持つことは良い習慣ですか?
他の人が述べたように、消費者がそれを必要とする場合、DTOは確かにそれほど大きくなる可能性があります。 DTOは非常に狭い目的を果たしているため、DTOを使用しても実際に「実用性」の変化がないため、このような大きなDTOが良い方法であるかどうかについてコメントすることは困難です。
質問2:独自のタイプのArrayListを持つDTOクラスを作成することは良い習慣ですか?それは循環参照を引き起こしませんか?
Modellingに関して、Person
は他の複数のPersons
で構成されていません。 Person
は、複数のsiblings
typePerson
を持つことができます。 persons
と呼ばれるメンバーであるポイントは、オブジェクトとの関係に関する関連情報を伝えません。一方、siblings
という名前のメンバーが、これらのPerson
インスタンス間のおなじみの関係を記述していることは明らかです。
50個のプロパティは少し過剰に見えます。あなたはそれらを可能な限りトリムしてみて、保つべきです。必要なものだけを含めてください。私は10-15のプロパティであるものを見てきましたが。
ArrayList
に関しては、問題なく作成できます。循環参照は、何らかの理由で、作業を行っている実際のアイテムを参照またはそのような変なものとして保存した場合にのみ発生します。