|
|
|
||
|
I'm constantly at odds with my developers over the importance of documenting why a piece of code does what it does. Having been in the code maintenance business for a long time, I have learned the hard way that a particular implementation is only valid for a particular set of conditions. Unless those conditions are well documented, there is no way to effectively determine if the code is valid in another (perhaps the same since there is no way to know) situation. Some examples of questions that should have documented "why"s:
Pulling an example from my own code: "There are certain optimizations that have been made in the writer based on the fact that the send timeout is a constant and is based on the time at which a message is added to the queue (i.e. the queue will contain monotonically increasing timeout values). This implies that until the currently active message's (the message currently being written) timeout occurs, no other message in the queue needs to be checked." As time went on, it was determined that there would be messages that never timed out. This means that the constraint that the timeout values are monotonicly increasing was no longer valid and therefore the implementation was no longer valid. Only by specifying the conditions under which the code was written (assumptions that were made) was it known that the implementation needed to be changed. It is common for the conditions under which an implementation is written to be defined in other systems or documents such as requirements or the bug tracking system. Unless the conditions are presented either within the code itself or the same directories as the code the correlation is lost. Also, the implementation typically has its own specific set of conditions that would not be found in requirements. There is little actual overhead in serializing these conditions as, by definition, they are all known at developement time. In other words, the conditions are all known, they simply must be written out. Once a suitable convention has been established for this documentation and the developers overcome the initial inertia of performing this task, it becomes very natural. Any minimal time lost over the process of typing is overshadowed by the extra level of communication that it provides. |
|
||
|
The final set of notes on measuring dark matter and energy (Part 3 of 3):
Lecture notes are available from Wayne Hu. Reference links: |
|
||
|
My wife asked me the other day why is it called a "smoke test". I honestly didn't know. Here's what Jargon has to say about it:
|
|
||
|
I have been involed in architecting and writing web applications as long as there has been a "web". Recently, I have been doing due dilligence on web architectures. Most architectures recognize the value in the Model 2 (or MVC) approach in their design. But is this this sufficient? This is a work in progress so excuse the mess and please check back for updates. Intended audience
This article is geared towards enterprise web applications. An enterprise web application in the context of this article consists of the following:
If your application does not fall under the above constraints then the concepts defined herein may not apply. For example, introducing Model 2 into an environment where there is only one developer may kill productivity due to the overhead associated with the multiple layers. Starting points
There are just as many starting points as there are web frameworks. Below is an attempt to enumerate a few of the initial conditions for a web-enabled application.
What's going on?
I was originally going to do a full write-up on the request / response, MVC, and the like but after re-reading Designing Enterprise Applications with the J2EE(TM) Platform, Second Edition and MVC Detailed it would be significantly redundant. I will be updating this entry with more information using the above link as a reference. |
|
||
|
I have been involved in a code review for the past few days. Time and time again I have come across code that fits into the "if you know something will never happen, it most certainly will" category of development. Take a look at this example:
List users = session.find("select u from User u where u.loginName = ?", ... );
if(users.size() > 0) {
...
return true;
}
return false;
This probably looks like 99% of the code out there. The problem is that you're only concerned with the case where the size is equal to one. The case where the size is greater than one is undefined. I know, you're thinking to yourself: "But that will never happen since I have unique constraints on my primary keys. The entry app will puke when it attempts to enter more than one row." Never say never. A few years ago I was working on an application with the same constraints. In order to speed up and allow for an ETL operation that the DBA was doing, he disabled the all of the constraints and forgot to re-enable them. Rather than having logging in place that would have caught this error immediately, a few weeks went by without anyone noticing. Needless to say, it took a few weeks to clean up the resulting mess. Oh, did I forget to mention that this was a production database? A more sensible and defensive coding strategy would be:
List users = session.find("select u from User u where u.loginName = ?", ... );
// NOTE: the size of users is expected to be [0, 1]
final int usersSize = users.size();
// if the size of users is greater than one, log an error
// but continue as this is not fatal
if(usersSize > 1) {
// log something
...
} /* else -- users size is not greater than 1 */
// there is at least one user. The first user will always
// be used.
// NOTE: more than one user may be present at this time.
// This case can be safely ignored at this point.
if(usersSize > 0) {
...
return true;
} else if(usersSize == 0) {
return false;
} else { // usersSize is less than zero
// this is an error that cannot be attributed to this code
// in any way.
throw new DeveloperException("<some helpful text>");
}
It is up to your particular application guidelines to determine whether or not the exception cases should be immediately bubbled out to the user as errors. Personally, I am not a big fan of |
|
||
|
It seems that every discussion about Eclipse these days quickly degrades into a fighting match about Swing (AWT) vs. JFace (SWT). "Swing is great and it's part of Java. You'd be a fool to anything else!" Rather than attempt to obliterate SWT why don't we embrace it as the must needed alternative. Compitetion is a good thing; it forces each product to a higher level of quality. APIs (especially those for UIs) are not one stop shops. Each product has its pros and cons and having multiple products allows each developers to choose what is best for a particular application. Like Linux to Microsoft, Pepsi to Coke or any coffee house to Starbucks, having Jface / SWT provides a much needed alternative to the firmly implanted incumbent. And having a choice makes everyone happy. |
|
||
|
int hashValue = 0;
for(final Iterator i=entrySet().iterator(); i.hasNext(); )
hashValue += i.next().hashCode();
(This is noted in the javadoc for If you have a significant number of entries in your I would recommend subclassing and overriding
int hashValue = 0;
for(final Iterator i=keySet().iterator(); i.hasNext(); )
hashValue += i.next().hashCode();
as it is common to use simple types for the keys of a map. The use of If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result. contract of If you're jamming a Don't forget that you're still going to get hit with the |
|
||
|
It seems that Kris has been lured toward the sirens that are the proposed JDK 1.5 static imports. I am convinced that static imports will reduce code clarity and therefore increase the bug rate. I offer a contrived example to demonstrate my position: I am working on a class that staticly imports According to the updated JLS: A static-import-on-demand declaration never causes any other declaration to be shadowed. That's fine but it does create confusion on the order of that that would be caused by operator overloading (which is not in Java due in part to "added complexity" associated with it). For the sake of this example, let's say that public static float sin(float angle); public static float sin(double angle); (again, this is contrived to prove a point) and the signature that I added to my class is: float sin(float angle); The call to ... float angle; ... rotated = PI * sin(angle); ... In this exmaple, it may be easy to determine which public static float sin(int angle); that I want to staticly import. I can't do it according to the JLS: If two single-static-import declarations in the same compilation unit attempt to import members with the same simple name, then a compile-time error occurs, unless the two members are the same member of the same type, in which case the duplicate declaration is ignored. So you hopefully see the mess that I'm in. I'll attempt to illustrate to drive the point home.
import static java.lang.Math.*;
import com.someco.MathFunctions; // can't be static
private float sin(final float angle) { ... }
public class ContrivedExample {
...
float angle;
...
rotated = PI * sin(angle); // from local sin()
...
other = sin(angle / 1.5); // from java.lang.Math (1.5 is double)
...
uugh = MathFunctions.sin((int)(angle / 65535)); // from MathFunctions
...
}
That is a debuggers worst nightmare. Of course placing strict coding constraints on how, when, where, and under what conditions static imports are used will help alleviate these problems but given a large set of imports, it might be easier said than done. David Flanagan has also touched on some other issues -- specifically, how one can import a method with the same name but different signatures. Static imports "solves" something that was never a problem to begin with (i.e. explicit names are a good thing). A more suituable solution to this "problem" would be in-line (or horizontal) code folding; just as some IDE's provide the ability to vertically fold various scopes, horizontal folding would fold the qualifier of the name. Updated April 23 at 11:45AM |
|
||
|
My education has been molded within the tenets of the natural sciences. We would follow the scientific method. We had sayings like "if you didn't document it, it didn't happen!". We had a set of common accepted techniques that were used as building blocks to achieve a desired result. Fast forward to the present day and my current foray into computer science. I have struggled to impress the tenets of the sciences into every environment I have participated in: tests exist to ensure correctness and conformity; all code is consistent and thoroughly documented; patterns and common libraries are used. It seems the efforts I take are not universal. I do not claim that I am the only one following these tenets, but I will insist that I am in the minority. Are we practicing computer science or computer art? :s/science/engineering/g
|
|
||
|
The last thing that the MRI technician said to me before leaving the room was: don't move. "Don't move", piece of cake ... or so I thought! Within moments of listening to the ear-plug muted rumblings of the machine I found myself being lulled into a semi-conscious state. Like a student nodding off in a boring lecture, I began the vicious cycle of stupor then jerking myself awake, stupor ... awake. Oh crap, I thought, if I don't find some way of staying awake I'm going to have to go through all of this again. Frantically recalling what Meg did in A Wrinkle in Time to save her from control of IT, I began to recite the multiplication tables in my head. I started with nine (I just like nine). We're going to have to do this the long way: 13 x 9 ---- 27 + 9 ---- 117 Now why do we do this whole "multiply by the ones and write down the result and then multiply by the tens and write that down shifted over one" nonesense? There has to be a simple answer but no one bothered to tell me! Rewriting the multiplication in a more linear form we get: (9)(13) = 117But thirteen can be written as a sum: (9)(3 + 10)Bringing in the distributive law of multiplication: (9)(3) + (9)(10)Simplifies to: 27 + 90Ah ha! I recalled one of my grade school teachers saying that you can put a zero in that "shifted over" spot. 13 x 9 ---- 27 + 90 ---- 117 That makes the two statements identical! This is pretty obvious when you think about it. Since we're taught the mechanics and not the theory it's just all taken for granted. By the time I worked through all of this (as well as some other interesting tidbits involving nine that I will write up later) the technician was rolling me out of the bowels of the great machine. I made it! (After the fact I did recall that it was IT that was trying to gain control over Meg by forcing her to recite the multiplication tables and was therefore the worst thing that I could have done but it had saved me. Thank you Madeleine L'Engle!) |
|
||
|
Some notes on the experimental side of dark matter (Part 2 of 3):
|
|
||
|
From the constructor of
// Find a power of 2 >= initialCapacity
int capacity = 1;
while (capacity < initialCapacity)
capacity <<= 1;
this.loadFactor = loadFactor;
threshold = (int)(capacity * loadFactor);
table = new Entry[capacity];
where
loadFactor = 0.75;
If So what does this all mean? Given a distribution of hash values that fills each bucket only once (such as adding integers) and the default load factor of It should also be noted that chaining is dominant for small non-power-of-two initial capacities (again, given the default load factor). Something to keep in mind. HashMap hash function problems in 1.4.0 |
|
||
|
Incidents like the code snippet below underline the root cause of failure on most projects (and why I fully expect to die from a heart attack at a very young age): if(!((yearObj.options[yearObj.selectedIndex].value / 4).toString().indexOf('.') == -1))
(Sorry about any line wrapping that may have occurred.) That beautiful specimen was purported to compute if a selected year was a leap year or not. No, really. I could spend the rest of this day discussing the failure of the industry to police itself to maintain minimum standards, how programmers are not just generic blobs that can be pulled from one project and jammed into another, how lack of time and infrastructure perpetuate catastropic problems, etc, etc, etc ... but I wont. Another one just in (from the same person as the beauty above): for (var i = 1; i < days + 1; i++)Of course there is nothing inherently wrong with the statement, but what is wrong is that there is a fundamental un-understanding (rather than a misunderstanding which implies that there is some understanding to begin with) of the principles of software engineering. |
|
||
|
While attempting to copy a file's contents to an array of Nothing earth shattering here but it was one of those Hmmmm moments. |
|
||
|
Tip: Be wary of something working on the first try. If something works on the first try, it's guaranteed to be screwed up in some way. A common one for me, as it's easy to forget, is enabling Java's assertions. They're disabled by default and if you use an IDE's fancy doo-dads to automatically run your JUnit tests then it wont have the assertions enabled (you typically have to manually enable them). All shows green and you move on. At some point later you hit an NPE ( |
|
||
|
Java Tip: Put string constants on the left side of a This prevents the dreaded
if(name.equals("rob"))
return;
should always be written as:
if("rob".equals(name))
return;
"rob" will never be null so this is NPE safe. |
|
||
|
Tip: Never check for a single value when you actually are interested in a range. The common case where this occurs is with sizes (list, arrays, etc). The statement:
if(list.size() == 3)
return;
or:
for(int i=0; i!=10; i++)
...
is error prone and should be avoided at all costs. Why? Most of the time the list will have multiple entries added (this is especially poignant in the case of MT (multi-threaded) code) and an equality can be missed. In the for-loop case, it is common (but oooohhh so bad) to see the loop counter manipulated in the loop body. So the correct statements would be:
if(list.size() >= 3) // or (list.size() > 2)
return;
and
for(int i=0; i<10; i++)
...
This is called coding defensively. You're preventing bugs before they've had a chance to form. |
|
||
|
I have worked in an upper management position in a number of small companies throughout my career. It is always been my position to offer up as much of my control to anyone that is willing and able to take it. After a decade of watching people either ignore the opportunity or take the opportunity and completely fall apart, I have reduced the experiences down to the following: If presented with a situation that will deliver everything that you've ever wanted in your career, will you:
Notes:
Corollary 1 If it is everything that you wanted in life and you could not handle it, will you be able to bow out gracefully? Corollary 2 If it is determined that the situation will not be able to deliver everything that you've wanted in your career (as conditions do change) will you be able to control and change the situation such that it delivers on a subset of your desires? There is much more to this post and as I have more time available I will elaborate. |
|
||
|
I am always looking for ways to increase code clarity and reduce confusion and maintenance associated with "dangling methods". What's a dangling method? It's a method that is only used by another method to reduce code duplication. The scope of this method should therefore be local to only the calling function. I tend to run into this problem when doing string manipulation. Currently I need to do a "last added character" for a CharBuffer. The only way to currently do this is to add a member function:
private char lastChar(final CharBuffer buffer)
{
// determine if there are already chars in the buffer. If
// there are none, throw.
if(buffer.position <= 0)
throw new IndexOutOfBoundsException();
/* else -- there are characters in the buffer */
// retrieve the last character placed into the buffer
// NOTE: the above check ensures that there will be a char
return buffer.get(buffer.position - 1);
}
to the class. This is no good since the scope of the method is too large. Large scope equals more time determining dependencies which equals more time to debug. If Java allowed for nested functions, one could write:
private String normalize(final String string)
{
...
// inner function for determining the last character
// added to a buffer
char lastChar(final CharBuffer buffer)
{
...
};
...
case '/':
if(lastChar(buffer) != '/')
...
...
}
Having nested (or inner) functions in Java would help enormously. Kris Wehner brought up a Smalltalk technique which would be somewhat useful in this case. What do you think a solution to this problem would be? |
|
||||||||||||
|
Performance of
For "normal" string processing there appears to be no difference between the two -- the effects are lost in the noise. For large strings (documents and the like), CharBuffer has a distinct advantage. CharBuffer has the perk of pointer-like manipulation via The only caveat with CharBuffer is that the size of the buffer must be known a priori. Notes:
|
|
||
|
"The common belief that we gain 'historical perspective' with increasing distance seems to me to utterly misrepresent the actual situation. What we gain is merely confidence in generalization that we would never dare to make if we had access to the real wealth of contemporary evidence." Otto Neugebauer |
|
||
|
I have found myself in a position where I am yet again wadding through the quagmire that are Java's URLs.
So what is the problem with Java's URLs? Archives (i.e. JAR and ZIP). Play around with URL's such as: jar:jar:file:///some/directory/file.jar!/nested/file1.jar!/finally.txtand you'll know the pain I feel. There will be more on this ... believe me! Side notes:
|
|
||
|
if(file.isDirectory())
// do something with a directory
else
// do something with a file
Unfortunately, the above is true if and only if Since it is possible for a file to be removed between
if(file.isDirectory())
// do something with a directory
else if(file.isFile()
// do something with a file
else
// do something with a non-existing file
A side note to this: |
|
||||||||
|
Saiko
I ordered the Heavens Door sake which started with a heavy chocolate flavor that would immediately vanish as you were trying to determine what it was. My wife had the Pride of the Village which was a crisp fruity flavor -- not overpowering. We started with a california roll (it is a standard that we use to baseline new sushi restaurants) which had real crab meat, avocado, roe and sesame seeds. My wife also had tako (octopus). Both were better than average quality. We are both looking forward to returning to try other sushi. For our entrees, I had the Cripsy Whole (Yokuzuna) Bass and my wife had the Sirloin Steak. Our server (Angelique) recommended presenting the bass and then having the chef filet it -- a wonderful idea. The presentation of the bass was excellent. Its flavor was subtle with a hint of spice tacked on the end and the skin remained surprisingly crispy thoughout the meal. The delicate flavor made this a perfect dish for me. My wife's steak was served with melted wasabi cheese served with pea shoots and a garlic oyster sauce. My wife is not a cheese lover so that was not her personal favorite but overall it was quite tasty. For dessert my wife had the Saiko chocolate which was a flourless chocolate cake topped with green tea cheese cake accompanied by mint ice cream on the side. Unfortunately the chocolate completely overwhelmed the flavors (not that that's a bad thing) but it would have been better if the green tea was a better complement. I had the Fuji Apple Tart which combined a delicate apple tartlet with ginger ice cream with caramel drizzled on the side. Being an apple tart lover this was an exceptional choice. |
|
||
|
Some notes from a lecture on Dark Matter and Structure formation in the Universe (Part 1 of 3):
Course slides are available. A useful astronmy reference. Thank you Andrey Kravtsov for a great talk! |
|
||
|
This site is officially open! Woo-hoo! |
|
|
Unless otherwise expressly stated, all original material of whatever nature created by Rob Grzywinski and included in this weblog and any related pages, including the weblog's archives, is licensed under a Creative Commons License. |