📄 lib0112.html
字号:
</table>
<pre class="literallayout">
1:public JMUNonStaticCodeTable
2: getAcctConslCodeTable (String aUserId,
3: String aSponsorId)
4: throws JMUException
5: {
6: try<a name="552"></a><a name="IDX-230"></a>
7: {
8: <span class="unicode">……………</span> // App code here
9: }
10: catch (JMUException ne)
11: {
12: throw ne;
13: }
14: catch (Throwable bigProblem)
15: {
16: throw new JMUException(
17: getJMUTransactionInfo(),
18: "getAcctConslCodeTable" +
19: "(String aUserId, "+
20: "String aSponsorId) "+
21: "obtaining accounts/consl for "+
22: "userId= "+ aUserId + "and "+
23: "sponsorId= "+ aSponsorId,
24: bigProblem);
25: }
26:
27: return _acctConslCodeTable;
28:}
</pre>
<table class="BlueLine" border="0" cellspacing="0" cellpadding="0" width="100%">
<tr>
<td bgcolor="000080" class="bluecell"><font size="2" face="Arial" color="010100"><b><img src="_.gif" width="1" height="2" alt="End example" border="0"></b></font></td>
</tr>
</table>
<table class="BlankSpace" border="0" cellspacing="0" cellpadding="0" width="100%">
<tr>
<td height="16"></td>
</tr>
</table>
</div>
</div>
<p class="para">
<b class="bold">Make exception messages meaningful.</b> When throwing exceptions, providing null or overly generic messages that state the obvious (for example, "Error occurred") aren't helpful to development staff or end users. Additionally, including the class name in a message is equally meaningless because it's already present in the stack trace. The method name needs to be included only if the method is overloaded. The specific method overload generating the error isn't available explicitly from the stack trace.</p>
<p class="para">Including information about argument values passed can help a developer reproduce the error. However, doing so can be tedious and time consuming if the argument is an object with multiple fields and can also lead to copied code if an object is common and used as an argument to many methods. To make exception handling easier, I make value objects capable of creating textual descriptions of themselves. One way to do this is to implement <span class="fixed">Describable</span> from CementJ. <a class="internaljump" href="#ch17list6a">Listing 17.6a</a> illustrates error processing without the benefit of a <span class="fixed">Describable</span> implementation.</p>
<div class="example">
<span class="example-title"><span class="example-titlelabel">Listing 17.6a: </span>Exception Processing Without a Describable Implementation</span><a name="553"></a><a name="ch17list6a"></a>
<div class="formalbody">
<table class="BlueLine" border="0" cellspacing="0" cellpadding="0" width="100%">
<tr>
<td bgcolor="000080" class="bluecell"><font size="2" face="Arial" color="010100"><b><img src="_.gif" width="1" height="2" alt="Start example" border="0"></b></font></td>
</tr>
</table>
<pre class="literallayout">
1:public void processOrderWithoutDescribe(
2: PurchaseOrderDTO order)
3: {<a name="554"></a><a name="IDX-231"></a>
4: if (order == null)
5: throw new IllegalArgumentException
6:("Null order not allowed.");
7: try
8: {
9: // App code here
10: }
11: catch (Throwable t)
12: {
13: StringBuffer errorMessage = new StringBuffer(256);
14: errorMessage.append("Error processing order: ");
15:
16: errorMessage.append("custId=");
17: if (order.getCustomerId() != null)
18:errorMessage.append(order.getCustomerId());
19: else errorMessage.append("null");
20:
21: errorMessage.append(", shipDt=");
22: if (order.getShipDate() != null)
23:errorMessage.append(order.getShipDate());
24: else errorMessage.append("null");
25:
26: // Replicate for each field of "order".....
27:
28: LogManager.getLogger().logError (
29:errorMessage.toString(), t);
30: }
31: }
</pre>
<table class="BlueLine" border="0" cellspacing="0" cellpadding="0" width="100%">
<tr>
<td bgcolor="000080" class="bluecell"><font size="2" face="Arial" color="010100"><b><img src="_.gif" width="1" height="2" alt="End example" border="0"></b></font></td>
</tr>
</table>
<table class="BlankSpace" border="0" cellspacing="0" cellpadding="0" width="100%">
<tr>
<td height="16"></td>
</tr>
</table>
</div>
</div>
<p class="para">
<a class="internaljump" href="#ch17list6b">Listing 17.6b</a> illustrates how to implement <span class="fixed">Describable</span> to streamline exception processing for every method that uses the <span class="fixed">PurchaseOrderVO</span> object.</p>
<div class="example">
<span class="example-title"><span class="example-titlelabel">Listing 17.6b: </span>Using describe() to Streamline Error Processing</span><a name="555"></a><a name="ch17list6b"></a>
<div class="formalbody">
<table class="BlueLine" border="0" cellspacing="0" cellpadding="0" width="100%">
<tr>
<td bgcolor="000080" class="bluecell"><font size="2" face="Arial" color="010100"><b><img src="_.gif" width="1" height="2" alt="Start example" border="0"></b></font></td>
</tr>
</table>
<pre class="literallayout">
1:public void processOrderWithDescribe(
2: PurchaseOrderDTO order)
3: {
4: if (order == null)
5: throw new IllegalArgumentException
6:("Null order not allowed.");
7: try
8: {
9: // App code here
10: }
11: catch (Throwable t)
12: {
13: LogManager.getLogger().logError (<a name="556"></a><a name="IDX-232"></a>
14:" Error processing order: "+ order.describe(), t);
15: }
16: }
</pre>
<table class="BlueLine" border="0" cellspacing="0" cellpadding="0" width="100%">
<tr>
<td bgcolor="000080" class="bluecell"><font size="2" face="Arial" color="010100"><b><img src="_.gif" width="1" height="2" alt="End example" border="0"></b></font></td>
</tr>
</table>
<table class="BlankSpace" border="0" cellspacing="0" cellpadding="0" width="100%">
<tr>
<td height="16"></td>
</tr>
</table>
</div>
</div>
<p class="para">
<b class="bold">Use native JDK exceptions before creating your own.</b> There's no need to reinvent the wheel. Many developers won't even check if there is an appropriate JDK-defined exception that they could use in lieu of creating an application-specific exception. As a real-world example, I've seen a developer create an exception called <span class="fixed">NullValueException</span> that was thrown when a method was provided a null argument instead of a valid value. <span class="fixed">Illegal-ArgumentException</span> (from <span class="fixed">java.lang</span>) would have been a better choice.</p>
<p class="para">
<b class="bold">Exploit Java's unchecked exception capability.</b> Methods that throw <span class="fixed">RuntimeException</span> don't force all callers into coding a try/catch block or listing the exception in the throws clause and will reduce the size and complexity of the caller. Callers still have the option of trapping exceptions with a try/catch or listing the exception in the throws clause, but they are not forced to. In addition, most developers create checked exceptions out of habit, not because of a deliberate choice. I've been guilty of this, too.</p>
<p class="para">My suggestion to use unchecked exceptions instead of checked exceptions is a bit unorthodox and controversial. I used to use checked exceptions religiously. After coding thousands of try/catch blocks, I realized that using <span class="fixed">RuntimeException</span> does save tremendous amounts of code and makes the remaining code much more readable. The response to most exceptions (whether they are checked or unchecked) in most applications is to log the error with enough context that it can be duplicated and fixed by a developer later. Using <span class="fixed">RuntimeException</span> allows you to choose where to place your try/catch code instead of forcing it to be a part of most methods in most classes. In many cases, the cost of throwing checked exceptions (in terms of extra coding/maintenance time) is not worth the benefits.</p>
<p class="para">Use of the <span class="fixed">RuntimeException</span> is appropriate. According to the JavaDoc for the JDK, <span class="fixed">RuntimeException</span> is intended for "exceptions that can be thrown during the normal operation of the Java Virtual Machine." Most application-level exceptions fall into this category. I use <span class="fixed">RuntimeException</span> in most of the applications I write and place the try/catch blocks in methods that have the ability to record enough context that I can replicate and fix the error.</p>
<p class="para">I'm not alone in my opinion. <a href="LiB0115.html#574" target="_parent" class="chapterjump">Johnson (2002)</a> provides a good argument for using unchecked exceptions instead of exceptions. Bruce Eckel (of "Thinking in Java" fame) also appears to have converted to using unchecked exceptions <a name="557"></a><a name="IDX-233"></a>(see <a target="_top" class="url" href="http://www.mindview.net/Etc/Discussions/CheckedExceptions">http://www.mindview.net/Etc/Discussions/CheckedExceptions</a>) along with Gunjan Doshi (see <a target="_top" class="url" href="http://www.onjava.com/pub/a/onjava/2003/11/19/exceptions.html">http://www.onjava.com/pub/a/onjava/2003/11/19/exceptions.html</a>).</p>
<p class="para">
<b class="bold">Limit the nesting depth of a try/catch block to two.</b> (Many of you, I know, would campaign for one.) If you need more, remove the inner blocks of code to separate private methods for readability. In addition, fixing bugs in nested try/catch scenarios can be difficult. As an aside, the need for deeply nested try/catch logic usually indicates a need to refactor this section of code.</p>
<p class="para">
<b class="bold">Don't catch exceptions and do nothing with them.</b> For programmatic convenience, some developers catch exceptions but fail to code the catch block. This practice eliminates a compiler error but makes the code harder to maintain. Many times, swallowing exceptions leads to derivative exceptions later on that are harder to find and fix. If you catch an exception, do something with it (at least log it).</p>
<p class="para">
<b class="bold">Never put a return statement in a finally block.</b> If something throws an exception within a try block, the finally block is executed before the exception is thrown. If you issue a return statement within the finally or something excepts within the finally, the original exception will never be thrown and never be seen. This will increase the time and effort needed to debug a problem.</p>
<p class="para">
<b class="bold">The architect and project manager should establish an exception-handling</b> <b class="bold">and logging strategy before coding begins.</b> Developers often have personal preferences for exception handling and logging. If you don't define a strategy for exception handling and logging, developers will choose their own, and you'll have no consistency across the application. Eventually, when different sections of the application are integrated, conflicts will arise. In addition, it'll be more difficult for an outsider to maintain the application.</p>
<p class="para">For example, suppose one developer adopts the philosophy of logging exceptions when they're instantiated, while another expects logging to occur at the deployment level. When all code is integrated, some errors will go unreported, which will greatly increase testing and maintenance time.</p>
<p class="para">One of the most valuable pieces of information generated by an exception is the stack trace of the root exception. The stack trace indicates where the exception was thrown definitively. In some cases, you will even get an exact line number. Ideally, you should see the stack trace of the root exception <a name="558"></a><a name="IDX-234"></a>combined with more descriptive context information. To accomplish this, I've incorporated two exceptions that are smart enough to record information from the root exception in CementJ: <span class="fixed">ApplicationException</span> and <span class="fixed">ApplicationRuntimeException</span> from the <span class="fixed">org.cementj.base</span> package.</p>
<p class="para">An important component of your exception-handling strategy should be a "root" exception that is the parent of all non-JDK-related exceptions generated by your application. <span class="fixed">ApplicationRuntimeException</span> or <span class="fixed">ApplicationException</span> would be wise choices.</p>
<p class="para">You also need to enforce your exception-handling strategy. I use group code reviews as a mechanism for enforcement and education. Code reviews, if conducted properly, are constructive. Developers can learn a lot by reviewing the code of other developers. Reviews also make it more difficult to skirt established policy, such as an exception-handling strategy. Additionally, code reviews allow you to identify any shortcomings in the exception-handling strategy and make adjustments, if necessary.</p>
<div class="section">
<h3 class="sect3-title">
<a name="559"></a><a name="ch17lev2sec2"></a>Sample Strategy</h3>
<ul class="itemizedlist">
<li class="first-listitem">
<p class="first-para">Use <span class="fixed">IllegalArgumentException</span> to flag all erroneous method arguments on public methods.</p>
</li>
<li class="listitem">
<p class="first-para">Always include enough information in the message of the exception to duplicate the condition in a testing environment.</p>
</li>
<li class="listitem">
<p class="first-para">All application exceptions should extend <span class="fixed">ApplicationRuntimeException</span> (from <span class="fixed">org.cementj.base</span>). New application exception proposals should be reviewed by the technical architect.</p>
</li>
<li class="listitem">
<p class="first-para">All try/catch blocks in the business logic layer or the data access layer should not interfere with the throwing of an <span class="fixed">ApplicationRuntimeException</span>. Throw the exception to the caller instead.</p>
</li>
</ul>
</div>
</div>
</div><br>
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr><td><div STYLE="MARGIN-LEFT: 0.15in;"><a href="toc.html"><img src="images/teamlib.gif" width="62" height="15" border="0" align="absmiddle" alt="Team LiB"></a></div></td>
<td align="right"><div STYLE="MARGIN-LEFT: 0.15in;">
<a href="LiB0111.html"><img src="images/previous.gif" width="62" height="15" border="0" align="absmiddle" alt="Previous Section"></a>
<a href="LiB0113.html"><img src="images/next.gif" width="41" height="15" border="0" align="absmiddle" alt="Next Section"></a>
</div></td></tr></table>
</body></html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -