<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>DesignCaffeine &#187; Usability</title>
	<atom:link href="http://www.designcaffeine.com/tag/usability/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.designcaffeine.com</link>
	<description>UX Design of Intuitive &#38; Resourceful Systems</description>
	<lastBuildDate>Sat, 05 Jun 2010 01:49:03 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Designing Mobile Search: Turning Limitations into Opportunities</title>
		<link>http://www.designcaffeine.com/2010/articles/337/</link>
		<comments>http://www.designcaffeine.com/2010/articles/337/#comments</comments>
		<pubDate>Tue, 09 Mar 2010 05:46:32 +0000</pubDate>
		<dc:creator>Greg Nudelman</dc:creator>
				<category><![CDATA[Articles]]></category>
		<category><![CDATA[Finding]]></category>
		<category><![CDATA[iPhone]]></category>
		<category><![CDATA[iPhone App]]></category>
		<category><![CDATA[Mobile]]></category>
		<category><![CDATA[Search Matters]]></category>
		<category><![CDATA[Search Results]]></category>
		<category><![CDATA[ThirstyPocket]]></category>
		<category><![CDATA[Usability]]></category>
		<category><![CDATA[UX Design]]></category>

		<guid isPermaLink="false">http://www.designcaffeine.com/?p=337</guid>
		<description><![CDATA[Originally Published on UXmatters.com March 8, 2010 ⇒
Thinking of porting your Web finding experience to iPhone, Android, or Windows 	Mobile? Just forget about the fact that these devices are basically full-featured 	computers with tiny screens. Having gone through this  design exercise 	a few times, I have realized that designing a great mobile finding experience [...]]]></description>
			<content:encoded><![CDATA[<p><a title="Read this article on UXmatters (Opens in a New Window)" href="http://www.uxmatters.com/mt/archives/2010/03/designing-mobile-search-turning-limitations-into-opportunities.php" target="_blank">Originally Published on UXmatters.com March 8, 2010 ⇒</a></p>
<p>Thinking of porting your Web finding experience to iPhone, Android, or Windows 	Mobile? Just forget about the fact that these devices are basically full-featured 	computers with tiny screens. Having gone through this  design exercise 	a few times, I have realized that designing a great mobile finding experience 	requires a way of thinking that is quite different from our typical approach 	to designing search for Web or desktop applications. To put it simply, designing 	a mobile finding experience requires thinking in terms of turning limitations 	into opportunities. In this column, I’ll discuss some of the limitations 	of mobile platforms, as well as the opportunities they afford, and share 	a few design ideas that might come in handy for your own projects.<br />
<span id="more-337"></span></p>
<h2>Understanding Mobile Platforms</h2>
<p>One of the challenges of mobile application design is understanding  both the 	capabilities <em>and</em> limitations of each platform. Let’s use the 	iPhone finding experience as an example. On the plus side, the iPhone  has 	a high-resolution screen, Multi-Touch controls, accelerometer,  persistent 	data storage, cool video transitions, push content delivery, GPS, and a  device 	ID. The benefits of these features have been pretty much beaten to  death 	in advertisements, so I will <em>not</em> discuss them here. On the  other hand, 	the problem constraints and limitations of mobile devices are much more  interesting. I have found few sources that discuss these in detail, so  in this column, 	I’ll attempt to describe the most important challenges of designing for  the new generation of smartphones—at least as they pertain to finding:</p>
<ul>
<li>the difficulty of typing</li>
<li>the small amount of screen real estate</li>
<li>awkward touch controls</li>
<li>the so-called <em>fat-finger problem</em></li>
</ul>
<h3>The Difficulty of Typing</h3>
<p>Searching requires users to type a search string, and typing on a  mobile 	phone is  difficult—partly because of the size of the device. In a 	July 2009 blog post on <em>Alertbox</em>, Jakob Nielsen called the  mobile experience “miserable” 	and reported, “Text entry is particularly slow and littered with typos, 	even on devices with dedicated mini-keyboards.”</p>
<p>For many users, touch 	devices like the iPhone exacerbate this problem. Their 	screens display a virtual keyboard and other buttons. One issue with  touch 	devices   is poor visibility, because a user’s fingers 	must, by necessity, cover buttons on the screen when a user is pushing  them. 	Thus, when pushing a button on a virtual keyboard, a user’s 	hand obscures his view of the keyboard.</p>
<p>The lack of tactile response on touch 	devices presents a particular problem when a user is  multitasking—whether 	riding in taxi, using public transportation, or walking down a city  street. 	On <em>any</em> mobile device, it’s very difficult to type when a  person is crammed 	into a bus or subway car and being jostled around. A user’s desire to 	avoid typing becomes a necessity when using a mobile device while 	driving.</p>
<p>Another challenge users encounter when searching on  smartphones is that they’re 	likely to lose anything they’ve laboriously typed into a search box if a  device 	receives an incoming phone call, because mobile applications cannot  block phone 	functions. Nor should an application, however useful, be able to  prevent a user 	from receiving incoming calls.</p>
<h3>The Small Amount of Screen Real Estate</h3>
<p>Mobile screens are, by necessity, small, because a mobile device has  to fit 	into a person’s pocket or purse. The small size of mobile screens  limits 	the number of controls and the amount of content that can appear on  them. 	In that <em>Alertbox</em> post 	I mentioned earlier, Jakob Nielsen reported, “Unsurprisingly, the  bigger 	the screen, the better the user experience.” According to Nielsen,  users’ success 	rates with touch phones like the iPhone are about double their success  rates 	with feature phones.</p>
<h3>Awkward Touch Controls</h3>
<p>One of the consequences of mobile devices’ having smaller screens and 	controls that users must manipulate through touch interfaces is that  some 	controls no longer look like their Web and desktop counterparts. For  example, 	rather than the usual drop-down list or set of option buttons, the  selection 	control on an iPhone is instead a spinning control called a <em>picker</em>, 	shown in Figure 1.</p>
<p>Figure 1—Three pickers in the Urban  Spoon iPhone app</p>
<p><img src="http://www.uxmatters.com/mt/archives/2010/03/images/figure_1_urbanspoon.jpg" alt="Pickers in the Urban Spoon iPhone app" width="320" height="460" /></p>
<p>A picker that a user can quickly manipulate with a  finger eats up precious 	real estate, so it’s not possible to show users much outside the  picker. Large 	pickers also place limitations on how users can filter search results  on mobile 	devices. Similar size restrictions exist for <em>all</em> of an  application’s touch controls.</p>
<h3>The Fat-Finger Problem</h3>
<p>The high-resolution screens on better mobile devices—like the iPhone,  Android, 	and Palm Pre—can accommodate fairly high information density.  Unfortunately, 	at the same time, touchscreens <em>limit</em> the on-screen density of  controls that 	users can accurately manipulate with a finger. Thus, placing multiple  controls 	close together on a touchscreen mobile device presents difficulties.  This challenge 	is known as the <em>fat-finger problem</em>.</p>
<p>Typically, an iPhone touchscreen can comfortably  support a maximum 	 of only three to five clickable buttons or tabs across a screen. More  than 	this leads to frequent frustrations due to users’ inadvertently  pressing 	the wrong control. While five controls across a screen is the absolute  limit 	for relatively frustration-free mobile computing for people with  relatively 	small fingers, I highly recommend a limit of four or fewer controls. Of  course, 	if one control is bigger than the others, you’ll 	have to reduce the overall number of controls accordingly. For example,  Figure 	2 shows the Yelp mobile application, which has only three controls  across 	the navigation bar at top of the screen, but four controls across tab  bar 	at the bottom.</p>
<p>Figure 2—Yelp iPhone app</p>
<p><img src="http://www.uxmatters.com/mt/archives/2010/03/images/figure_2_yelp.png" alt="Yelp iPhone app" width="327" height="490" /></p>
<p>Because Yelp’s search box takes up a large part of the  navigation 	bar, the designers had to reduce the overall number of controls to  three 	to ensure users’ wouldn’t 	press the wrong controls with their fingers too often.</p>
<h2>Understanding User Experience within a Mobile Context of Use</h2>
<p>The challenge in designing mobile applications is the need to  accommodate 	the design constraints and usability challenges mobile devices impose,  while 	focusing on users’ goals within a mobile context of use. Simply 	duplicating the functionality of a Web application—while trying to work 	around the mobile design challenges I’ve described—<em>always</em> results 	in a subpar mobile application. It’s not enough to think: <em>How 	can I duplicate our Web application’s user experience within the  limitations 	of the mobile platform?</em> Instead, it’s better to start from  scratch, focusing 	on <em>What experience would work best for mobile users?</em> Putting  users’ goals 	first allows a design team to concentrate on the new opportunities a  mobile 	application presents rather than seeing the challenges of mobile simply  as 	barriers to implementing a Web application’s existing functionality.</p>
<p>Next, I’ll present some ideas about how to approach the 	design of finding experiences for mobile devices in a way that lends  itself 	to taking full advantage of their capabilities within a 	mobile context of use. These ideas by no means represent an exhaustive  catalog 	of all possibilities. I merely want to provide a few examples that may  inspire 	further exploration as part of your own finding projects.</p>
<h3>Preloading Pertinent Search Results</h3>
<p>As I discussed earlier, typing on mobile devices is difficult. Plus, a  phone 	call, a text message, or an opportunity to take a picture is likely to  interrupt 	a user’s finding experience at least once. Saving a user’s previous 	searches is an obvious and simple way of re-engaging users in a finding  task 	and provides useful context when an application first opens.</p>
<p>Unlike Web applications, 	native mobile applications are persistent, so it’s easy to cache their 	search results. Cached results load quickly, users’ re-engagement is  immediate, 	and there is little to compete for a user’s attention—that is, at 	least until another phone call comes in. Some mobile device APIs even  enable 	native applications to detect whether a phone call interrupted a user’s 	previous session or the user exited an application normally and  determine 	how much time has elapsed since a user last opened the application.  These 	capabilities present interesting possibilities for fine-tuning an  application’s 	welcome-back screen to re-immerse users in their previous tasks or  offer 	pertinent new content and present new possibilities for interaction.</p>
<h3>Providing Local Results</h3>
<p>On mobile devices that support GPS, Wi-Fi, or other location-tracking  mechanisms, 	you can determine the current location of  another person who is using a 	mobile device, allowing applications to offer location-aware services.  Mobile 	applications with search capabilities can serve highly relevant, fresh  results 	that perfectly match a user’s 	current mobile context. For example, Loopt is just one of a whole cadre  of 	social networking mobile applications that allow users to track the  locations 	of their friends who are currently nearby and exchange messages with  them, 	get coupons from local merchants, and discover neighborhood happenings, 	as Figure 3 shows.</p>
<p>Figure 3—Local search results in the 	Loopt iPhone app</p>
<p><img src="http://www.uxmatters.com/mt/archives/2010/03/images/figure_3_loopt2.png" alt="Local search results in Loopt iPhone app" width="320" height="977" /></p>
<h3>Offering a Value-Added Interpretation of the Real World</h3>
<p>Using mobile devices for sense-making in the real world offers one of  the 	most intriguing possibilities for mobile applications. Luke Wroblewski  described 	some interesting possibilities, including augmented reality  applications, 	in his <em>Smashing Magazine</em> article “Enhancing User Interaction 	with First Person User Interface.”</p>
<p>When it comes to finding, however, 	the Amazon Mobile iPhone app offers the <strong>Amazon Remembers</strong> feature, which is 	a forerunner of many exciting things to come. Amazon Mobile lets  customers 	take pictures of any real world items and add them to their lists of  things 	to remember. Once a user uploads a photo, Amazon figures out what the  item 	is and, if there is a corresponding item available for sale on Amazon,  displays 	that item and sends the customer an email alert, encouraging her to  purchase 	the item. Customers often get a response in a matter of minutes, but  the 	search can take up to 24 hours. This application lays a solid  foundation for 	the idea of a mobile Internet of objects that I described in one of my  previous 	columns, “<a title="Brave New World of Visual Browsing" href="http://www.uxmatters.com/mt/archives/2009/08/brave-new-world-of-visual-browsing.php">Brave 	New World of Visual Browsing</a>.”</p>
<h3>Providing Various Sorting Options</h3>
<p>As I previously mentioned in my column “<a title="The Mystery of Filtering by Sorting" href="http://www.uxmatters.com/mt/archives/2009/07/the-mystery-of-filtering-by-sorting.php">The 		Mystery of Filtering by Sorting</a>,” 	sorting options are an excellent way of opening up an ecommerce site’s 	inventory for browsing. One of the best ways of doing this is to  provide 	two or more buttons on a search results screen that allow multiple ways  of 	dissecting an inventory, without ever failing to serve <em>some</em> results. 	By using sorting options together with geolocation, customers can even  avoid 	having to type in 	<em>any</em> query at all. As Figure 4 shows, the ThirstyPocket iPhone  app lets 	customers simply press the <strong>Search Nearest</strong> or <strong>Search  	Newest</strong> button to see 	a sample of local results without having to type anything in the search  box.</p>
<p>Figure 4—ThirstyPocket iPhone app</p>
<p><img src="http://www.uxmatters.com/mt/archives/2010/03/images/figure_4_thirstyPocket.jpg" alt="ThirstyPocket iPhone app" width="320" height="480" /></p>
<p>Of course, a customer can always type keywords in a  search box 	to narrow down the results. As I explained in my <a title="presentation" href="http://www.slideshare.net/gnudelman/experience-design-for-a-viral-mobile-community">presentation</a><a title="presentation" href="http://www.slideshare.net/gnudelman/experience-design-for-a-viral-mobile-community"><img src="http://www.uxmatters.com/images/new-window-arrow.gif" alt="" width="14" height="12" /></a> at 	the Net Squared Conference in May 2009, using this design pattern lets  customers 	engage with an inventory of items or content immediately, then invest  the 	effort of typing in keywords once they have caught the scent of  something 	that interests them or to refine the search results further. Of course,  given 	the fat-finger problem and a mobile device’s limited screen real 	estate, we can’t provide more 	than three to five sort options on the screen at one time. However, as  the 	ThirstyPocket example shows, even a couple of sort options is often  enough 	for customers to begin exploring.</p>
<h3>Considering Custom Controls</h3>
<p>One consequence of mobile devices’ having a smaller screen is <em>not</em> having 	the space to create a navigation bar of filters, facets, or categories  on 	the left, providing a key to the properties by which a user can  effectively 	narrow down a result set. Consider, for example, the all-important  category 	filter. If an application were to use the standard iPhone picker, shown  in 	Figure 1, it would obscure most of the search results on the screen.  Various 	applications deal with this challenge in different ways. Some mobile  applications 	have created custom category-filter controls—like those in Amazon  Mobile, 	shown in Figure 5.</p>
<p>Figure 5—Custom category-filter  controls in Amazon Mobile</p>
<p><img src="http://www.uxmatters.com/mt/archives/2010/03/images/figure_5_amazon_categories2.png" alt="Amazon Mobile category filters" width="474" height="340" /></p>
<p>Clicking <strong>By Category</strong> takes a customer  to a different, full-sized screen on 	which he can select a category. The ThirstyPocket application, shown in  Figure 	4, uses the same principle for its custom category selector, with an  added twist: 	the visual design of the category selector is reminiscent of the  familiar drop-down 	list, taking full advantage of customers’ existing mental model and  helping 	them understand what behavior to expect.</p>
<h2>Changing Search Paradigms</h2>
<p>Because of the unique mix of constraints and opportunities that  mobile application 	design presents, this design space is rich with possibilities for  changing 	the existing paradigms for search and finding. Consider speech  recognition, 	for example. While, on the desktop, speech recognition does not yet  enjoy 	widespread popularity and use, mobile represents an entirely different  context—where 	speech recognition can offer an ideal solution. Not interpreting a  spoken 	word correctly on a mobile device might not be quite as big a deal as  it 	is on the desktop, because the accuracy of speech recognition may  actually 	approach, if not exceed, that of typing on a mobile phone’s awkward  mini-keyboard. 	Combine speech recognition with the use of an accelerometer and  magnetometer, 	allowing gestural input, and you have the Google Mobile search  application 	for the iPhone, shown in Figures 6 and 7.</p>
<p>Figure 6—Google Mobile iPhone app</p>
<p><img src="http://www.uxmatters.com/mt/archives/2010/03/images/figure_6_google_mobile.png" alt="Google Mobile iPhone app" width="320" height="480" /></p>
<p>Figure 7—Google Mobile Voice Search  on the iPhone</p>
<p><object width="474" height="380" alt="Google Mobile Voice Search on the  iPhone"><param name="movie" value="http://www.youtube.com/v/y3z7Tw1K17A&amp;hl=en_US&amp;fs=1&amp;" /><param name="allowFullScreen" value="true" /><param name="allowscriptaccess" value="always" /><embed type="application/x-shockwave-flash" width="474" height="380" src="http://www.youtube.com/v/y3z7Tw1K17A&amp;hl=en_US&amp;fs=1&amp;" allowscriptaccess="always" allowfullscreen="true"></embed></object></p>
<p>The iPhone application Google Mobile recognizes the  		gesture of a person’s swinging the phone up to his ear to know when to 	record a search command. When the user speaks, the search engine  accepts 		and interprets his voice commands, then serves up search results. This 		user interface implements what is literally a game-changing design  paradigm, 		because its designers have taken the time to truly consider the mobile 		context of use and map natural interactions like speech and gestures 		to mobile device functions. As Peter Morville said in his book <em>Search  		Patterns</em>, “We 		simply raise our phones to our ears and speak our search, relying on 		Google Mobile to derive what we want from who we are, where we stand, 		and what we say…. Like placing your hands under a tap to turn on the 		water, this is the type of smart design that ‘dissolves in behavior’.”</p>
<p>When it comes to designing for a mobile context, we are 		just starting to scratch the surface. As Tom Chi of <em>OK/Cancel</em> famously quipped 		in his interview with Luke Wroblewski, “A well defined-and exciting  problem (and its associated constraints) 	is the catalyst that makes design go.” When we stop thinking about the 	limitations of mobile platforms and, instead, truly focus on the user  goals 	we are working to support, we might just find those limitations turning  into 	opportunities for redefining how people find, remember, and discover  things 	in their world.<a title="Top" href="http://www.uxmatters.com/mt/archives/2010/03/designing-mobile-search-turning-limitations-into-opportunities.php#top"><img src="http://www.uxmatters.com/images/ux-bug.gif" alt="" width="18" height="18" /></a></p>
<h4>References</h4>
<p>Morville, Peter. <em>Search Patterns</em>.  Sebastopol, 	CA: O’Reilly, 2010.</p>
<p>Nielsen, Jakob. “<a title="Mobile  Usability" href="http://www.useit.com/alertbox/mobile-usability.html">Mobile 		Usability</a>.”<a title="Mobile  Usability" href="http://www.useit.com/alertbox/mobile-usability.html"><img src="http://www.uxmatters.com/images/new-window-arrow.gif" alt="" width="14" height="12" /></a> <em>Alertbox</em>, July  20, 2009. 	Retrieved February 27, 2010.</p>
<p>Wroblewski, Luke. “<a title="Enhancing User Interaction With First Person User Interface" href="http://www.smashingmagazine.com/2009/09/21/enhancing-user-interaction-with-first-person-user-interface/">Enhancing  User Interaction With First Person User Interface</a>.”<a title="Enhancing User Interaction With First Person User Interface" href="http://www.smashingmagazine.com/2009/09/21/enhancing-user-interaction-with-first-person-user-interface/"><img src="http://www.uxmatters.com/images/new-window-arrow.gif" alt="" width="14" height="12" /></a> <em>Smashing Magazine</em>, September 21, 2009. Retrieved February 27,  2010.</p>
<p>Wroblewski, Luke. “<a title="Defining the  Problem: Q&amp;A with Tom Chi" href="http://www.lukew.com/ff/entry.asp?336">Defining 	the Problem: Q&amp;A with Tom Chi</a>.”<a title="Defining the  Problem: Q&amp;A with Tom Chi" href="http://www.lukew.com/ff/entry.asp?336"><img src="http://www.uxmatters.com/images/new-window-arrow.gif" alt="" width="14" height="12" /></a> <em>Functioning Form</em>,  April 27, 2006. Retrieved February 27, 2010.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.designcaffeine.com/2010/articles/337/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Numeric Filters: Issues and Best Practices</title>
		<link>http://www.designcaffeine.com/2010/articles/321/</link>
		<comments>http://www.designcaffeine.com/2010/articles/321/#comments</comments>
		<pubDate>Mon, 08 Feb 2010 18:34:44 +0000</pubDate>
		<dc:creator>Greg Nudelman</dc:creator>
				<category><![CDATA[Articles]]></category>
		<category><![CDATA[Article]]></category>
		<category><![CDATA[Filters]]></category>
		<category><![CDATA[Finding]]></category>
		<category><![CDATA[Search Matters]]></category>
		<category><![CDATA[Search Results]]></category>
		<category><![CDATA[Usability]]></category>

		<guid isPermaLink="false">http://www.designcaffeine.com/?p=321</guid>
		<description><![CDATA[Originally published on UXMatters.com February 8, 2010 ⇒
Faceted search has been around for a long time and has become the de facto 	standard for search on most ecommerce sites. However, filters with numeric values 	remain among the most confusing, because many sites have not able to design 	usable numeric filters that people can use in [...]]]></description>
			<content:encoded><![CDATA[<p><a title="Read this article on UXMatters (Opens in a New Window)" href="http://www.uxmatters.com/mt/archives/2010/02/numeric-filters-issues-and-best-practices.php" target="_blank">Originally published on UXMatters.com February 8, 2010 ⇒</a></p>
<p>Faceted search has been around for a long time and has become the de facto 	standard for search on most ecommerce sites. However, filters with numeric values 	remain among the most confusing, because many sites have <em>not</em> able to design 	usable numeric filters that people can use in an intuitive manner. Recently, 	powerful user interface controls called <em>sliders</em> have become all the rage for 	specifying numeric attributes in finding user interfaces. Unfortunately, in 	their rush to implement this latest, greatest feature, many companies have <em>not</em> designed easy-to-use sliders. Rather than solving usability problems, poorly 	designed sliders create even more issues around numeric filter usability. In 	my experience, the following three usability issues surface most often with 	numeric filters:</p>
<ul>
<li>representing discrete values for aspects as sets of ranges</li>
<li>inadvertently emphasizing overly constrained filter states</li>
<li>being parsimonious with inventory information</li>
</ul>
<p>In this column, I’ll examine each of these issues and present 	the best practices that solve these problems. <a title="Link" href="http://www.uxmatters.com/mt/archives/2010/02/numeric-filters-issues-and-best-practices.php"></a><br />
<span id="more-321"></span></p>
<h2>Representing Discrete Values for Aspects as Sets of Ranges</h2>
<p>One of the most common pitfalls with numeric aspect filters is their representing 	discrete values as sets of ranges. What makes a filter value discrete? Certain 	types of products typically have widely recognized, discrete values—for example, 	a digital camera might have a 10MP resolution and a lens with 3X zoom—which 	can often consist of either whole numbers or fractions. These discrete values 	tend to be consistent across competing brands and models and rarely change 	from one vendor to the next.</p>
<p>Many sites offering faceted search do <em>not</em> present discrete numeric values 	for aspect filters in the most useful fashion. Designers are often tempted to 	present such values to customers as sets of ranges. Presenting discrete filter 	values as a set of ranges typically results in a user interface similar to that 	in Figure 1, showing a camera-resolution filter on a major ecommerce site.</p>
<p>Figure 1—Camera-resolution filter with values presented as a set of ranges</p>
<p><img src="http://www.uxmatters.com/mt/archives/2010/02/images/Figure_1_ranges.png" alt="Set of ranges" width="188" height="182" /></p>
<p>My usability studies have shown that many people have trouble finding <em>10MP</em> within a <em>9.9MP-11.9MP</em> range and applying the filter correctly. The problem is 	a mental model mismatch. People don’t think of <em>10MP</em> as falling within some range. 	When customers look for discrete values in aspect filters, they are most often 	looking for a specific value—for example, <em>10MP</em>—that forms a critical part of 	the information scent. Most people typically think of a camera resolution as 	a specific value—<em>not</em> as a numeric value within some range.</p>
<div></div>
<p><!-- End pullquote -->When users try to look for the value <em>10MP</em> within a 	range, usability problems arise, because the resolutions <em>9.9MP</em> and <em>11.9MP</em> simply 	do <em>not</em> exist as discrete 	values. While, numerically, it is true that <em>10MP</em> lies within 	a <em>9.9MP-11.9MP</em> range, this range has very poor information scent and 	requires additional thinking and interpretation on the part of users, which 	does <em>not</em> make for intuitive and efficient finding. Having to think 	hard just to find something causes problems for users.</p>
<p>In my user research, 	I have often observed that people who are in a rush, distracted, or simply 	not that great at working with fractions in math often select the wrong range 	of discrete values, wonder <em>why</em> they are not getting the results they expected, 	get frustrated, and quickly leave a site to navigate to a more usable one.</p>
<p>Instead of providing ranges, it is much more effective to 	present lists of discrete values, showing <em>all</em> of the possible values 	for an aspect filter. If doing this results in a filter with too many options, 	simply rounding off the value to <em>around 10MP</em> works very well, as shown 	in Figure 2. (Note that the word <em>around</em> is usually implied. Although 	it sometimes shows up in a short form such as <em>~10MP</em> or <em>10+MP</em>. 	However, this extra precision is usually unnecessary, as I will explain 	in a future column.)</p>
<p>Figure 2—Recommended redesign of camera-resolution filter</p>
<p><img src="http://www.uxmatters.com/mt/archives/2010/02/images/Figure_2_recommended_mpx2.png" alt="Recommended redesign" width="474" height="216" /></p>
<p>When a customer clicks a link representing a discrete 	value, one option is to expand a list of subvalues—for example, clicking 	an <em>around 10MP</em> link would show all of the available subvalues: <strong>10.0MP</strong>, <strong>10.2MP</strong>, 	<strong>10.3MP</strong>, <strong>10.4MP</strong>, as shown in Figure 2A. Should 	you bother designing and implementing this? Usually not. Unless your customers 	are professionals who might be looking for a particular value, they typically 	would <em>not</em> drill down past the <em>around 10MP</em> link. Is there really 	that much difference between digital-camera resolutions of 10.2MP and 10.3MP 	to a typical consumer? So, unless your company’s inventory is extremely 	large or you have a very specialized clientele, it is often much more useful 	to present an approximate value like <strong>10MP</strong>.</p>
<p>Should you support single or multiple selection for displaying 		discrete filter values? The answer is always: <em>It depends</em>. Up until 		a few years ago, guidelines generally recommended the simplicity of single-selection 		links for discrete values. Today, however, I highly recommend that my 		clients implement multiple selection for discrete filter values—provided 		the finding user interface also supports multiple selection. Mark Burrell 		of Endeca echoed this sentiment in the 2010 UIE Webinar, “<a title="Leveraging Search &amp; Discovery Patterns for Great Online Experiences" href="http://www.uie.com/events/virtual_seminars/search_patterns/">Leveraging Search &amp; Discovery Patterns for Great Online Experiences</a>,”<a title="Leveraging Search &amp; Discovery Patterns for Great Online Experiences" href="http://www.uie.com/events/virtual_seminars/search_patterns/"><img src="http://www.uxmatters.com/images/new-window-arrow.gif" alt="" width="14" height="12" /></a> in 		which he emphasized that most ecommerce customers no longer find multiple 		selection hard to use and prefer the flexibility it offers.</p>
<h2>Numeric Sliders for Filters</h2>
<p>Numeric sliders have recently become the rage for faceted search. Sliders 	can add a touch of interactive fun to what many business people refer to as 	<em>a boring finding interface</em>. Note that boring user interfaces usually work extremely 	well, because they are intuitive and easy to use, and UX powerhouses like Amazon 	and Netflix do quite well without any sliders whatsoever.</p>
<p>That said, sliders <em>can</em> be helpful in some applications, because 	they give people tremendous filtering power when they are implemented correctly. 	With great power, however, comes great responsibility: Sliders deserve special 	consideration from designers—<em>precisely</em> because they make it <em>so</em> easy for your 	customers to screw up and overconstrain their queries, leading to search 	results that are either incorrect or of poor quality. This, in turn, results 	in frustrated customers who leave your Web site quickly without buying anything. 	The two issues I see most often with sliders are:</p>
<ul>
<li>inadvertently emphasizing overconstrained filter states</li>
<li>being parsimonious with inventory information</li>
</ul>
<p>Let’s take a closer look at each of these issues and what we can do about 	them.</p>
<h3>Issue: Inadvertently Emphasizing Overconstrained Filter States</h3>
<p>To examine the issue of overconstrained filter states, let’s take a look at 	an example—the slider rating control on TripAdvisor, shown in Figure 	3. Ratings seem deceptively simple, yet raise a host of usability issues 	when they are implemented incorrectly.</p>
<p>Figure 3—Slider rating control on TripAdvisor</p>
<p><img src="http://www.uxmatters.com/mt/archives/2010/02/images/Figure_3_tripadvisor_rating.png" alt="Slider rating control" width="160" height="101" /></p>
<p>TripAdvisor has implemented ratings using a <em>double</em> slider 	control, which presents a perfect example of inadvertently emphasizing overconstrained 	filter states. This double slider control allows a wide variety of ranges—some 	of which are much more useful than others when it comes to finding particular 	items or content of interest. For example, rating ranges spanning <em>only</em> a 	single star—such as <em>0-1 stars, 1-2 stars,</em> or <em>2-3 stars</em>—are 	simply <em>not</em> useful, 	because short ranges do <em>not</em> match the mental model of the people using 	the system.</p>
<p><!-- End pullquote -->Most of the time, people click a rating filter to get <em>all</em> of 	the merchandise <em>above</em> a certain rating—that is, as a way to find higher-quality 	items in their search results. However, a double slider control <em>overemphasizes</em> the ability to constrain the range from both sides, making it 	very easy to get this wrong by overconstraining a query.</p>
<p>A much more useful way of approaching this design problem is to use a single 	slider or a set of links like those shown in Figure 4, which shows ratings as 	they are currently implemented on Amazon.</p>
<p>Figure 4—Amazon’s more useful implementation of ratings as links</p>
<p><img src="http://www.uxmatters.com/mt/archives/2010/02/images/Figure_4_amazon_star_rating.png" alt="Amazon's ratings" width="200" height="117" /></p>
<p>We could further improve the rating control shown in Figure 	4. Since the goal of people using this  control is to get better-quality 	items, it is <em>not</em> actually that useful to show <em>only</em> a single star—the lowest 	possible rating. Instead, it might be more intuitive to replace the single 	star with the word <strong>Any</strong> and make that the default filter state. The filter 	shown in Figure 4 also has the important added advantage of providing vital 	inventory information. Item counts following each set of stars help people 	using the ratings filter to clearly understand the consequences of their 	actions and builds appropriate expectations. Understanding what to expect 	from their actions lets people be more efficient and effective, leading to 	higher customer satisfaction and better usability. Next, we’ll explore how we 	can use sliders to show inventory information.</p>
<h3>Issue: Being Parsimonious with Inventory Information</h3>
<p><!-- End pullquote -->Do <em>all</em> dual sliders emphasize overconstrained filter states? Not 	necessarily. For some filters dual sliders are entirely appropriate—for 	example, for price ranges. 	However, the problem  that I described earlier of overconstraining queries 	and, as a result, providing inadequate information never really goes away. 	As Figure 5 shows, the dual slider control for the price range filter on 	TripAdvisor provides no inventory information, so customers 	would have <em>no</em> idea what the effect of manipulating the sliders might 	be. Any 	action customers take might be a hit or a miss—but it is <em>never</em> clear 	in advance which it will be, because the system simply does <em>not</em> provide 	the necessary inventory information.</p>
<p>Figure 5—Dual-slider filter for price range on TripAdvisor</p>
<p><img src="http://www.uxmatters.com/mt/archives/2010/02/images/Figure_5_tripadvisor_price.png" alt="Dual-slider filter" width="160" height="106" /></p>
<p>Compare the dual slider for price range on TripAdvisor to the 	price control on Staples.com, pictured in Figure 6.</p>
<p>Figure 6—Price control on Staples.com with multiple check boxes</p>
<p><img src="http://www.uxmatters.com/mt/archives/2010/02/images/Figure_6_staples_price.png" alt="Price control" width="157" height="168" /></p>
<p>As I mentioned in one of my previous columns, “<a title="The Mystery of Filtering by Sorting" href="http://www.uxmatters.com/mt/archives/2009/07/the-mystery-of-filtering-by-sorting.php">The 		Mystery of Filtering by Sorting</a>,” overconstraining search results by price 		is one of the most common human errors we see in usability testing for 		search user interfaces. Which control would you expect to result in more 		overconstrained queries: a dual slider or a set of check boxes? The control 		that makes overconstraining results easier is the dual slider, because 		it gives <em>no</em> clue 		as to what to expect from a particular action. On the other hand, the 		dual slider provides the <em>bling</em> many business people crave as a means 		of differentiating their user interface from the competition.</p>
<p><!-- End pullquote -->In this age of highly interactive Ajax interfaces, clicking links or typing in numbers 	to specify a price range seems so old-fashioned. Is there a control that 	would provide the interactivity and fun of the slider, yet offer the inventory 	information so necessary to helping customers make informed decisions?</p>
<p>One approach would be to use a dual slider for setting a price 	range, combining it with a sparkline that graphically represents the available 	inventory information for every price in the range. Figure 7 depicts my 	suggested redesign of the TripAdvisor price filter, which combines a dual 	slider with a sparkline, showing the available inventory for each price in 	the range.</p>
<p>Figure 7—Suggested redesign of the price range filter on TripAdvisor</p>
<p><img src="http://www.uxmatters.com/mt/archives/2010/02/images/Figure_7_dual_slider_with_sparkline.png" alt="Redesigned price range filter" width="160" height="106" /></p>
<p>In his book <em>Beautiful Evidence, </em>Edward Tufte describes 	<em>sparklines</em> as “data-intense, 	design-simple, word-sized graphics.” Although I have no idea who first 	thought of combining a slider with a sparkline, I have been recommending 	this solution to my clients for over four years, and I can claim to have 	arrived at this idea independently. Unfortunately, so far, most of my fellow 	designers and the business people with whom I have worked have remained cold 	to this idea, stating that a slider with a sparkline would be “above and 		beyond what an average user could understand.” However, my own experience 	as a user researcher backs the opposite conclusion. Every usability test 	participant who has seen the user interface  in Figure 7 has confidently 		stated that he or she understood that the sparkline represented the number 		of items available, eloquently proving once again that “clarity and simplicity 		are completely opposite of simple-mindedness,” as Tufte said in his book <em>Envisioning 	Information</em>.</p>
<p>Recently,  during the UIE Webinar I mentioned earlier, I was 	supremely gratified to hear Mark Burrell of Endeca recommend dual sliders 	with histograms as one of the best ways of showing price ranges. Burrell’s 	experience with dual sliders was similar to mine: he said that most people have 	no trouble understanding that histogram bars—a step-wise variant of a sparkline—represent 	item inventory for each part of a slider’s range and that histograms clearly 	help people to avoid overconstraining their queries.</p>
<h2>In Conclusion</h2>
<p><!-- End pullquote -->In our age of rapid development, new ideas and new controls hit the Web almost 	every day. Even as companies are struggling to overcome the old challenges 	 of numeric faceted filters, designers are innovating interesting new controls 	with which they can solve those challenges. One such control is the slider, 	which gives unparalleled power to customers. However, successfully offering 	this power requires careful UX design in order to avoid potential pitfalls. 	Again, the following three issues with numeric filters surface most often:</p>
<ul>
<li>representing discrete values for aspects as sets of ranges</li>
<li>emphasizing overconstrained filter states</li>
<li>being parsimonious with inventory information</li>
</ul>
<p>Designers’ uninformed use of sliders for filters often 	exacerbates these issues. In this column, I have presented  best practices 	as well as some novel approaches to help designers overcome these challenges. 	But there is simply no substitute for empathy and solid qualitative 	user research. Understanding <em>why</em> customers 	do certain things is extremely important in designing effective user interfaces. 	No matter what metrics we have, we cannot improve our user interfaces 	unless we understand <em>what</em> our customers’ goals are and <em>why</em> people fail 	to reach them. With every new filtering innovation, it becomes ever more 	important to stay focused on our customers, with patience, empathy, and understanding.<a title="Top" href="http://www.uxmatters.com/mt/archives/2010/02/numeric-filters-issues-and-best-practices.php#top"><img src="http://www.uxmatters.com/images/ux-bug.gif" alt="" width="18" height="18" /></a></p>
<h4>References</h4>
<p>Morville, Peter, and Mark Burrell. “<a title="Leveraging Search &amp; Discovery Patterns for Great Online Experiences" href="http://www.uie.com/events/virtual_seminars/search_patterns/">Leveraging 			Search &amp; Discovery Patterns for Great Online Experiences</a>.”<a title="Leveraging Search &amp; Discovery Patterns for Great Online Experiences" href="http://www.uie.com/events/virtual_seminars/search_patterns/"><img src="http://www.uxmatters.com/images/new-window-arrow.gif" alt="" width="14" height="12" /></a> <em>UIE 			Virtual Seminar, </em>2010. Retrieved February 7, 2010.</p>
<p>Tufte, Edward. <em>Beautiful Evidence</em>. Cheshire, 		CT: Graphics Press, 2006.</p>
<p>—— <em>Envisioning Information</em>. 4th     ed. Cheshire, CT: Graphics Press, 1990.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.designcaffeine.com/2010/articles/321/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Research and Design for eBay Customer-Centric Homepage</title>
		<link>http://www.designcaffeine.com/2010/portfolio/127/</link>
		<comments>http://www.designcaffeine.com/2010/portfolio/127/#comments</comments>
		<pubDate>Sun, 17 Jan 2010 05:45:55 +0000</pubDate>
		<dc:creator>Greg Nudelman</dc:creator>
				<category><![CDATA[Portfolio]]></category>
		<category><![CDATA[eBay]]></category>
		<category><![CDATA[Homepage]]></category>
		<category><![CDATA[Participatory Design]]></category>
		<category><![CDATA[Personas]]></category>
		<category><![CDATA[Usability]]></category>
		<category><![CDATA[UX Design]]></category>

		<guid isPermaLink="false">http://www.designcaffeine.com/?p=127</guid>
		<description><![CDATA[Client: eBay.com
Product: Customer-Centric Homepage
Problem: Redesign the eBay Homepage to reflect member-specific content and categories of items available on eBay in a flexible, compelling way that meets the needs of a wide variety of international and domestic audiences.
Solution: Partner with eBay members and new customers to understand what kind of information they find useful and compelling. [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.designcaffeine.com/wp-content/uploads/2010/01/ebay_homepage_final.jpg"><img class="alignleft size-medium wp-image-128" title="eBay Homepage Final Design" src="http://www.designcaffeine.com/wp-content/uploads/2010/01/ebay_homepage_final-284x300.jpg" alt="eBay Homepage Final Design" width="284" height="300" /></a><strong>Client: </strong>eBay.com<strong></strong></p>
<p><strong>Product: </strong>Customer-Centric<strong> </strong>Homepage</p>
<p><strong>Problem:</strong> Redesign the eBay Homepage to reflect member-specific content and categories of items available on eBay in a flexible, compelling way that meets the needs of a wide variety of international and domestic audiences.</p>
<p><strong>Solution:</strong> Partner with eBay members and new customers to understand what kind of information they find useful and compelling. Test the page internationally with variety of audiences.</p>
<p><strong>Services: </strong>participatory design, coordinating international usability studies in 6 countries, personas, roles, concept sketching, DOI algorithm design.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.designcaffeine.com/2010/portfolio/127/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Improve the Usability of Search-Results Pages: Add Sophisticated but Easy-to-Use Filtering and Sorting Controls</title>
		<link>http://www.designcaffeine.com/2010/articles/116/</link>
		<comments>http://www.designcaffeine.com/2010/articles/116/#comments</comments>
		<pubDate>Sat, 09 Jan 2010 01:37:15 +0000</pubDate>
		<dc:creator>Greg Nudelman</dc:creator>
				<category><![CDATA[Articles]]></category>
		<category><![CDATA[Filtering]]></category>
		<category><![CDATA[Finding]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Search Results]]></category>
		<category><![CDATA[Software Engineering]]></category>
		<category><![CDATA[Sorting]]></category>
		<category><![CDATA[Usability]]></category>

		<guid isPermaLink="false">http://www.designcaffeine.com/?p=116</guid>
		<description><![CDATA[Originally Published in JavaWorld.com, January 23, 2006 ⇒
E.R. Tufte, in his phenomenal book Envisioning Information, states, &#8220;Clarity and simplicity are completely opposite of simple-mindedness.&#8221; This false simple-mindedness is often evident in the design of a search-results page. Even on some of the leading e-commerce sites, this important page is frequently made hard to use by [...]]]></description>
			<content:encoded><![CDATA[<p><a title="Read on JavaWorld.com (Opens in a New Window)" href="http://www.javaworld.com/javaworld/jw-01-2006/jw-0123-usability.html" target="new">Originally Published in JavaWorld.com, January 23, 2006 ⇒</a></p>
<p>E.R. Tufte, in his phenomenal book <em><a href="http://www.javaworld.com/javaworld/jw-01-2006/jw-0123-usability.html#resources">Envisioning Information</a></em>, states, &#8220;Clarity and simplicity are completely opposite of simple-mindedness.&#8221; This false simple-mindedness is often evident in the design of a search-results page. Even on some of the leading e-commerce sites, this important page is frequently made hard to use by excessive visual clutter or the complete absence of appropriate sorting and filtering controls. This is especially daunting as more and more raw data return as search results, without the appropriate tools to manipulate them.</p>
<p>A well-designed search-results page is well worth the effort, since it is the key to helping your users successfully achieve their goals and enticing them back to your site. The engineering challenge is to provide just the right kind of sophisticated yet easy-to-use sorting and filtering tools that map well to your customers&#8217; goals and mental models. In this article, I present some design ideas to start you on your way to creating a more usable search-results page.<span id="more-116"></span></p>
<h3>Blood, sweat socks, and chicken soup</h3>
<p>According to usability research, while most consumers search occasionally, more then half of all users are &#8220;search dominant,&#8221;                         meaning they go straight for the search and ignore the rest of the navigation on the site (see <em><a href="http://www.javaworld.com/javaworld/jw-01-2006/jw-0123-usability.html#resources">Designing Web Usability: The Practice of Simplicity</a></em>). Thus, the search-results page is used by a vast majority of the customers, and the usability of this page is often key to the usability of your entire site. Given this data, it is astonishing how many companies ignore the usability design of this important functionality and are content to let their users muddle through.</p>
<p>Nowhere is this more evident than in e-commerce, as the following tale of Internet shopping illustrates: My wife has a well-concealed passion for cooking, but loves cookbooks about chocolate. For her birthday, I wanted to present her with a well-illustrated tour guide to chocolate cooking nirvana, and I figured this guide should range somewhere between 5 and 0. I knew exactly what I wanted, so I bravely set my browser to Amazon.com and typed &#8220;Chocolate Cookbook&#8221; in the site&#8217;s search function. Would you believe that (at the time of this writing) I found 14,597 books?</p>
<p>The first item was listed for .99, which fell outside my desired price range. The next two items had no visible price at all. Navigating to the next search-results page seemed fruitless: at 10 items per page, it would take 1,459 pages to see all 14,597 items. At a brisk browsing pace of 1 minute per page, this browsing process would take just more than 24 hours!</p>
<p>Sorting by &#8220;Avg. Customer Review&#8221; netted a very highly rated <em>A Tale of Blood and Sweatsocks</em> and a disappointedly chocolate-free <em>Chicken Soup for the Teenage Soul</em> (shown in Figure 1):</p>
<p><img src="http://www.javaworld.com/javaworld/jw-01-2006/images/jw-0123-usability1.gif" alt="http://www.javaworld.com/javaworld/jw-01-2006/images/jw-0123-usability1.gif" /></p>
<p><strong>Figure 1. Sorting chocolate-cookbook search results by Avg. Customer Review. Click on thumbnail to view full-sized image.</strong></p>
<p>It is possible that the search engine mistook the unfortunately named <em>Chicken Soup</em> series for a real cookbook, but a tale about sweat socks seemed completely out of place. At that point, I was completely stuck: Not only was there an excessive number of items in the search results, but most of the items seemed to be either in the wrong category or in the wrong price range. Most importantly, and absolutely critical to enable me, the user, to reach my chocolate shopping goals, there did not seem to be a way to <em>filter</em> the search results in such a way as to narrow them down from 14,597.                         <!--#include virtual="/cgi-bin/pgnav.pl?cont=yes&#038;pages=${pages}&#038;${compare}"--></p>
<p><!--#endif --> <!--#if expr="${compare} = /^page\=2$/ || ${compare} = /^page\=full/" --></p>
<h3>Too much data</h3>
<p>My unfortunate shopping experience is indicative of the larger problem affecting many large consumer sites: excessive amounts of raw search-results data with no way to manipulate the results. Between the Herculean efforts to give customers &#8220;something&#8221; from their searches and brilliant marketing initiatives to maximize the number of &#8220;impulse buys,&#8221; many Web companies have lost track of their core competency: helping customers quickly find exactly what they are looking for.</p>
<p>Companies ignore usability of key functions at their peril. In his excellent book <em><a href="http://www.javaworld.com/javaworld/jw-01-2006/jw-0123-usability.html#resources">Don&#8217;t Make Me Think!</a></em>, Steve Krug quotes his wife&#8217;s excellent statement on the key importance of usability: &#8220;If something is hard to use, I just don&#8217;t use it as much.&#8221; Anyone who has had a negative and frustrating experience with a Website will hesitate to use it again. Search programs can and should be made smarter (think Google), but the usability of the <em>search-results</em> page can also be improved dramatically.</p>
<p><!--#if expr="${compare} != /^page\=full/" --><!--#endif -->The lack of appropriate controls to filter search results by category and price range made it impossible for me to purchase my cookbook at Amazon.com. To paraphrase the opening quote, engineers at Amazon.com substituted &#8220;simple-mindedness&#8221; for &#8220;clarity and simplicity.&#8221; What Tufte brilliantly wrote of information design is equally true of interaction design: &#8220;&#8230;the operating moral premise of information design should be that our readers are alert and caring; they may be busy, eager to get on with it, but they are not stupid.&#8221;</p>
<p>To keep users coming back to your site, all of the pages essential to the successful shopping experience must be well designed to support your user&#8217;s goals. One of the most frequently used pages is the search-results page, and it is well-worth the additional design effort. The challenge is to provide just the right kind of sophisticated sorting and filtering tools that are easy to use and map well to your users&#8217; goals and mental models. As discussed in the next section, providing &#8220;just the right tools&#8221; is no easy task.</p>
<h3>The right toolkit: Everything users need and nothing more</h3>
<p>As a great designer <a href="http://www.javaworld.com/javaworld/jw-01-2006/jw-0123-usability.html#resources">Milton Glaser</a> once famously said: &#8220;Less is not necessarily more&#8230;just enough is more.&#8221; Following this advice is by no means easy. To provide                         &#8220;just enough&#8221; functionality, a designer must focus directly on the goals of <em>one particular user</em>, and understand that user&#8217;s specific needs and behaviors extremely well (see <em><a href="http://www.javaworld.com/javaworld/jw-01-2006/jw-0123-usability.html#resources">About Face 2.0</a></em>).</p>
<p>In my specific case, I knew exactly what I wanted, and my goals were clear:</p>
<ul>
<li><strong>Media type:</strong> Book</li>
<li><strong>Category:</strong> Cookbook</li>
<li><strong>Subject:</strong> Chocolate/deserts</li>
<li><strong>Price range:</strong> 5 to 0</li>
<li><strong>Rating:</strong> Highest customer rating and/or bestseller</li>
</ul>
<p>To help me achieve my goals, Amazon provided a good <em>sorting</em> functionality for sorting the result set by user ratings or bestseller. The important feature missing from the Amazon search                         results was <em>filtering</em>, which compromised the result set&#8217;s usability. To improve the site usability, <em>in light of my specific goals</em>, Amazon could have introduced two simple filters: category and price range.                         <!--#include virtual="/cgi-bin/pgnav.pl?cont=yes&#038;pages=${pages}&#038;${compare}"--></p>
<p><!--#endif --> <!--#if expr="${compare} = /^page\=3$/ || ${compare} = /^page\=full/" -->Either one of these filters would have dramatically decreased the number of search results. Together, these filters could have helped me zero-in on the manageable 20 to 50 chocolate cookbooks to examine in a reasonable amount of time. Having these filters would have allowed me to take control of the searching process and complete the buying task successfully.</p>
<p>It is worth noting that filtering itself is a foreign concept to most users. For this reason, Krug suggests that good search filter design should use a customer&#8217;s own terms and natural language, so the filter reads like a sentence. For example:</p>
<p><!--#if expr="${compare} != /^page\=full/" --><!--#endif --><em>&#8220;Search ____ for ___________ in ___________ Price from $___ to $___&#8221;</em></p>
<p>Implementation is shown in Figure 2.</p>
<p><img src="http://www.javaworld.com/javaworld/jw-01-2006/images/jw-0123-usability2.gif" alt="http://www.javaworld.com/javaworld/jw-01-2006/images/jw-0123-usability2.gif" /></p>
<p><strong>Figure 2. Implementing a natural language filtering scheme using HTML controls. Click on thumbnail to view full-sized image.</strong></p>
<p>Good search filters are challenging controls to design. The filters in Figure 2 would definitely increase the task completion                         success rate <em>in my particular use case</em>, because they help me zero-in on the right product category in my price range. However, would these filters help all users in all cases? Would additional controls confuse newbie Web users and add clutter to the screen unnecessarily? Simple guesswork won&#8217;t give you the right answers to these questions. To design search filters correctly, you need to know your target users intimately.</p>
<p>The concept of a &#8220;persona&#8221; introduced by Alan Cooper in <a href="http://www.javaworld.com/javaworld/jw-01-2006/jw-0123-usability.html#resources"><em>About Face 2.0</em></a> is a powerful and sophisticated tool for user behavior modeling. Personas are not real people, but they are based on the behaviors and motivations of real people gathered through usability research. Cooper defines a persona as &#8220;a precise descriptive model of the user, what he wishes to accomplish and why.&#8221; This model of a fictional archetypical user comes in handy for understanding user goals and exploring the ranges of behavior in specific contexts—such as your system&#8217;s interface.</p>
<p>Using personas allows the designer to prioritize which users are the most important ones to design for—those models become one or two &#8220;primary personas&#8221; for your project. Other important classes of users become &#8220;secondary personas.&#8221; Thus, the key to a successful &#8220;just-enough&#8221; design is to address the needs of the primary personas, without unduly inconveniencing the secondary personas. (For more discussion about personas as an effective design tool, see <a href="http://www.javaworld.com/javaworld/jw-01-2006/jw-0123-usability.html#resources">Resources</a> for links to Cooper&#8217;s excellent books <em>About Face 2.0</em> and <em>The Inmates Are Running the Asylum</em>.)</p>
<p>Regardless of whether you choose to use persona-modeling in your design, it is invaluable to conduct frequent usability tests with real users throughout the design process. The usability tests should be conducted frequently, and as early in the design process as possible, using static HTML or even low-tech paper prototypes. It may initially seem like a hassle, but if you consider the high cost of modification and quality assurance of the finished software product versus the low cost of modifying a paper prototype, obviously performing detailed product design before coding begins is well worth the effort. Persona-based design coupled with frequent usability testing is the most cost-effective way of ensuring that your final product will be successful in helping your users reach their goals.<!--#include virtual="/cgi-bin/pgnav.pl?cont=yes&#038;pages=${pages}&#038;${compare}"--></p>
<p><!--#endif --> <!--#if expr="${compare} = /^page\=4$/ || ${compare} = /^page\=full/" -->Although filters are hard to design, the good news is they are often relatively easy to implement, which you will see as we                         add the category and price filter to my chocolate-cookbook search-results page.</p>
<h3>Designing a category filter with drop-down control</h3>
<p>One popular implementation of the category filter is a drop-down control. This implementation is especially handy if many categories need to be displayed and screen real estate is tight. Note that providing this filter up-front often makes little sense. Currently, Amazon has 35 categories, and few people will ever make the effort to read all 35 categories to find out which one fits the subject best. Most likely, if users wanted to put in the time to learn the categories, they would browse instead of search.</p>
<p><!--#if expr="${compare} != /^page\=full/" --><!--#endif -->The key to a successful category filter is instead to narrow the categories only to those constrained by the search results, so the user can more easily choose the most likely category from the smaller, more relevant list. Usually, the category with the most items contains the most relevant search results, so it makes sense to show it first, with other relevant categories appearing in the order of descending item count. To make this ordering scheme clear to the user, it helps to show the item count after the category name. In our case, &#8220;Cooking&#8221; would clearly be the first category with the most items, making for easy user selection. For someone looking for cookbooks, it would be obvious to select &#8220;Cooking&#8221; and avoid the &#8220;Persperational Fiction&#8221; category, as shown in Figure 2.</p>
<p>Search filters can be implemented using SQL in the following way. Assuming the original search query used to retrieve &#8220;chocolate&#8221; items was:</p>
<div id="codewrap">
<div id="codewrap79">
<pre> <code>SELECT *
FROM item
WHERE title = 'chocolate'  -- simplified for clarity
ORDER BY average_customer_review DESC
</code></pre>
</div>
</div>
<p>The list of categories for these search results can be obtained by using the original SQL search condition of <code>title = 'chocolate'</code>, modified with a <code>GROUP BY</code> clause:</p>
<div id="codewrap">
<div id="codewrap86">
<pre> <code>SELECT category_name, category_id, COUNT(*) AS items_count
FROM item
WHERE title = 'chocolate'
GROUP BY category_name, category_id
ORDER BY COUNT(*) DESC
</code></pre>
</div>
</div>
<p>To construct the filter, we first need to create a small helper class, <code>HtmlOption</code>. Each object of this class represents one option in the drop-down (HTML select) control:</p>
<div id="codewrap">
<div id="codewrap92">
<pre> <code>//Value objects must be serializable for use in distributed Java systems like J2EE
public final class HtmlOption implements java.io.Serializable {

  //Both value and text are strings
  private final String val;
  private final String text;

  //Constructor
  public HtmlOption(String text, String val) {
    this.text = text;
    this.val = val;
  }

  //Accessors only, with no corresponding mutators
  public String getValue() {
    return val;
  }

  public String getText() {
    return text;
  }

}
</code></pre>
</div>
</div>
<p>Each object of this class will be immutable, which is a good object-oriented design practice and has many advantages. As Joshua                         Bloch writes in his famous book <em><a href="http://www.javaworld.com/javaworld/jw-01-2006/jw-0123-usability.html#resources">Effective Java</a></em>, immutable objects are simple, have only one state, can be shared freely, and are inherently thread-safe.                         <!--#include virtual="/cgi-bin/pgnav.pl?cont=yes&#038;pages=${pages}&#038;${compare}"--></p>
<p><!--#endif --> <!--#if expr="${compare} = /^page\=5$/ || ${compare} = /^page\=full/" -->Now we have everything we need to write the code that will generate our category filter:</p>
<div id="codewrap">
<div id="codewrap116">
<pre> <code>ResultSet rs; //Standard JDBC, the rest omitted for clarity

ArrayList categoriesFilter = new ArrayList();

//Add the first option: "All  Categories"
String text = "All Categories";
String val = "0";
categoriesFilter.add(new HtmlOption(text,val)); //Add the default no-filter option

while(rs.next()) {
  text = rs.getString("category_name") + " (" + rs.getString("items_count") + ")";
  val = rs.getString("category_id");
  categoriesFilter.add(new HtmlOption(text,val));
}
</code></pre>
</div>
</div>
<p>After this method runs, we will end up with an <code>ArrayList</code> <code>categoriesFilter</code> full of the custom <code>HtmlSelect</code> objects that represent the options in the HTML select control. Under a typical Model-View-Controller framework, the completed                         <code>categoriesFilter</code> object can be sent to a JSP (JavaServer Pages) page or Apache Velocity template to render the category filter as a simple                         HTML drop-down control. One simple way to do this is to use the following Velocity macro:</p>
<p><!--#if expr="${compare} != /^page\=full/" --><!--#endif --></p>
<div id="codewrap">
<div id="codewrap131">
<pre> <code>#macro(renderHtmlSelectOptions $optionsList $selectedValue)
  #foreach($htmlOption in $optionsList)
    #if($selectedValue == $htmlOption.Value)
      &lt;option value="$htmlOption.Value" SELECTED&gt;$htmlOption.Text&lt;/option&gt;
    #else
      &lt;option value="$htmlOption.Value"&gt;$htmlOption.Text&lt;/option&gt;
    #end
  #end
#end</code></pre>
</div>
</div>
<p>Note that in the context of Velocity, we can safely use the <code>==</code> operator to compare Java <code>String</code> objects. (For more information about Apache Velocity, see <a href="http://www.javaworld.com/javaworld/jw-01-2006/jw-0123-usability.html#resources">Resources</a>.) To render the category filter, the above macro is called as follows from the Velocity template:</p>
<div id="codewrap">
<div id="codewrap139">
<pre> <code>&lt;select name="categoryid" size="1"&gt;
  #renderHtmlSelectOptions($categoriesFilter, $gridViewRequest.CategoryId)
&lt;/select&gt;
</code></pre>
</div>
</div>
<p>Note that Velocity will conveniently map the shorthand attribute notation <code>$gridViewRequest.CategoryId</code> to the corresponding getter method call <code>$gridViewRequest.getCategoryId()</code> according to the standard JavaBeans convention. At runtime, assuming none of the categories have been selected, the above                         macro will yield:</p>
<div id="codewrap">
<div id="codewrap146">
<pre> <code>&lt;select name="categoryid"&gt;
  &lt;option value="0" SELECTED&gt;All Categories &lt;/option&gt;
  &lt;option value="2"&gt;Cooking (2,234)&lt;/option&gt;
  &lt;option value="5"&gt;Persirational Fiction (1,987)&lt;/option&gt;
  &lt;option value="8"&gt;History (1,385)&lt;/option&gt;
  &lt;option value="34"&gt;Agriculture (998)&lt;/option&gt;
&lt;/select&gt;
</code></pre>
</div>
</div>
<p>It is easy to see how immediately useful such filtering control would be to users—they can just select from the list of the                         categories relevant to their search, without learning all 35 categories on your site.</p>
<p>Users often find the interface to be less confusing if the order of the categories in the drop-down menu always remains in                         the same &#8220;relevance order&#8221; (highest <code>items_count</code> first), regardless of how the items in the grid are sorted. Of course, how you sort the categories in your particular filter                         will depend entirely upon what your users are trying to accomplish.</p>
<h4>Designing minimum and maximum price filters</h4>
<p>In Figure 2 above, the item price-range filter is prefilled with the available maximum and minimum prices of the range of items in the search results, providing both a good visual clue to how the control operates and a good starting point for the user&#8217;s exploration. Prefilling appropriate filter controls with a range of valid values is a nice touch and fairly easy to implement. Maximum and minimum prices for the particular search results can be obtained similarly to the categories above:<!--#include virtual="/cgi-bin/pgnav.pl?cont=yes&#038;pages=${pages}&#038;${compare}"--></p>
<p><!--#endif --> <!--#if expr="${compare} = /^page\=6$/ || ${compare} = /^page\=full/" --></p>
<div id="codewrap">
<div id="codewrap155">
<pre> <code>SELECT MAX(price) AS max_price, MIN(price) AS min_price
FROM item
WHERE title = 'chocolate'
</code></pre>
</div>
</div>
<p>The values yielded by this query could be stored in a simple custom <code>MinMaxPriceFilter</code> value object that can be rendered easily in the JSP or Velocity template using standard JavaBeans display code. The values for the maximum and minimum price controls need only be loaded from the database if they are not already provided by the <code>GridViewRequest</code> object, as discussed below.</p>
<h3>Implementing sorting: Headers or drop-down?</h3>
<p>Sorting is another essential tool that allows users to manipulate the search results. Unlike filtering, <em>sorting</em> is a term that most users understand intuitively and can be simply labeled as <em>Sort by:________</em>, as Amazon.com does (Figure 3). Just as we did earlier with the filtering controls, we made the entire sorting control (including the caption) read like a natural English sentence.</p>
<p><!--#if expr="${compare} != /^page\=full/" --><!--#endif --><img src="http://www.javaworld.com/javaworld/jw-01-2006/images/jw-0123-usability3.gif" alt="" width="244" height="161" /></p>
<p><strong>Figure 3. Amazon.com&#8217;s sorting implementation using the &#8220;Sort by:&#8221; caption</strong></p>
<p>To come up with a good sorting scheme, focus on the particular persona&#8217;s goals (that do not change) instead of on tasks (that change as technology and design evolve). For example, in the chocolate-cookbook use scenario, I set a goal to find the best quality cookbook in the 5 to 0 dollar range. My tasks were to filter based on price and category, and to sort in the &#8220;most highly rated items first&#8221; direction. I did not need the tool that sorted items based on category, publishing date, author&#8217;s last name, or the number of recipes. Adding those options would create more cognitive noise, clutter up the UI, and make it hard to find the functionality I actually needed to accomplish my goal.</p>
<p>Sorting can be implemented in the UI using clickable table headers (like those on Appartments.com) or using the HTML drop-down control (like those on Amazon.com). Clickable table headers save screen real estate and look slick. However, most large e-commerce sites implement result sorting as a drop-down control for several good reasons: First, not all the attributes on which personas would like to sort by are feasible to put in a grid. Second, the sort drop-down is more flexible and completely independent from the format and nature of the data in the grid. Third, and most importantly, in this implementation, we can specify precisely what items will appear first using natural language that maps best to a user&#8217;s mental model. For example, the &#8220;Price: Cheapest First&#8221; sort is clear, unambiguous, and goal-oriented.</p>
<p>In the drop-down sort control, avoid industry terms such as <em>descending</em> or <em>ascending</em>—most users find them confusing. Use sorting terms appropriate to the attribute being sorted: &#8220;Publication Date: Newest First&#8221;                         is a good way to describe the SQL query sort <code>ORDER BY publish_date DESC</code>.</p>
<p>A site with good usability should have a valid default sort order (which loads before the user has a chance to change anything) suitable for goals of a primary persona. The code below illustrates how to create a dynamic drop-down sort control. The default sort order is &#8220;Price: Cheapest First&#8221;:<!--#include virtual="/cgi-bin/pgnav.pl?cont=yes&#038;pages=${pages}&#038;${compare}"--></p>
<p><!--#endif --> <!--#if expr="${compare} = /^page\=7$/ || ${compare} = /^page\=full/" --></p>
<div id="codewrap">
<div id="codewrap178">
<pre> <code>ArrayList sortOrder = new ArrayList();
sortOrder.add(new HtmlOption("Price: Cheapest First", "0");
sortOrder.add(new HtmlOption("Price: Highest First", "1");
sortOrder.add(new HtmlOption("Publication Date: Newest First", "2");
</code></pre>
</div>
</div>
<p>The drop-down control can be rendered in the Apache Velocity template using the <code>renderHtmlSelectOptions</code> Velocity macro introduced previously:</p>
<div id="codewrap">
<div id="codewrap184">
<pre> <code>&lt;select name=" sortorder" size="1"&gt;
  #renderHtmlSelectOptions($sortOrder, $gridViewRequest.SortOrderId)
&lt;/select&gt;
</code></pre>
</div>
</div>
<p>At runtime, the call to the macro generates the following HTML:</p>
<p><!--#if expr="${compare} != /^page\=full/" --><!--#endif --></p>
<div id="codewrap">
<div id="codewrap189">
<pre> <code>&lt;select name="sortorder" size="1"&gt;
  &lt;option value="0" selected&gt;Price: Cheapest First&lt;/option&gt;
  &lt;option value="1"&gt;Price: Highest First&lt;/option&gt;
  &lt;option value="2"&gt;Publication Date: Newest First&lt;/option&gt;
&lt;/select&gt;
</code></pre>
</div>
</div>
<p>It is easy to go overboard with adding sorting and filtering options to the UI when confusion pervades the persona&#8217;s goals and how specific sorting and filtering options help her accomplish those goals. Once again, picking the right primary persona to design for and running frequent usability tests will ensure that you add only the appropriate filtering and sorting controls, and avoid visual noise. Remember, the entire sorting control, including the caption, should read like a natural sentence.</p>
<h3>Capturing filtering and sorting values using GridViewRequest</h3>
<p>One way to simplify capturing users&#8217; selections of sorting and filtering UI objects is to use the <code>GridViewRequest</code> object introduced in the code samples above. This value object (VO) interacts with the preloaded filter objects in the Velocity template to generate the dynamic values of all the filters and sorting selections in the user interface. <code>GridViewRequest</code> provides a convenient bridge between <code>HttpServletRequest</code> and business objects code and uses good object-oriented design practices such as achieving object immutability by using a                         static factory method that calls a private constructor (see <em><a href="http://www.javaworld.com/javaworld/jw-01-2006/jw-0123-usability.html#resources">Effective Java</a></em>):</p>
<div id="codewrap">
<div id="codewrap201">
<pre> <code>//Value objects must be serializable for use in distributed Java systems like J2EE.
public class GridViewRequest implements java.io.Serializable {

  //Private attributes.
  private String parameter; //Simplified for clarity: use tokenize. implementation

  private int categoryId; //Store as int, but provide 2 accessors: as int and as String.
  private int sortOrderId; //Store as int, but provide 2 accessors: as int and as String.
  private float minPrice;
  private float maxPrice;

  //Private constructor ensures immutability.
  private GridViewRequest() {
    super();
  }

  //This static factory method replaces the constructor.
  //It creates the VO directly from the HttpServletRequest object.
  //We need to collect the incoming values for:
  //1) search parameter (tokenized)
  //2) category
  //3) min price
  //4) max price
  //5) sort direction.
  //All these user inputs need to be cleaned and parsed.
  //Default is 0.0 for prices and 0 for categoryId and sortOrderId.
  public static GridViewRequest getInstance(HttpServletRequest request) {
    GridViewRequest gridViewRequest = new GridViewRequest();

    //Note: tokenize the user's search parameters as needed using a good
    //Java.util.Tokenizer implementation or similar --
    //see the Resources section for examples.
    listViewRequest.parameter = cleanString(request.getParameter("param"));

    //Min Price Filter:
    try {
      //If empty, min price is 0.0.
      if(isEmpty(request.getParameter("minprice"))) {
        listViewRequest.minPrice = 0.0;
      } else {
        //Try to parse the min price.
        listViewRequest.minPrice = Float.parseFloat(cleanString(request.getParameter("minprice")));
      }
    } catch (Exception e) {
      //Exception resets min price back to 0.0.
      listViewRequest.minPrice = 0.0;
    }

    //Parse and set maxPrice in a similar manner
    //(omitted for clarity).

    //Parse and set sortOrderId.
    //Default is 0 (i.e. sort order = "Cheapest first").

    try {

      //If empty, sortOrderId is 0.
      if(isEmpty(request.getParameter("sortorderid"))) {
        listViewRequest.sortOrderId = 0;
      } else {

        //Try to parse the sortOrderId.
        listViewRequest.sortOrderId = Integer.parseInt(cleanString(request.getParameter("sortorderid")));
      }
    } catch (Exception e) {
      //Exception resets sortOrderId back to 0.
      listViewRequest.sortOrderId = 0;
    }
  }

  //Parse and set categoryId in the similar manner
  //(omitted for clarity).

  //Provide only accessor methods for all attributes to keep the object immutable.

  //Velocity calls toString() method on the attribute.
  //This method returns String to support Apache Velocity's attribute quick-access scheme.
  public String getCategoryId() {
    return Integer.toString(categoryId);
  }

  //Velocity calls toString() method on the attribute.
  //This method returns String to support Apache Velocity's attribute quick-access scheme.
  public String getSortOrderId() {
    return Integer.toString(sortOrderId);
  }

  //Add similar getter methods for parameter, minPrice, maxPrice, etc.,
  //utility methods.
  //A quick static helper method to cleanup the string.
  public static String cleanString(String dirty) {
    if(null == dirty)
      return "";

    //You may also want to remove or encode
    //any unsafe string attributes(', ", &gt;,&lt;, &amp;, etc.)
    //in this method (omitted to safe space).

    return dirty.trim();
  }

  //A quick static helper method determine if the string is empty or null.
  public static boolean isEmpty(String dirty) {
    if(null == dirty || dirty.length() &lt; 1)
      return true;

    return false;
  }

} //End of GridViewRequest class.
</code></pre>
</div>
</div>
<p>The object of the <code>GridViewRequest</code> class renders itself completely and cleanly, directly from the <code>HttpServletRequest</code> object in one simple call to the static factory method and remains immutable for its entire lifetime. Once formed, it can be passed to the method that constructs the SQL for the item search, where the user&#8217;s chosen values for the filter and sort controls can be applied dynamically to the search-results query:</p>
<div id="codewrap">
<div id="codewrap258">
<pre> <code>...findItemWithFilter(GridViewRequest vo) {

  //Make appropriate adjustments for tokenized parameter.
  String sql = "SELECT * FROM item  WHERE title = '" + vo.getParameter() + "' ";

  //Apply category filter if supplied.
  if(! vo.getCategoryId().equals("0")) { //or use != 0 for the vo accessor method that returns int
    sql += " AND category_id = " + vo.getCategoryId() + " ";
  }

  //Apply min price filter if supplied.
  //getMinPrice() returns a float, it must be formatted appropriately for use in SQL.
  if(vo.getMinPrice() &gt; 0.0) {
    //Using &gt; and subtracting 1 cent from MinPrice is a quick method to defeat Java float's formatting inconsistencies.
    sql += " AND price &gt; " + vo.getMinPrice() - 0.01 + " ";
  }

  //Use analogous method for MaxPrice (only make sure to add 1 cent instead of subtracting it)
  //(omitted for clarity).

  //Apply the sort order using Java case statement.
  //Make sure to use vo accessor method that returns an int.
  switch(gridViewRequest.getSortOrderIdAsInt()) {
    case GridViewRequest.NEWEST_FIRST: //2
      sql += "ORDER BY PUBLISH_DATE DESC";
    break;
    case GridViewRequest.MOST_EXPENSIVE_FIRST: //1
      sql += "ORDER BY PRICE DESC";
    break;

    //(the rest of sorting options are omitted to save space)

    default: //default sort order, case GridViewRequest.CHEAPEST_FIRST or 0
      sql += "ORDER BY PRICE ASC";
  }

//Run the SQL normally to get the items
}
</code></pre>
</div>
</div>
<p>This method would be more efficient if I had used a <code>StringBuffer</code> to construct the SQL query string, but I used the overloaded <code>+</code> operator for clarity. It goes without saying that your finished code should have search parameter parsing, and all of the                         error handling and data cleanup of the incoming parameters associated with a Web application.</p>
<p>Traditional HTML controls are not the only way to implement filtering functionality. As I explain in the next section, to create a truly world-class experience for your users, sometimes it pays to look outside the traditional Internet browser box.</p>
<h4>World-class filtering using category links</h4>
<p>As I already mentioned, the concept of filtering is foreign to most users. Provided that sufficient amount of screen real estate is available, a truly world-class alternative to a drop-down filtering control is to use category links, as shown in Figure 4.</p>
<p><img src="http://www.javaworld.com/javaworld/jw-01-2006/images/jw-0123-usability4.gif" alt="http://www.javaworld.com/javaworld/jw-01-2006/images/jw-0123-usability4.gif" /></p>
<p><strong>Figure 4. Category links and price extraction provide a natural and obvious filtering alternative. Click on thumbnail to view                               full-sized image.</strong></p>
<p>Following one of the links will have the same effect as selecting one of the categories from a drop-down control and pressing                         the Go button in the previous desgin. However, links have many advantages over the drop-down control:</p>
<ol>
<li>They are highly visible and intuitive to use</li>
<li>They are the original and universally accepted form of Web navigation</li>
<li>They don&#8217;t require an additional click to see or to select from a drop-down control</li>
<li>Users inherently feel safe selecting links, because if things go wrong, users feel they can just click the Back button</li>
<li>Most importantly, well-designed links have the advantage of having a powerful <em>information scent</em>: that intuitive gut feeling of following the right path in the clutter of the UI overhead, ads, and pop-ups</li>
</ol>
<p>For best results, category links should appear near the search box, not in the left- or right-side panel. In Figure 4, the search box and the link filters are aggregated in close spatial proximity, pointing to their close functional relationship. In addition, both controls are surrounded by a box to further reinforce the impression that they are indeed related controls. To reduce visual clutter that results from multiple links, show only the top four to five relevant categories and provide a link to &#8220;more&#8230;&#8221; that will show the remaining categories relevant to the user&#8217;s search results. The ellipsis (&#8230;) is a standard way to show that &#8220;more&#8221; is not in itself a category, but instead provides a link to additional information.</p>
<p>Note that the number of items listed after the links is approximate by design and will make an excellent homework assignment for the willing. This approximation is an attempt to make the item number appear more &#8220;human.&#8221; If you ask a fellow human being how many items were returned by the original chocolate-cookbook query, he will not, as the computer did, tell you &#8220;14,597 items,&#8221; because he understands that you do not really care about having full precision for such a meaningless number. Instead, he will tell you &#8220;about 14 thousand items&#8221; in the same way he would say &#8220;about eight o&#8217;clock&#8221; without giving you precise milliseconds of the Java timestamp. This approximation in typical human communication saves cognitive load and provides the receiving party with only the most important <em>meaning</em> of the information, without the cognitive clutter that the computer so &#8220;helpfully&#8221; delivers. Human beings are inherently less precise then computers in their communication, and the closer we can come to &#8220;humanizing&#8221; our user interface, the more intuitive, polite, and user-friendly we will make the system for our users. For more discussion about using approximate values to make the software more &#8220;polite,&#8221; see Cooper&#8217;s <em><a href="http://www.javaworld.com/javaworld/jw-01-2006/jw-0123-usability.html#resources">The Inmates are Running the Asylum</a>.</em></p>
<p>Providing only the most relevant links as clickable category filters seamlessly combines the best features of both browsing and searching to create an optimal system interaction experience for users. Once the user clicks on the main category link, this interaction method naturally lends itself well to drilling down further into the relevant subcategories of the main &#8220;Cooking&#8221; category.</p>
<h4>World-class filtering using price extraction control</h4>
<p>Another useful and often overlooked filtering method is a natural language extraction control, also pictured in Figure 4. While the &#8220;made up&#8221; filters, such as product category, do not lend themselves well to being parsed using this method, many real-world concepts, such as a phone number, have a fairly predictable narrow range of user-entered formats that can be parsed with only a small amount of effort.</p>
<p>In our particular case, we can create an extraction control that naturally takes the place of the dedicated price filter in Figure 2. As this functionality is currently still quite rare, we should provide an example or simple instructions to show the users that this advanced functionality is available at our site. With some work, the system can support a variety of real-world pricing formats, such as &#8220;5-0&#8243; (pictured in Figure 4) as well as &#8220;5 to 0,&#8221; &#8220;around 5,&#8221; &#8220;less then 0,&#8221; etc. This functionality must implement a robust error-handling facility. Even if the program cannot completely parse an inherently esoteric and imprecise human request, it should be robust enough to make an educated guess that users can correct as appropriate (think Google&#8217;s self-correcting search errors). Given that it is relatively easy to create extraction controls, it is astonishing how few companies currently implement this (Google Local is one). I hope this article will help remedy this situation in some small way.</p>
<h4>World-class filtering using a dual slider</h4>
<p>To create world-class system interaction, it is important to come up with a UI control that offers both strong clues to its operation and the appropriate mapping to the system property being controlled. According to the classic usability book <em><a href="http://www.javaworld.com/javaworld/jw-01-2006/jw-0123-usability.html#resources">The Design of Everyday Things</a>,</em> &#8220;&#8230;the term <em>affordance</em> refers to the perceived and actual properties of the thing, primarily those fundamental properties that determine just how the thing could possibly be used&#8230; Plates are for pushing. Knobs for turning. Slots are for inserting things into.&#8221;</p>
<p>Consider that users have no restrictions on what they can type into the minimum and maximum price text boxes suggested above, including nonsense like &#8220;-,000,000&#8243; and &#8220;AbC.&#8221; Let&#8217;s extend the quote above: &#8220;The text box is for typing things into.&#8221; This statement does not necessarily suggest or constrain what can be typed. An alternative control with rich visual feedback and correct affordance for the price filtering task is a dual slider pictured in Figure 5.</p>
<p><img src="http://www.javaworld.com/javaworld/jw-01-2006/images/jw-0123-usability5.gif" alt="" width="424" height="68" /></p>
<p><strong>Figure 5. Dual slider control with correct affordance for specifying price ranges.</strong></p>
<p>The control pictured in Figure 5 is actually Amazon.com&#8217;s dual slider control used in the diamond search page. I modified it slightly so that it always shows the minimum and maximum prices of the price range for relevant items. Another great thing about this control is the item count that is loaded dynamically as the user slides the control to show how many items are now found in constrained search results (see <a href="http://www.javaworld.com/javaworld/jw-01-2006/jw-0123-usability.html#resources">Resources</a> for more information).</p>
<p>Regardless of the actual UI implementation, the output of the dual slider will be the user-defined positions of the minimum and maximum pegs that can be easily plugged into the backend code we discussed above. Dual sliders are wonderfully intuitive for setting the active minimum and maximum end-points for a range of values because they look and feel so much like real-world controls. Unfortunately, sliders of any kind are not yet a part of the standard HTML toolkit and are extremely rare. (See <a href="http://www.javaworld.com/javaworld/jw-01-2006/jw-0123-usability.html#resources">Resources</a> for some Ajax slider implementations.) At the time of this writing, even Amazon.com is using the slider only for the diamond search page and not for the common items&#8217; search pages. Perhaps some day soon these types of visual filtering controls with rich, modeless feedback will become more widespread to help us accomplish our online computing goals faster and easier.</p>
<h3>Conclusion</h3>
<p>Usability and interaction design is the new Internet frontier. The increasing popularity of easy-to-use sites like Google with the appropriate toolkits of &#8220;just the right kind&#8221; of simple and powerful controls shows that focusing on user experience is the key to long-term success and incredible customer loyalty. No amount of cool technology will rescue the product if the UI is hard to use, cluttered, or poorly designed. As an engineer, if you are not asking your team, &#8220;Can Alan, my primary usability persona, accomplish his goal?&#8221; and &#8220;How is this function helping Alan kick butt?&#8221;, you are just wasting precious time creating useless features that only clutter the interface and confuse the users.</p>
<p>As my Amazon experience shows, the search-results page is a great place to start improving your system&#8217;s usability. Even on some of the leading e-commerce sites, this important page is frequently made hard to use by visual clutter or absence of appropriate sorting and filtering tools. In this article, I presented some ideas for improving the search-results page and provided some examples of world-class search filter design. I also tried to show that good interaction design of sorting and filtering functionality does not happen in a vacuum, but is instead based solidly on the goals and behaviors of real users modeled as appropriate user personas. Successful design also involves testing the usability of prototypes early and often, and watching carefully as real customers struggle with your cool new system.</p>
<p>Giving your users sophisticated controls involves treating them with respect, as alert and caring (though frequently distracted and impatient) partners in the human-computer interaction. In the end, the design goal to strive for is an interface so simple, elegant, and straightforward that it simply disappears, allowing the user to arrive at his or her goal &#8220;magically.&#8221; Granted, many companies currently have a strict policy of not presenting software prototypes or unwashed engineers to their users, but this approach will have to change fast. Otherwise, a user who finds sweat socks when looking for chocolate will start buying cookbooks somewhere else.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.designcaffeine.com/2010/articles/116/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Plotting PIA</title>
		<link>http://www.designcaffeine.com/2010/articles/111/</link>
		<comments>http://www.designcaffeine.com/2010/articles/111/#comments</comments>
		<pubDate>Sat, 09 Jan 2010 01:32:45 +0000</pubDate>
		<dc:creator>Greg Nudelman</dc:creator>
				<category><![CDATA[Articles]]></category>
		<category><![CDATA[Article]]></category>
		<category><![CDATA[ASP.NET]]></category>
		<category><![CDATA[PIA]]></category>
		<category><![CDATA[Software Engineering]]></category>
		<category><![CDATA[Usability]]></category>

		<guid isPermaLink="false">http://www.designcaffeine.com/?p=111</guid>
		<description><![CDATA[Cohesive &#8220;real-world&#8221; guidance to creating, mapping, and deploying .NET Primary Interop Assemblies (PIAs). Practical advice on creating PIAs, three alternative methods for .NET solution mapping, and robust ClearCase integration using absolute file path.  Published in ASP.NET Pro November 2005 »
]]></description>
			<content:encoded><![CDATA[<p>Cohesive &#8220;real-world&#8221; guidance to creating, mapping, and deploying .NET Primary Interop Assemblies (PIAs). Practical advice on creating PIAs, three alternative methods for .NET solution mapping, and robust ClearCase integration using absolute file path.  <a href="http://www.aspnetpro.com/features/2005/11/asp200511gn_f/asp200511gn_f.asp" target="new">Published in ASP.NET Pro November 2005 »</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.designcaffeine.com/2010/articles/111/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Timestamp-Based Caching Framework: Current Data with Peak Performance</title>
		<link>http://www.designcaffeine.com/2010/articles/109/</link>
		<comments>http://www.designcaffeine.com/2010/articles/109/#comments</comments>
		<pubDate>Sat, 09 Jan 2010 01:30:12 +0000</pubDate>
		<dc:creator>Greg Nudelman</dc:creator>
				<category><![CDATA[Articles]]></category>
		<category><![CDATA[Article]]></category>
		<category><![CDATA[Cache]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[LRU]]></category>
		<category><![CDATA[Software Engineering]]></category>
		<category><![CDATA[Usability]]></category>

		<guid isPermaLink="false">http://www.designcaffeine.com/?p=109</guid>
		<description><![CDATA[Originally Published on JavaWorld.com, January 3, 2005 ⇒
Build a dynamic LRU cache framework using standard Java utility classes
In his timeless masterpiece, The Art of War, Sun Tzu states: &#8220;&#8230;one who is skilled in warfare principles &#8230;takes the enemy&#8217;s walled city without attacking&#8230; His aim must be to take All-Under-Heaven intact. Therefore, weapons will not be [...]]]></description>
			<content:encoded><![CDATA[<p><a title="Read this article on JavaWorld.com (Opens in a New Window)" href="http://www.javaworld.com/javaworld/jw-01-2005/jw-0103-caching.html" target="new">Originally Published on JavaWorld.com, January 3, 2005 ⇒</a></p>
<p><strong>Build a dynamic LRU cache framework using standard Java utility classes</strong></p>
<p>In his timeless masterpiece, <em><a href="http://www.javaworld.com/javaworld/jw-01-2005/jw-0103-caching.html#resources">The Art of War</a>,</em> Sun Tzu states: &#8220;&#8230;one who is skilled in warfare principles &#8230;takes the enemy&#8217;s walled city without attacking&#8230; His aim must be to take All-Under-Heaven intact. Therefore, weapons will not be blunted, and gains will be intact.&#8221;</p>
<p>When we garbage-collect perfectly good objects, we &#8220;blunt our weapons&#8221; and waste precious system resources by re-creating these objects all over again. By implementing a caching framework, we hope that most of our &#8220;gains&#8221; resulting from our creation of complex objects will remain &#8220;intact.&#8221; If we store completed objects in the cache, we avoid repeatedly re-creating these objects and thus enjoy a boost in application performance.</p>
<p>Time-to-live, or TTL, caching systems answer the needs of most applications that can afford to use slightly stale data. However, if the objects we cache contain time-sensitive pricing or availability information, they must always remain up to date and cannot be shown to the user as stale. These objects have no reliable time-to-live attribute, and to cache them effectively, we must employ different approaches. One successful approach, a timestamp-based caching framework (TBCF), is presented in this article.<span id="more-109"></span></p>
<h3>Not caching Britney Spears</h3>
<p>Imagine, if you will, a simple Website for selling CDs. On our site, users can search for CDs, check prices, and make purchases. For every user search, our application loads a CD cover picture, item details, price, and user ratings from the database. After all the information is loaded, the application creates CD value objects to massage and store the information from one or more JDBC (Java Database Connectivity) result sets. Then the application renders these CD objects as HTML and delivers the result to the user&#8217;s browser. After the search results display, our application completes the user&#8217;s request by allowing the JVM to garbage-collect the complex CD value objects it went to such great lengths to create. The same process then repeats for every other user who executes the same search for the same CD, for example, a Britney Spears CD.</p>
<p>While this typical design approach is straightforward and simple to implement, its performance is often suboptimal. With every                         user request, CD value objects undergo a &#8220;create-use-destroy&#8221; cycle, also known as <em>object churning.</em> This cycle does not typically cause problems for small, simple CD objects under low system loads. However, it does cause significant performance degradation if the CD objects are large and complex, requiring several JDBC calls and complex calculations. As the load on the application increases, object-churning problems rapidly intensify. Since objects are not reused, <em>n</em> users will always require the creation of <em>n</em> sets of CD objects and the use of <em>n</em> sets of system resources by our application. This problem is illustrated in Figure 1.</p>
<p><img src="http://www.javaworld.com/javaworld/jw-01-2005/images/jw-0103-caching1.gif" alt="" width="270" height="131" /></p>
<p><strong>Figure 1. No cache: <em>n</em> users require <em>n</em> sets of objects, causing problems at higher loads</strong></p>
<p>To address the object-churning problem, assembled CD value objects can be stored in a cache. A cache is typically implemented as a map, where CD objects are stored as values, with their IDs as keys. Each CD search request searches the cache first, before querying the database. If an existing CD value object is found in the cache, that object is used; a query to the database to create a new CD object is not required. Thus, with the cache in place, more CD objects are reused, and fewer objects are created and destroyed as part of the churning cycle. In a typical caching mechanism, all the objects that reside in the cache have a time-to-live (TTL) attribute. When the objects stay in the cache longer then their TTL, they become <em>stale.</em> Stale objects are periodically removed from the cache by a background thread (see &#8220;<a href="http://www.javaworld.com/javaworld/jw-07-2001/jw-0720-cache.html">Develop a Generic Caching Service to Improve Performance</a>&#8221; (<em>JavaWorld,</em> July 2001) for a good implementation of a TTL cache).                         <!--#include virtual="/cgi-bin/pgnav.pl?cont=yes&#038;pages=${pages}&#038;${compare}"--></p>
<p><!--#endif --> <!--#if expr="${compare} = /^page\=2$/ || ${compare} = /^page\=full/" -->This approach works well for most applications, especially where objects have a well-defined TTL attribute or where users can accept somewhat stale data—for example, running a monthly report of product demand trends on data loaded nightly. Day-old data is perfectly acceptable in this case and can be safely used to create the desired report. We also know that TTL is exactly 24 hours, so our cache can be cleared and then refreshed nightly from the database, safely speeding up data delivery throughout the day.</p>
<p>The problem emerges when we try to cache time-sensitive objects. In most cases where product availability or pricing is concerned, we require an absolutely reliable way to determine staleness, as using stale data can have expensive consequences. Examples include product prices that change instantly with market demands, day-trader stock market quotes, mortgage rates, and any reports that must portray real-time results. In these complex applications, the TTL cache&#8217;s background-thread refresh-scheme is simply not rigorous enough because no specific TTL exists for the cached objects, as pricing information changes randomly throughout the day. The challenges of TTL caching are shown in Figure 2.</p>
<p><!--#if expr="${compare} != /^page\=full/" --><!--#endif --><img src="http://www.javaworld.com/javaworld/jw-01-2005/images/jw-0103-caching2.gif" alt="" width="189" height="238" /></p>
<p><strong>Figure 2. Since TTL is not known, the object in the cache might be stale</strong></p>
<p>Since we cannot serve a stale stock price to a day trader, we must first determine if the object in our cache is stale. In the famous mental experiment involving Schrodinger&#8217;s Cat, the outside observer cannot reliably ascertain whether the cat is alive or dead without opening the sealed box. A TTL cache presents us with the same uncertainty: since TTL is not known, the object in the cache might be stale or it might still be fresh. The only way to know for sure is to open the proverbial box, i.e., look in the database. Thus, if we want to return only fresh data, we must go to the database to determine the staleness of every object we return, thereby negating any efficiency improvements we gained by using the TTL cache. In other words, the act of observing staleness destroys efficiency.</p>
<p>To guarantee the delivery of the most up-to-date information to the users, we need a more systematic approach to determine staleness. We must engineer some kind of a quick &#8220;staleness lookup&#8221; into every request addressed to our cache. The timestamp-based caching framework (TBCF) I present in this article does exactly that.</p>
<h3>Timestamp-based caching framework requirements</h3>
<p>We know enough about our Music Shop application to outline the software requirements of our new timestamp-based caching framework (TBCF):</p>
<ol>
<li>Our new caching framework should increase application performance by minimizing the object churning and maximizing object                            reuse.</li>
<li>We want to display only the most up-to-date pricing and availability, thus <strong>TBCF must never return a stale object.</strong></li>
<li>We need to address compatibility with legacy systems. It would do us no good to have the world&#8217;s greatest framework that can&#8217;t be used in our application! Therefore, our new framework should have plug-and-play architecture: it should be easy to install and configure on any system. Ideally, TBCF should allow any existing application object to be cached, as long as it implements a simple <code>Cacheable</code> interface.</li>
<li>We want to make it easy to plug any caching algorithm into our framework. Because one caching algorithm does not fit all applications, we should have the ability to plug in a least-frequently used (LFU), a least-recently used (LRU), or a first-in-first-out (FIFO) caching class, as long as it implements the required <code>Cache</code> interface.</li>
</ol>
<h3>Timestamp: The key to the cache kingdom</h3>
<p>We are seemingly stuck with two main conflicting requirements: cache as much as we can and guarantee freshness of all the                         retrieved objects.</p>
<p>To satisfy these requirements we use <code>Object</code>&#8217;s <code>Timestamp</code> attribute. <code>Timestamp</code> is simply the date and time the object was last modified by the system. <code>Timestamp</code> is typically stored as either <code>Date</code> or <code>long</code>. (On a related note, the <code>Version</code> attribute is related to <code>Timestamp</code> and can also be used in our framework to determine object freshness. <code>Version</code> is typically stored as an <code>int</code>.)</p>
<p>The easiest way to implement a <code>Timestamp</code> attribute is to use a database trigger. If any of the specified fields in a table row are modified in the database, the trigger                         automatically updates the timestamp column of the table with the latest SQL <code>Sysdate</code>. Database triggers are by nature application-independent, which renders them the ideal solution if multiple applications                         are accessing the same database tables. In our Music Shop application, we use a <code>long</code> representation of the Java <code>Date</code> attribute as our <code>Timestamp</code>. This <code>long</code> number is the count of milliseconds that separates &#8220;now&#8221; from the first moment of 1970—the year that launched the glorious                         decade that gave us Star Wars, Abba, punk, and Kermit the Frog.</p>
<h3>A UML sequence diagram is worth 1,000 words</h3>
<p>To help visualize how the <code>Timestamp</code> attribute is used, I created a UML sequence diagram found in Figure 3.</p>
<p><img src="http://www.javaworld.com/javaworld/jw-01-2005/images/jw-0103-caching3.gif" alt="http://www.javaworld.com/javaworld/jw-01-2005/images/jw-0103-caching3.gif" /></p>
<p><img src="file:///C:/DOCUME%7E1/GNUDEL%7E1/LOCALS%7E1/Temp/moz-screenshot.png" alt="" /></p>
<p><strong>Figure 3. UML sequence diagram of loading objects from the cache and database using TBCF. Click on thumbnail to view full-sized                               image.</strong></p>
<p>To summarize, a user request for Britney Spears CDs proceeds as follows:</p>
<ol>
<li>Obtain a list of IDs of &#8220;Britney&#8221; CDs</li>
<li>Retrieve <code>Timestamp</code>s for these IDs from the database</li>
<li>Cache: Use the <code>Id-Timestamp</code> combination to collect the following:
<ol type="a">
<li>Fresh objects found in the cache</li>
<li>IDs only for objects where the cache&#8217;s timestamp differs from the database&#8217;s timestamp (i.e., cached object is stale)</li>
<li>IDs were not found in the cache (i.e., object is missing from the cache)</li>
</ol>
</li>
<li>Database: Load the data and create missing/stale objects</li>
<li>Load all new and reloaded objects into cache</li>
<li>Combine 3a&#8217;s list with 4&#8217;s list</li>
<li>Sort the combined list</li>
<li>Send CDs to users rendered as HTML</li>
</ol>
<h3>Object-oriented design ideas in action</h3>
<p>To make our framework modular and extendable, we have used the best principles of object-oriented design to create our Java                         classes. For objects to participate in the TBCF, we only require them to implement the <code>Cacheable</code> interface (instead of extending some base class):</p>
<div id="codewrap">
<div id="codewrap101">
<pre> <code>public interface Cacheable extends Timestamped {

   public Collection loadFromIds(Collection idsToRefreshFromDb, Connection conn) throws ResourceLoadException;

}
</code>
</pre>
</div>
</div>
<p>For objects that lack a <code>loadFromIds()</code> method, we also provide an alternative <strong>static</strong> database-loading method based on the Java Reflection API. To use this loading method, objects need only implement a simpler                         <code>Timestamped</code> interface, shown in the code below. Access to a static loading method allows TBCF to be compatible with an even greater range                         of legacy systems.                         <!--#include virtual="/cgi-bin/pgnav.pl?cont=yes&#038;pages=${pages}&#038;${compare}"--></p>
<p><!--#endif --> <!--#if expr="${compare} = /^page\=3$/ || ${compare} = /^page\=full/" --></p>
<div id="codewrap">
<div id="codewrap113">
<pre> <code>public interface Timestamped {

   public String getId();
   public long getTimestamp();

}
</code>
</pre>
</div>
</div>
<p>To allow users the ability to plug in any caching scheme (LRU, LFU, etc.) a simple <code>Cache</code> interface (which extends <code>java.util.Map</code>) is used to denote the caching class:</p>
<div id="codewrap">
<div id="codewrap122">
<pre> <code>public interface Cache extends java.util.Map {

   /* Additional methods for Cache (not found in Map). */
   //Returns the maximum size of the cache.
   public int getMaxSize();

}
</code>
</pre>
</div>
</div>
<p>The caching class is based on a popular LRU algorithm, called <code>LRULInkedMapCache</code> because it was created by extending the <code>LinkedHashMap</code> class, which has been provided in the standard <code>java.util</code> package since J2SE 1.4. Whenever possible, we use standard <code>java.util</code> interfaces (<code>Collection</code>, <code>List</code>, and <code>Map</code>) as return types for our framework methods.</p>
<p><!--#if expr="${compare} != /^page\=full/" --><!--#endif -->To address multithreading and synchronization concerns, we use synchronized wrappers in the Sun Java naming convention (<code>xxxTable</code> wraps <code>xxxMap</code>).</p>
<p>We have developed our <code>CacheManager</code> using the Data Access Object (DAO) design pattern: the <code>CacheManager</code> automatically loads some objects from the cache and others from the database, insulating the caller from implementation.                         <code>CacheManager</code> is a singleton, implementing the static <code>Cache</code> initialization.</p>
<p>The entire timestamp-based caching framework has a dynamic Java runtime configuration that can be loaded from a properties                         file or entered on the command line. Whenever possible, we use immutable objects (like <code>TimestampedId</code>) for simplicity and reliability. We always throw layer-appropriate exceptions (like <code>ResourceLoadException</code>). To make our design intentions clear, we have created our <code>Util</code> class to be non-instantiatable and <code>final</code>, because it contains only static utility methods.</p>
<p>The resulting UML class diagram is shown in Figure 4.</p>
<p><img src="http://www.javaworld.com/javaworld/jw-01-2005/images/jw-0103-caching4.gif" alt="http://www.javaworld.com/javaworld/jw-01-2005/images/jw-0103-caching4.gif" /></p>
<p><strong>Figure 4. UML class diagram of the timestamp-based caching framework. Click on thumbnail to view full-sized image.</strong></p>
<h3>A look at the code</h3>
<p>Note that in the code snippets below, I have omitted error handling, comments, and many other essential details in the interest                         of moving the narrative along. I encourage you to look at the actual source code (downloadable from <a href="http://www.javaworld.com/javaworld/jw-01-2005/jw-0103-caching.html#resources">Resources</a>) while you peruse this section.</p>
<h4>Step 1. User&#8217;s search: getCdIdsThatMatchUserSearch()</h4>
<p>In this section, we load all the IDs/timestamps for CDs authored by Britney Spears. If we were not using a cache, our first step would be to create the complete list of CDs containing all of the required data. However, since our first TBCF requirement is to decrease object churning, we instead load a list of small immutable objects: <code>TimestampedIds</code>. As the name suggests, this object aggregates exactly two attributes: <code>Id</code> and <code>Timestamp</code>. The <code>Id</code> is used to retrieve CD objects from the cache or database, whereas the <code>Timestamp</code> attribute is used to determine the freshness of the cache&#8217;s CD objects to ensure none of the delivered objects are stale.                         <code>TimestampedId</code> objects are designed to be light; they can be created quickly and put minimum load on our application:</p>
<div id="codewrap">
<div id="codewrap169">
<pre> <code>public static List getCdIdsThatMatchUserSearch(String artist)
   throws ResourceLoadException { 

   rs = stmt.executeQuery("SELECT id, timestamp FROM cd WHERE artist = '" +  artist + "'");         

   TimestampedId id = null;
   while (rs.next()) {
      id = new TimestampedId(rs.getString(1), rs.getLong(2));
      returnVec.addElement(id);
   }

   return returnVec;
}
</code>
</pre>
</div>
</div>
<h4>Step 2. CacheManager main method: loadFromCacheAndDB()</h4>
<p>The <code>loadFromCacheAndDB()</code> method represents the brains of our framework. It has been developed with a Data Access Object (DAO) design pattern: we give                         the <code>loadFromCacheAndDB()</code> method a list of the object IDs we want, and it does the rest, automatically picking valid objects from the cache and loading                         the remainder from the database.                         <!--#include virtual="/cgi-bin/pgnav.pl?cont=yes&#038;pages=${pages}&#038;${compare}"--></p>
<p><!--#endif --> <!--#if expr="${compare} = /^page\=4$/ || ${compare} = /^page\=full/" -->Here is how this method works: First, <code>loadFromCacheAndDB()</code> sets up two lists: <code>currentCached</code> and <code>refreshFromDb</code>. Using the list of <code>TimestampedIds</code> from Step 1 (which contains all of the CDs we need to load), we fill both current and refresh lists using the <code>fillFreshAndStaleFromCache()</code> method. Second, the IDs in the refresh list (containing stale and missing IDs) are used to load objects from the database,                         using the <code>Cacheable</code> interface&#8217;s <code>loadFromIds()</code> method specific to the object being cached. In this way, we optimize our retrieval strategy: we only hit the database to                         create objects we did not already have in our cache.</p>
<p>One caveat of this methodology is that it requires an instance of the <code>Cacheable</code> class on which to call an automatic loader method. Why do we need this instance? To allow us the flexibility of using a Java                         interface to call various <code>load()</code> methods. The JVM simply needs to know which <code>load()</code> method to call. For example, <code>cd.loadFromIds()</code> will load from a different table than <code>author.loadFromIds()</code>, so we need an instance of the <code>Cacheable</code> class to help the JVM select the right method.</p>
<p><!--#if expr="${compare} != /^page\=full/" --><!--#endif -->In many cases, a static <code>load()</code> method is preferred, but since static methods can&#8217;t be a part of the interface, we must provide a dummy instance of the <code>Cacheable</code> object on which to call our loading method. In a later section, I discuss an alternative loading mechanism that uses Java                         reflection to allow us to call static <code>load()</code> methods.</p>
<p>Once all the stale or missing objects are loaded, we update our cache with these objects according to the LRU algorithm implemented in <code>LRULInkedMapCache</code>. If the cache is already full, this algorithm will always remove the least-recently used item from the cache and replace                         it with one of the incoming stale or missing objects. Finally, we combine our <code>currentCached</code> and <code>newlyLoaded</code> lists to make a single list of fresh, completely loaded objects from the cache and database:</p>
<div id="codewrap">
<div id="codewrap204">
<pre> <code>public static List loadFromCacheAndDB(List timestampedIds,
   Cacheable cacheable) throws ResourceLoadException {

   Vector currentObjectsFromCache = new Vector();
   Vector idsToRefreshFromDb      = new Vector(); 

   fillFreshAndStaleFromCache(
      timestampedIds,
      idsToRefreshFromDb,
      currentObjectsFromCache);

   Collection objectsFromDb = cacheable.loadFromIds(idsToRefreshFromDb);

   updateCache(objectsFromDb);

   currentObjectsFromCache.addAll(objectsFromDb); //combine lists

   return currentObjectsFromCache;
}
</code>
</pre>
</div>
</div>
<h4>Step 3. CacheManager filtering: fillFreshAndStaleFromCache()</h4>
<p>In the <code>fillFreshAndStaleFromCache()</code> method, we iterate through an incoming list of <code>TimestampedIds</code> of Britney CDs to determine which objects need to be reloaded from the database and which can be retrieved from the cache.                         We use the <code>TimstamedId.Id</code> attribute to look up objects in the cache. If the object is not found in the cache, we add the missing ID to the <code>idsToRefresh</code> list. If the object is found, we compare the cached object&#8217;s timestamp with the incoming <code>TimestampedId.timestamp</code> attribute. If the timestamps are equal, the object in the cache is fresh, and we add it to the <code>currentObject</code> list. If the timestamps are <strong>not equal</strong>, the object is stale, and we add its ID to the <code>idsToRefresh</code> list.</p>
<p>This method uses two lists, <code>currentObjectsFromCache</code> and <code>idsToRefreshFromDb</code>, which we pass by reference from the caller method. This design is less then perfect since we are modifying two global lists, but it&#8217;s justified here because this method is private and this design is efficient. It allows us to fill both current and refresh lists by iterating through the <code>TimestampedId</code> list just once:</p>
<div id="codewrap">
<div id="codewrap222">
<pre> <code>private static void fillFreshAndStaleFromCache(
   List timestampedIds,
   Vector idsToRefreshFromDb,
   Vector currentObjectsFromCache) {

   TimestampedId timestampedId = null;
   Timestamped c = null;

   for(Iterator i = timestampedIds.iterator(); i.hasNext();) {

      timestampedId = (TimestampedId)i.next();
      c = (Timestamped)cache.get(timestampedId.getId());

      if(null == c) {
         //&lt;NOT FOUND&gt;
         idsToRefreshFromDb.add(timestampedId.getId());
         continue;
      } else if(c.getTimestamp() != timestampedId.getTimestamp()) {
         //&lt;STALE&gt;
         idsToRefreshFromDb.add(timestampedId.getId());
         continue;
      } else {
         //&lt;FRESH&gt;
         currentObjectsFromCache.add(c);
      }
  }
}
</code>
</pre>
</div>
</div>
<h4>Step 4. Cacheable loadFromIds()</h4>
<p><code>loadFromIds()</code> is a straightforward instance method that we use to create the stale or missing CDs we found in Step 3. We pass this method                         a list of IDs, and a <code>Collection</code> of completely loaded <code>CD</code> objects returns. To load all of the CDs in a single shot, we use a handy <code>Util</code> class method to marshal the IDs list into a <code>commaDelimitedIds</code> string with commas and single quotes, such as <code>"'1','2,'3'"</code>, suitable for use in the SQL <code>IN</code> statement:</p>
<div id="codewrap">
<div id="codewrap243">
<pre> <code>public Collection loadFromIds(Collection idsToRefreshFromDb)
   throws ResourceLoadException {

   Vector objectsFromDb = new Vector();
   String commaDelimitedIds =
      Util.makeCommaDelimitedStringsList(idsToRefreshFromDb);

   String sql = new StringBuffer()
      .append("SELECT id, title, artist")
      .append("FROM cd WHERE id IN (")
      .append(commaDelimitedIds) //"'1','2,'3'"
      .append(")").toString();

   stmt = conn.createStatement();                

   rs = stmt.executeQuery(sql);       

   //Load complete CDs from the result set
   CD cd = null;
   while (rs.next()) {
      cd = new CD();
      cd.id = rs.getString(1);
      cd.title = rs.getString(2);
      cd.artist = rs.getString(3);
      objectsFromDb.addElement(cd);
   }

   return objectsFromDb;
}
</code>
</pre>
</div>
</div>
<h4>Step 5. Sort and display results: performFinalSort()</h4>
<p>The <code>performFinalSort()</code> method is run <strong>after</strong> TBCF retrieves the list of CDs. Here, we order the combined, completely loaded CD list by title, using <code>Comparator</code> to produce a sorted <code>List</code> of CDs. It is worth noting that we have to use Java sorting in our framework, because the incoming CD list is always unordered.                         To form this <code>List</code>, we must combine lists of objects from the cache and database. Even if each of the original lists were ordered, once they are added together, the order of the resulting combined list is indeterminate. Here is the code that performs the final sorting. It uses <code>Comparator</code> implemented via an anonymous inner class:</p>
<div id="codewrap">
<div id="codewrap263">
<pre> <code>private static void performFinalSort(List cds) {

   Collections.sort(cds,
      new Comparator() {

         public int compare(Object cd1, Object cd2) {
            return ( ((CD)cd1).getTitle() )
               .compareTo( ((CD)cd2).getTitle() );
         }

      }
   );
}
</code>
</pre>
</div>
</div>
<p>A complete list of CDs sorted by the <code>performFinalSort()</code> method is now ready to be used elsewhere in our application or rendered as HTML and sent to the user&#8217;s browser. Our new timestamp-based caching framework increased the efficiency of our Music Store application by loading all of the available fresh CD objects from the cache and the rest from the database. Only stale or missing objects are created; object churning is minimized, without compromising the data&#8217;s freshness.<!--#include virtual="/cgi-bin/pgnav.pl?cont=yes&#038;pages=${pages}&#038;${compare}"--></p>
<p><!--#endif --> <!--#if expr="${compare} = /^page\=5$/ || ${compare} = /^page\=full/" --></p>
<h3>Alternative approach to loading objects from the database</h3>
<p>Although the methods I have described work well for most applications, all of our CD objects must implement the <code>Cacheable</code> interface to auto-load <code>CD</code> objects from the database to participate in TBCF. As described above, to implement the design using the <code>Cacheable</code> interface, we need an instance of the <code>Cacheable</code> object to load the CDs via the <code>loadFromIds()</code> method defined in the <code>Cacheable</code> interface. This solution is elegant and easy to incorporate into most legacy applications. Most business classes already                         loosely follow the EJB (Enterprise JavaBean) entity-bean method-set and include some permutation of the <code>load()</code> method, which loads the object based on the passed object ID. However, some legacy frameworks may not include convenient                         instance <code>load()</code> methods we can use, or it may simply be more desirable to call a <strong>static</strong> <code>load()</code> method instead.</p>
<p>Does the absence of the instance <code>load()</code> method mean we can&#8217;t use our new framework? Not at all! For these situations, the Java Reflection API provides a handy alternative for invoking the desired static method. Admittedly, the Reflection approach is more error prone, less elegant, and slightly more resource intensive. However, it is also exceptionally flexible and easy to use.</p>
<p><!--#if expr="${compare} != /^page\=full/" --><!--#endif -->The Java Reflection API provides a way to perform a runtime <code>load()</code> method configuration and allows the <code>load()</code> method itself to be declared static. This permits TBCF to cache an even greater variety of objects and provides maximum plug-and-play functionality for compatibility with a greater number of legacy systems. The objects we want to cache will still need to implement the <code>Timestamped</code> interface. The framework needs access to <code>Id</code> and <code>Timestamp</code> attributes to determine object freshness—there is no way around that. But Reflection neatly addresses the problem of accessing                         any user-defined static <code>load()</code> method. Reflection-based and instance-based <code>loadFromCacheAndDB()</code> methods are overloaded, so they can happily coexist in our framework.</p>
<h4>CacheManager Reflection API load: loadFromCacheAndDB()</h4>
<p>The <code>loadFromCacheAndDB()</code> method works as follows: As before, we set up two lists—<code>currentCached</code> and <code>refreshFromDb</code>—and fill them both from the cache. However, the loading mechanism is now implemented differently; instead of a dummy instance                         of the <code>Cacheable</code> class, we pass the name of the static <code>load()</code> method we wish to invoke and the name of the class that houses that method. This static <code>load()</code> method is then called using the Java Reflection API. After the missing and stale objects are loaded, we again proceed as                         we did previously: to update <code>Cache</code> with new objects, combine the <code>currentCached</code> and <code>newlyLoaded</code> lists and sort the resulting list. The final result is exactly the same: we obtain a sorted list of completely loaded, guaranteed fresh CD objects obtained from the cache and database:</p>
<div id="codewrap">
<div id="codewrap304">
<pre> <code>public static List loadFromCacheAndDB(
   List timestampedIds,
   String loaderClassName,
   String loaderMethodName,
   Connection conn)
   throws ResourceLoadException {

   Vector currentObjectsFromCache = new Vector();
   Vector idsToRefreshFromDb      = new Vector(); 

   fillFreshAndStaleFromCache(
      timestampedIds,
      idsToRefreshFromDb,
      currentObjectsFromCache);

   /* Java Reflection-based call replaces cacheable.loadFromIds() */
   Collection objectsFromDb = loadStaleOrMissingFromDB(
      idsToRefreshFromDb,
      loaderClassName,
      loaderMethodName,
      conn);

   updateCache(objectsFromDb);

   currentObjectsFromCache.addAll(objectsFromDb);

   return currentObjectsFromCache;
}
</code>
</pre>
</div>
</div>
<h4>Loading using Reflection: loadStaleOrMissingFromDB()</h4>
<p>This section demonstrates the mechanism by which the static <code>load()</code> method is called using the Java Reflection API. We pass to <code>loadStaleOrMissingFromDB()</code> the name of our static <code>load()</code> method and the name of the class we will use to invoke it. We use the familiar Reflection invocation code to set up the <code>Class</code> array of loader-method parameter types and obtain a reference to the <code>load()</code> <code>Method</code> object. We then set up the <code>Object</code> array of loader method arguments, passing it the list of Britney CD IDs to load and a database connection. Then we call <code>invoke()</code> on the <code>load() Method</code> object to execute the <code>load()</code> method. Because the <code>load()</code> method we&#8217;re calling is static, our <code>invoke()</code> method&#8217;s first argument (the object on which the <code>load()</code> must be invoked) is null:</p>
<div id="codewrap">
<div id="codewrap325">
<pre> <code>private static Collection loadStaleOrMissingFromDB(
   Vector idsToRefreshFromDb,
   String loaderClassName,
   String loaderMethodName,
   Connection conn)
   throws ResourceLoadException {

   Class loaderClass = Class.forName(loaderClassName);
   Class[] loaderMethodParameters = new Class[2];
   loaderMethodParameters[0] = Collection.class;
   loaderMethodParameters[1] = Connection.class;

   Method loaderMethod = loaderClass.getMethod(
      loaderMethodName,
      loaderMethodParameters);

   Object[] loaderMethodArguments = new Object[2];
   loaderMethodArguments[0] = idsToRefreshFromDb;
   loaderMethodArguments[1] = conn;

   objectsFromDb = (Collection)loaderMethod.invoke(
      null, //NULL = static method
      loaderMethodArguments);

   return objectsFromDb;
}
</code>
</pre>
</div>
</div>
<p>Using Java Reflection helps us access static <code>load()</code> methods and adds compatibility with a greater range of legacy systems.                         <!--#include virtual="/cgi-bin/pgnav.pl?cont=yes&#038;pages=${pages}&#038;${compare}"--></p>
<p><!--#endif --> <!--#if expr="${compare} = /^page\=6$/ || ${compare} = /^page\=full/" --></p>
<h3>Requirements fulfilled</h3>
<p>In closing, let&#8217;s revisit the requirements I defined at the beginning of this article to make sure we have met them.</p>
<ol>
<li>The new timestamp-based caching framework has maximized our application&#8217;s efficiency by loading all of the available fresh objects from the cache and the rest from the database. Only stale or missing objects were created; object churning was minimized without compromising the data&#8217;s freshness.</li>
<li> <!--#if expr="${compare} != /^page\=full/" --><!--#endif --></li>
<li>To determine staleness we used the <code>Timestamp</code> attribute of a particular instance of an object, instead of the generic TTL. Therefore, we are certain to always deliver the most up-to-date information. As long as the timestamp is updated using a SQL database trigger or other reliable mechanism, TBCF will never return a stale object.</li>
<li>We addressed the compatibility of TBCF with legacy systems. TBCF is easy to install and can be configured at runtime via a Java process command line or properties file. TBCF allows any existing legacy application object to be cached, as long as it implements a simple <code>Cacheable</code> interface. In addition, we also provided a straightforward way to access static loading-methods at runtime via the Java Reflection API, allowing an even greater number of legacy classes to be cached.</li>
<li>Although we created the framework with an LRU caching class, we also made it easy to plug any caching algorithm into our framework, as long as it implements the required <code>Cache</code> interface.</li>
</ol>
<h3>Conclusion</h3>
<p>TBCF can be used to achieve high levels of performance and decrease the churning of time-sensitive objects, without compromising freshness of the data delivered to the user. This framework uses a sophisticated strategy of automatically optimizing the loading of objects from the cache and database, using the object&#8217;s timestamp to reliably determine freshness. The timestamp-based caching framework has a simple, straightforward design and low resource overhead. It is built using Java&#8217;s excellent utility classes, can be easily plugged into many legacy systems, and can be used with any caching algorithm. Using this framework, developers can dramatically boost application performance by minimizing the number of objects that the JVM must destroy and re-create, while guaranteeing the availability of the most up-to-date information.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.designcaffeine.com/2010/articles/109/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
