web-dev-qa-db-ja.com

コレクションをJavaでラップすることは良い習慣ですか?

私はこのようなスニペットに出くわし、それがエンジニアリングを超えていることに気付きました。それは良い習慣ですか?

public class SchoolList extends ArrayList<School> {
}

public class School extends ArrayList<StudentList> {
}

public class StudentList extends ArrayList<Student> {
}

public class Student {
}

実際、私は簡単なオプションを好みます:

List<List<List<Student>>> schoolList;
2
Truong Ha

通常は、既存のJDK APIを使用します。どうして?あなたのコードについていくつかのフィードバックをさせてください:

public class School extends ArrayList<StudentList> {
}

または、言い換えると、学校はIS-Aの学生のリスト(グループ/クラスのリスト?)のリストです。本当に学校とは何ですか?

学校は制度です。内部(HAS-A)、部屋、教授などに学生を見つけることができます。上記のコードを再設計したい場合は、次のように記述します。

public class School {
  private List<Student> students; // or list of list? your choice
}

また、パブリックメソッドを見ると、addAllメソッドがあり、Collection<StudentList>を引数として。 School IS -StudentList(あなたの実装)のリストなので、これを行うことができます:

School s1 = ...;
School s2 = ...;
s1.addAll(s2);

上記のコードは論理的ですか?私は単にSchoolが別のSchoolを追加することを想像することはできません:) addStudentsメソッドは次のように適切です:

s1.addStudents(s2.getStudents());

この行はそれ自体を物語っています:生徒は来年別の学校に行きます:)


現在のクラス/インターフェースを拡張/実装する必要があるのは、自分に合わないと思われる場合のみです。循環リストを想像してください。イテレータはリストの最後の最初の要素に移動する必要があります。 JDK APIの循環リストの実装がわからないので、自分で実装することを余儀なくされ、他に選択肢はありません。あなたがやる。

11
Silviu Burcea

List<List<Map<String, Integer>>>などをドメイン固有のクラスに置き換えることをお勧めします。しかし、ほとんどすべての場合において、最善の方法は継承ではなく集計による方法です。これにはいくつかの利点があります:

  • スーパークラスとの密結合はありません
  • 公開するメソッドを選択できます。 ArrayListのすべてのメソッドを使用したくない場合があります
  • カプセル化に違反しない

一般に、コレクションを拡張することは悪いことです。 List機能が必要な場合でも、機能の集約を使用してListインターフェースを実装する必要があります。この詳細については、Effective Java第2版、Joshua Bloch著)の項目16を参照してください。

あなたの場合、代わりに

public class School extends ArrayList<Student> {
}

使用する

public class School {
    private List<Student> list;
}

または、List機能が必要な場合:

public class SchoolList implements List<Student> {
     private List<Student> list;
}
10
m3th0dman

一般的なコンテナを拡張しても、通常、ビジネスオブジェクトを表すものには意味がありません。

public class School extends ArrayList<Student>

この例では、学校はArrayList of Studentsです。しかし、それは学校がすべてであることですか?ビジネス要件により、学校がArrayList<Teacher>ArrayList<ClassRoom>などのより多くの属性を取得することを意味する場合、どうしますか?

クラスがデータを表すコンテナを拡張しないほうがはるかに優れています。その代わりに、クラスはデータを含むコンテナを含むコンポジットである必要があります。

public class School {
     private List students;
     private List teachers;
     private List rooms;

     // methods for accessing and manipulating the above lists
}

しかし、コレクションを拡張することが理にかなっているケースはありますか?はい、より高度なコンテナが必要な場合は、そうします。以前に、どの変更が加えられたかがわかるマップが必要でした。そこで、私はpublic class LoggingHashMap extends HashMap<Object, Object>を作成しました。これは、すべてのミューテーターメソッドをオーバーライドして、プライベートジャーナルにもエントリを作成し、そのジャーナルを取得およびリセットするメソッドも備えていました。

4
Philipp

mightは良い習慣ですが、ジェネリック型ではできなかった新しい型でできることがある場合に限ります。

コンテキストがないと、ジェネリック型が発明される前に必要だった構造を誰かが学習し、不要になったものを使い続けるように見えます。結局のところ、ジェネリックが最初に発明されたのは、タイプセーフなコレクションアクセスがほとんどの場合です。

ただし、extendsが実際に意味をなす場合や、特殊な用途がある場合もあります。たとえば、総称型の変数を可変引数パラメーターを必要とするメソッドに渡すのは難しい場合があります。その問題が実際に発生する場合、ドメインモデル内の一般的なオブジェクトに自己定義型を使用すると、追加の定義よりも読みやすくなるという利点があります。

0
Kilian Foth

場合によります。ドメインの動作をそのようなコレクションに起因させる場合、または通常のListのすべてのメソッドがそのコレクションで意味をなさない場合は、それを良い方法と見なします。
どちらかといえば、それは意図を非常によく伝えますが、それがそれを行う唯一の理由である場合は、過度に設計されていると主張することができます。

0
Stefan Billiet