0
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 个 Piece 子类,我不想通过将我想要的子类类型作为参数传递给方法来使用方法来实例化它们。

4

6 回答 6

1

如果您不想到处声明 throws 语句或 try/catch 块。

创建一个类并扩展RuntimeException类或使用RuntimeException它自己或相关的RuntimeException扩展类。

然后将 try-catch 块放置到根(您的工厂类方法)并将抛出的异常包装到运行时异常类型(以上之一,无论您选择哪个)。

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

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

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

于 2011-01-13T14:48:47.263 回答
1

@SuppressWarnings和敲响throws Throwable警钟。请参阅Bloch 的有效 Java

于 2011-01-13T14:52:00.400 回答
1

您正在尝试反思性地创建一个Piece对象。

Class.forName()throws ClassNotFoundException,而Class.newInstance()throws InstantiationException, IllegalAccessException (因此你需要 throw 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,它未经测试,只是展示了一种更好的方法。

希望这可以帮助!:)

于 2011-01-13T14:53:54.280 回答
1

需要在某个地方捕获异常。如果我理解正确,你有一个围绕这个工厂的类,比如FactoryWrapper. 在代码中的某个地方,您使用该包装类,并且您可以抛出异常或捕获它,因为您使用它的方法可能在某个点(可能在基类中)被 try/catch 块包围,而其他地方(您传递FactoryWrapper参考)可能是最后的手段(这不是一个好习惯,但它可能是一个永远不会调用的方法)需要捕获异常的地方。

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

您可以使用一个抽象工厂,然后为每种类型使用一个工厂。在此处此处查看详细信息。希望这可以帮助。

编辑 :

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

于 2011-01-13T15:01:04.277 回答
1

我建议您熟悉编写异常处理程序。否则,您唯一的选择是用throws声明来混淆您的代码,这会变得烦人并导致困难,正如您已经看到的那样。

在这种情况下,您的方法的主体可以抛出两个检查异常: ClassNotFoundException来自forname()调用和InstantiationException来自newInstance()调用。

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

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

于 2011-01-13T15:21:05.970 回答
0

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

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

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 是不好的做法,因为它将所有错误集中在一起,并且使错误处理对客户来说非常麻烦和不透明。除非您没有其他选择,否则不要这样做。

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

Wrap them in a runtime exception which needs not be caught by clients. This is not always a good idea. The fact that code you are calling is throwing checked exceptions usually has a reason. In your example you were doing reflection, and this can go wrong in many ways (the API declares LinkageError, ExceptionInInitializerError, ClassNotFoundException, IllegalAccessException, InstantiationException and SecurityException). Wrapping the checked exceptions in a runtime exception does not make that problem go away. I consider doing this a 'code smell'. If the error means an unrecoverable system failure, then it might be a valid choice, but in most cases you would want to handle such failures more gracefully.

Throw a custom checked exception for a complete subsystem. See for example Spring's org.springframework.dao.DataAccessException which is used to wrap all implementation specific data access exceptions. This means clients will have to catch just one exception type and can figure out the details if they need to. In your case you could create a checked PieceCreationException and use that to wrap all the reflection errors. This is a valid exception handling pattern, but I think it might be a little to heavy-handed for your PieceFactory.

Return null. You could catch all the exceptions within your code and simply return null if they occur. Just make sure your JavaDoc clearly indicates this. A drawback of this approach is that clients might have to check for nulls everywhere.

Return a specific error type. This is a geeky (very object-oriented) approach I saw in the Java core API somewhere a while back (darn, can't remember where). For this you would create an extra Piece type:

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

And return an instance of this type when an error occurs during creation. The advantage is that it is more self-documenting than returning a simple null-value but clients would of course have to check for this using something like instanceof. If they don't, then they will encounter the UnsupportedOperationException thrown when the Piece methods are called. A bit heavy, but very explicit. I'm not sure I would go this far, but it's still an interesting idea.

于 2011-01-13T16:39:34.200 回答