Monday, October 17, 2011

A tribute to Steve Jobs

For some inexplicable reason, the death of Steve Jobs has deeply touched me. I felt as if I had personally lost someone dear. It wouldn't be a big exaggeration to say that this death was probably the first death of a public figure that influenced me so deep. So this post is an attempt to understand why.

I bought my first Apple product in 2006. It was a 30GB iPod (for those who forgot, back then it only played music :) ). I use it up until now and don't see myself changing it in a near future. I was fortunate enough to buy it in an Apple store, and as it is always with Apple, the excitement started there. I remember to this very day that it was like entering a museum of modern art. Everyone who has ever experienced this would understand what I'm talking about. The design in its best - in every little detail. The message that by buying at this place you join a circle of chosen people. The desire to linger there despite the fact that I knew exactly what it is I want to buy. To act as if I'm still not sure - just to breath the atmosphere of this place. Then, the package. Several years passed until I have disposed of the bag the iPod was sold in. The fabric, the logo, the "unlike-anything-else"-ness of this made me want to repeat the moment of opening the package. And finally, the iPod itself. As few buttons as possible - just like in the first Macintosh mouse. Actually, the first impression is that it doesn't have buttons at all! A case that looks as a single body, as if it was molded as a whole, without any trace of assembly. Several years later I discovered that it is possible to open it - it was in a desperate attempt to save it after it has fallen in a stream (together with myself). I remember it continuing to play music for some 30 seconds, being immersed in the water completely.

If I need to summarize what attracts me the most, it is the proof that perfection can actually work. My whole professional life tries to teach me the opposite - that it doesn't pay off to strive for perfection. That perfection is nice only in the theoretical computer science. That we are in the industry and shipping product on time with reasonable (or sometimes not) amount of bugs is more important than the internal architecture, the beauty of the interfaces, the robustness of the infrastructure. Well, I'm glad that there is at least one example that shows that it is not true. That it is important to make your product perfect. Because then your end-users will really love it! Not just use it because it's not as bad as competitors' but because it's really good. Because it's a pleasure to use it. I once listened to Joel Spolsky joking that the front of an IBM laptop is uglier than the back of a Mac. Well, he is right. I'm sure the reason Mac doesn't have the standard VGA connector is that the VGA connector simply looks ugly! No way this thing could have found a way into the beautiful Mac!

I'm typing this on my iPad which I own for less than two months. Perhaps this explains my passion. I must admit that there's something in this iPad which is so compelling that I'm using it for typing despite the fact that a keyboard would surely be more convenient and the editing would probably be easier on a normal screen. The reason probably is that it resembles a toy much more than a computer. We all prefer to have fun than to work. Or at least make sure our work looks like we are having fun. I have recently read in a newspaper article that Apple products don't require their users to learn to use them. It was used as an argument that Apple products promote stupidity. Well, for me, I think it's brilliant! No one comes up with an argument that a fridge is a bad product because it doesn't require one to learn it, right? When learning to drive a car the most difficult part is to learn to cope with the other drivers, not mastering the wheel. Thank goodness we don't have to "learn" how to use different models of elevators. It's about time computers turn into a commodity as well. Apple has done just this. Ever since I have an Internet connection at home, I'm wondering how people who are not programmers manage to keep it working. Well, my mother has me. What the rest of the people do? A friend of mine has told me that they plan to buy an iPad for her 90-year old grandmother. Doesn't it speak for itself?

And this is the second reason I love Apple products. Simplicity! Don't get me wrong, inside it's a state-of-the-art electronics. But for the user "the mouse should have only one button, to prevent confusion". In order to connect to my Exchange account, my iPad only asked me for my e-mail address and my password. My calendar and e-mail are now synchronized. I still didn't figure out how to do it properly on Ubuntu, so I'm using iPad to read and write my business e-mails.

And the overall process of buying and installing the applications? Isn't it brilliant? Both the simplicity of the process and the prices of the software! Meanwhile the most "expensive" application I bought costed $5, but I'm sure that at the moment I find myself writing documents on iPad, I will gladly pay $10 for the relevant application. I sometimes find it difficult restraining myself from actually buying it "just for the fun of it".

Do I know all the above sounds one-sided? I do. Do I realize I have been "charmed" by the cleverly  organized marketing strategy? Yes I do. Don't I realized I'm being used for the benefit of a single company? I do and I gladly submit myself. But! As long as I truly enjoy every time I take their product(s) in my hands. I once used to be a big fan of Nokia phones. Their idea of a single big button that "does the job" every time you need it was brilliant. But as years passed and models replaced one another, buttons multiplied. The "magic" button became just the "left button". Before I finally gave up and bought an iPhone, the last model I owned made me regret the moment I bought it every time I took it out of my pocket. I hear the next ones will have Windows inside. I'm glad I won't have to endure that.

Why am I saying this? Because there's no guarantee Apple will remain Apple after Steve Jobs has left this world. Especially in the case of Apple. I really hope I'm wrong, but I don't think there are too many people in this world who can do wonders that Steve Jobs has done. May his memory inspire the young entrepreneurs to come and prevent them from turning into regular "businessmen".

Friday, March 25, 2011

The pains of upgrade and how to alleviate them

User customizations! The same thing that many times makes the product successful, turns into a nightmare for its developers. Anyone who faced the challenges of deploying an upgrade over an existing installation knows what I'm talking about. If only things could be as simple as scraping the old installation and unpacking a new set of files! Alas! The cunning user has made changes in our precious files! He loves our product because of that! Everything is stored in readable, textual files! He reverse-engineered them and discovered that he could turn our great product into a fantastic one by tweaking these two little parameters! And now we're overriding everything! Oh, no!

My experience taught me that although the problem happens again and again, there is a set of rules which if followed judiciously, helps if not to avoid the problem altogether, then to minimize it at least. I'll state the rules and explain them with a set of examples.

Rule #1
Draw a clear line between the things you allow your users to customize and the things you don't. For instance, if the configuration is stored in files, put the customizable files in one dedicated folder. Name the folder appropriately. When the structure is organized this way, even if it is not documented, it conveys a message to your user - this is the area where you can make customizations and only here!

Rule #2
Separate the factory (out-of-the-box) settings from the user settings. For example, if you have a configuration file with default settings you allow your user to change, try to split it in two - "your" file that contains the default (hidden deep inside your files structure) and "user" file, which contains comments guiding him what and how he can customize.

Rule #3
Do not expose the factory defaults in a form that provokes changing them. For instance, instead of placing them in a file, place them in a resource packed inside a JAR and read it as an InputStream.

Sometimes the reality is tough and you find yourself being led by your users. I mean that sometimes a majority of them starts customizing things you didn't intend them to customize but you have to support it because you value your users. Even in this case, try to fix the situation post-factum by organizing things differently and implementing a clever upgrade tool that would transform the existing situation to a new format.

Why is it important to follow these rules? The first is rather simple - it is unpleasant to find yourself in a situation where you have overridden user changes in a certain place just because you didn't have a clue anyone besides yourself would want to change it. As I have just said, it can still happen even if you follow the rule #1. But in this case you'd at least have an excuse - this specific file does not reside in the "customizable" area, we didn't intend to support customizations done there. Again, if the user has a justified case, it's a subject for negotiations and support, but deep inside the user will know you're right.

The second rule is less obvious and one has to make a mental effort to follow it. Many times the format of a configuration file is predefined by some third-party tool or library we use and it's very easy to miss the point of decision. It wants a settings file - let's create this file and use it. The problem is that you'll never see any problem with it until the next release where you come with an upgrade. Then several bad things can happen.
  • You may upgrade the third-party library and see that it needs an additional parameter. If you override the configuration file, you override your user's possible modifications and make him angry.
  • You may reach the conclusion that you want to change the default value of one of the parameters. If you change it blindly, you may break the behavior for those users who customized it. If you don't change, you save them but the rest of the users don't get the benefit of the improved default.
  • The third-party library provider may decide to change the format of the configuration file. It rarely happens (everyone is in the same game) but if it does, you're in a deep sh*t.
However, if we consider these scenarios in advance, we may save a lot of headache in the future. This is the time to be creative. If the format supports the "include" directive, use it to split the file into the "factory" and the "user" portion (placing them in different directories as per Rule #1). If not, perhaps you may simulate it yourself by first parsing the two portions and combining them into a single temporary file to be fed to your third-party. The format is not friendly for a split? Perhaps it's worth to invent your own and again, convert it on the fly to something the third-party expects. If the third-party supports configuration by API this might be the best option. The same rule holds if you store the configuration in the database - do not mix the defaults and the user settings in one table because of the same problems outlined above. In short - think forward and try to save yourself from a complicated upgrade in the future. 

The meaning of the last rule is - if you don't want your user to customize something, don't provoke him. For instance, if you have (for any reason) a file that lists the names of your classes implementing a certain interface, do not store it in the filesystem. Turn it into a resource and hide it inside a JAR file. In general, avoid files because they are much harder to keep track of, to redistribute, to overwrite etc. When something is a resource, you can be 100% sure it is available, up-to-date and matches the corresponding code. So resources should always be your first choice. Resort to files only when it is absolutely inevitable.

To summarize - I believe that the absolute majority of upgrade problems can be prevented if the features are written with the upgrade in mind. The complexity of upgrade is not inherent, it is mainly caused by our own design mistakes. Design for upgrade!

Sunday, November 14, 2010

Me and Aunt Marge

I'm hopeless. I'm an old, narrow-sighted, rigid UNIX-school developer. I will never be able to raise above the cultural differences between Windows and UNIX. Let's face it - I don't like Microsoft. After finally reading Joel Spolsky's article on Biculturalism I can appreciate the reasons behind the "Microsoft culture" but I guess I'll never be able to associate myself with it.

My wife has recently completed a course on VBA in Excel. A couple of days ago she suggested we sit together and try to implement something. Well, I thought it would be fun. If only I knew how wrong I was!

Now, what bothers me is that if Joel is right about this "Aunt Marge" philosophy, I wonder how it applies to VBA in Excel. Right, my wife is an accountant, not a programmer. But programming macros in VBA is as close to programming in a non-programmers' world as one could possibly think. Let's put it this way - I would be surprised to see this application in the Aunt Marge's toolbar (unless she has dragged it there by mistake).

Anyway, we opened Excel and started. Here's a spec of what we intended to do. In a certain column, find a cell that matches a certain template - this indicates a start of a section. A table is expected to be located two rows below this number. The table is concluded with a "total" line. Copy the initial number and the "total" to a new row in the second sheet. Repeat the search process until the end of the sheet, adding another row to the second sheet every time. Fairly simple.

Well, as a programmer having plenty of the first virtue, I assumed there must be a function that finds a cell matching a certain pattern. So I opened the Help and searched for 'find regular expression'. Great - here's a Find function! I'm starting to write the VB code, but when I hit the opening parenthesis I pay attention that the parameters in the tooltip do not exactly match, so to say, the parameters in Help. It took me a while to realize that there is more than one version of Find. No problem, I tried to locate another. Didn't work out. What does a frustrated programmer do these days? Google it! Surely, within a second I was able to find an article about the "correct" Find method.

Now, it seems that Microsoft developers have decided that since most of the programmers use Google anyway, API reference is no longer necessary. The internet's article hinted that in order to look for a regular expression, one should use the parameter "SearchFormat". Excel's help gives the following information about this parameter: SearchFormat - Optional - Variant. Description - "The search format". Duh! It's great they use a word breaker for automatically producing help files. Surely, no more light is shed on the mysterious "Variant" parameter in the Remarks section. And I'm not mentioning that the syntax of regular expressions is nowhere to be found. Oh, sorry, I forgot - Aunt Marge doesn't need those!

On the other hand, the mentioned Remarks section contains an absolutely remarkable (sorry for a wordplay) note that has shaken my concepts of good and evil. The values for the various optional parameters are saved each time you use this method. Which means that if you don't specify their values on the second invocation, the values from the first one are used! It seems that the designers of this particular piece have never heard of the Principle of Least Astonishment.

This is actually a very good proof of the Joel Spolsky theory. This behavior stems from pure GUI thinking. It's perfect for humans to have their previous choices remembered. For programmers it's disastrous because it makes the behavior unpredictable. I can bet there's a corresponding UI dialog for this Find function and it's simply a bad design to have this logic anywhere lower than the UI layer. But the Microsoft's culture is so "Aunt Marge" oriented that perhaps they simply don't have enough people who know how to write software for developers.

I'm ashamed to admit that I gave up trying to make this Find method work for me and resorted to some hand-made search with a While loop. Each one of us has his limits, sorry.

By the way, one of the times I desperately hit F1 brought me a third version of Find - something completely different! Which reminded me why back in 1999 I fell in love with Java. In Java there is only one type representing a sequence of characters - String. Young programmers who haven't seen the Win32-COM-MFC-ATL nightmare won't understand what I'm talking about. But those who remember words like char*, wchar_t*, TCHAR*, LPCTSTR, CString, CComStr, BSTR etc. would be nodding in agreement. Ah, yes, there was also an OleString. Wasn't it? Never mind. I'm sure every single one of those had its rightful raison d'ĂȘtre. But could someone please tell me what does it have to do with Aunt Marge?

Thursday, November 11, 2010

About usability

Recently I became concerned with the usability of the software we write. I came to it from two different perspectives in parallel. First, I have recently moved from one project to another. Being a newcomer, it's easy to criticize the product. It's also a rare chance because after a couple of weeks one gets used to various nuisances. They call it a fresh look.

The second perspective is much more narrow but is much easier to use for the demonstration. At my leisure time I'm developing a small Java application for my Nokia N97. A small thing that records a track using the built-in GPS and exports it in the GPX format. Just to prevent a flood of comments - I know there are dozens of application doing just this. But this one is mine - I like it, I can add little features to it and it also gives me a little insight into the J2ME world. Just for the fun of it.

However, this small application continues to teach me usability lessons. It's amazing how easy it is to write a "regular" application and what it takes to plan a really easy-to-use one.

As an excuse, I have to say that at first I did download some application for track recording, but didn't quite like it (usability-wise). This is what brought me to making my own. The sad thing is that the approach of that application "spoiled" me - I only "improved" it here and there but didn't change the whole concept.

A couple of examples to demonstrate what I mean. The first "working" version of my application constantly displayed the latitude and longitude, altitude and their respective accuracies (the numbers GPS has to offer). There was a "Start" button that changed the display to two text fields, "Name" and "Description". Name was mandatory but had a default value "Track", Description was optional. After entering the name one had to press "Start" again to actually start the recording. Then the application started accumulating points. Later, when the "Finish" button (displaying instead of the "Start") was pressed, the application dumped the file.

It took me a while to realize a number of problems with this approach. First, it doesn't make sense to accumulate points in memory when the application may start writing them to the file immediately. Apart from memory optimization, it's a huge improvement in usability. For one part, since I still didn't find out how to sign the midlet, there's an annoying security confirmation dialog popping up whenever the application is trying to open the file for writing. It's better to display it right away than to delay it to the end. Another immediate benefit is that now pressing "Finish" takes no time - the file is written already and the application only needs to close it properly. Finally, if the application gets stuck (unfortunately, it happens once in a while for some obscure reason), the part of the track accumulated before the lock-up doesn't get lost.

When trying to "reverse-engineer" why on Earth didn't I do it from the beginning, I recalled that in the original application there was a feature of adding waypoints along the way. In GPX, they have to be written in the beginning of the file, so the program has to defer the writing until all waypoints are known (which doesn't happen until the end of the recording). However, even if I decide to add this feature, it still makes sense to write the file along the way and store the waypoints in memory instead. When the track is finished, rewrite the file (if any waypoints have been added). By the way, this is a typical example of how a marginal feature has the potential of destroying the main flow. We, programmers, spend so much time with computers that we got used to think like them - 'then' is no different from 'else'. For humans it makes all the difference.

Another observation has to do with the fact that the phone is not a small computer. Well, technically it is, but not as far as the user is concerned. The user of the phone doesn't like using the keyboard! Even on N97 with full "qwerty" the keys are smaller than thumbs. However, in my first version the user had to change the default value for the track name - otherwise the previous one would get overwritten. An additional consideration is that the recorded track is later transferred to the computer anyway and may be easily edited there. So practically it doesn't matter what name is given to the track at the time of recording. From here the solution is obvious - give it a unique name automatically! The best is to encode the date and time so it would be easy to identify it later. In the current version I still display the "name-description" dialog, but I believe this should be removed altogether. I never entered a description and after having changed the default of the name to be unique I don't edit it either.

Here goes the last one (at least for now). After a couple of uses I realized that apart from the fact that they constantly change (and thus indicate that the application still works), the latitude and longitude numbers do not make any sense. On the other hand, on my trips I usually have a large-scale map and would love to make sure I am indeed at the point I think I am. Which made me find the formulae for calculating the "Transverse Mercator" projection coordinates (in practical words, the new Israeli coordinates network). So my current version displays these numbers instead, which is much more useful.

Surely, I'm rediscovering the wheel - Apple have understood for a long time already the difference between a regular product and a "great" one. Still, it's funny to watch this on a small and distilled example. Think usability!

Thursday, October 21, 2010

My "anti-patterns" - 3

After the previous entry with rather bombastic statements ("don't use inheritance!") this is going to be a rather modest and specific one.

Pair

It looks that almost in every project there's someone who once wrote this:
public class Pair<F,S> {
    private final F first;
    private final S second;
    public Pair(F first, S second) {
        this.first = first;
        this.second = second;
    }
    public F first() {
        return first;
    }
    public S second() {
        return second;
    }
}
When I first saw this, I thought "if it's so useful, why doesn't Java have it already?" It didn't occur to me then, but after some time I got to the conclusion why. Not only is this class useless, it actually does harm.

Most obviously, it obscures the code. pair.second() - what is it? What this statement is talking about?! Who's on first? What's on second? Things should be referenced by their meaning, not by a position inside a structure! What is it? Assembly language? [SP+8]?!

Secondly, I believe that it actually hides the reluctance of a developer to name things and think about them. It's much easier to hide behind an anonymous "pair" than to think whether this "alliance" between two objects is really needed and how it should be called.

Sometimes it is also a quick way to circumvent the restriction that a method has to have only one return value. Need a second one? (Most probably an additional flag.) Make it return a pair - the original object plus a boolean! What's the meaning of this boolean? Don't bother, it's "second"! Good luck in reverse engineering what this code does!

Recently I happened to see the following "masterpiece":
HashMap<String, Pair<Integer, HashMap<String,
    Pair<String, String>>>>
Notice this "operator" at the end? Amazing how robust the Java compiler is!

By the way, the above example reminds me of another idea (and this probably deserves a topic of its own). I'm starting to think that collections, especially maps, should probably never be used per se, in particular when being used as return values. They should rather be wrapped into classes whose name describe the meaning of the collection, not its type signature. Don't be alarmed, in most of the cases the wrapper class will have to expose only a very small subset of the methods of the collection. However, they would have a chance to have much better names, explaining what purpose the collection serves in this particular context. For instance, if you have a mapping from "logical" names of something to their "physical" names (whatever it means), a code like namesMapping.getPhysicalName("Attr1") really looks much nicer than namesMapping.get("Attr1").

Back to the "pair". Whenever one uses this "pattern", a duplication of its declaration is inevitable. Imagine a method that returns a pair. The description of the pair (its two types) will first be declared at the signature of the method, another time at the return statement of the method (accompanied with the new keyword) and every time this method is used (most probably the return value would be stored in a local variable). So the same combination of types is being repeated all over the place, cluttering the code.

Hopefully I convinced you to avoid it. If not, think of a more advanced class - a triple!

Added on Nov 4, 2010: The world has definitely gone crazy: http://www.javatuples.org/. As a response, see also http://thedailywtf.com/Articles/Overloaded-Help.aspx.

Monday, September 20, 2010

My "anti-patterns" - 2

Continuing the "anti-patterns" subject. By the way, "anti-patterns" are not, strictly speaking, the "anti" of "patterns". "Anti-patterns" are specifically "don't do" while patterns do not necessarily mean "do". However, it's much easier to say "don't" than to say "do". Out of the 10 commandments the vast majority is "don't". Out of 613 mitzvot, 365 are negative. So let's continue the wave...

Inheritance

I just love to make controversial statements! So bombastic - do not use inheritance! OK, you know it was a joke. But! I see so much cases of inheritance being used for no good reason that I'm becoming worried. It seems that the new generation of developers was raised on a hype of object-oriented. Functions are passé, we should program in objects! The next (highly dangerous!) clause coming out of this is "when there's a common piece of code, one should use inheritance". Wrong, wrong, completely wrong!

Several horrible constructions come out of this thinking. Abstract classes that are full of unrelated methods placed there only in order to be accessible in subclasses. "This new class is almost like the one we have - let's derive it from the latter." "We have a flow that differs in some details - let's create a super-class with a couple of abstract methods and implement them in subclasses. The latter is called "template pattern". Finally, there's a pure abuse of inheritance (especially interfaces) that has to do with creating small abstract classes (or interfaces) no one really uses.

Let's look at these examples one by one. In this case there is already an hierarchy of classes (for whatever reason) and two of them need to share some code (generally a utility function). The temptation is to put this code into one of the abstract classes common to both. I guess it comes from the junior developers' fear to create new units of code - be it classes, interfaces or whole modules. It turns out this requires some sort of courage (who would have thought!). So a new protected method gets created in an abstract class.

Two immediate problems come out of this - the fact that the method is placed in the abstract class invites future users of this method to disguise themselves as members of this hierarchy of classes (otherwise they won't get access to this useful method) without real reason. The second problem arises when one tries to refactor the abstract class to a new infrastructure module. This offensive method in many cases knows some abstractions of higher levels and prevents the abstract class from being extracted.

It's generally very easy to find out these "weeds" - try to put the word 'static' before the definition of such method. If the code continues to compile, you can safely extract it out to a utility class (yes, there's nothing wrong in using utility classes with static methods!).

Second case - perhaps the most dangerous one. There's a class that does almost what we want? Let's extend it and override the offending method(s). This specifically creates inheritance for no good reason. In this case the most important thing to look for is a possible violation of the Liskov substitution principle. Can an instance of this new class be used in whatever context an instance of existing class is being used? Probably not. So try to refactor the existing class. Either make it suite your needs or take out the required functionality to another place. Beware of the Conway's Law.

The third case (aka template pattern) may be an evolution of the previous one but may also be created on its own. It's often characterized by an abstract class having methods 'postSomething' and 'preSomething'. Very curious idea - naming a method not according to what it should do but to when it is supposed to be called - sort of callbacks. However, contrary to callbacks which are supposed to react to events, there are no events here. For instance:

public abstract class AbstractCommand {
    public final void execute() {
        validate();
        doExecute();
        postProcess();
    }
    protected abstract void validate();
    protected abstract void doExecute();
    protected abstract void postProcess();
}

Wow! What a code! Excuse me, what exactly this "design" gives? To prevent any future command from forgetting to "validate"? And, as you may guess, 28 out of 30 commands have their 'postProcess' method empty. By the way, another LessAbstractCommand quickly comes up with a method 'doExecute' doing some "common" stuff and calling 'doRealExecute'. Within a couple of levels one quickly gets lost in those "real-real" executes. Great fun to debug as well!

Of course, this is an extreme example, but it's amazing how tempting it is to "establish a flow" and make derived classes only implement "plug" methods. Next time you are considering something of the king, think instead of an easy-to-use set of utilities, or corner stones to be used by future implementors. It's OK if more than one of them would call the same two methods in the same sequence - no harm done. This is not the kind of code that should be reused. With the template method, however intelligent you are, you won't be able to cover for all cases in your "universal" flow and one day your successor will add an ugly "prePostSomething" method implemented in only one subclass.

Finally! Little interfaces - one of my favorite ones. "Many of our objects have a name and a description - let's create a common interface (superclass) NamedEntity (AbstractNamedEntity)!" My favorite saying goes "the fact that both a kangaroo and a soldier are capable of running doesn't mean they should both implement Runnable". The main question you should ask yourself - will there be any code that gets an instance of any NamedEntity and treats it as such? Check yourself thoroughly and refrain from doing it.

I was once swearing hard when I had to study a system where every object that had a 'startup' and a 'shutdown' method was declared to implement a common interface. Surely there was no place in the system that handled all instances of this interface. But imagine what pain it was trying to understand where the 'startup' method of a certain object is called from. One gets 15 places, 14 of which are irrelevant for the specific case - now go find the right one!

So - is inheritance evil? No way! As indicated in a comment to my previous post - "guns don't kill, people do". Thanks, I liked it! But it's amazing how often the inheritance is being abused, this is all I want to say.

Friday, September 17, 2010

My "anti-patterns" - 1

Whole books are written about "anti-patterns" in software design, so I probably won't be saying anything new here. There are several anti-patterns however which I consider sort of "my favorites". I first thought about writing about all of them in one post, but then I recalled one of my readers criticizing me for putting too much statements together. So I'm going to publish them one by one instead.

Visitor pattern

It took me quite a time to grasp the idea of this pattern. Perhaps I'm kind of challenged but I still don't think something that complicated should be used unless really (and I mean really) justified. So my first problem with this construction - it's hardly readable. I hate reading a code where methods have obscure names. I always get lost in the labyrinth of 'visit' and 'accept' calling each other.

However, this is not the single reason. Look at the example from the Wikipedia article.
class CarElementPrintVisitor implements CarElementVisitor {
    public void visit(Wheel wheel) {      
        System.out.println("Visiting " +
            wheel.getName() + " wheel");
    }
 
    public void visit(Engine engine) {
        System.out.println("Visiting engine");
    }
 
    public void visit(Body body) {
        System.out.println("Visiting body");
    }
 
    public void visit(Car car) {      
        System.out.println("Visiting car");
    }
}
I guess I'm going to hurt the feelings of the advocates of this pattern, but this snippet makes me wonder how the pattern made its way to the classical book on object-oriented design. Sorry, but what I really see above is nothing else but a 'switch' statement nicely disguised in methods overloading. Wouldn't it be more straight to simply use 'instanceof' instead? At least the code would be more readable and would not require those obscure 'accept' methods.

Finally, my last objection has to do with modularization of the code. Normally, when extending a class, one doesn't necessarily have to place the derived class in the same module where the base class is. With visitor pattern, the family of classes derived from CarElement is a closed circle. Not only when adding a new derivative one has to go over all visitors, but the derivative has to be in the same module the rest of the family is. Surely, any design assumes something about possible future changes. The same design cannot support more than one direction of changes. This specific one supports adding new visitors but makes it practically impossible to add new elements visited.

To summarize, I have an impression that the visitor pattern is an illusion. It creates more problems than it solves and unnecessarily complicates the code. The only positive side is that it sounds cool when one claims at an interview "I have mastered the visitor pattern!"