Archive for February, 2009

Don’t modify your language privately

February 28, 2009

When I was 16, I worked at a company that very soon went bankrupt (but that wasn’t my fault). We used PowerBuilder, an environment similar to Delphi, but with a language of its own.

In that language we were allowed to use minus in identifiers. Which means that we had to surround the subtraction operator with spaces (a-b would be considered a single identifier). At that time I wasn’t in the habit of surrounding operators with spaces. In the options I found a checkbox, that disabled using minuses in variable names, checked it and continued coding happily the way I was used to. Until at the next weekly code merge (which deserves its own post) my code didn’t compile.

Advertisements

How NOT to apply the Single Responsibility Principle to functions

February 27, 2009

As I said earlier, I was rather surprised when I heard Uncle Bob’s version of the Single Responsibility Principle. His way of splitting code into functions, explained right after that, also made me think “WTF?” The idea is to have only one set of curlies per function. Only one loop, only one “if”, and so on.

Not making a big ball of mud out of your class is surely important, but this approach is extremist programming. It seems rather strange to explain obvious things, but since Uncle Bob has an experience of 40 years (and I much less), I’ll do it.

As Scott said on the same show, it’s important how much one sees on the screen simultaneously. And this is a reason not to split code into too many small functions. Each function adds 3 lines (closing curly, empty line, opening curly), so if the functions are very short, you see considerably less on the same screen. But what’s worse, you don’t see your code in the order it executes, and that’s confusing. Code split into many small functions, whose relationships are not clear, is confusing. I’m not theorising here, this is my experience.

The same reason, that seeing all of “something” on the screen together helps understanding, is one reason to split long functions. But this splitting should be made according to some logic, such as the SRP, not the arbitrary rule “Don’t use curlies”.

What Uncle Bob is doing here is using functional programming instead of structured programming. In the previous post I discussed his use of object-oriented programming instead of functional programming. This seems like a pattern, though I don’t quite get its significance.

Single Responsibility is not Functional Decomposition

February 20, 2009

There is only so much to be said on the principles vs. advice topic, and having said that, it’s time to go and learn from the advice. Except that, in some cases, the advice is bad. Specifically, when Uncle Bob was “demystifying” the Single Responsibility Principle at Hanselminutes, he (unintentionally) gave some examples of harmful coding practices. One of these leads back to functional programming, and the other to unreadable and therefore unmaintainable code. I will discuss the former here and the latter in the next post.

Bob Martin gives an example of an Employee class that has methods like Payroll(), WriteToDB(), GenerateReport(), etc. So he suggests to split it into EmployeePayrollCalculator, EmployeeReportGenerator, etc., because the method of calculating salary can change independently of persistence and report generation. The benefit of this separation somehow misses me. Sure, Uncle Bob explains that otherwise any change causes a major recompilation in C++, but really it’s only a relink.  The harm, on the other hand, is obvious: It’s called the Functional Decomposition Antipattern.

A class represents a concept. That is why a class should have an obvious name. Design is about enabling oneself to program in the same terms one thinks in. Classes correspond to terms that are nouns, functions to terms that are verbs. A noun can have several verbs associated with it, that don’t necessarily all correspond to the same responsibility. EmployeePayrollCalculator is a bad class because it is a very artificial “noun”; it doesn’t represent a concept.

In too many cases, responsibilities can’t be separated for various reasons. Sometimes joining responsibilities in a class is dictated by an interface that joins them. You will say that this means the interface has been badly designed. But an interface corresponds to the needs of the code that uses it. For example, the article on SRP on objectmentor.com (where Uncle Bob is the president) gives the example of a modem class that has dialing / hanging-up functionality as well as sending / receiving functionality, and suggests splitting those into separate interfaces:

However, there are two responsibilities being shown here. The first responsibility is connection management. The second is data communication. The dial and hangup functions manage the connection of the modem, while the send and recv functions communicate data.
Should these two responsibilities be separated? Almost certainly they should. The two sets of functions have almost nothing in common. They’ll certainly change for different reasons. Moreover, they will be called from completely different parts of the applications that use them. Those different parts will change for different reasons as well.

I can envision a function that dials up, sends data and hangs up. Why not? And if it does, a single interface would be better. Actually, the separation suggested in the article will usually be the case, but for a completely different reason: because send and recv will comprise a more generic stream interface. This is because of abstraction levels, not responsibilities.

The other reason not to separate classes in such cases is that, contrary to the quotation above, both kinds of functionality can have a lot in common, like maybe a private function for low-level hardware access. The article later concedes that the separation may have to be between the interfaces only, not the classes, for “reasons having to do with the details of the hardware or OS”. Similar reasons will exist in most cases.

Moreover, some data will be common, too. If we replace the modem by a socket, we won’t be able to split the class in two, because the socket handle (or port number) will be common.

Another example given in the article is a rectangle class, which consists of geometric and GUI functionality. Accoring to Object Mentor, we should split it into GeometricRectangle, that has no GUI functionality, and GUIRectangle, that owns a GeometricRectangle. This is undoubtably correct, but why do we need the SRP to understand it? I could have given any number of reasons for this separation before I had heard of the SRP: That geometry and GUI are very different levels of abstraction, that geometry usually resides in a separate library, that some rectangles are never drawn, etc.

So is the SRP totally useless? No, but it should be applied differently. The similarity between the Employee class and Functional Decomposition is not accidental. It is because the Single Responsibility Principle is a good rule of thumb for splitting code into functions. Each function should have only one reason to change.

However, even when applying the SRP to functions, Uncle Bob takes it to the extreme. But that will be the subject of the next post.

On programming principles

February 13, 2009

I’ve decided to add my 2¢ to the Joel and Jeff vs. Scott Hanselman and Uncle Bob argument on code quality, SOLID principles, TDD and principles in general.

First, here is where I think each side is wrong:

People that say things like this have just never written a heck of a lot of code.

This sentence, said by Joel, is a personal attack. No wonder Uncle Bob got all heated up. And then he allowed himself to fight back by calling Joel a “business wonk”. Apart from being offensive, this attack went partly amiss: while it is true that one can’t dedicate a lot of effort to running a business and improve their programming skills at the same time, running the business gives an important perspective: Sometimes business considerations are more important than writing clean code.  For example, deadlines should be met.

Another mistake on Uncle Bob’s part is the use of the word “principle”. Many commenters on Jeff’s post have pointed out that the principles are not intended to be used all the time, but are more like tools. As tools they are useful, but if this was indeed Uncle Bob’s intent, then he shouldn’t use the word “principle”. “Principle” is defined, e.g., as “a basic truth or law or assumption”. So maybe they should have been called “design patterns”.

In every text that makes several statements, people tend to select the statement or idea they consider the most important. My digest of Joel’s post is this: Don’t overengineer. Don’t blindly use the SOLID or other principles just because they are there.

The real principle is this: Don’t let design get in the way of project requirements or common sense. I’ve learned it from experience after participating in building a set of libraries, which had several problems:

  • Interface complexity: The interface was very generic and allowed to do complicated things. Simple things were a special case of complicated ones, so that it was not simpler to do something simple than something complex.
  • An inner platform
  • Unneeded frameworks

… and more.

I consider this a very important experience and I’m glad I had it right at the start of my career.

I admit I don’t know what “agile programming” or “extreme programming” actually mean, but the project leader was an extreme programming geek, and what I understood is that “extreme” in XP has exactly the same meaning as in “extreme left” and “extreme right”. So maybe the correct term is Extremist Programming.

A good general rule is that most “principles” of programming are useful in some cases, but useless in others. So while knowing them is beneficial, one should beware of dogmatists who will break functionality to fix compiler warnings. (No, that was not a hypothetical example. And if you say “with unit tests, that wouldn’t have happened”, you have a point, but that’s no excuse for breaking functionality.)

In general, as one of the epigraphs in Stroustroup’s book says, “keep things as simple as possible, but not simpler”. For example, if your whole project is 20 lines long, you needn’t even bother to split it into functions. Of course, you should do so as soon as it becomes longer. After all, refactoring is agile, isn’t it? When the code grows to 100 lines long, it will contain several functions, maybe 2 or 3 classes, but most probably no design patterns yet. And so on.

And a final word. While experience certainly does matter, don’t forget that we are not living in a world where old people are the most revered because of their experience. Instead, this is a world of start-ups, where everyone is judged according to their success. And anyone who doesn’t question authority should switch to literate programming.