문제

I am trying to use a factory pattern to create a QuestionTypeFactory where the instantiated classes will be like MultipleChoice, TrueFalseQuestion etc.

The factory code looks something like this

class QuestionFactory {
    public enum QuestionType {
        TrueFalse,
        MultipleChoice,
        Essay
    }

public static Question createQuestion(QuestionType quesType) {
    switch (quesType) {
        case TrueFalse:
            return new TrueFalseQuestion();
        case MultipleChoice:
            return new MultipleChoiceQuestion();
        case Essay:
            return new EssayQuestion();
    }
    throw new IllegalArgumentException("Not recognized.");
}
}

This works ok for now. If I want to add another question type I will need to modify the factory class and I do not want to do that.

How can I set it up so that each question class registers itself with the Factory so that when I add a new question type, I do not have to change the code for the factory? I am a bit new to java and am not sure how to do this.

Edit

Additional Information

All the question classes implement an IQuestion interface. I am looking for a way to implement a method like

public static void registerType(QuestionType quesType, Class<IQuestion> ques)

so that I can call this method from a static block from my classes so that when I add a new question type, I will not have to change or add any code in the Question Factory. I know I would have to change the current implementation to make it generic. I am not sure the method that I wrote above is correct in terms of its arguments syntactically or not but it shows what I want in concept.

도움이 되었습니까?

해결책

You can probably do that with the register method you've shown, through the Reflection API (that Class thingy).

I am not proficient enough with Java Reflection to write a more helpful answer, but if you look for some getConstructor method or something you'll probably get there.

To call that method you should do something like (note the .class syntax):

QuestionFactory.registerType(QuestionType.TrueFalse, TrueFalseQuestion.class);

EDIT Ah, whatever, I have the time to investigate. Try this:

public class QuestionFactory {
    static final Map<QuestionType, Constructor<? extends Question>> map =
        new HashMap<QuestionType, Class<? extends Question>>();

    public static void registerType(QuestionType quesType, Class<? extends Question> ques) {
        map.put(quesType, ques.getConstructor());
    }

    public static Question createQuestion(QuestionType quesType) {
        return map.get(quesType).newInstance();
    }
}

I haven't compiled this, but it should work, or at least guide you in the right direction. For this to work the Question implementations must have a constructor without arguments.

Because you're using a static factory (aka, object-oriented global variables) you can make questions register themselves in their static initializer.

public class TrueFalseQuestion implements Question {
    static {
        QuestionFactory.registerType(QuestionType.TrueFalse, TrueFalseQuestion.class);
    }
    // Whatever else goes here
}

다른 팁

One possibility:

public enum QuestionType {
    TrueFalse(TrueFalseQuestion.class),
    MultipleChoice(MultipleChoiceQuestion.class),
    Essay(EssayQuestion.class)

    private final Class<? extends Question> implementationType;
    QuestionType(Class<? extends Question> implementationType) {
       this.implementationType = implementationType;
    }

    public Question createQuestion() { 
       return implementationType.newInstance(); 
    }
}

Of course, this gets rid of the factory and assumes all your questions have no-args constructors, but as far as I can tell, it covers all the cases of the code sketch above. If construction for the particularly classes is more complicated, you can always setup something like:

public enum QuestionType {
    TrueFalse { public Question createQuestion() { /* construction logic goes here */ } }
    public abstract Question createQuestion();
}

I'm pretty sure you just want to create a method on your enum type which will return the appropriate Question object. So anytime someone adds an enum value to QuestionType they will also have to update the method. Doesn't solve your problem of having to update that method though...

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top