Posts

Inheritance as Polymorphism vs Inheritance as Code Sharing

We've all heard the mantra that we should favor composition over inheritance, and in most cases this is good advice. Occasionally, though, we really do need an inheritance relationship. Inheritance provides that useful “is-a” relationship that sometimes fits a situation so well. The pitfall we have to watch out for is using inheritance for code sharing between classes that should otherwise not be related, and not for polymorphism, that “is-a” relationship. What makes code sharing through inheritance so bad in the first place? An inheritance relationship is one of the tightest dependencies that could be made between two classes. It is hard-compiled in, and cannot be swapped out at runtime. It's an all-or-nothing relationship, and anything the parent wants to expose, the child must also expose. The child class may be able to override some of the parent behaviors, but the parent may finalize some methods and prevent this, locking the child class in even more tightly. This may be...

Trapped in the Purgatory of a Big Refactoring

Sometimes, it's not just a method that needs refactoring, or even a class. Sometimes it's several large packages with a spider web of dependents that just need a complete "rehacktoring." This can happen when years of legacy code build up in a system. It is complicated by the fact that the thing you want to change or delete or rename or move has tendrils of dependents spread all across the system, sometimes into classloaders and reflection and dynamic scripts that you cannot begin to search-and-replace all of. It is going to be a big, long slog into the swamp. We don't want our massive change to be a surprise to our team, though. We want to commit and merge the bits as we're going along. They don't represent complete refactorings. We might have pulled out a class just to put some code in a separate place, knowing that down the road we're going to merge it together with some other class and move that other method somewhere else. We're not quite su...

YAGNI vs Uncertainty

You're sitting there staring at the (pretty good if you say so yourself) code you just wrote. It solves a pretty tricky problem in a pretty elegant way. But something is worrying you: this code might need to change. You're not sure when and you're not sure how, but you just get this feeling tingling in the back of your neck that this code is going to have to change, and probably sooner rather than later. Somehow sensing your hesitation from across the room, that guy comes over. Yo, bro, looks like you got a problem. Just remember, bro, YAGNI! You ain't gonna need all that! You've heard it all before from that guy . You've even followed his advice a couple of times. Once, yeah, he was right, but that other time... oh, that other time. So much extra work that you just know you could have avoided, somehow. Maybe I exaggerate a bit, but we've all seen or heard of or (shudder) worked with someone who strictly follows the One True YAGNI. They never add any co...

#FOChaCo: Fear of Changing Code

In code, as in life, things are always changing, whether we want them to or not. (Focusing on the code part,) Operating systems, libraries, protocols, languages, standards, and best practices are changing on the regular underneath every bit of code we write. While many of these systems work hard to change in "backwards-compatible" ways, sometimes there are changes that break things. As software developers, our job is to write our code that is robust to change, so that it still works well when the future arrives. But sometimes there are those among us who suffer from #FOChaCo: the fear of changing code. Despite the inevitability of the future, they fight tooth and nail to keep things exactly as they were. They might insist that we cannot support anything later than version X of our platform, or that we should not update any libraries unless there is a dire need to do so. These developers are well-intentioned. They may have been bit in the past by a library update. They might...

How Big Is Your Object's Interface? The Answer Might Surprise You!

We have a Person object, eddie . Part of the responsibilities of a Person object is to operate a Car , accessible as eddie.getCar() . Car s are made of parts like Door s and Seat s and an Engine . Starting a Car entails starting its Engine . So how would we start eddie 's Engine ? eddie.getCar().getEngine().start(); This accomplishes the task, but we see a violation of the Law of Demeter in this code. We can see this quite plainly: look at all the dots! But what is this "law," and who passed it? I don't think Congress concerns itself with these matters (at least not yet). How bad can writing code like this really be? To understand why we should follow Demeter, we have to see what happens when we don't. Our Person , eddie seems to have a very simple interface so far, a getCar() method. He just has that one method, but that method gives us back a Car with a getEngine() method. That method then gives us an Engine with a start() method. Each intermedi...

This One Subtle Bug You Might Not See Coming

Sometimes bugs are obvious. You used a ++ where you should have used a -- . Other times, bugs are much less obvious. A regular expression was missing an escape of a . . Then there are those bugs that are so subtle and insidious that they deserve their own blog post just to document and excoriate them. This post is about one of those bugs. Fortunately for me, this wasn't my bug. It was a bug hidden in a Programmers.SE question . The questioner was blaming himself for not seeing or understanding a change in an external library, and he was desperately looking for a way to write better code to account for such changes in the external library. What did that library do? There was a method that returned a long, where the long was milliseconds since epoch. In the update, the method was changed so that it still returned a long, but the long now represented nanoseconds since epoch. Putting aside the discussion about whether a long has enough precision, this change is a special type of a...

Moving Sideways with Lateral Abstractions?

When I'm doing design and refactoring, part of me worries that I'm just creating a mess of objects and classes. One of the kinds of mess that I feel I make the most are lateral abstractions. What I mean by lateral abstraction is an abstraction that is at the same level as the code I abstracted it out of. It doesn't add very much to the conversation, and it doesn't introduce a finer vocabulary than was already there. When looking over the code I wrote in Writing a Better Code Narrative , I get a slight feeling in the back of my head that some of that code is lateral abstraction. Without re-posting all of the code (which can be found here ), I started with a CommandParser class that read a string of character commands and "executed" those commands to produce a new output string. The original code was intentionally hairy, but I refactored it down into a series of Command classes to encapsulate the individual behaviors, and a CommandFactory to encapsulate the...