try-catch-finally 与抽象方法
在我们的系统中,我们有一个抽象类,我们称之为BasicAction,它包含几个抽象方法。其中最重要的是执行。它处理来自 JSP 页面的请求。主处理程序的工作方式如下:
// Sample 1:
String actionName;// The name of action to use
Map<String, BasicAction> mapping;//The mapping of names and actual handlers
BasicAction action = mapping.get(actionName);
try {
action.execute(request);//Handle the http request from the JSP page
} catch (Exception ex) {
// Handle here any exceptions
}
现在,一切看起来都很好,但基本上所有派生处理程序都实现相同的代码:
// Sample 1:
public class ConcreteAction extends BasicAction {
@Override
public void execute(HttpServletRequest request) {
// The managers provide the middle layer between
// web presentation and database
TrafficManager trafficManager = null;
CargoManager cargoManager = null;
try {
trafficManager = new TrafficManager();
cargoManager = new CargoManager();
// Perform here all the logic required using managers
} catch (Exception ex) {
// handle the exception or rethrow it
} finally {
// Should remove all managers clearly and release the connection
removeManager(trafficManager);
removeManager(cargoManager);
}
}
}
在我拥有的每个处理程序中编写这样的块似乎有点烦人。看起来我们在这里模仿每个处理程序的进入/退出点,但并不像想象的那样。我认为我们这里需要的是在 BasicAction 中定义两个更抽象的方法,称为 createManagers 和 disposeManagers。然后主处理程序将如下所示:
// Sample 2:
String actionName;// The name of action to use
Map<String, BasicAction> mapping;//The mapping of names and actual handlers
BasicAction action = mapping.get(actionName);
try {
action.createManagers(); // The enter point
action.execute(request);//Handle the http request from the JSP page
} catch (Exception ex) {
// Handle here any exceptions
} finally {
action.disposeManagers(); // The exit point
}
之后,每个派生操作处理程序可以这样定义:
// Sample 2:
public class ConcreteAction extends BasicAction {
private TrafficManager trafficManager = null;
private CargoManager cargoManager = null;
@Override
public void createManagers() {
trafficManager = new TrafficManager();
cargoManager = new CargoManager();
}
@Override
public void disposeManagers() {
removeManager(trafficManager);
removeManager(cargoManager);
}
@Override
public void execute(HttpServletRequest request) {
// Perform here all the logic required using managers
}
}
哪种方法更好使用 - 在每个处理程序中使用 try-catch-finally 或使用标准进入/退出点。
in our system we have an abstract class, let's call it BasicAction, which contains several abstract methods. Most important of them is called execute. It handles the requests from the JSP pages. The main handler works like this:
// Sample 1:
String actionName;// The name of action to use
Map<String, BasicAction> mapping;//The mapping of names and actual handlers
BasicAction action = mapping.get(actionName);
try {
action.execute(request);//Handle the http request from the JSP page
} catch (Exception ex) {
// Handle here any exceptions
}
Now, everything looks fine, but basically all the derived handlers implement the same code:
// Sample 1:
public class ConcreteAction extends BasicAction {
@Override
public void execute(HttpServletRequest request) {
// The managers provide the middle layer between
// web presentation and database
TrafficManager trafficManager = null;
CargoManager cargoManager = null;
try {
trafficManager = new TrafficManager();
cargoManager = new CargoManager();
// Perform here all the logic required using managers
} catch (Exception ex) {
// handle the exception or rethrow it
} finally {
// Should remove all managers clearly and release the connection
removeManager(trafficManager);
removeManager(cargoManager);
}
}
}
It seems a little bit annoying to write such blocks in every handler that I have. It seems like here we imitate the enter/exit points for each handler not as supposed to. I think what we need here is to define in the BasicAction two more abstract methods called createManagers and disposeManagers. Then the main handler would look like this:
// Sample 2:
String actionName;// The name of action to use
Map<String, BasicAction> mapping;//The mapping of names and actual handlers
BasicAction action = mapping.get(actionName);
try {
action.createManagers(); // The enter point
action.execute(request);//Handle the http request from the JSP page
} catch (Exception ex) {
// Handle here any exceptions
} finally {
action.disposeManagers(); // The exit point
}
After that, each deriving action handler can be defined like this:
// Sample 2:
public class ConcreteAction extends BasicAction {
private TrafficManager trafficManager = null;
private CargoManager cargoManager = null;
@Override
public void createManagers() {
trafficManager = new TrafficManager();
cargoManager = new CargoManager();
}
@Override
public void disposeManagers() {
removeManager(trafficManager);
removeManager(cargoManager);
}
@Override
public void execute(HttpServletRequest request) {
// Perform here all the logic required using managers
}
}
Which approach is better to use - with try-catch-finally in each handler or with standard enter/exit points.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
就我个人而言,我会选择抽象类方法,因为它听起来像策略模式。而且,代码更干净、更容易遵循;另外 - 你不会一遍又一遍地重复这个结构。但这只是我的意见,有人可能会提出相反的建议。
Personally, I would go for the abstract class approach, as it sounds like the Strategy pattern. Moreover, the code is cleaner and easier to follow; plus - you are not repeating the structure over and over. But that is only my opinion, someone might suggest the other way.
让 createManagers() 返回经理列表。然后,调用类可以对每个类调用removeManager(),从而不需要disposeManagers()。
此外,您还可以使用继承来耦合 BasicAction 和 ConcreteAction。这是没有必要的。您可以通过组合将它们耦合起来。如果 ConcreteAction 实现 IBasicAction 接口,则单独的类 ActionRunner 可以对操作调用 createManagers() 和execute()。您可以将 ConcreteAction 实例传递给 ActionRunner。
Let createManagers() return a list of managers. The calling class can then call removeManager() on every one, making the disposeManagers() unneeded.
Also, you couple your BasicAction and ConcreteAction by using inheritance. This is not necessary. You can couple them by composition instead. If ConcreteAction implements a IBasicAction interface, a seperate class ActionRunner can call createManagers() and execute() on the action. You can pass a ConcreteAction instance to the ActionRunner.
抽象化(选项 2)。
公共代码,特别是公共处理流程应该被抽象。这使得子类可以自由地实现差异,并且意味着可以单独测试抽象代码 - 例如使用模拟/测试实现。
请注意,您可能会走得太远,因此常识适用,但请始终注意抽象点。
Go abstract (option 2).
Common code, especially common processing flow, should be abstracted. This leaves the subclasses free to implement the differences, and means to can test the abstract code in isolation - eg with a mock/test implementation.
Note that you can take this too far, so common sense applies, but always keep your eye out for abstraction points.
我通常使用后一种方法,因为它使重用更加简单。通常,我需要相同的管理器来执行许多操作,因此我可以实现一次创建/处置代码,然后剩下的就是一个小的
execute()
方法。如果这种方法不起作用,我仍然可以重写原始处理程序方法,这样我就可以两全其美。
I usually use the latter approach because it makes reuse more simple. Often, I need the same managers for many actions, so I can implement the create/dispose code once and then all that's left is a small
execute()
method.And if that approach doesn't work, I can still override the original handler method, so I get the best of both worlds.
抽象类将提供某种保证,确保始终调用清理代码,并且它减少了重复,因此在我看来,它比现有结构更好。当然,您无法知道
disposeManagers
的子类实现将准确地杀死它之前创建的那些管理器 - 但在编写标准finally
块时您也会遇到同样的问题。我想我会更进一步。一方面,
execute
方法需要这两个管理器来完成工作,因此我将其定义为现在让我们假设您的大多数操作都使用相同的管理器实现。我们在超类中将这些方法定义为(尽管不是将它们设为最终方法):
因此,现在超类可以通过调用该方法自行创建这些实例,并将它们传递给执行。如果子类需要与默认不同的实现,它可以根据需要重写该方法。
看看清理 - 我们可以采用与上述类似的方法并定义抽象实现。但是,如果管理器需要被清理,那么他们可能会实现
close()
方法或类似的方法。在这种情况下,我们可以直接调用它 - 这保证了它们将被正确处理,无论子类的实现如何,并且try
和finally
不可能不同步。或者,如果您的逻辑需要的话,您可以对每个调用
removeManager
。 (这可能会进一步改进,但这取决于该方法的语义以及它“存在”的位置。)总而言之,BasicAction 的主要代码如下所示
The abstract class would provide a guarantee of sorts that the cleanup code was always called, and it reduces duplication, so it's better than your existing structure in my opinion. Of course you can't know that a subclass' implementation of
disposeManagers
will kill exactly those managers that it created earlier - but you have this same problem with writing standardfinally
blocks.I think I'd go one step further though. For one thing, the
execute
method needs those two managers to do the work, so I'd define it asNow let's assume that most of your actions use the same manager implementations. We define these method in the superclass (not making them final though) as:
So now the superclass can create these instances itself by invoking the method, and pass them into execute. And if a subclass needs a different implementation from the default it can override the method as it desires.
Looking at the cleanup - we could take a similar approach to the above and define abstract implementations. However, if the managers need to be cleaned up, then presumably they implement a
close()
method or similar. In which case we can just call that - which guarantees that they'll be disposed of properly, regardless of subclasses' implementations, and it's not possible for thetry
andfinally
to get out of sync.Or you can just call
removeManager
on each, if that's what your logic requires. (This could possibly be improved further but it depends on the semantics of that method, and where it "lives".)Altogether then the BasicAction's main code looks like