Please join me at my new location bryankyle.com

Showing posts with label best practices. Show all posts
Showing posts with label best practices. Show all posts

Monday, October 11, 2010

Nil Advice

Recently I wrote that one of the things I really like about Objective-C is how it handles nil-values when calling methods. It certainly makes for code that's easier to read however it does have a downside as mentioned by Daniel Jalkut of Red Sweater software. In Don't Coddle Your Code, Daniel writes:

It s also worth noting that LaMarche s defense of nil ing out instance variables hinges on the presumption that messaging nil is safe. True, messaging nil will not cause a crash. But is that safe? Not if it changes the behavior of your code in unpredictable ways.

He goes on to demonstrate his point with some contrived but applicable code:

if ([mDiplomat didReturnGestureOfPeace] == NO)
{
    [[NuclearWarehouse nuclearMissile] launch];
}

Daniel's point is this:

The biggest disservice you can do to your users is to allow them to run code that has unpredictable behavior. The whole point of programming is to make computers accomplish tasks in predictable ways.

I think he makes an excellent point. To paraphrase -- and I'm sure C++ developers will agree -- just because you can do something doesn't mean that you should. This language feature is a boon for those that understand how to use it effectively and design interfaces that work well with it.

Thursday, January 28, 2010

On Subclassing

And now for another excursion into the depths of my experience and thinking about object oriented design and philosophy. This time I'm going to talk about some pitfalls I've seen with one of the most commonly used, and abused tools in the object oriented developer's toolbox: subclassing and access modifiers.

Subclassing has been around since the dawn of objected oriented programming. It's just one of many ways to enhance code reuse, but I've seen enough blood shed due to the misuse of this feature that I've stopped using it in cases where I can get away with another approach. The core of the problem with subclassing tends to be an eagerness to reuse as much code as possible without any consideration of how code reuse decisions will effect the overall design. Yes, reusing code means that there are fewer places to change when a change must be made, but subclassing is a merely a means to an end. There are other, better approaches that give the same benefits without the problems.

So what are some of the problems with subclassing? I think the central failing is that subclassing is the most tight form of coupling there is. When you decide to subclass an implementation, you've solidified part of your implementation. A common word of wisdom is that you should always code to interfaces so that implementations can be changed at a later date with a minimal of fuss. By subclassing an implementation you are publically declaring that you are and always will be a subclass of X.

Another problem with subclassing is that you cannot narrow the interface of the class to anything smaller than the subclasses interface, even if it doesn't make sense. A textbook example of this in Java is the java.util.Stack class. java.util.Stack extends java.util.Vector which in turn extends java.util.AbstractList which is an abstract implementation of java.util.List. The java.util.List interface contains methods such as add, get, indexOf, etc. If you've followed along that means that java.util.Stack, in addition to its own interface also has these methods. A stack should really only have two methods for mutating the stack (push and pop) and perhaps some methods to determine how many items are on the stack. By subclassing, java.util.Stack has guaranteed that it implements the java.util.List interface and therefore has methods that allow a developer to work around the stack encapsulation. Ah, but you can override those methods in the subclass with ones that do nothing or throw a java.lang.MethodNotImplemented exception. Certainly you could, but then if an instance of java.lang.Stack was passed to a method that accepted java.util.List it would not produce the desired result -- either by throwing an exception or not performing the operation.

No discussion of subclassing would be complete without mentioning the Liskov Substitution Principle. I think it's great advice it theory, but as we've all seen it's not exactly easy to follow. If the JDK developers can make such an error with all of their review process, how does a lowly developer stand a chance?

Aside from these problems there's always the minefield of access modifiers. In my opinion there should really only be 2 at the class level: public and private. Protected variables are a major cause of headaches since subclasses can easily muck around with the internals of a superclass, violating encapsulation. Protected methods provide a second interface to the class which has to be maintained and documented -- as if a single interface wasn't enough problems. So, private and public is really all that's needed - stuff that's internal and stuff that's public.

So if not subclassing then what? In a word delegates, specifically the delegation pattern. With the delegation pattern you simply implement interfaces, if applicable, and delegate parts of the implementation to a concrete class. By doing so you don't have to expose any information about how your class works internally. And since you're not subclassing you don't inherit interface that doesn't make sense for your class. As an added bonus, since you're using the public interface to the concrete class you don't have to worry about methods internal to the implementation changing or becoming deprecated.

Delegates are also an effective strategy for reusing code. You can encapsulate logic into a single class and use it from anywhere in your application. This is a great benefit when you have an irregular class heirarchy, it effectively gives you mixins without the headaches of trying to figure out which pieces of implementation are coming from which mixin.

Obviously there are times when subclassing is the right thing to do. But in my experience I see it used much more often than it should be and usually for the wrong reasons. I hope my thoughts have given you something to think about next time you reach for that subclassing hammer.

Thursday, August 20, 2009

Exception in thread "main" java.lang.NullPointerException

I've learned over my years of programming in object oriented languages is that null values can be really painful. Due to poor decisions by API designers code has to be littered with null checks to ensure that values returned from API calls return something other than null. Failing to make these kind of checks while oftentimes harmless, can cause problems once you start testing code off of the happy-path.

There are few things a consumer of a poorly designed class can do other than simply read the documentation, add null checks and test thoroughly. So, if you're designing a new class it's really important that you get it right the first time. While I can't give much advice about the design of your specific class, I can give some hints nulls and when it's appropriate to use them.

These rules aren't hard and fast, there are times when these rules are completely wrong. But it's been my experience that these rules are applicable in almost all cases.

Return null when you mean null.

A common mistake that programmers make (myself included) is to simply default values to null. I attribute this to laziness. It's much easier to simply use null as a default value instead of some sensible value. Certainly there are times when null is a good choice, but I think as programmers we tend to fall into the trap of using null because its convenient.

The first thing to understand about null is that it means the absence of a value. It means there's nothing to see here, move on. It doesn't mean empty string, it doesn't mean 0, it doesn't mean empty instance of a class it means that there is nothing to refer to, no value. Unless your program needs to distinguish between an empty value and no value provided there is little need to use null.

Return an empty list or collection instead of null.

If you have a method that is supposed to return a list of something, you should always return a list regardless of whether there is anything to put in the list. Typically, when a caller asks for a list they are usually going to loop over its contents performing some operation. Since a loop over an empty collection is effectively a no-op returning an empty collection simplifies the calling code by removing the need to check for null.

Return a safe instance instead of null.

When possible, return a do-nothing instance of a class instead of null. This is really useful for classes that contain other objects that are used frequently. This allows callers chain method calls together without having to worry about null return values. Typically this is useful for classes that are treated as services.

An example of this might be an a getXPathEngine() method of a Document class. If there is no implementation of XPathEngine available, then Document.getXPathEngine() should return a safe implementation of XPathEngine instead of null that way callers don't have to constantly check for the presence of the engine, they can assume that it exists.

Tuesday, March 10, 2009

Tell Me Why!

Whenever I start trying to learn a new codebase, one of the first things I do is try to get an understanding of how the code works at a high level. Generally this isn't too hard, I just find out where the program starts, which is usually pretty obvious, and work through the code from there. But there's a problem I always run into the code only tells me what it does, it doesn't tell me the most important thing: why. Unfortunately, most projects don't maintain a good set of code-level documentation. Instead all documentation is locked away inside of heads of its developers as tribal knowledge.

So if documentation is needed where's the best place to keep it? In my opinion, the the documentation needs to be as close to the code as possible, so put your documentation in the code. The further away it is, the more likely it is that the documentation will get out of date, and the only thing worse than no documentation, is wrong documentation.

Now, what should be in those comments? Well, as I eluded to previously, it should tell me the why. If someone is reading through the code they surely have enough knowledge to be able to tell what's happening to which objects, so you can safely leave that out.