簡単な入れ子構造を扱う #8

何ということはない。大枠はバインディングツールが自動生成していたものを手書きしたようなものだ。

school/School.java
package school;

import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;

public class School implements Iterable<Grade> {
    private LinkedHashMap<String, Grade> grades = new LinkedHashMap<String, Grade>();

    public Grade add(String name) {
        Grade g = grades.get(name);
        if (g == null) grades.put(name, g = new Grade(name));
        return g;
    }

    public Iterator<Grade> iterator() {
        return Collections.unmodifiableCollection(grades.values()).iterator();
    }
}
school/Grade.java
package school;

import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;

public class Grade implements Iterable<Group> {
    private String name;
    private LinkedHashMap<String, Group> groups = new LinkedHashMap<String, Group>();

    Grade(String name) {
        if (name == null) throw new IllegalArgumentException("null name");
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public Group add(String name) {
        Group g = groups.get(name);
        if (g == null) groups.put(name, g = new Group(name));
        return g;
    }

    public Iterator<Group> iterator() {
        return Collections.unmodifiableCollection(groups.values()).iterator();
    }
}
school/Group.java
package school;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;

public class Group implements Iterable<Student> {
    private String name;
    private ArrayList<Student> students = new ArrayList<Student>();

    Group(String name) {
        if (name == null) throw new IllegalArgumentException("null name");
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public Student add(String name, int height) {
        Student s = new Student(name, height);
        students.add(s);
        return s;
    }

    public Group add_(String name, int height) {
        add(name, height);
        return this;
    }

    public Iterator<Student> iterator() {
        return Collections.unmodifiableList(students).iterator();
    }
}
school/Student.java
package school;

public class Student {
    private String name;
    private int height;

    Student(String name, int height) {
        if (name == null) throw new IllegalArgumentException("null name");
        if (height <= 0) throw new IllegalArgumentException("height(" + height + ") <= 0");
        this.name = name;
        this.height = height;
    }

    public String getName() {
        return name;
    }

    public int getHeight() {
        return height;
    }
}
SchoolUser4.java
import school.*;

public class SchoolUser4 {
    public static void main(String[] args) {
        for (Grade ge : createSchool()) {
            System.out.println("[" + ge.getName() + "]");
            for (Group gp : ge) {
                System.out.println("  [" + gp.getName() + "]");
                for (Student s : gp) {
                    System.out.println("    [" + s.getName() + ", " + s.getHeight() + "cm]");
                }
            }
        }
    }

    private static School createSchool() {
        School s = new School();
        Grade g = s.add("ねんちょうさん");
        g.add("さくら").add_("Arlene", 154).add_("Bret", 170).add_("Cindy", 157);
        g.add("もも").add_("Don", 161).add_("Emily", 153);
        g = s.add("ねんしょうさん");
        g.add("ひまわり").add_("Franklin", 167);
        g.add("ちゅうりっぷ").add_("Gert", 164).add_("Harvey", 169).add_("Irene", 158);
        g.add("あさがお").add_("Jose", 154).add_("Katia", 159);
        g = s.add("おませさん");
        g.add("すみれ").add_("Lee", 171);
        g.add("たんぽぽ").add_("Maria", 168);
        return s;
    }
}

実用にするには用意した機能が足りないが、これまでのSchoolUserなんちゃらの結果と同じ程度にはなる。
少し違うのはSchoolが集約するGradeやGradeが集約するGroupには、
集約するインスタンス単位で集約されるものに同じ名前のものがないという制約が課せられている。
XMLであれば、

<school>
  <grade name="ねんちょうさん">
    ...
  </grade>
  <grade name="ねんしょうさん">
    ...
  </grade>
  <grade name="ねんちょうさん">
    ...
  </grade>
</school>

の最初のGradeと3番目のGradeは木構造における別のノードだが、
このコードでは3番目のGradeが持つ要素は最初のGradeに集約される。
あとは、それぞれが何を集約するためのものであるか決まっているので、
それぞれがIterableを実装することで使用側の記述がちょっと楽になっている。