Using JUnit 4 Theories to Test Contracts 5
I’ve been putting off upgrading to JUnit 4 for a while. After all, just how much does it really buy you? It turns out, that JUnit 4 grants you a number of advantages that weren’t available with JUnit 3. One of those features is currently in an experimental phase: Theories. Theories let you specify a bunch of data points, which can be applied to each theory in your class. For example, if you want to test some boundary conditions on a class, you can make it happen easily like this:
@RunWith(Theories.class)
public class VerifyMyAlgorithm {
@DataPoint
public static int limLow = 0;
@DataPoint
public static int limHigh = 3000;
@DataPoint
public static int belowLimLow = limLow - limHigh;
@DataPoint
public static int aboveLimHigh = limHigh * 2;
@DataPoint
public static int median = (limHigh - limLow) / 2;
@Theory
public void verifyTwoParameters(int first, int second) {
assertInRange(limLow, limHigh, MyMath.algorithm(first,second));
}
}
I wanted to keep it simple just so you can get the basic idea. Essentially the @RunWith() annotation changes the basic behavior of your test class. It enables the use of the following annotations: @DataPoint, @DataPoints (more on this later), and @Theory. The data points you specified are collected and matched together so that each unique combination of datapoints is applied to the parameters in your theory. If your theory takes one parameter, the theory is run once per data point. If your theory takes two parameters it is run with every unique combination (15 times in this case).
But wait, there’s more! Sometimes we want to generate our datapoints with code. For example, we may need prime numbers up two four significant digits, or we need to initialize our data. In order to do that, we can use the @DataPoints annotation. The return type of your static method will be an array of datapoints. I could rewrite the example above like this:
@RunWith(Theories.class)
public class VerifyMyAlgorithm {
@DataPoints
public static int[] attemptLimits() {
return new int[] {0,3000,-3000,6000,1500};
}
@Theory
public void verifyTwoParameters(int first, int second) {
assertInRange(limLow, limHigh, MyMath.algorithm(first,second));
}
}
The DataPoints functionality in concert with the theories are the foundation of what is ncessary to automatically test the contracts of each service. I’ve thrown together a rudimentary class scanner that uses reflection to iterate over classes in the classpath to determine if they match the criteria for the service. For example, you can look at all classes that implement an interface, or extend a base class, or even have an annotation. From that list of classes we can test the contracts of the implementations. The nice aspect of this approach over creating a base test class and extend it manually for each implementation we write, is that it automatically finds the new implementations for you. I have yet to release the class scanner, but you can implement your own pretty well. Here is an example of how it would be used:
@RunWith(Theories.class)
public class EnforceLocakableContracts {
@DataPoints
public static Lockable[] collectImplementations() {
Collection<Class<?>> klasses = new ClassCollector()
.assignableTo(Locakable.class).recurse().collect();
Lockable[] implementations = new Lockable[klasses.size()];
int i = 0;
for (Class<?> klass : klasses) {
implementation[i] = klass.newInstance();
i++;
}
return implementations;
}
@Theory
public void lockShouldApplyToOneUser(Lockable lockable) {
User one = TestSupport.createUser("one");
User two = TestSupport.createUser("two");
assertFalse(lockable.isLocked());
lockable.acquireLock(one);
assertFalse(lockable.canAccess(two);
}
@Theory
public void lockableShouldBeAccessibleByLocker(Lockable lockable) {
User one = TestSupport.createUser("test");
assertFalse(lockable.isLocked());
lockable.acquerLock(one);
assertTrue(lockable.canAccess(one);
}
}
So on and so forth. Just add a new Theory for each aspect of the implementing class you want to test. With this approach, testing implmentations of an interface is essentially future proofinf yourself against lazy programmers. There are some aspects that this approach doesn’t handle well just yet, such as complex setup for each object.
As of JUnit 4.7 there are a few problems, but they are not insurmountable:
- You see one pass/fail per theory (not per implementation)
- Failures do not show what implementation failed (fixed in the stacktraces provided by JUnit 4.8.1)
- The first failure kills future tests. You may have errors in multiple implementations but you won’t be able to find it until you fix the first implementation.
The best thing is to upgrade to JUnit 4.8.1, which is still not available in Maven repositories yet. However, you can also use the System.out approach to find out what the last thing tested was.
There is a feature request for theories to allow you to run them discretely. I.e. each run gets displayed in your test report individually with the parameters supplied in the test name. That will address the shortcomings and make this an even better approach.
Wicket Impressions 1
I chose Wicket for this project, partly because I met some people who were very enthusiastic about the framework, and partly because I wanted something that was easily tested. My client requires a Java solution, so Ruby on Rails was out. I did a quick little shoot out between three Java framework options, and Wicket passed the first impression stage.
Now I’ve got a couple of iterations of software development with it under my belt and I’m starting to feel a bit more comfortable with the framework. As far as Java web frameworks go, its pretty good, but… I would do things a bit differently. The application is written in a style that would actually better fit Ruby, SmallTalk, Lisp, or some language that supported passing in procedures or lambdas (as the language gurus call them) natively. Take for instance the Link class. In order to do anything useful, you need to subclass and implement the onClick callback method—even if all you want is to go to another page. Here is an example:
add(new Link("wicketId") {
@Override
public void onClick() {
// do fancy logic
setResponsePage(new IncidentPage(incident));
}
});
If this were Ruby the code would look like something like this:
self << Link.new("wicketId") do
// do fancy logic
respond_with(IncidentPage.new(incident)
end
So am I complaining about semantics? Possibly. However, there are more “Java” ways of accomplishing the same class—particularly if you want to manage all business logic in a few classes. The more “Java” way would be something like using the java.lang.Runnable interface. That would allow you to set up an enum to include all your business logic like this:
enum IncidentLogic implements Runnable {
EDIT {
@Override
public Page run() {
// do fancy logic
return new IncidentPage(incident);
}
}
}
and it would be used in your page like this:
add(new Link("wicketId", IncidentLogic.EDIT));
I’ve also experimented with using a dynamic proxy to bind any method to an interface. That would let you set up a logic utility class and reference it like this:
add(new Link("wicketId", Linker.link(IncidentUtility.class, "edit"));
It’s not as pretty but it works. Now here’s the nice thing about Wicket. If I feel strongly enough about it, I can create my own subclass of the Link object to implement that approach myself. My DynamicLink would look something like this:
public class DynamicLink extends Link {
public static interface Executable {
Page do();
}
private final DynamicLink.Executable click;
public DynamicLink(String wicketId, DynamicLink.Executable action) {
super(wicketId);
this.click = action;
}
@Override
public void onClick() {
setResponsePage(click.do());
}
}
In fact, I think I may just do that. It’s better than having bits and pieces of behavior spread throughout the Pages and panels defined in the application. I think I find it a bit frustrating when i need to do things like that just to format dates the way I want. Why can’t the provided DateFormatter let you set the pattern you wanted to use? Why do I have to subclass the one in Wicket just to get that feature?
At the end of the day, my complaints are minor and the Wicket community is helpful. I’m fine with that. The Wicket framework is probably one of the leaders in providing a test harness that can be used easily in your JUnit tests. That is a much bigger selling point to me.
Wicket, Spring, Hibernate, Testing... Yeah, it's like that.
I have to deliver a product using Java. Due to politics and approved baselines, etc. I can’t use Ruby on Rails. I’ve done my own thing in the past, and so this time I decided to save some time by incorporating Wicket, Spring, and Hibernate using auto-wiring, and attributes. Getting it all to talk together took the better part of a day, but I got it to deploy and run fine.
I’m an avid unit tester, so I was happy to learn that Wicket has some facilities to make testing a bit easier without forcing you to create your own Servlet mock objects. I was really happy about that, so I decided to try it out. Unfortunately, I ran into a rather nasty roadblock. Spring was complaining about the ContextLoaderListener not being loaded. It’s defined in my web.xml file, but the autowiring requires the spring context to be loaded in a particular location. Finding out how to fix that problem took me the better part of half a day.
The problem is that Wicket hides the ServletContext object away from you. You can’t add values, because the test framework is initialized on construction. Attempts to obtain the ServletContext and manually add the Spring web application context just weren’t working. That’s because it is configured when the WicketTester object is constructed and ignored after that. I needed to be able to inject my own ServletContext with the Spring integration taken care of. I finally figured it out here, because the forums just working for me. Please note that this is tested with Wicket 1.4 and not any of the earlier versions:
final MyApplication app = new MyApplication();
tester = new WicketTester(app) {
@Override
public ServletContext newServletContext(final String path) {
// web context
ServletContext context = new MockServletContext(app, path);
// spring context
XmlWebApplicationContext spring = new XmlWebApplicationContext();
spring.setServletContext(context);
// configure spring
spring.setConfigLocation("classpath:application.xml");
// put spring where Wicket can find it
context.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, spring);
return context;
}
};
So let me explain what I did. I created an anonymous subclass of the WicketTester class so I could override the factory method “newServletContext” to inject my spring configuration. Done properly, the wicket folks can take care of this by providing a SpringWicketTester subclass in their spring integration library. The SpringWicketTester would let you specify the location of your Spring configuration file, and it would override that method for you. For now, you’ll have to do it yourself.
I put the WicketTester initialization in a subclass of the JUnit TestCase class. That way I don’t have to repeat myself for setting up my Wicket testing.
What's wrong with this line? 3
Here it is, brace yourself:
private final Logger logger = Logger.getInstance(MyClass.class);
Oh man, is that a dead ringer for bad code. After all… Oh, wait a minute—isn’t that how you initialize loggers all the time? Ok, what if I told you that MyClass is an enum? That’s right, in Java 6 you can’t even access the class static member variable for an enum. In Java 5 this is perfectly legal and your code will compile and work. In Java 6, the powers that be have decided that you can’t do this even if you are compiling for Java 5! This is wrong, and a good way to break everyone’s code.
I’m sure you’re going to ask me what I’m doing with setting up a logger in my enum aren’t you? The trick is that we need to access some property information to determine if we can use certain values in our environment. The logger is there just in case something goes wrong, and something always goes wrong the first time. In the future, the values will be populated with information from a database, so it won’t be an enum any more.
I have to ask, Why would accessing the static class property that is a part of every class object cause a compilation error? Perhaps, in Java 6 the class never truly exists. I don’t know. Still, if you have that ability in Java 5, why would you remove it in the next version? Please Sun, act like you have some sense.
To Throw a Checked Exception or Not 6
I understand the concept behind “correctness” and the compulsion to throw checked exceptions by Java developers. It’s the only language that has a distinction between runtime exceptions and checked exceptions, and it has wreaked its havoc on the world. Consider the problem of finding a piece of data. For example, Location.find(String) would return a Location object that is related to the string that can be an ID, routing number, or name. The typical Java developer would then dutifully create a LocationNotFoundException and throw that any time find can’t return a result. That’s even more likely if the Location information is stored in a database. The alternative would be for Location to return null if it can’t find the object. So, let’s look at what might happen in some code:
// From an address object
Location getLocation()
{
Location location = null;
try
{
location = Location.find(routingNumber);
}
catch(LocationNotFoundException lnfe)
{
try
{
location = Location.find(addressName);
}
catch(LocationNotFoundException lnfe)
{}
}
return location;
}
There is so much extra crap in here that is required because of the fact the method throws an exception. All we want to do is try to find a location by a routing number, and then try looking up the location by name if the mapping hasn’t been made yet. Now, what if we didn’t have to worry about an exception?
// From an address object
Location getLocation()
{
Location location = Location.find(routingNumber);
if ( null == location )
{
location = Location.find(addressName);
}
return location;
}
Isn’t that so much cleaner? Sure, I could have let the method propogate the exception, but if the could assumes that no exception is thrown, it will never reach the second try. That’s why consistency, even if it is cumbersome, is important. There is a time and place for exceptions, but in many ways it would be more graceful to handle the error condition differently.
Now what about debugging? If a method returns null if it can’t find the result, what if the reason is because the database connection went down? Even if the thrown exception is the same, Java allows you to attach the exception that caused this one. The information is available. However, it is also my experience that many developers throw exceptions away. Did you see that in the first example? Logging helps, but in the long run, it is better to use unit tests to verify that everything behaves as expected.
Helper classes in Java ActionPack: Design Decisions
One of the nice features of Ruby on Rails is the ability to mix in helper classes with your controller. This works seamlessly because of the design of the Ruby language. When you try the same approach in Java it’s just plain impossible. For one thing, Java has no way to mix things in properly. You could have a setter/getter for the helper class, but you run into type issues if you want to do it automatically. Either you have to explicitly cast all helpers when you use them, or you have to rely on Java’s broken generics approach. It’s twofold the problem because the controller needs to access the helper, and the helper also needs to access the controller.
Now, Ruby on Rails uses the same helper class mixed in to the controller as well as into the view. At this stage in the game, I think that’s pretty much a pipe dream. I mean, it’s possible to add the helper class instance to a “helper” request attribute, but JSTL and JSP EL doesn’t allow you to call methods with parameters so it’s kind of a moot point. I’d like to fix these limitations in an Java ActiveView project, but that’s in the future.
Without worrying about all the niceties of the way I would prefer to work, I still need to work within the constructs and design mindset of Java. Sometimes the straitjacket forces you to be creative, but I can’t think of any clean way that will work without hijacking the compiler. That’s not really an option because it creates the need for tools just to work with the framework. It’s highly invasive, and it would make me less likely to use the tool—and I’m the one writing it.
The short term solution is to make all the protected support methods in the controller public. That allows users to pass the controller to the helper class, and the helper can invoke whatever methods it needs. That also means that I need to make the “layout” property have setters and getters. Perhaps a medium term solution would be to create a Request Package that included the request object, session object, response object, and all the helper methods to manipulate them (like “redirectToAction” and “sendMessage”). The controller would either use this object directly, or the same methods would simply delegate to the request package object. That object would then be used for both the helper and the controller. That will allow you to have a series of static methods that consume the request package or the controller directly. It’s up to you how you want to do it.
I haven’t really finished my decision process, but I’m all for any ideas that any of you may want to throw my way.
Controller Mixins? 2
One of the things I’m wanting to solve (in a simple and elegant manner) is the problem of caching. Essentially I want to introduce an interface that you can place on things that would be put in a request or session attribute so that I can determine the cacheability of the page dynamically. It would be easy enough to introduce a base class that takes care of this for me. The problem is that not every controller needs to have caching, and some controllers need to force no caching within the same application. Not to mention, if we want to add a set of actions to an existing controller like the Ruby on Rails scaffolding mixin we would be required to either have a multiplicity of base classes or we would need a mixin functionality like Ruby.
Now, the cool thing with ActionPack is that we are already calling methods introspectively on a class, so what could be difficult about calling methods on another controller that is “mixed in” to your controller? That allows you to manage functionality chunks effectively. The mixin controllers don’t have to be (and really shouldn’t be) in the same package as the rest of your controllers. The only issue comes up where a controller has the same action of a mixed-in controller. The action has to be simple and predictable every time. My current thinking is to provide the same action as inheritance. I.e. the new action would override the old action. The only issue comes with calling super.actionName() within the action. How do we capture that? The only way I can think of is that we provide one method to handle the delegation if it is called. For example yield() would tell the controller to look in it’s mixins for the corresponding action and call it.
The idea is pretty cool, and the ability to snap in aspects of functionality into your web application is truly useful. It’s a truer version of separation of concerns without the intrusiveness and lack of clarity as the more traditional aspect oriented programming (AOP) efforts. However, it does introduce a certain intellectual complexity. We have to make it really obvious to developers that mixins are being used, and we have to use a design principle that Java developers are used to. That’s why I intend to imitate the functionality of inheritance as best I can. Mixing in multiple controllers also requires predictability. The order that a developer mixes in controllers must be respected. If two controllers don’t play nice together, the developer needs to have the ability to override the order that the actions are called.
I’m balancing the payoff for the complexity involved. I’m also thinking about how to implement the thing, to keep things as transparent as possible to users of the ActionPack framework, yet apparent as possible to developers of the ActionPack framework. The one thing I haven’t touched is what to do with views associated with the mixins. That’s going to be a big issue to work out later. First I want to do logic only mixins. Yet another question is when are mixins allowed to be added/created? It might be terribly useful to add mixins based on user parameters. For example, if the application supports users from several companies, and they want navigation cues specific to their company then adding in the mixin specific to that company would be something done in a filter. If we provide no limitations, and only require that the mixin is applied before it is used, that might also be an option.
I’m thinking that the mixin approach is definitely going to be used, the question is really how to make it easy to understand .
Annotations: The Language Shoehorn?
A while back, before they had such a wide variety of shoe sizes to fit everybody’s foot we had this tool called a shoehorn. The shoehorn was there to help force your foot into a shoe that no longer fit. Well, it’s purpose was to keep the heel of the shoe from collapsing—but back in the day we used it to get a little more life out of a shoe that was too small. Buying a new pair of shoes was out of the question, because it was a choice between spending the $30 on shoes or some other need that we had. Times have changed, and so has my budget.
Background
Software languages have undergone many different fundamental shifts in thought over the years as well. Originally we had machine language and its symbolic cousin Assembly Language. Originally they had little structure and GOTOs (AKA JMP instructions) were written to specific addresses. Some natural evolutions happened as processors became more complex and subroutines were born, enabling some of the more Calculus like breakdown of applications. The first thought shift was Structured Programming which replaced hard addresses with symbols and registers with variables, and enabled an arbitrary number of variables—despite the limitations of the processor underneath. Then came Procedural Programming with C and its collection of similar languages. You’d think that Object Oriented Programming came next, but that was just one of several other shifts in thought. You also had declarative languages, logic languages, etc. For a longer breakdown you can visit Wikipedia’s Programming paradigm page. Each programming train of thought lends itself to a particular set of problems.
The ultimate sophistication is simplicity. Leonardo da Vinci
There are many aspects to simplicity, and it’s easy for me to throw this term around and add the disclaimer about how it’s easier said than done. Instead, let’s explore what makes software simple. What goes into this word simplicity that I keep harping on?
- Economy of concepts. There should be no more than one or two fundamental design principles that are used consistently, and without exception. If there are two principles, the choice between them also has to be consistently applied and clear when to use which principle.
- Consistency. As soon as you add one exception to any rule, you’ve just made it a lot more difficult to understand. For you math happy folks think of the complexity in terms of Re+1 where R is the number of rules and e is the number of exceptions (in total). Smaller numbers are more simple and easier to understand. For example, if you have 9 rules and rule 1 has 2 exceptions and rule 3 has 4 exceptions the complexity is 96+1 or 4,782,969. That’s bad.
- Predictability. Every action that you can make has to have a clear and predictable outcome. To understand the impact of unpredictability, treat the math for predictability like the math for consistency. For everything that can go wrong, you’ve just made the system that much harder to comprehend.
- Self evident. Based on the consistently applied principles and the predictability of actions, the course of action for the user to take should be self evident. In short, once you’ve learned how things work you shouldn’t need anyone to hold your hand when you have to make a change.
Now you get an idea of what I mean by simplicity, and how it is actually hard to pull off for anything but simplistic systems. I mean if you only have two rules and two exceptions you still have a relatively simple system. Of course, once you start adding more rules and actions the complexity increases exponentially.
Annotations Are Mixing Concepts
I’ll be the first to say that Annotations are not completely useless, and they can help address some otherwise potentially sticky problems. However, as soon as you use Annotations to perform a programming task you are using declarative programming. Java and C# are supposed to be Object Oriented Programming Languages. Build languages like ANT and Make are declarative languages. You declare some tasks and their dependencies and allow the build process to sort it all out. Java wasn’t designed like that, but because there were some aspects of programming that are too hard to do in Java’s version of OOP we introduce annotations.
Annotations provide a way to mix concepts in a declarative fashion.
All the Annotation does is mark something in the class with some sort of tag. In essence it’s like a marker interface for the class, method, attribute, or parameter. The most innocuous use of Annotations is simply to mark a method for documentation purposes. It doesn’t take up compiled class file space or runtime memory, just gets added to the documentation. That’s a reasonable and predictable use for annotations.
The tricky part is when we change what the application does when we use annotations. For example, marking methods as a web service endpoint changes how the method is used. Marking a method as a test method affects how that method is called. Or in the case of Java ActionPack, marking a method with @Filter has more side effects. That method will now never be used for an Action, and the filter will be called before/after all the actions that it applies to. I recently added a convenience method so that you can query whether an action is marked with an annotation or not.
I used this solution so that I could mark all my AJAX support methods with the @AJAXSupport annotation. Why would I want to do that? Two reasons: for documentation purposes, and so that I don’t have the Single Sign On (SSO) service bring me back to an AJAX support page. The system would automatically set a callback URL in a filter applied for every action in the system. If someone called an AJAX support action that returns a partial page result, and then clicks on the SSO link the return URL from the SSO service would be for the partial page result. I had to do something (other than manually listing the actions and testing each one of them) to prevent that from happening. So I added an annotation so that I could check if the action has that annotation or not.
When I explained that to someone on the team, they responded with “I would never have guessed that”. Of course they wanted the equivalent of a modal dialog box on the web—which I believe is bad even in desktop application design. Another complaint was that I was mixing application logic with display logic. To be fair, it is navigation logic which is sometimes considered application logic and sometimes considered display logic in different situations. However, from a pure “separation of concerns” principle I was now mixing concerns in my code.
Does that mean that the solution was bad? It works, and it was the simplest thing to maintain and perform. It does not mean it is the most elegant solution or the purely simple solution. For one thing, I’m mixing declarative logic with object oriented logic. Another problem is that I’m mixing navigation logic with application logic. To some extent you can’t get around that, but that can be a symptom of design smell as well.
So am I saying that Annotations are evil, and because they are mixing concerns and programming paradigms we should avoid them at all costs? No. I’m saying that they trade code complexity for conceptual complexity. The complexity is not really gone from the system, it’s just that it has been moved to a different form of complexity. You might be able to grasp each concept in isolation, but mixing the two will probably make it hard to maintain in the future.
Part of the reason that Ruby on Rails works so well is that they did keep the concepts as simple as they could. There are relatively few rules to learn, and the rules are applied consistently. It was also built on a language that was flexible enough to keep everything object oriented, so there is no need to resort to the declarative annotation model.
Justification for On Complexity
I chose the formula because as soon as you introduce one exception, the human mind has to sort out whether or not that exception applies to this rule. Even if there is one exception to a list of 10 rules, we have to recall whether the rule we are using has the exception or not, and if it does whether the exception is in effect. A list of rules with no exception still has the complexity of deciding which rule (or set of rules) applies so it has a base exponent of 1. You add one exception, and you’ve squared your complexity. For example a set of ten rules with no exceptions has a complexity score of 10, and as soon as you add one exception it becomes 100.
The complexity values have no units and no real meaning other than to provide a relative indication of what you are getting yourself into. It helps you understand the number of decisions the brain has to sort through before it is satisfied that it can predict that you are doing the right thing. Sometimes a little change can provide a huge impact on the relative complexity. The idea is to keep the numbers as small as you can.
When Code Outgrows its Design
I’d like to tell you I get everything right the first time, but then I’d be lying. However, what if I told you I got it wrong on purpose? Would you believe me? That said, the fundamental premise of YAGNI (You Ain’t Gonna Need It) is that you do the simplest thing that can work, and refactor later. The principle behind continuous design is that you put in enough structure to work, and refactor and modify as you go on. As you learn how your application’s structure is growing, you find places where you’ll need to help it out a bit.
One such example is the Controller base class in the Java ActionPack code. I started out with a base structure that I need an instance of a controller, populated with the current request and response objects—similar to how JSPs work. Then I started adding some helper methods to make working with controllers easier. Today, I have a mixture of an object factory (and its supporting structure) and helper methods and attributes. The Controller code is getting pretty big, and it may be hard to draw the line between support and factory related code.
So what do you do? The answer is obvious, you refactor. Now is the time to separate the factory code from the Controller base class. According to the GoF Design Patterns book, the proper way to do a factory is to have a factory class whose whole responsibility is to create instances of a different class. In short one objects makes a whole bunch of another object. So why didn’t I start there? In the beginning the Controller class was primarily a factory itself. We really didn’t need to separate things. YAGNI. Now that the code has grown, and we’ve added a lot more support, the time has come to use the factory pattern as specified in the GoF book.
Designing for Change
It should be obvious, but one of the chief principles of object oriented programming is to hide the details of how to do things, and you just tell objects to do a job. You only make available what you intend users of the object to use. That way we can hide changes to the algorithm without breaking compatibility. For example, now that I’ve identified a weak point in the Java ActionPack code, I need to be free to fix it without breaking every web application using the software. Part of the reason for having the Dispatcher and Controller classes in the same package is to provide package access to the factory methods on the Controller class. That means I can safely make the change because that part of the API was never made available to the average user.
Both public and protected methods are usually exposed to the user in some way. Either they are helper methods such as “redirectToAction”, or they are meant to be accessed from anywhere. Package protected (a Java unique feature) and private methods are typically where the guts of things live. They are hidden APIs that can be adjusted as you wish without fear of affecting users.
Example of Simplicity in Design
It’s all well and good to preach simplicity, but if you don’t provide practical examples then no-one can get it. I’m going to highlight one section of Java ActionPack to show simplicity in action. The first mistake that people make when talking about simplicity is confusing it with simplisticness.
Simplicity is the ultimate sophistication. Leonardo DaVinci
So what does it mean to use simplicity in code? First, simplicity has to do with keeping the responsibilities of your code small and focused . You should resist adding responsibilities to a bit of code unless there is no better place to put it. The org.dhaven.actionpack.Route class in Java ActionPack takes a URL pattern, and set of default values and populates request attributes with the substitution values. That’s it. Nothing else. It’s the controller code that reacts to the special attributes “controller” and “action” to find the controller and action to execute. The org.dhaven.actionpack.Routes class will allow you to register a series of Route objects to evaluate in order. It also provides a method to iterate through the Routes and return when the first match has been found. Pretty sweet, huh? Two classes that have a relationship, but their responsibilities are very small.
Nice overview, but can we look at things in more detail? Sure thing. I wanted to make sure it was easy to programatically set up the Routes you wanted your application to use. If you didn’t want a routes file hanging around, or you wanted to embed this framework someplace I hadn’t expected, it shouldn’t be hard to do. I.e., I shouldn’t have to try and set up an object tree like the Apache Digester based projects. When you try to start from the configuration file format, instead of how you should add new routes to the system it gets really ugly really fast. So I wanted to make it simple , or easy to do. My solution? Why not simply call Routes.connect() ? If you were to set up the routes manually, it would look like this:
public static void setUpMyRoutes() {
Routes.connect("/{controller}/{action}/{id}");
Routes.connect("/{controller}/{action}");
Map<String,String> initParams = new HashMap<String,String>();
initParams.put("controller", "home");
initParams.put("action", "index");
Routes.connect("/{controller}", initParams);
Routes.connect("", initParams);
}
Notice that I’m only passing strings into the Routes object? In turn, the Routes object doesn’t do anything with these strings other than creating Route objects with the parameters passed in to the constructor. It’s the Route object itself that knows what to do with the provided strings. The initParams map is also a simple map, which provides default values. The default values are overridden by the values interpreted by the URL. What I haven’t done, and I probably ought to do is to make it so that if defaults are present, the same Route can match partial URLs like the Ruby on Rails version of the functionality can. Perhaps that is a future enhancement. Remember, it is only the Route object I have to alter to make that possible. Setting up the Routes is as simple as it gets.
So what if I don’t want to worry about setting up the routing myself? Well, why not create a route parser? That’s exactly what we did. The org.dhaven.actionpack.RouteMapParser handles parsing a very simple route file format that isn’t XML. In fact, it’s similar to the Ruby format (with minor differences). I didn’t want a full on Ruby parser, just a convenient format. If you call org.dhaven.actionpack.RouteMapParser.parseFile() while passing in an InputStream or Reader, you can have it parse the file for you. The parser is very simple as well. First, all routes are completely defined on one line. Second, blank lines and lines that start with the ’#’ character are ignored. Why the ’#’ character? Because it is used as the comment marker in Properties files, shell scripts, and Ruby files. It should be reasonable to assume people know that is a comment. So what does a line look like? Well, let’s do the same thing with our file that we did in the method above.
"/{controller}/{action}/{id}"
"/{controller}/{action}"
"/{controller}", action => "index"
"", controller => "home", action => "index"
The first part of the line is the URL in quotes. This is the “route” that is being connected through the Routes class. All elements of a route are separated by a comma. That means the route and all default request attributes are separated by commas. The key/value of each map entry is separated by the ’=>’ combination. That separator is a convention used in Ruby and Javascript, but it looks right. No quotes for the key, and the element is surrounded by quotes. There really isn’t much of a reason for this convention other than I thought it looked easy on the eyes. With everything in quotes it got tough to keep straight what were the keys and what were the values. Now, all the key/value pairs are strings. It’s up to you to perform the conversions to other types in the controller if you want. The reason for that has to do with simplicity of design, and not getting the whole route map process too convoluted.
The RouteMapParser takes care of calling the Routes.connect() method which in turn takes care of creating the Route and putting it in our list of routes to match against. Each responsibility is clearly mapped to one class. If someone else wanted a different format for setting this up, there would be nothing to stop them from doing it. It also makes understanding each class much easier. The RouteMapParser only has the responsibility of converting a file to a set of routes. The Routes object only has the responsibility of managing the list of routes and testing a request against that list. The Route object has the responsibility of trying to match the URL, and then populating the attributes if there is a match. The Route object may be expanded in the future to take the parameters and generate a URL from them (as Ruby On Rails currently does), but that is a future enhancement.
The bottom line is that the responsibilities are finite and related to each other. More importantly, the concerns of configuring the system are kept separate from the code that does the work. The number of objects needed to do simple things is very small, and the interactions are well defined and easy to understand. The simplicity allows the objects to be used in ways I may have not foreseen, but that doesn’t really matter. What matters is that the sophisticated uses I haven’t foreseen are because of the simplicity of the classes governing that responsibility.
Older posts: 1 2
