Item 37: Optimize judiciouslyThere are three aphorisms concerning optimization that everyone should know. They are perhaps beginning to suffer from overexposure, but in case you aren't yet familiar with them, here they are:
All of these aphorisms predate the Java programming language by two decades. They tell a deep truth about optimization: It is easy to do more harm than good, especially if you optimize prematurely. In the process, you may produce software that is neither fast nor correct and cannot easily be fixed. Don't sacrifice sound architectural principles for performance. Strive to write good programs rather than fast ones. If a good program is not fast enough, its architecture will allow it to be optimized. Good programs embody the principle of information hiding: Where possible, they localize design decisions within individual modules, so individual decisions can be changed without affecting the remainder of the system (Item 12). This does not mean that you can ignore performance concerns until your program is complete. Implementation problems can be fixed by later optimization, but pervasive architectural flaws that limit performance can be nearly impossible to fix without rewriting the system. Changing a fundamental facet of your design after the fact can result in an ill-structured system that is difficult to maintain and evolve. Therefore you should think about performance during the design process. Strive to avoid design decisions that limit performance.� Consider the performance consequences of your API design decisions.� The effects of API design on performance are very real. Consider the getSize method in the java.awt.Component class. The decision that this performance-critical method was to return a Dimension instance, coupled with the decision that Dimension instances are mutable, forces any implementation of this method to allocate a new Dimension instance on every invocation. Even though, as of release 1.3, allocating small objects is relatively inexpensive, allocating millions of objects needlessly can do real harm to performance. In this case, several alternatives existed. Ideally, Dimension should have been immutable (Item 13); alternatively, the getSize method could have been replaced by two methods returning the individual primitive components of a Dimension object. In fact, two such methods were added to the Component API in the 1.2 release for performance reasons. Preexisting client code, however, still uses the getSize method and still suffers the performance consequences of the original API design decisions. Luckily, it is generally the case that good API design is consistent with good performance. It is a very bad idea to warp an API to achieve good performance. The performance issue that caused you to warp the API may go away in a future release of the platform or other underlying software, but the warped API and the support headaches that it causes will be with you for life. Once you've carefully designed your program and produced a clear, concise, and well-structured implementation, then it may be time to consider optimization, assuming you're not already satisfied with the performance of the program. Recall that Jackson's two rules of optimization were "Don't do it," and "(for experts only). Don't do it yet." He could have added one more: Measure performance before and after each attempted optimization. You may be surprised by what you find. Often attempted optimizations have no measurable effect on performance; sometimes they make it worse. The main reason is that it's difficult to guess where your program is spending its time. The part of the program that you think is slow may not be at fault, in which case you'd be wasting your time trying to optimize it. Common wisdom reveals that programs spend 80 percent of their time in 20 percent of their code. Profiling tools can help you decide where to focus your optimization efforts. Such tools give you run-time information such as roughly how much time each method is consuming and how many times it is invoked. In addition to focusing your tuning efforts, this can alert you to the need for algorithmic changes. If a quadratic (or worse) algorithm lurks inside your program, no amount of tuning will fix the problem. You must replace the algorithm with one that's more efficient. The more code in the system, the more important it is to use a profiler. It's like looking for a needle in a haystack: The bigger the haystack, the more useful it is to have a metal detector. The Java 2 SDK comes with a simple profiler, and several more sophisticated profiling tools are available commercially. The need to measure the effects of optimization is even greater on the Java platform than on more traditional platforms, as the Java programming language does not have a strong performance model. The relative costs of the various primitive operations are not well defined. The "semantic gap" between what the programmer writes and what the CPU executes is far greater than in traditional compiled languages which makes it very difficult to reliably predict the performance consequences of any optimization. There are plenty of performance myths floating around that turn out to be half-truths or outright lies. Not only is the performance model ill-defined, but it varies from JVM implementation to JVM implementation and from release to release. If you will be running your program on multiple JVM implementations, it is important that you measure the effects of your optimization on each. Occasionally you may be forced to make trade-offs between performance on different JVM implementations. To summarize, do not strive to write fast programs-strive to write good ones; speed will follow. Do think about performance issues while you're designing systems and especially while you're designing APIs, wire-level protocols, and persistent data formats. When you've finished building the system, measure its performance. If it's fast enough, you're done. If not, locate the source of the problems with the aid of a profiler, and go to work optimizing the relevant parts of the system. The first step is to examine your choice of algorithms: No amount of low-level optimization can make up for a poor choice of algorithm. Repeat this process as necessary, measuring the performance after every change, until you're satisfied.
|
Thursday, October 15, 2009
Item 37: Optimize judiciously
Revisiting the Spinner
In the setProperties method of public void setProperties(UIComponent component) {Table Note
The Sample ApplicationFigure Figure 9-8. Directory structure of |
1. package com.corejsf.util; |
Listing 9-13.
spinner2/src/java/com/corejsf/SpinnerTag.java
1. package com.corejsf; |
Listing 9-14.
spinner2/web/WEB-INF/faces-config.xml
1. <?xml version="1.0"?> |
javax.faces.component.EditableValueHolder
|
javax.faces.component.ActionSource
|
javax.faces.component.ActionSource2 JSF 1.2
|
javax.faces.event.MethodExpressionValueChangeListener
JSF 1.2
|
javax.faces.validator.MethodExpressionValidator
JSF 1.2
|
javax.faces.event.MethodExpressionActionListener
JSF 1.2
|
javax.faces.event.ValueChangeEvent
|
javax.faces.component.ValueHolder
|
javax.faces.component.UIComponent
|
javax.faces.context.FacesContext
|
javax.el.ValueExpression JSF 1.2
|
javax.faces.application.Application
|
8.4 Determining a Formatting Locale
| |
8.4 Determining a Formatting LocaleSo far in this chapter, all of the code examples have used <fmt:setLocale> to specify the locale used by the <fmt:formatNumber>, <fmt:parseNumber>, <fmt:formatDate>, and <fmt:parseDate> actions. But in practice, it's usually not necessary to use <fmt:setLocale> to establish a formatting locale because the formatting actions perform a rather elaborate search for a locale. This section discusses that search. Before we discuss the search for a formatting locale, you must understand the concept of a localization context. You can read about localization contexts in "Localization Context Lookup" on page 268, but in a nutshell, a localization context is a simple JavaBeans component (bean) that maintains a resource bundle and a locale. For our purposes in this chapter, the resource bundle is immaterial, but the locale stored in a localization context is often used by formatting actions. The search that formatting actions perform to locate a formatting locale proceeds as follows:
Let's discuss each of the preceding steps in more detail. 1 An Enclosing <fmt:bundle> ActionFormatting actions that are nested in a <fmt:bundle> action use the locale stored in that <fmt:bundle> action's localization context; for example:
In the preceding code fragment, the <fmt:bundle> action tries to locate a resource bundle whose base name is messages; if it finds that resource bundle, it creates a localization context and stores the resource bundle and the locale that was used to locate that resource bundle in its localization context.[17] All of the <fmt:message> actions and all of the formatting actions nested in that <fmt:bundle> action use the same localization context; for example, in the preceding code fragment, the <fmt:message> action uses the resource bundle stored in the <fmt:bundle> action's localization context, and the <fmt:formatNumber> action uses the locale stored in the <fmt:bundle> action's localization context.
2 The FMT_LOCALIZATION_CONTEXT Configuration SettingIf a formatting action is not nested in a <fmt:bundle> action and the FMT_LOCALIZATION_CONTEXT configuration setting has been set, that formatting action uses the locale stored in the FMT_LOCALIZATION_CONTEXT configuration setting's localization context; for example:
In the preceding code fragment, the FMT_LOCALIZATION_CONTEXT configuration setting is set by the <fmt:setBundle> action.[18] The locale stored in the FMT_LOCALIZATION_CONTEXT configuration setting is used by the <fmt:formatNumber> action.
If the localization context stored in the FMT_LOCALIZATION_CONTEXT configuration setting does not have a locale, formatting actions that do not reside in the body of a <fmt:bundle> action perform a formatting locale lookup, which is described in the next step. 3 Formatting Locale LookupIf a formatting action is not nested in a <fmt:bundle> action and the FMT_LOCALIZATION_CONTEXT configuration setting has not been set, that formatting action performs a formatting locale lookup; for example:
In the preceding code fragment, the <fmt:formatNumber> action is not nested in a <fmt:bundle> action. If the FMT_LOCALIZATION_CONTEXT configuration setting has not been set, that <fmt:formatNumber> action performs a formatting locale lookup, which is discussed in the next section. Formatting Locale LookupFormatting actions that are not nested in a <fmt:bundle> action perform a formatting locale lookup if the FMT_LOCALIZATION_CONTEXT configuration setting has not been set or if that configuration setting does not contain a locale. The formatting locale lookup tries to find an appropriate locale among a set of available locales. For <fmt:formatNumber> and <fmt:parseNumber>, the available locales are determined by a call to the getAvailableLocales method from java.text.NumberFormat. For <fmt:formatDate> and <fmt:parseDate>, the available locales are determined by a call to the getAvailableLocales method from java.text.DateFormat. The formatting locale lookup proceeds as follows:
A preferred locale (or the fallback locale) matches an available locale if:
|
| |
Section 7.4. Processing Forms
7.4. Processing FormsIt's easy to process forms 7.4.1. MethodsAs we already discussed, there are two HTTP methods that a client can use to pass form data to the server: GET and POST. The method that a particular form uses is specified with the method attribute to the form tag. In theory methods are case-insensitive in the HTML, but in practice some broken browsers require the method name to be in all uppercase. A GET request encodes the form parameters in the URL in what is called a query string:
A POST request passes the form parameters in the body of the HTTP request, leaving the URL untouched. The most visible difference between GET and POST is the URL line. Because all of a form's parameters The biggest difference between GET and POST requests, however, is far more subtle. The HTTP specification says that GET requests POST requests are not idempotent. This means that they cannot be cached, and the server is recontacted every time the page is displayed. You've probably seen your web browser prompt you with "Repost form data?" before displaying or reloading certain pages. This makes POST requests the appropriate choice for queries whose response pages may change over timefor example, displaying the contents of a shopping cart or the current messages in a bulletin board. That said, idempotence is often ignored in the real world. Browser caches are generally so poorly implemented, and the Reload button is so easy to hit, that programmers tend to use GET and POST simply based on whether they want the query parameters shown in the URL or not. What you need to remember is that GET requests should not be used for any actions that cause a change in the server, such as placing an order or updating a database. The type of method that was used to request a PHP page is available through $_SERVER['REQUEST_METHOD']. For example:
7.4.2. ParametersUse the $_POST, $_GET, and $_FILES arrays to access form parameters from your PHP code. The keys are the parameter names, and the values are the values of those parameters. Because periods are legal in HTML field names but not in PHP variable names, periods in field names are converted to underscores (_) in the array. Example 7-1 shows an HTML form that chunkifies a string supplied by the user. The form contains two fields: one for the string (parameter name "word") and one for the size of chunks to produce (parameter name "number"). Example 7-1. The chunkify form (chunkify.html)
Example 7-2 lists the PHP script, chunkify.php, to which the form in Example 7-1 submits. The script copies the parameter values into variables and uses them. Although the register_globals option in php.ini would automatically create variables from the parameter values, we don't use it because it complicates writing secure PHP programs. Example 7-2. The chunkify script (chunkify.php)
Figure 7-1 shows the both the chunkify form and the resulting output. 7.4.3. Automatic Quoting of ParametersPHP ships with the magic_quotes_gpc option enabled in php.ini. This option instructs PHP to automatically call addslashes( ) on all cookie data and GET and POST parameters. This makes it easy to use form parameters in database queries, as we'll see in Chapter 8, but can cause trouble with form parameters not used in database queries, because all single quotes, double quotes, backslashes, and NUL-bytes are escaped with backslash characters. Figure 7-1. The chunkify form and its outputFor instance, if you enter the word "O'Reilly" in the form in Figure 7-1 and hit the Chunkify button, you'll see that the word that's actually chunked is "O\'Reilly." That's magic_quotes_gpc at work. To work with the strings as typed by the user, you can either disable magic_quotes_gpc in php.ini or use the stripslashes( ) function on the values in $_GET, $_POST, and $_COOKIES. The correct way to work with a string is as follows:
If you plan to work with lots of string values, it's wise to define a function to handle this for you:
You call the function like this:
7.4.4. Self-Processing PagesOne PHP page can be used to both generate a form and process it. If the page shown in Example 7-3 is requested with the GET method, it prints a form that accepts a Fahrenheit temperature. If called with the POST method, however, the page calculates and displays the corresponding Celsius temperature. Example 7-3. A self-processing temperature-conversion page (temp.php)
Figure 7-2 shows the temperature-conversion page and the resulting output. Figure 7-2. The temperature-conversion page and its outputAnother way for a script to decide whether to display a form or process it is to see whether or not one of the parameters has been supplied. This lets you write a self-processing page that uses the GET method to submit values. Example 7-4 shows a new version of the temperature-conversion page that submits parameters using a GET request. This page uses the presence or absence of parameters to determine what to do. Example 7-4. Temperature conversion using the GET method
In Example 7-4, we copy the form parameter value into $fahr. If we weren't given that parameter, $fahr contains NULL, so we can use is_null( ) to test whether we should display the form or process the form data. 7.4.5. Sticky FormsMany web sites use a technique known as sticky This sticky behavior is easy to implement. Example 7-5 shows our temperature-conversion script from Example 7-4, with the form made sticky. The basic technique is to use the submitted form value as the default value when creating the HTML field. Example 7-5. Temperature conversion with a sticky form
7.4.6. Multivalued ParametersHTML selection lists, created with the select tag, can allow multiple selections. To ensure that PHP recognizes the multiple values that the browser passes to a form-processing script, you need to make the name of the field in the HTML form end with []. For example:
Now, when the user submits the form, $_GET['languages'] contains an array instead of a simple string. This array contains the values that were selected by the user. Example 7-6 illustrates multiple selection. The form provides the user with a set of personality attributes. When the user submits the form, he gets a (not very interesting) description of his personality. Example 7-6. Multiple selection values with a select box
In Example 7-6, the submit button has a name, "s". We check for the presence of this parameter value to see whether we have to produce a personality description. Figure 7-3 shows the multiple selection page and the resulting output. Figure 7-3. Multiple selection and its outputThe same technique applies for any form field where multiple values can be returned. Example 7-7 shows a revised version of our personality form that is rewritten to use checkboxes instead of a select box. Notice that only the HTML has changedthe code to process the form doesn't need to know whether the multiple values came from checkboxes or a select box. Example 7-7. Multiple selection values in checkboxes
7.4.7. Sticky Multivalued ParametersSo now you're wondering, can I make multiple selection form elements sticky? You can, but it isn't easy. You'll need to check to see whether each possible value in the form was one of the submitted values. For example:
You could use this technique for each checkbox, but that's repetitive and error-prone. At this point, it's easier to write a function to generate the HTML for the possible values and work from a copy of the submitted parameters. Example 7-8 shows a new version of the multiple selection checkboxes, with the form made sticky. Although this form looks just like the one in Example 7-7, behind the scenes there are substantial changes to the way the form is generated. Example 7-8. Sticky multivalued checkboxes
The heart of this code is the make_checkboxes( ) subroutine. It takes three arguments: the name for the group of checkboxes, the array of on-by-default values, and the array mapping values to descriptions. The list of options for the checkboxes is in the $personality_attributes array. 7.4.8. File UploadsTo handle file uploads The following code displays a form that allows file uploads to the same page:
The biggest problem with file uploads is the risk of getting a file that is too large to process. PHP has two ways of preventing this: a hard limit and a soft limit. The upload_max_filesize option in php.ini gives a hard upper limit on the size of uploaded files (it is set to 2 MB by default). If your form submits a parameter called MAX_FILE_SIZE before any file field parameters, PHP uses that value as the soft upper limit. For instance, in the previous example, the upper limit is set to 10 KB. PHP ignores attempts to set MAX_FILE_SIZE to a value larger than upload_max_filesize. Each element in $_FILES is itself an array, giving information about the uploaded file. The keys are:
The correct way to test whether a file was successfully uploaded is to use the function is_uploaded_file( ), as follows:
Files are stored in the server's default temporary files directory, which is specified in php.ini with the upload_tmp_dir option. To move a file, use the move_uploaded_file( ) function:
The call to move_uploaded_file( ) automatically checks whether it was an uploaded file. When a script finishes, any files uploaded to that script are deleted from the temporary directory. 7.4.9. Form ValidationWhen you allow users to input data, you typically need to validate that data before using it or storing it for later use. There are several strategies available for validating data. The first is JavaScript on the client side. However, since the user can choose to turn JavaScript off, or may even be using a browser that doesn't support it, this cannot be the only validation A more secure choice is to use PHP to do the validation. Example 7-9 shows a self-processing page with a form. The page allows the user to input a media item; three of the form elementsthe name, media type, and filenameare required. If the user neglects to give a value to any of them, the page is presented anew with a message detailing what's wrong. Any form fields the user already filled out are set to the values she entered. Finally, as an additional clue to the user, the text of the submit button changes from "Create" to "Continue" when the user is correcting the form. Example 7-9. Form validation
In this case, the validation is simply a check that a value was supplied. We set $validated to be true only if $name, $type, and $filename are all nonempty. Other possible validations include checking that an email address is valid or checking that the supplied filename is local and exists. For example, to validate an age field to ensure that it contains a nonnegative integer, use this code:
The call to strspn( ) finds the number of digits at the start of the string. In a nonnegative integer, the whole string should be composed of digits, so it's a valid age if the entire string is made of digits. We could also have done this check with a regular expression:
Validating email addresses is a nigh-impossible task. There's no way to take a string and see whether it corresponds to a valid email address. However, you can catch typos by requiring the user to enter the email address twice (into two different fields). You can also prevent people from entering email addresses like "me" or "me@aol" by requiring an at sign (@) and a period after it, and for bonus points you can check for domains to which you don't want to send mail (e.g., whitehouse.gov, or a competitor). For example:
Field validation is basically string manipulation. In this example, we've used regular expressions and string functions to ensure that the string provided by the user is the type of string we expect. |
Wednesday, October 14, 2009
Selecting R'EPOSITORIES'
| [ Team LiB ] |
Selecting REPOSITORIESThere are five ENTITIES in the design that are roots of AGGREGATES, so we can limit our consideration to these, since none of the other objects is allowed to have REPOSITORIES. To decide which of these candidates should actually have a REPOSITORY, we must go back to the application requirements. In order to take a booking through the Booking Application, the user needs to select the Customer(s) playing the various roles (shipper, receiver, and so on). So we need a Customer Repository. We also need to find a Location to specify as the destination for the Cargo, so we create a Location Repository. The Activity Logging Application needs to allow the user to look up the Carrier Movement that a Cargo is being loaded onto, so we need a Carrier Movement Repository. This user must also tell the system which Cargo has been loaded, so we need a Cargo Repository. Figure 7.4. REPOSITORIES give access to selected AGGREGATE roots.For now there is no Handling Event Repository, because we decided to implement the association with Delivery History as a collection in the first iteration, and we have no application requirement to find out what has been loaded onto a Carrier Movement. Either of these reasons could change; if they did, then we would add a REPOSITORY. |
| [ Team LiB ] |
6.6. Hyperlinks
< Day Day Up > |
6.6. HyperlinksDid you know there is a web browser built into the Java editor? Well, there is�sort of. The editor lets you navigate around your program as if it were a web site. Hold down the Ctrl key and move your mouse through your source code. An underline will appear to indicate hyperlinked symbols. You can leave the mouse cursor over the symbol to see its definition, or click on it to open the declaration in the editor. Like a browser, Eclipse maintains a history of all the pages you've visited. Use the Back command ( ; Alt+Left; or Navigate Left) to go to the previous location, and use Forward ( ; Alt+Right; or Navigate Right) to go to the next one. |
< Day Day Up > |