简体   繁体   中英

Good design pattern to collect data in a complex object

Let's say, I have a complex object hierarchy, something like:

public class ClassRoom
{
    private ClassRoomInfo classRoomInfo;
    private List<Student> students;
    ...
}

I get this object as an input from client, but the object might not be filled completely. For example, client can provide very minimal details like studentId(s) or minor information in the classRoomInfo object. I want to write a DataCollector class for this object, which would collect all the missing data upfront.

What would be the best design pattern in this case?

I think you need to Lazy Load data when needed.The getters for the members of ClassRoom class should suffice for this purpose.

A separate DataCollector class is not recommended as it will force you to expose setters in ClassRoom for all members.

This link (and Book) should tell you more about Lazy load .

Here's one approach. The idea is to treat the ClassRoom as an instance of the composite pattern , where every object in the hierarchy knows how to "fill itself in" using a DataCollector . If an object is already filled in, it responds to being asked to fill itself in by returning itself; if it is not filled in, it gets the necessary information from the DataCollector and builds a new instance of its own class which is filled in (the objects themselves are therefore immutable - you might prefer to have the objects mutate themselves, but i usually lean towards using immutable objects to represent data).

I've defined an interface Fillable for objects which know how to fill themselves in. However, this isn't really necessary, as the fillIn method is never invoked polymorphically. It's just documentation, really.

I've assumed that the filling-in process is pretty simple. Obviously, it could be more complex, both in detecting what is filled in (eg an empty list of students might indicate that the list has not been populated), and in how it goes about filling it in. If you applied this design to your real problem, you will find that the DataCollector becomes incredibly complicated. You will need to factor it differently; you might want to move more of the collection logic into the domain classes, or to split it into per-class DAOs ( ClassRoomInfoDataCollector etc).

public interface Fillable<T> {
    public T fillIn(DataCollector collector);
}

public class ClassRoom implements Fillable<ClassRoom> {
    private final ClassRoomInfo classRoomInfo;
    private final List<Student> students;

    private ClassRoom(ClassRoomInfo classRoomInfo, List<Student> students) {
        this.classRoomInfo = classRoomInfo;
        this.students = students;
    }

    @Override
    public ClassRoom fillIn(DataCollector collector) {
        ClassRoomInfo filledInClassRoomInfo = classRoomInfo.fillIn(collector);
        List<Student> filledInStudents = new ArrayList<Student>();
        for (Student student : students) {
            filledInStudents.add(student.fillIn(collector));
        }
        if (filledInClassRoomInfo == classRoomInfo && filledInStudents.equals(students)) return this;
        return new ClassRoom(filledInClassRoomInfo, filledInStudents);
    }
}

public class ClassRoomInfo implements Fillable<ClassRoomInfo> {
    final String roomNumber;
    final Integer capacity;

    private ClassRoomInfo(String roomNumber, int capacity) {
        this.roomNumber = roomNumber;
        this.capacity = capacity;
    }

    @Override
    public ClassRoomInfo fillIn(DataCollector collector) {
        if (capacity != null) return this;
        return new ClassRoomInfo(roomNumber, collector.getClassRoomCapacity(roomNumber));
    }
}

public class Student implements Fillable<Student> {
    final int id;
    final String name;

    private Student(int id, String name) {
        this.id = id;
        this.name = name;
    }

    @Override
    public Student fillIn(DataCollector collector) {
        if (name != null) return this;
        return new Student(id, collector.getNameOfStudent(id));
    }
}

public class DataCollector {
    public String getNameOfStudent(int id) {
        ...
    }

    public int getClassRoomCapacity(String roomNumber) {
        ...
    }
}

Try "Facade Design Pattern". Hope it can help you. you can find a good description on Facade in http://javapapers.com/design-patterns/facade-design-pattern/

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM