我的工厂类有什么问题吗?
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 PieceFactory {
@SuppressWarnings("rawtypes")
public Piece createPiece(String pieceType) throws Throwable{
Class pieceClass = Class.forName(pieceType);
Piece piece = (Piece) pieceClass.newInstance();
return piece;
}
}
I'm not all used to handling exceptions yet therefore I'm just throwing them, but everywhere I use a method that uses this factory it tells me I have to throw exceptions like throwable.
For example, in one of my classes I have a method that instantiates a lot of objects using the method that uses the factory. I can use the method in that class by just throwing the exception, however it won't work if I try to pass a reference to that class to another class and then use the method from there. Then it forces me to try catch the exception.
I probably don't need a factory but it seemed interesting and I'd like to try to use patterns. The reason I created the factory was that I have 6 subclasses of Piece and I wan't to use a method to instantiate them by passing the type of subclass I want as an argument to the method.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
您正在尝试反射性地创建一个
Piece
对象。Class.forName()
抛出ClassNotFoundException
,而Class.newInstance()
抛出 InstantiationException、IllegalAccessException(因此您需要抛出Throwable
。通过类类型创建对象的更好方法可能是执行以下操作:
PS,它未经测试,只是展示了一种更好的方法
:) 。
You are trying to reflectively create a
Piece
object.Class.forName()
throwsClassNotFoundException
, whileClass.newInstance()
throws InstantiationException, IllegalAccessException (hence why you need to throwThrowable
.A better way to create an object through class types is probably by doing the following:
PS, it's untested, just showing a better way to do it.
Hope this helps! :)
@SuppressWarnings 和 throws Throwable 都敲响了警钟。请参阅 Bloch 的 Effective Java。
Both
@SuppressWarnings
andthrows Throwable
ring alarm bells. See Effective Java by Bloch.如果您不想到处声明 throws 语句或 try/catch 块。
创建一个类并扩展 RuntimeException 类
或使用 RuntimeException 本身
或相关的 RuntimeException 扩展类。
然后将 try-catch 块放置到根(您的工厂类方法)并将抛出的异常包装到运行时异常类型(上述之一,无论您选择哪种)。
但是,这实际上取决于您的需要。在某些时候,您仍然需要处理异常,否则您的应用程序将在运行时抛出异常,并且如果您不处理它们,应用程序将不会执行预期的操作。
请记住,您仍然需要处理异常。这并不意味着这一切都会顺利。
If you dont want to declare throws statements or try/catch block everywhere.
Create a class and extend
RuntimeException
classor use
RuntimeException
itselfor related
RuntimeException
extending classes.Then place try-catch block to the root (your factory class methods) and wrap the exceptions thrown into a Runtime exception type (one of above, whichever you choose).
However, it really depends on what you need. At some point you will still need to handle exceptions or your application will throw exception at runtime and app wont do what was expected if you dont handle them.
Remember you still need to handle the exception. It does not mean this is gonna work all well.
需要在某个地方捕获异常。如果我理解正确的话,您有一个围绕该工厂的类,例如
FactoryWrapper
。在代码中的某个地方,您使用该包装类,并且您可以抛出异常或捕获它,因为您使用它的方法可能在某个时刻(可能在基类中)被 try/catch 块包围,而其他地方(您将FactoryWrapper
引用传递到的位置)可能是需要捕获异常的最后手段(这不是一个好的做法,但它可能是一个永远不会被调用的方法) 。这只是对为什么不能在其他类中抛出异常的可能解释。正如其他人已经提到的,尝试不使用反射,因为这比其他替代方案慢得多。
您可以使用一个抽象工厂,然后为每种类型使用一个工厂。请参阅此处和这里。希望这有帮助。
编辑:
此处< /a> 是一篇关于反射的有趣文章。它会让您知道何时使用它。
Exceptions need to be caught somewhere. If I understood correctly you have one class that is wrapped around this factory, say a
FactoryWrapper
. Somewhere in your code you use that wrapper class and you are able to either throw you exception or catch it because the method where you are using it is probably surrounded at some point (in a base class probably) by a try/catch block while the other place (to which you are passing yourFactoryWrapper
reference) is probably a last resort (it is not a good practice, but it could be a method that is never called) where the exception need to be caught.This is just a possible explanation to why you can not throw the exception in your other class. As other have already mentioned, try not using reflection because that is much slower that the other alternatives.
You could use one abstract factory and then a factory for each type. See details here and here . Hope this helps.
EDIT :
Here is an interesting article about reflection. It will give you an idea when to use it.
我建议您努力熟悉编写异常处理程序。否则,您唯一的选择就是在代码中添加 throws 声明,这会变得烦人并导致困难,正如您已经看到的那样。
在这种情况下,方法的主体可以抛出两个已检查的异常:来自
forname()
调用的ClassNotFoundException
和来自InstantiationException
>newInstance() 调用。如何最好地处理这些取决于您的应用程序和您的偏好。正如另一个答案所建议的,一种选择是简单地捕获它们并抛出未经检查的异常。如果您希望应用程序完成后永远不会发生这些异常,那么这是明智的。这可能是我在这里采用的方法,因为如果发生这些异常中的任何一个,则可能表明配置错误。
但是,如果有理由认为这些异常在正常使用中可能合理发生,那么您应该考虑如何处理它们。例如,如果用户将 Piece 子类名称输入到 GUI 中(我知道不太可能,但只是为了说明这一点),那么
ClassNotFoundException
的可能性就更大,因为该名称很容易拼写错误。在这种情况下,允许此方法抛出该异常,并要求调用者捕获并处理它可能是有意义的(例如,通过向用户提供一条消息,表明所请求的类不存在)。I recommend that you work on getting comfortable with writing exception handlers. Otherwise your only option is to riddle your code with
throws
declarations which becomes annoying and causes difficulties, as you've seen already.In this case, the body of your method can throw two checked exceptions:
ClassNotFoundException
from theforname()
call, andInstantiationException
from thenewInstance()
call.How to best handle these depends on your application and your preference. As suggested by another answer, one option is to simply catch them and throw unchecked exceptions. This is sensible if you expect that these exceptions should never occur once the application is complete. This is probably the approach I would take here, since if either of these exceptions occurred it would likely indicate a configuration error.
But if there is reason to think these exceptions could reasonable occur in normal usage, you should think about how you want to handle them. For instance, if the Piece subclass name were being entered into a GUI by a user (not likely, I know, but just to make the point) then a
ClassNotFoundException
becomes much more likely since the name could easily be misspelled. In that situation, it might make sense to allow this method to throw that exception, and require the caller to catch and handle it (e.g. by providing a message back to the user that the requested class does not exist).像您这样使用反射并不理想。一旦您重命名 Piece 类并且客户端传递硬编码的完全限定类名,您的应用程序就会崩溃。 Elite Gent 的建议避免了这个问题,但仍然要求客户知道具体的类,这正是工厂模式试图隐藏的内容。我认为更好的方法是使用枚举来表示 Piece 类型并让客户端将其作为参数传递给工厂方法,或者为每种类型创建单独的工厂方法(有 6 个 Piece 类型是可行的)。
因为无论如何我都在拖延,所以这里有一个枚举方法的示例:
我在这里将所有内容都设为静态,以保持示例的独立性。通常,Field、Piece、AbstractPiece 和 Rook 是顶级模型类,PieceFactory 也将是顶级模型类。
我认为对于您的示例来说这是一个更好的方法,并且可以避免异常处理问题。
回到这一点,您可以考虑几种方法(基于您的反射方法):
像您一样抛出 Throwable 是一种不好的做法,因为它将所有错误集中在一起,并使错误处理对客户端来说非常麻烦且不透明。除非您没有其他选择,否则不要这样做。
在您的方法上为所有已检查的异常声明“抛出”。要确定这是否有效,请考虑客户端是否应该知道并理解您抛出的异常类型。在您的示例中,想要创建 Rook 的客户端是否应该知道并理解您的反射代码抛出的 InstantiationException、IllegalAccessException 和 ClassNotFoundException?在这种情况下可能不是。
将它们包装在客户端不需要捕获的运行时异常中。这并不总是一个好主意。您调用的代码抛出已检查异常的事实通常是有原因的。在您的示例中,您正在进行反射,这可能会在很多方面出错(API 声明 LinkageError、ExceptionInInitializerError、ClassNotFoundException、IllegalAccessException、InstantiationException 和 SecurityException)。将检查的异常包装在运行时异常中并不能解决该问题。我认为这样做是一种“代码味道”。如果错误意味着不可恢复的系统故障,那么它可能是一个有效的选择,但在大多数情况下,您希望更优雅地处理此类故障。
为完整的子系统引发自定义检查异常。例如,请参阅 Spring 的 org.springframework.dao.DataAccessException,它用于包装所有特定于实现的数据访问异常。这意味着客户端只需捕获一种异常类型,并且可以在需要时找出详细信息。在您的情况下,您可以创建一个已检查的 PieceCreationException 并使用它来包装所有反射错误。这是一个有效的异常处理模式,但我认为这对于 PieceFactory 来说可能有点过于严厉。
返回空值。您可以捕获代码中的所有异常,并在发生异常时简单地返回 null。只要确保您的 JavaDoc 明确指出了这一点即可。这种方法的一个缺点是客户端可能必须检查各处是否有空值。
返回特定的错误类型。这是我不久前在 Java 核心 API 中看到的一种极客(非常面向对象)的方法(该死,不记得在哪里了)。为此,您将创建一个额外的 Piece 类型:
并在创建过程中发生错误时返回该类型的实例。优点是它比返回简单的空值更具自记录性,但客户端当然必须使用诸如
instanceof
之类的东西来检查这一点。如果不这样做,那么在调用 Piece 方法时就会遇到抛出 UnsupportedOperationException 的情况。有点沉重,但非常明确。我不确定我会走这么远,但这仍然是一个有趣的想法。Using reflection like you do is not ideal. As soon as you rename a Piece class and clients pass hardcoded fully-qualified-classnames, your app will break. The Elite Gent's suggestion avoids that problem, but still requires clients to know the concrete class, which is exactly what the factory pattern tries to hide. A better approach in my opinion would be to either use an enum to represent Piece types and let the client pass that as the argument to your factory method, or create separate factory methods for each type (with 6 piece types that is feasible).
Since I am procrastinating anyway, here an example of the enum-approach:
I made everything static here to keep the example self-contained. Normally Field, Piece, AbstractPiece and Rook would be top-level model classes and PieceFactory would be top-level too.
This is a better way to go for your example I think, and avoids the exception handling issue.
Returning to that, there are a couple of approaches you can consider (based on your reflection approach):
Throwing Throwable like you did is bad practice since it lumps together all errors and makes error handling very cumbersome and untransparent for clients. Do not do that unless you have no other options.
Declare 'throws' on your method for all checked exceptions. To decide if this is valid, consider if the client should know and understand the exception type you are throwing. In your example, should the client that wants to create a Rook know and understand the InstantiationException, IllegalAccessException and ClassNotFoundException thrown by your reflection code? Probably not in this case.
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:
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.