我的工厂课有什么问题吗?

时间:2011-01-13 14:41:35

标签: java factory instantiation factory-pattern throwable

class PieceFactory {     
     @SuppressWarnings("rawtypes")
     public Piece createPiece(String pieceType) throws Throwable{
        Class pieceClass = Class.forName(pieceType);
        Piece piece = (Piece) pieceClass.newInstance();

         return piece;       
     }
}

我并不是所有人都习惯于处理异常,因此我只是抛弃它们,但是在任何地方我都使用了一个使用这个工厂的方法,它告诉我必须抛出像throwable这样的异常。

例如,在我的一个类中,我有一个方法,它使用使用工厂的方法实例化大量对象。我可以通过抛出异常来使用该类中的方法,但是如果我尝试将对该类的引用传递给另一个类然后从那里使用该方法,它将无法工作。然后它迫使我尝试捕捉异常。

我可能不需要工厂,但它似乎很有趣,我想尝试使用模式。我创建工厂的原因是我有6个子类,我不想使用一个方法来实例化它们,方法是将我想要的子类类型作为参数传递给方法。

6 个答案:

答案 0 :(得分:1)

如果你不想在任何地方声明throws语句或try / catch块。

创建一个类并扩展RuntimeException类 或者使用RuntimeException本身 或相关的RuntimeException扩展类。

然后将try-catch块放在根(工厂类方法)中,并将抛出的异常包装到Runtime异常类型中(上面的一个,无论你选择哪个)。

然而,这实际上取决于你需要什么。在某些时候,您仍然需要处理异常,或者您的应用程序将在运行时抛出异常,如果您不处理它们,应用程序将不会执行预期的操作。

try{ 
    "your code which throws exception" 
} catch(ThrownExceptionType e){ 
   throw new RuntimrException(e);
}

请记住,您仍然需要处理异常。这并不意味着这一切都会很好。

答案 1 :(得分:1)

@SuppressWarningsthrows Throwable都会响起警钟。请参阅布洛赫的Effective Java

答案 2 :(得分:1)

您正试图反思性地创建Piece对象。

Class.forName()抛出ClassNotFoundException,而Class.newInstance()抛出InstantiationException,IllegalAccessException(因此你需要抛出Throwable

通过类类型创建对象的更好方法可能是执行以下操作:

class PieceFactory {     

    public Piece createPiece(String pieceType) throws Throwable{
        Piece piece = null;

        if ("SubPiece1".equals(pieceType)) {
            piece = new SubPiece1();
        } else if ("SubPiece2".equals(pieceType)) {
            piece = new SubPiece2();
        }

        return piece;       
    }
}

PS ,这是未经测试的,只是展示了更好的方法。

希望这有帮助! :)

答案 3 :(得分:1)

需要在某处捕获异常。如果我理解正确,你有一个围绕这个工厂的课程,请说FactoryWrapper。你的代码中的某个地方你使用了这个包装类,你可以抛出异常或者捕获它,因为你使用它的方法可能被try / catch块包含在某个点(在基类中)其他地方(你通过你的FactoryWrapper引用)可能是最后的手段(这不是一个好习惯,但它可能是一个永远不会被调用的方法),需要捕获异常。

这只是一个可能的解释,为什么你不能在你的其他类中抛出异常。正如其他人已经提到的那样,尽量不要使用反射,因为这比其他选择慢得多。

您可以为每种类型使用一个抽象工厂,然后使用工厂。详情请见herehere。希望这会有所帮助。

编辑:

Here是一篇关于反思的有趣文章。它会让你知道何时使用它。

答案 4 :(得分:1)

我建议你努力学习编写异常处理程序。否则,您唯一的选择就是使用throws声明来捣乱您的代码,这些声明会变得烦人并导致困难,正如您已经看到的那样。

在这种情况下,您的方法正文可以抛出两个已检查的例外情况:来自ClassNotFoundException来电的forname()和来自InstantiationException来电的newInstance()

如何最好地处理这些取决于您的应用程序和您的偏好。正如另一个答案所建议的那样,一个选择是简单地捕获它们并抛出未经检查的异常。如果您希望应用程序完成后永远不会发生这些异常,这是明智的。这可能是我在这里采用的方法,因为如果发生这些异常中的任何一个,则可能表示配置错误。

但如果有理由认为这些例外在正常使用中可能合理发生,那么您应该考虑如何处理它们。例如,如果用户将片段子类名称输入GUI(不太可能,我知道,但只是为了表明这一点),那么ClassNotFoundException变得更有可能,因为名称很容易拼写错误。在这种情况下,允许此方法抛出该异常并要求调用者捕获并处理它(例如,通过向用户提供所请求的类不存在的消息)可能是有意义的。

答案 5 :(得分:0)

像你一样使用反射并不理想。只要您重命名Piece类并且客户端传递硬编码的完全限定类名,您的应用就会中断。 Elite Gent的建议避免了这个问题,但仍然要求客户知道具体的类,这正是工厂模式试图隐藏的。在我看来,更好的方法是使用枚举来表示Piece类型,让客户端将其作为参数传递给工厂方法,或者为每种类型创建单独的工厂方法(可以使用6种类型)。 / p>

因为我无论如何都在拖延,这里是一个枚举方法的例子:

import java.util.ArrayList;
import java.util.List;

class PieceFactoryExample {

    static enum PieceFactory {
        ROOK {
            Piece create() {
                return new Rook();
            }
        };
        abstract Piece create();
    }

    static class Field {
        char line;
        int row;

        public Field(char line, int row) {
            this.line = line;
            this.row = row;
        }

        public String toString() {
            return "" + line + row;
        }
    }

    static interface Piece {

        void moveTo(Field field);

        List<Field> possibleMoves();
    }

    static abstract class AbstractPiece implements Piece {

        Field field;

        public void moveTo(Field field) {
            this.field = field;
        }
    }

    static class Rook extends AbstractPiece {

        public List<Field> possibleMoves() {

            List<Field> moves = new ArrayList<Field>();
            for (char line = 'a'; line <= 'h'; line++) {
                if (line != field.line) {
                    moves.add(new Field(line, field.row));
                }
            }
            for (char row = 1; row <= 8; row++) {
                if (row != field.row) {
                    moves.add(new Field(field.line, row));
                }
            }
            return moves;
        }
    }

    public static void main(String[] args) {

        Piece pawn = PieceFactory.ROOK.create();
        Field field = new Field('c', 3);
        pawn.moveTo(field);
        List<Field> moves = pawn.possibleMoves();
        System.out.println("Possible moves from " + field);
        for (Field move : moves) {
            System.out.println(move);
        }
    }
}

我在这里做了一切静态以保持示例自包含。通常Field,Piece,AbstractPiece和Rook都是顶级模型类,而PieceFactory也是顶级模型。

这是我认为更好的方法,并避免了异常处理问题。

回到这一点,您可以考虑几种方法(基于您的反思方法):

像你一样投掷Throwable是不好的做法,因为它将所有错误混为一谈,并使错误处理对于客户来说非常麻烦且不透明。除非你没有其他选择,否则不要这样做。

对所有已检查异常的方法声明'throws'。要确定这是否有效,请考虑客户端是否应该了解并了解您要抛出的异常类型。在您的示例中,想要创建Rook的客户端是否应该知道并理解反射代码抛出的InstantiationException,IllegalAccessException和ClassNotFoundException?可能不是在这种情况下。

将它们包装在运行时异常中,不需要客户端捕获。这并不总是一个好主意。您调用的代码抛出已检查的异常这一事实通常是有原因的。在您的示例中,您正在进行反射,这可能在许多方面出错(API声明了LinkageError,ExceptionInInitializerError,ClassNotFoundException,IllegalAccessException,InstantiationException和SecurityException)。在运行时异常中包装已检查的异常不会使该问题消失。我认为这是一个'代码味'。如果错误意味着不可恢复的系统故障,那么它可能是一个有效的选择,但在大多数情况下,您会希望更优雅地处理此类故障。

为完整的子系统抛出自定义检查的异常。请参阅Spring的org.springframework.dao.DataAccessException,它用于包装所有特定于实现的数据访问异常。这意味着客户端必须只捕获一种异常类型,并且可以根据需要找出详细信息。在您的情况下,您可以创建一个已检查的PieceCreationException并使用它来包装所有反射错误。这是一个有效的异常处理模式,但我认为对你的PieceFactory来说可能有点笨拙。

返回null。您可以捕获代码中的所有异常,只要它们发生就返回null。只需确保您的JavaDoc清楚地表明了这一点。这种方法的缺点是客户端可能必须在任何地方检查空值。

返回特定的错误类型。这是我在Java核心API中看到的一种令人讨厌的(非常面向对象的)方法,一段时间(darn,不记得在哪里)。为此,您将创建一个额外的片段类型:

class NullPiece implements Piece {
    public void moveTo(Field field) {
        throw new UnsupportedOperationException("The Piece could not be created");
    }
    public List<Field> possibleMoves() {
        throw new UnsupportedOperationException("The Piece could not be created");
    }
}

在创建过程中发生错误时返回此类型的实例。优点是它比返回简单的空值更自我记录,但客户当然必须使用instanceof之类的东西来检查。如果他们不这样做,那么他们将遇到调用Piece方法时抛出的UnsupportedOperationException。有点沉重,但非常明确。我不确定我会走得这么远,但这仍然是一个有趣的想法。