1/11/2008

You don't know Jack about Objects!

I have been conducting phone interviews over the last few days, and I feel less positive about the computer programmers out there. Every candidate out there has Object Oriented Design on their resume in spite of the fact they can't define polymorphism or worse encapsulation! If you can't define polymorphism or encapsulation without resorting to examples then you can't write Object Oriented Design as one your skills. You don't even know what object oriented is!

The world is full of what I term imperatives. Essentially, it's the style of code you in most imperative languages like Fortran, C/C++, and sometimes Perl (well that's if you actually understand someone else's Perl). Object oriented programming is a total shift in the way you think. It's not just a language, and to effectively use it well, you have to see others using it effectively. And, that's so hard because so much code out there is imperative at its core.

Loads of static utilities classes, singletons, and objects with only getters/setters on it litter the landscape of the mainstream. If you've ever written an entity object with only getter/setters then each entity has a companion manager class voila that's how imperatives roll. What's the big deal? The big deal is you're not getting the benefits of true OO. I recently re-read the white XP book, and in there Kent Beck talks about the cost of change, and while you don't need object orient languages to lower the cost of change. "However, in my experience the cost of change rises more steeply without objects than with objects." This has been my experience too. Object oriented languages keep the cost of change lower than other imperative equivalents. But, only if you use them correctly. Now why would Kent say that?

He's referring to encapsulation, particularly the fact that certain parts of your program should not interact with each other. That's precisely what happens in object oriented programs. What makes changing programs hard, or worse scary, is the dependencies between parts of your program. It's easier to modify your program if your changes aren't going to affect some other unrelated part. Most people think of encapsulation as effective use of access modifiers like public, protected, and private. Access modifiers are the micro-level idea of encapsulation. There exists a macro-level version of encapsulation in how your objects communicate with each other. This macro level is more important than the micro level.

Object oriented languages typically work by calling methods on instances. That means if you don't have a reference to an instance then you can't invoke methods on that object. This fact forms the basis for the macro-level concept of encapsulation. It's all about these references between objects. This macro level encapsulation more commonly referred to as architecture. A great example of this form of encapsulation is the MVC pattern.

How does the MVC model help with maintenance? Well I think it comes from understanding how to make effective reuse of your code. According to the MVC pattern Views should be empty shells waiting to be filled. Void of any business logic. Controllers responsibilities are interacting with the model, choosing which view should be rendered, and binding the model to the view. The interesting part of this pattern is that model objects are used by the view, and the controller. It's this property that makes the model so important because it's the one portion of the system that can be accessed from any other part of the program without increasing your cost of change.

Here is an example that I recently had. I had two Controllers that were serving up thumbnails of movies, and pictures. Interestingly, I needed to stream the bytes of those images over the response. The one thing that's common between these two controllers is that they both use the Thumbnail object. Imperative systems you'll find some sort of ThumbnailUtil.send() method that will share between the Controllers to do this. Ugggh. So there is the duplication and separation that begins to creep into your system when you start programming in an imperative style in an object oriented language. It would be trivial to reuse that logic if it belonged to my Thumbnail object:


public ModelAndView handleRequest( HttpServletRequest request, HttpServletResponse response ) {
Thumbnail thumbnail = thubmbnailService.loadThumbnail( thumbnailId );
thumbnail.send( resposne.getOutputStream() );
return null;
}


It's that simple! For one you're using fewer classes. The send() method is accessible to any controller that can access a thumbnail. Your model objects should have behavior, and this really goes along with any language. I started getting this idea in Java, and I started to really crystallize when I was doing Ruby/Rails. You notice two things. It's easy to reuse logic, and quick to build functionality because after a while you build up a language that models your business logic. Things are now very high level uses of these basic pieces of business logic. This is exactly like Paul Graham's discusses in Programming Bottom-Up.

Another side effect of this is that it's easy to test. In Rails they went great lengths to try and test your model, controller and model, and full integration view, controller, and model. Notice how Rails doesn't try to separate Controller from model which I think is very practical approach. But, the first level of unit testing is testing the model alone. If you're business logic is pushed down into that level it's much easier to test it than if it's inside the controller. You don't need the model and the controller so there's less dependencies you have to initialize and setup in your test environment. The more logic you can test in your model, the more coverage your getting, and the more robust your application will be.

Another example I recently ran into of more static loving imperative code was called ViewUtil. Our application is based on SpringMVC so someone added a ViewUtil class for retrieving the ModelAndView object required by SpringMVC. There was several portions of logic included in there for retrieving XML based views vs. HTML depending on properties in the HTTP request. Here are what this might look like:


public ModelAndView handlRequest( HttpServletRequest request, HttpServletResponse ) {
Model model = modelService.getModel( ... );

ModelAndView mv = ViewUtil.getView( request, "modelview" ).addObject("model", model).addObject("someParam", request.getParam("someParam");
mv.addObject( "model", model );
mv.addObject( "someParam", request.getParameter("someParam");
mv.addObject("someOtherParam", request.getParameter("someOtherParam");
return mv;
}


However, there are other problems that pertain to Views that end up inside our controllers because of the choice of using statics. For example, quite often when errors on our forms occur we have to copy parameters from the request coming in onto the ModelAndView leaving. Particularly, hidden fields on the form that don't belong inside objects. One of the problems with statics is that you can call them from anywhere. Using getView() method really only belongs in the Controller since that's the responsibility of the Controller, and not anywhere else.

Here is a more object oriented approach. One we move this method to retrieve views into our Controllers in a common super class. That way only our controllers can call it.



public class ApplicationController extends AbstractController {

protected View getView( String viewName, HttpServletRequest request, HttpServletResponse response ) {
... // all of our existing logic inside ViewUtil goes here.

ModelAndView mv = new ModelAndView(...);
return new View( request, mv );
}
}


Notice how in this example I'm not actually returning the ModelAndView class as the ViewUtil does. The reason for this is we're going to add our behavior to that object thus giving it the illusion of behavior on our ModelAndView class. So here is an example of our View class:


public class View {
private ModelAndView view;
private HttpServletRequest request;

public View addObject( String key, Object obj ) {
view.addObject( key, obj );
return this;
}

public View copyParams( String... params ) {
for( String key : params ) {
view.addObject( key, request.getParameter( key ) );
}
return this;
}
}


So here we're exposing just two simple methods. One is a delegated method to the addObject() on ModelAndView. Notice that I didn't include getters or setters for the HttpServletRequest or the ModelAndView. That's because they aren't really needed if you have a View object. Whatever sorts of code I need to do that operates on either of those objects I will put in my View object, and simply call that from my Controllers. I could even go further and add a new method to my ApplicationController.doRequest() that returns a View object instead of ModelAndView then that way my subclasses don't need a View.getView() to satisfy the Spring MVC method, and leave that to my ApplicationController.

Now I've created a easier way to handle things like copy parameters from my request to my view, and I don't need yet another static method on ViewUtil or something like that I just have smart View object. This technique is really the same has doing currying in functional languages. Instead of creating a scoped function we're created a scoped object. What's amazing about this technique is it allows one part of the program to transfer knowledge of a section of subsystems without another part of your program having to understand details about that subsystem. It's like a specialized context for another part of the program that's highly tailored to the needs at a point of time for only a short time.

Now on some level you can argue that ModelAndView object should have the copyParams method on it. But, it's not practical to think the needs of every program could be anticipated by our framework developers. It would be too cumbersome and difficult to understand because you'd need to separate out what you need from what you don't.

This illustrates that when you really stop thinking in statics and start thinking in terms of objects opportunities, that would be too cumbersome and repetitive in imperative land, open up in object oriented land. Also notice how I didn't use inheritance to accomplish this. This is the power of composition, and delegation. Inheritance is useful, but most often abused. Composition is an easy technique to learn, but seldom do imperatives ever use it effectively.

5 comments:

gwenhwyfaer said...
This comment has been removed by a blog administrator.
V said...

I divide the world into "people who care to check their spelling" and "those who don't". The poster belongs to the latter one:

Thumbnail thumbnail = thubmbnailService.loadThumbnail( thumbnailId );
thumbnail.send( resposne.getOutputStream() );

I would hesitate to use the "thubmbnailService" as well as the "resposne" variable.

Brian McKendrick said...

Cheese and rice Vik - how about giving it a rest. Decent post on a subject that gets alot of coverage ( with cause ) ...

michaelv said...

This is a great post. I have long held the view that getter/setter methods miss the point of OOP.

Very well written.

OO is hard; programming is hard,and done well OO helps precisely because it gives language level support to the profoundest of good practice: separation of concerns, aka decoupling.

What's really hard about all this is how deceptively easy it is to get started using poor practice. The price gets paid later, but it does get paid.

Quick and dirty is guaranteed to be dirty!

chubbsondubs said...

@Michaelv,

Wow! Thanks for the great comment. That comment made my day! As much as I took it on the chin at dzone - some valid points most just mean - it's nice that some people thought it was good. I'm going to follow up on it so keep your eyes peeled.

Contrary to popular belief I did work hard on the flow and read of the post trying to keep it relevant to the theme. But even with all the editing and double checking mistakes made it thru as Vik enjoyed pointing out, but nobody's perfect.

Thanks @me, and no that wasn't me.