【问题标题】:augment the factory pattern in java在java中增加工厂模式
【发布时间】:2010-04-06 02:35:06
【问题描述】:

我正在尝试使用工厂模式来创建 QuestionTypeFactory,其中实例化的类将类似于 MultipleChoice、TrueFalseQuestion 等。

工厂代码看起来像这样

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.");
}
}

目前可以正常使用。如果我想添加另一个问题类型,我需要修改工厂类,我不想这样做。

如何设置它,以便每个问题类都向工厂注册,这样当我添加新的问题类型时,我不必更改工厂的代码?我对java有点陌生,不知道该怎么做。

编辑

附加信息

所有问题类都实现了一个 IQuestion 接口。我正在寻找一种方法来实现像

这样的方法
public static void registerType(QuestionType quesType, Class<IQuestion> ques)

这样我就可以从我的类的静态块中调用此方法,这样当我添加新的问题类型时,我就不必在问题工厂中更改或添加任何代码。我知道我必须更改当前的实现以使其通用。我不确定我上面写的方法在语法上是否正确,但它显示了我想要的概念。

【问题讨论】:

  • 我认为你的意思是“增强”工厂模式
  • 我想到了两个很好的解决方案,但是 Java 缺乏 Real Life-Sized Generics™ 和闭包使它们一文不值:(。我认为你被手工劳动或一些代码模板魔法所困扰,或反射。
  • @TP: 第二个参数应该是Class&lt;IQuestion&gt;吧?你想说的是要创建的类,而不是它的实例(因为,如果你已经有一个实例,你就不需要工厂)。
  • @ Martinho Fernandes 这可能是我需要的参数。我是 java 新手,所以我不知道实现这类事情的确切语法
  • ... 这是 XML、注释和反射的例子 grin

标签: java design-patterns factory-pattern


【解决方案1】:

您可能可以通过反射 API(Class thingy)使用您展示的注册方法来做到这一点。

我对 Java 反射不够精通,无法编写更有用的答案,但如果您寻找一些 getConstructor 方法或其他方法,您可能会到达那里。

要调用该方法,您应该执行以下操作(注意.class 语法):

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

编辑啊,无论如何,我有时间调查。试试这个:

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();
    }
}

我还没有编译这个,但它应该可以工作,或者至少可以引导你朝着正确的方向前进。为此,问题实现必须有一个不带参数的构造函数。

因为您使用的是静态工厂(也称为面向对象的全局变量),所以您可以让问题在其static initializer 中注册。

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

【讨论】:

  • 问题类中的静态注册方法应该在哪里调用呢?
  • 如果你想移除静态工厂对问题的依赖,你可以用另一种方式:让问题依赖于静态工厂,即注册自己。可能在静态初始化器中。
  • 这正是我想要实现的目标。让问题自己登记。我不确定在哪里放置向工厂注册问题类的代码。
  • @TP:在问题类的静态初始化块中,例如:public class TrueFalseQuestion implements Question static { QuestionFactory.registerType(QuestionType.TrueFalse, TrueFalseQuestion.class); }
  • 在我实际实例化 TrueFalseQuestion 之前,不会运行静态块。我该如何解决这个问题?
【解决方案2】:

一种可能性:

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(); 
    }
}

当然,这摆脱了工厂并假设您的所有问题都有无参数构造函数,但据我所知,它涵盖了上面代码草图的所有情况。如果特定类的构造更复杂,您可以随时设置:

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

【讨论】:

  • 是的,是的,枚举内部要好得多!这可能是我希望在 C# 中看到的主要 Java 特性。
  • 但这仍然没有解决依赖问题。当我创建一个新的问题类时,我仍然需要更新枚举。有没有办法用这种方法摆脱依赖?
  • 我将如何在 main 方法中使用它来实际创建特定问题类型的实例?
  • @TP: Question foo = QuestionType.TrueFalse.createQuestion();.
  • @TP:不,这并不能解决依赖问题。做到这一点的唯一方法是使用 Martinho 建议的方法来摆脱枚举。对于少量的问题类型,我认为枚举相当优雅,但对于无限的选项,你真的需要切换到更冗长的东西。
【解决方案3】:

我很确定您只是想在您的枚举类型上创建一个方法,该方法将返回适当的 Question 对象。因此,每当有人向 QuestionType 添加枚举值时,他们也必须更新该方法。虽然不能解决您必须更新该方法的问题...

【讨论】:

  • 但据推测,工厂可以决定为给定类型创建替代实现(即枚举和调用代码都不需要知道的实现)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-06-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-19
相关资源
最近更新 更多