Implementation
The first rule is relatively common advice: separate the model from the view and controller (the MVC paradigm). Usually this is pretty easy, and once you get into the habit of doing this it goes a long way towards making it possible to use the application in a non-interactive mode. But there were always a few places where I wanted to output a message, or ask for confirmation, or do some other kind of I/O that would tie my code to the view.My solution to this problem is to create an IBasicUi interface to handle I/O, an IResources interface to handle resources, an Application class that implements those interfaces for normal graphical use, and a BatchApplication class that implements those interfaces for non-interactive batch use. At runtime, based on the startup options, I instantiate an instance of either Application or BatchApplication as my Application object.
The Application object is responsible for three things:
- Console output such as errors, warnings, questions and info.
- Console input such as confirmations or file names.
- Initializing Resources (resource bundles).
The Application object contains most of the methods that behave differently between GUI and batch use, although occasionally I will use a non-object-oriented approach by putting some of that in other classes and implementing an
isBatch()
method in the Application object to allow
the other classes to change their behavior.
I also use the Application object to store other application-wide state, such as command line options that affect the behavior of the application.
Usage
I would typically pass a pointer to the Application object in to the constructors of my other objects. Alternatively, I sometimes would define an Application class and declare a static variable which I would set to the Application object on initialization, after which I could retrieve it with a static getter method. Finally, if for some reason neither of those two approaches was acceptable, I might put the Application object into a thread local variable and retrieve it from there when needed.I suppose using today's lingo the act of passing in an Application object that could be either an Application or a BatchApplication would be referred to as dependency injection.
An additional benefit I got from having every object use the Application object for input, output and resources was that it made unit testing much simpler. I could create a TestApplication class in which the input and output methods were tied to my testing data files, and the resource methods used a small resource file with resources specific to the test being run.
Another Detail
By default when you start a Java application the JVM initializes the graphical environment. If you are running your batch command from your windowing system, this is not a problem; but if you are running it from a true batch script, where there is no windowing system available, this is not good. To prevent Java from initializing the graphical environment, you have to tell it that you are running in a headless environment. You do this by including the JVM option-Djava.awt.headless=true
when starting your Java application.
The app can tell if this command line option has been set by calling
GraphicsEnvironment.isHeadless()
.
No comments:
Post a Comment