lundi 2 mars 2015

OO design - injecting factories deep into hierarchical class structure

I have a hierarchical class structure. Let's say I am modelling students in a city:



class District {
private Map<String, School> schools;

public School getSchool(String name) {
School school = schools.get(name);
if (school == null) {
school = new School();
schools.put(name, school);
return school;
}
}


and



class School {
private Map<String, Classroom> classrooms;

public Classroom getClassroom(String name) {
// similar lazy init code
}
}


and



class Classroom {
private Map<String, Pupil> pupils;

public Pupil getPupil(String name) {
// similar lazy init code
}
}


and finally



class Pupil {
private PupilProfile profile = new DefaultPupilProfile();
}


Each object is dynamically and lazily created as it is required.


Now lets say I want to use different implementations of PupilProfile depending on (for sake of argument) the pupil's age. However, I don't want to put the logic of which PupilProfile implementation is used in Pupil - I want to separate it out. I may want to change it in future.


I write a factory interface:



interface PupilProfileFactory {

PupilProfile newPupilProfile(Pupil pupil);

}


and my initial implementation:



class AgeDependentPupilProfileFactory implements PupilProfileFactory {
@Override
public PupilProfile newPupilProfile(Pupil pupil) {
switch (pupil.getAge()) {
// Case statements returning different implementations
}
}
}


This implementation exists as a singleton in my application (eg in the Spring app context).


My problem is that I now have to get this factory into the Pupil:



class Pupil {
private PupilProfile profile = new DefaultPupilProfile();

public Pupil(PupilProfileFactory factory) {
this.profile = factory.newPupilProfile(this);
}
}


In practise this means passing the PupilProfileFactory down every layer of the object graph, from top (District) to bottom (Pupil) via constructors or setters. Each intermediate layer needs to have a field for this factory, even though they only use it to pass it down to the level below when they lazily create one.


This strikes me as ugly (and a pain to refactor) and I'm sure there must be a better way - is there?


Aucun commentaire:

Enregistrer un commentaire