Category: computer

  • Singletons Cause Cancer

    It’s been said before. I’ll say it again. The singleton pattern sucks. From a pragmatic point of view, it has two primary drawbacks: reuse and testability.

    Reuse

    A public static getInstance() method is, by definition, statically bound at compile time. Since you can’t override static methods, reusing singleton code via inheritance means you’ll need to create a new getInstance2() method. No matter how creative you get with this method name, you have to accept that users of your code will periodically call the parent types public getInstance() method instead of your spiffy new getInstance2(). Working off an interface largely becomes a moot point since the developer must know the exact type of singleton they want to use at compile time in order to invoke the correct getInstance() method.

    How do you configure a singleton without a parameter to getInstance(), which would not be consistent with the intentions of the pattern? Since the instance is constructed internally using a non-publically-accessible constructor, there isn’t a convenient way of introducing configuration information before it’s created.. unless the singleton is aware of a configuration source at compile time with yet more static binding. This makes the code very inflexible, as developers intending to reuse it will be at the mercy of your pre-chosen configuration mechanism, which may not be appropriate for their circumstances, or even unit testing.

    Testability

    Unit tests generally require control over the lifecycle of the class under test to fully validate proper state transition and contractual validity. Since you, the master of the known universe, are writing the software, you’ll certainly write negative scenarios into your unit tests to assert proper failure handling. If intentionally introducing a negative test results in an irrecoverable state, how do you throw out the singleton and start the next case with a new one? You can’t. What if your test case is creating a tricky concurrency scenario emulating multiple systems within the sandbox of a single JVM? You can’t (trivially). What happens when you discover you need multiple instances of the singleton within your application? You can’t. Time to refactor.

    Additionally, unit testing of code using static singleton dependencies has a high potential of awkwardness due to an inability to swap out implementations for mock objects. Under the principle of designing for testability, quality and maintainability, hackishness is not a quality to aspire to.

    Conclusion

    Singletons can be hazardous to your health, seriously jeopardize your family’s safety, and have been classified as ‘terrorist patterns’ by the U.S. government. The fact that an application only needs one instance of something does not mean the object should be designed that way, and there aren’t very many scenarios where singletons are appropriate. Do as the Jedi do and use them with consideration and responsibly.

  • On Brooks

    I’ve written this same basic argument for small teams three or four times now, so to comply with the Don’t Repeat Yourself (DRY) principle I though I’d post it. In short, this argument repeats Brooks’ Mythical Man-Month concept 🙂 , and asserts that you must restructure the communications dynamics of a team when it changes in size, regardless of whether or not your project is late.

    To paraphrase one of the points made in Frederick Brooks’ The Mythical Man Month, complexity in a system scales with the number of interfaces. Applied to a team communications system of N people, the worst-case number of total communications channels is when everybody talks to everybody else. This can be expressed by N(N-1)/2. In other words, a given person in the team (the Nth person) is communicating with everyone else (N-1 people). So the problem with large horizontal teams is that as N increases, the communications overhead of being able to operate has the potential to increase exponentially.

    As a member of a 5 person team, keeping open, frequent communication with 4 others is no big deal. Even with 5(5-1)/2 = 10 different relationship combinations, all 5 people can keep current with the other 4.

    If this team grows to 15 people, however, communicating is not as simple. In the worst case, not only do you have to communicate with 14 other peers, there are now 15(15-1)/2 = 105 total relationships between people, each with their own dynamics and influences. The team is only 3x bigger, but now has 10.5x the number of interpersonal relationships. And the majority of the information going across those channels may be irrelevant to your job, but if it’s relevant to the team as a whole, you’ll be sucked into meetings and email threads spending time and mental cycles just on overhead of keeping the collective ship floating in the right direction.

    Now, this is all worst-case abstract theory, but does highlight the scaling problems of communication in teams of fluctuating size. As your team scales, you need to reevaluate communication culture to keep operating overhead low and excessive communication to a minimum.

  • Avoiding if(DEBUG_ON)

    Consider the following Java code..

    public class Counter {
    
    	private final static boolean DEBUG_ON = false;
    
    	protected int mCount = 0;
    
    	public int getCount() {
    		return mCount;
    	}
    
    	public void increment() {
    		if(DEBUG_ON) {
    			System.out.println(“about to increment! count=” + getCount());
    		}
    		mCount++;
    	}
    }
    
    }
    

    Some people use “debug” or “test” flags because they can provide a fast way of troubleshooting situations that would otherwise be inconvenient to debug. If increment() was recursive, for example, it may be convenient to simply keep an eye on stdout rather than set breakpoints and have to step through it. (Note: see an earlier post on conditional breakpoints to avoid this entirely.)

    While the extra DEBUG_ON code is fairly harmless and benign, it’s nevertheless unnecessary code that could potentially break a production system, and even when/if you do remember to comment it out, it distracts the reader from the meaningful, real code you’re trying to express.

    An alternative solution? Simple. Subclass it. Try this instead..

    public class Counter {
    
    	protected int mCount = 0;
    
    	public int getCount() {
    		return mCount;
    	}
    
    	public void increment() {
    		System.out.println(“about to increment! count=” + getCount());
    		mCount++;
    	}
    
    }
    
    public class TestCounter extends Counter{
    
    	public void increment() {
    		System.out.println(“about to increment! count=” + getCount());
    		super.increment();
    	}
    }
    

    Now you can leave your print statements intact, and just use TestCounter as a drop-in replacement for Counter in your unit tests. Alternatively, you could use one of many design patterns to aggregate or compose the Counter rather than extend it, or use dynamic proxies or aspects if you’re looking to intercept calls that exist in a horizontal manner.

  • E-Prime For Software Engineers

    I recently restarted reading Quantum Psychology by Robert Anton Wilson. (Not the easiest read. If you put it down for to long you may have to start over.) The most practical thing I’ve gotten from it thus far is the usefulness of E-Prime. You may already use it and not realize it.

    To attempt to summarize E-Prime with a single example, consider the following two statements..

    1. Alice’s code is broken.
    2. When I ran the unit tests this morning, all the code last checked in by Alice (according to the logs) failed to pass. Thus, I think her code is broken.

    While the first statement is concise, I’d much rather prefer the second because it conveys information about the observer in addition to the observation.

    • The means of measuring brokenness are specified (“the unit tests”). The reader otherwise does not know how the conclusion of brokenness is being determined. Without this information, how is Alice supposed to go about reproducing the issue?
    • A timeframe of failure is specified (“this morning”). Alice may have made the same observation and made fixes shortly after the unit tests were run. The code may have indeed been broken, but only appeared so at the time it was measured.
    • The observations is attributed to the observer (“[w]hen I ran”, “I think”), rather than claiming the an intrinsic state of brokenness about the code.
    • The scope of the brokenness is described (“all the code last checked in by Alice”). All of Alice’s code probably isn’t broken.
    • It is acknoledges that the observation is limited by the means of measurement (“Thus, I think..”). When judged by another means, the code may appear fine.

    In short, E-Prime acknowledges that our capabilities or understanding seem to be limited by our instruments of perception (be them eyes, ears, unit tests or ammeters). Take a looksy at some other examples.

  • Java Development w/The Eclipse IDE: Five Underused Features

    Conditional Breakpoints

    Have you ever wanted to suspend execution only under a certain circumstance while debugging, such as every hundredth loop iteration? You can. Set your breakpoint, right-click it, select “Properties”, check “Enable Condition” and enter a condition in normal Java code that will be evaluated every time the breakpoint is hit to determine whether or not to suspend the JVM.

    Remote Debugging

    Sometimes things work on everybody’s box.. except Bob’s. You’ve watched him produce the issue and have tried to reproduce it on your development machine, but it just ain’t happenin’ for anybody but him. It’s time for a remote debugging session. Start up the JVM on Bob’s box like so..

    java -Xdebug -Xrunjdwp:transport=dt_socket,address=8000,suspend=n,server=y
    -jar YourApplication.jar

    Within Eclipse IDE on your development machine, select Run -> Debug… and create a new “Remote Java Application” configuration. Select the project that contains the source code, enter Bob’s boxes hostname (or IP address) and the port his JVM is listening on (8000 in this case). Click the “Debug” button to close the dialog and your “Debug” view should now show a new session, connected to Bob’s JVM, and the threads of your application just as if you were debugging it locally. You can set breakpoints and use all the other features described in this article, or impress your mom by starting another instance of your application locally and step through both Bob’s and your own JVM instance simultaneously, side-by-side!

    Dynamic Variable & Code Replacement

    If you’re debugging a tricky piece of SQL in a servlet that isn’t working right, it’s tempting to abide by the typical write, compile, deploy, run, debug, repeat cycle every time you need to tweak the String. This is tedious and time consuming. Instead, set a breakpoint someplace after the String is defined but before it is used. When you hit it, right-click the variable in the “Variables” view and select “Change Value…”. You can figure it out from there. (Note: This doesn’t seem to work for “final” variables, and so-so with statically declared things.)

    If you modify and save a class while you have a debugging connection established, Eclipse will try its best to update the code on the remote JVM without having to restart it. This doesn’t always work (especially if there are stack frames using the changed code), but you’ll be notified if the remote JVM could not be updated. This is especially useful when testing a complex algorithm in a situation that is a pain in the arse to set up.

    Custom Code Templates

    Typing “fore[autocomplete]” in a block in Java code will add a “foreach” code template that you can fill in using the TAB key. These are customizable, and you can even add your own. For example, I prefix all generated local variable and Iterator declarations with the “final” keyword, and have added my own custom templates for inserting my most frequently used Java 5 annotations. Go to Window -> Preferences… -> Java -> Editor -> Template and have some fun.

    Refactoring Actions

    All of the refactoring features of Ecilpse IDE are worth learning. I can’t think of one that isn’t. Once you have a method defined and used, there isn’t much reason to manually modify the declaration. “Move”, “Change Method Signature…”, “Pull Up…”, “Push Down…” etc. give you basic, commonly used tools, and actions such as “Extract Interface…”, “User Supertype Where Possible…” etc. provide support for larger jobs. Understanding what all these do helps relieve some of the pain felt when refactoring heavily used, public APIs, and helps keep you focused on the bigger picture by taking care of the details.

  • If A Unit Test Fails In The Woods, Does It Make A Sound?

    No, it doesn’t. Unit tests that execute a large amount of code but fail to make assertions along the way give you a false sense of confidence in the code. They pass when they should fail. These problems, formally known as type 2 errors, are a huge liability for a development team because the tests are believed to be verifying the intended behavior of the software, but are really doing nothing in a really lengthy way.

    For a new person maintaining the code under test, the problems worsen. The new maintainer will not understand what the code is supposed to be doing: what it’s currently written to do or what is implied by the possibly out-dated and incomplete unit tests. Good luck finding API documentation if the unit tests suck, and have fun with those future API changes when you must attend to the “unit tests” that need to be updated to successfully add no value to the project, just as they were originally written. No thanks.

    Units test exist to prove that software is behaving as intended, not simply “mock” user actions. This means being particular about states of things during a process, and doing mean negative testing by passing nil into that function that clearly requires a non-nil value. The rule of thumb is this: if, for whatever reason, you cannot write, fix, or otherwise finish work for a correct and complete unit test, assert false. You have not proven the software works correctly, so it doesn’t work. Period.

  • iClock: How To Get The Time.. Apple Style

    What the heck do you do with an unsightly Hershey’s Kiss shaped Apple Airport? Turn it into a clock! The RJ-45 and power ports have been relocated as dongles on the back of the unit to make room for the clock battery. The mechanical clock hardware is inside the base station.
    iClock (Thumbnail)

  • Ant Rant

    I’ve come to loath ant.

    XML is great for a lot of things, but is simply painful for expressing control flow. Now don’t get me wrong, it’s still easier to work with than make; you can use any XML editor or a web-browser to validate the well-formedness of an ant build file. It’s just that programmatic thought isn’t elegantly represented by a tree structure full of angle brackets. Trees are simplifications computer scientists use to fit information into limited structures that can be easily parsed and manipulated, however, as a user of the system I don’t give a flying flock that it uses a standard SAX parser. I do care about defining basic control-flow operations in a natural way, and not having to create artificial work-arounds to cater to the structure of the tree. Elegance, however, is the least of ants problems. It lacks a fundamental development capability that is missing from other build systems such as make and jam, and even long-established shells such as bash: debuggability.

    Here’s the deal, plain and simple: if you’re writing a development tool, you must provide some mechanism for easy debugging. “Hello, world!” does not require troubleshooting. Every other real-world “script” does. If you’re automating a build process for a large application, it will have bugs, and when you do get them squashed the system will soon be inadequate and require modification. Now, when I say “debuggability”, I’m not talking about scattered echo/print elements throughout the code or color-coded syntax. I’m talking about stepping through the script line by line and examining the inputs and outputs of every step in the process, just like you would with any C++ or Java program,

    The last item in today’s rant is support for basic data structures. Variable passing is pukingly awkard in ant, and calling other targets sometimes requires firing up another ant process. Umm.. riiiiight. “If today is Saturday turn debugging support on, run all known unit tests and send a status report to all team members” is not an unrealistic use of these systems. Debugging this should not be such a chore.