我的工厂课有什么问题吗?
-
10-10-2019 - |
题
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
目的。
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, ,未经测试,只是显示出更好的方法。
希望这可以帮助! :)
其他提示
两个都 @SuppressWarnings
和 throws Throwable
环警钟。看 有效的Java 由布洛克。
如果您不想声明抛出语句或在任何地方尝试/捕获块。
创建一个类并扩展 RuntimeException
课程或使用 RuntimeException
本身或相关 RuntimeException
扩展课程。
然后将try-Catch块放在根部(您的出厂类方法)上,并将抛入运行时异常类型的异常(以上之一,无论您选择哪个)。
但是,这实际上取决于您的需求。在某个时候,您仍然需要处理异常,否则您的应用程序会在运行时抛出异常,并且如果您不处理它们,则应用程序不会做期望的事情。
try{
"your code which throws exception"
} catch(ThrownExceptionType e){
throw new RuntimrException(e);
}
请记住,您仍然需要处理异常。这并不意味着这会很好。
需要将例外捕获到某个地方。如果我正确理解您有一个在该工厂周围包裹的课程,请说一个 FactoryWrapper
. 。在代码中的某个地方,您使用该包装器类,并且可以抛出异常或捕获异常,因为您使用的方法可能在某个时候(可能在基类中)被try/Catch Block包围。另一个地方(您通过 FactoryWrapper
参考)可能是最后的手段(这不是一个好习惯,但可能是从未被称为的方法),需要抓住例外。
这只是为什么您不能在其他班级中抛出异常的原因。正如其他已经提到的那样,请尝试不使用反射,因为这要比其他选择要慢得多。
您可以为每种类型使用一个抽象工厂,然后使用一个工厂。阅读详情 这里 和 这里 。希望这可以帮助。
编辑 :
这里 是一篇关于反思的有趣文章。它将给您一个何时使用它的想法。
我建议您努力在编写异常处理程序方面变得舒适。否则,您唯一的选择是用代码弄乱 throws
正如您已经看到的那样,声明变得令人讨厌并造成困难。
在这种情况下,您的方法的主体可以抛出两个检查的例外: ClassNotFoundException
来自 forname()
打电话,然后 InstantiationException
来自 newInstance()
称呼。
如何最佳处理这些取决于您的应用程序和偏好。正如另一个答案所建议的那样,一种选择是简单地抓住它们并抛出未选中的例外。如果您期望在应用程序完成后永远不会发生这些异常,这是明智的。这可能是我在这里采用的方法,因为如果发生这些异常中的任何一个,则可能表明配置错误。
但是,如果有理由认为这些例外情况可以合理地在正常使用中发生,那么您应该考虑如何处理它们。例如,如果用户将作品子类名称输入GUI(我知道,但仅仅是为了说明),则 ClassNotFoundException
由于这个名称很容易被拼写错误,因此变得更加可能。在这种情况下,允许该方法抛出该异常并要求呼叫者捕获和处理它可能是有意义的(例如,通过向用户提供一条消息,该消息不存在请求的类不存在)。
像您一样使用反射是不理想的。一旦您重命名作品课,并且客户通过了精心编码的完全合格的classNames,您的应用将破坏您的应用程序。 Elite Gent的建议避免了这个问题,但仍然要求客户知道具体类,这正是工厂模式试图隐藏的。我认为,一种更好的方法是使用枚举来表示零件类型,并让客户将其作为参数传递给您的工厂方法,或为每种类型创建单独的出厂方法(具有可行的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);
}
}
}
我在这里使一切都保持在这里,以保持示例独立。通常,字段,作品,抽象作品和rok将是顶级模型类,而曲目也将是顶级的。
我认为这是一个更好的方法,并避免了异常处理问题。
返回到这一点,您可以考虑几种方法(基于您的反思方法):
像您所做的那样投掷是不好的练习,因为它会将所有错误汇总在一起,并使对客户的错误处理非常繁琐且不透明。除非您没有其他选择,否则请勿这样做。
为所有检查异常,在您的方法上声明“抛出”。要确定这是否有效,请考虑客户是否应该知道并了解您要投掷的异常类型。在您的示例中,要创建Rook的客户是否应该知道并了解InstantiationException,IllegalAccessexception和ClassNotFoundException您的反射代码?在这种情况下可能不是。
将它们包裹在不需要被客户捕获的运行时例外。这并不总是一个好主意。您正在调用的代码抛出检查例外情况的事实通常是有原因的。在您的示例中,您进行了反思,这可能在许多方面出错(API声明LinkageError,ExceptioninitializerError,ClassNotFoundException,IllegalAccessexception,InstantiationException和SecurityException)。在运行时例外包装检查的异常并不能消除该问题。我认为这样做是“代码气味”。如果错误意味着无法恢复的系统故障,那么它可能是一个有效的选择,但是在大多数情况下,您希望更优雅地处理此类失败。
为完整的子系统提供自定义检查例外。例如,请参见Spring的org.springframework.dao.dataaccessexception,用于包装所有实现特定数据访问例外。这意味着客户将只需要捕获一种例外类型,并且可以弄清楚详细信息。在您的情况下,您可以创建一个检查的零件exception,并使用它来包装所有反射错误。这是一个有效的异常处理模式,但我认为对于您的作品,这可能有点笨拙。
返回null。您可以在代码中捕获所有异常,如果出现了NULL,则可以返回NULL。只需确保您的Javadoc清楚地表明了这一点。这种方法的缺点是客户可能必须检查各地的无效。
返回特定的错误类型。这是我一段时间以前在Java Core 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
. 。如果他们不这样做,那么当调用零件方法时,他们将遇到抛出的无pportedoperationException。有点沉重,但很明确。我不确定我会走这么远,但这仍然是一个有趣的主意。