How (not) to write Factorial in Java.

I will admit this post was inspired by How would you write factorial(n) in java? So excuse me while I rant in code: I have a point to make at the bottom of this article.

Now most people who write factorial in Java may start with something simple, like:

    public static int factorial(int n)
    {
        if (n == 0) return 1;
        return n * factorial(n-1);
    }

Wrapping this in a class (since we’re talking about Java, of course), they may write it inside a utility class. For example:

public class FactorialUtil
{
    public static int factorial(int n)
    {
        if (n == 0) return 1;
        return n * factorial(n-1);
    }
}

Simple, no?

There is a non-recursive solution as well:

public class FactorialUtil
{
    public static int factorial(int n)
    {
        int ret = 1;
        for (int i = 1; i <= n; ++i) ret *= i;
        return ret;
    }
}

Now the observent reader may note “gosh, it’s entirely possible the value will be longer than an integer”, so they’ll perhaps rewrite in terms of BigInteger or at least in terms of long, depending on the requirements of the program. Thus:

public class FactorialUtil
{
    public static BigInteger factorial(int n)
    {
        BigInteger ret = BigInteger.ONE;
        for (int i = 1; i <= n; ++i) ret = ret.multiply(BigInteger.valueOf(i));
        return ret;
    }
}

Notice that thus far, of course, I’ve made no use the fact that I’m constantly recalculating the intemediate values from 1 to n. If those values were cached, of course, I could save myself a lot of computations. One way to do this is to use recursion, but if we’ve already calculated the value, store it away for future use. Thus (using HashMap, because it’s there), we get:

public class FactorialUtil
{
    static HashMap<Integer,BigInteger> cache = new HashMap<Integer,BigInteger>();
    
    public static BigInteger factorial(int n)
    {
        BigInteger ret;
        
        if (n == 0) return BigInteger.ONE;
        if (null != (ret = cache.get(n))) return ret;
        ret = BigInteger.valueOf(n).multiply(factorial(n-1));
        cache.put(n, ret);
        return ret;
    }
}

Easy enough, right?

Now each of these different methods entail different tradeoffs, and given that we may wish to reuse this library in the future, perhaps what we want to use is a technique routinely used in other Java libraries: a pluggable mechanism that allows us at runtime to decide which technique (the slower but small memory footprint technique, or the more memory intensive, but faster technique). So first, we need to refactor our class as a singleton, because pluggable things require instantiated classes and a default singleton class to implement the default mechanism.

So we create a class whose job it is to maintain a singleton for our factory class, and a reference to the algorithm that actually implements our method. This class provides the old interface we have from above, but defers to a separate (and new and improved!) algorithm engine:

public class FactorialUtil
{
    private static FactorialUtil singleton;
    private FactorialAlgorithm algorithm;
    
    /**
     * Default (internal) constructor constructs our default algorithm.
     */
    private FactorialUtil()
    {
        algorithm = new CachedFactorialImplementation();
    }
    
    /**
     * New initializer which allows selection of the algorithm mechanism
     * @param algorithm
     */
    public FactorialUtil(FactorialAlgorithm a)
    {
        algorithm = a;
    }
    
    /**
     * Default public interface for handling our factorial algorithm. Uses
     * the old standard established earlier for calling into our utility class.
     * @param n
     * @return
     */
    public static BigInteger factorial(int n)
    {
        if (singleton == null) {
            // Use default constructor which uses default algorithm
            singleton = new FactorialUtil();
        }
        return singleton.doFactorial(n);
    }

    /**
     * New mechanism which allows us to instantiate individual factorial
     * utilitiy classes and invoke customized factorial algorithms directory.
     * @param n
     * @return
     */
    private BigInteger doFactorial(int n)
    {
        // Defer to our algorithm
        return algorithm.factorial(n);
    }
}

Notice the above class is now responsible for creating the singleton and deferring to the algorithm class. We even have a private initializer which initializes to any algorithm, and a way to create an instance which uses the algorithm we select.

This all depends on an algorithm interface:

public interface FactorialAlgorithm
{
    BigInteger factorial(int n);
}

And here is our cached factorial implementation referred to from above:

public class CachedFactorialImplementation implements FactorialAlgorithm
{
    static HashMap<Integer,BigInteger> cache = new HashMap<Integer,BigInteger>();
    
    @Override
    public BigInteger factorial(int n)
    {
        BigInteger ret;
        
        if (n == 0) return BigInteger.ONE;
        if (null != (ret = cache.get(n))) return ret;
        ret = BigInteger.valueOf(n).multiply(factorial(n-1));
        cache.put(n, ret);
        return ret;
    }
}

Look how beautiful this structure is! I mean, we can also add a non-caching non-recursive algorithm easily:

public class LoopedFactorialImplementation implements FactorialAlgorithm
{
    @Override
    public BigInteger factorial(int n)
    {
        BigInteger ret = BigInteger.ONE;
        for (int i = 1; i <= n; ++i) ret = ret.multiply(BigInteger.valueOf(i));
        return ret;
    }
}

The weakness of this design from a Java perspective should be obvious: it does not allow us to select the algorithm we wish to use at runtime, which was a major design feature we were striving for. (I mean, after all, if our new program has different constraints, we should be able to select the specific algorithm used, right?) So clearly we need to load a configuration property and select the algorithm we want. We can do this by having our utility class look in the System properties, for example, which has the nice property that we can then, higher up, wire up to an external interface as needed. Ideally our main method would look like:

    public static void main(String[] args)
    {
        System.getProperties().setProperty("com.chaosinmotion.factorialalgorithm", "cachedAlgorithm");
        System.out.println("5! = " + FactorialUtil.factorial(5));
    }

This implies that we need a hash map of algorithms that we pick from prior to creating our singleton inside the factorial method.

So we need a factory that can generate the algorithms. We store both a map of instantiated factory singletons by name in mapping, and we store the class references in classMapping, so we don’t create the algorithm class until we actually need it. (No point in running a bunch of constructors and allocating resources we don’t use.)

/**
 * Factory class manages the factorial algorithms in our system.
 * @author wwoody
 *
 */
public class FactorialAlgorithmFactory
{
    private static HashMap<String,FactorialAlgorithm> mapping = new HashMap<String,FactorialAlgorithm>();
    private static HashMap<String,Class<? extends FactorialAlgorithm>> classMapping = new HashMap<String,Class<? extends FactorialAlgorithm>>();
    private static FactorialAlgorithm defaultAlgorithm = new CachedFactorialImplementation();
    
    /** Static initializer registers some of my known classes */
    static {
        try {
            Class.forName("com.chaosinmotion.factorial.LoopedFactorialImplementation");
            Class.forName("com.chaosinmotion.factorial.CachedFactorialImplementation");
        }
        catch (ClassNotFoundException e) {
            // Should never happen.
        }
    }
    
    /** Get the default algorithm for computing factorials */
    public static FactorialAlgorithm getDefaultAlgorithm()
    {
        if (defaultAlgorithm == null) {
            // Warning: this will fail if for whatever reason CachedFactorialImplementation
            // is not in the class path.
            defaultAlgorithm = getAlgorithm("cachedAlgorithm");
        }
        return defaultAlgorithm;
    }
    
    /** Get the factorial algorithm specified by name */
    public static FactorialAlgorithm getAlgorithm(String name)
    {
        FactorialAlgorithm f = mapping.get(name);
        if (f == null) {
            // We haven't created an instance yet. Get it from the class mapping.
            Class<? extends FactorialAlgorithm> c = classMapping.get(name);
            if (c != null) {
                // Create a new instance of the factorial algorithm specified
                try {
                    f = c.newInstance();
                    mapping.put(name, f);
                    return f;
                }
                catch (Exception e) {
                    // Log the error
                    Logger.getLogger("com.chaosinmotion.factorial").
                    warning("Unable to instantiate algorithm " + 
                            c.getCanonicalName() + ", named " + name);
                }
            }
            return getDefaultAlgorithm(); // return something.
        }
        else return f;
    }
    
    /** Register the class so we can construct a new instance if not already initialized */
    public static void registerAlgorithm(String name, Class<? extends FactorialAlgorithm> f)
    {
        classMapping.put(name, f);
    }
}

Rewriting our FactorialUtil class to use our named algorithms instead, we get:

public class FactorialUtil
{
    private static FactorialUtil singleton;
    private FactorialAlgorithm algorithm;
    
    /**
     * Default (internal) constructor constructs our default algorithm.
     */
    private FactorialUtil()
    {
        String name = System.getProperty("com.chaosinmotion.factorialalgorithm", "cachedAlgorithm");
        if (name == null) {
            algorithm = FactorialAlgorithmFactory.getDefaultAlgorithm();
        } else {
            algorithm = FactorialAlgorithmFactory.getAlgorithm(name);
        }
    }
    
    /**
     * New initializer which allows selection of the algorithm mechanism
     * @param algorithm
     */
    public FactorialUtil(FactorialAlgorithm a)
    {
        algorithm = a;
    }
    
    /**
     * Utility to create by name. Calls into FactorialAlgorithmFactory to
     * actually get the algorithm.
     * @param name
     */
    public FactorialUtil(String name)
    {
        algorithm = FactorialAlgorithmFactory.getAlgorithm(name);
    }
    
    /**
     * Default public interface for handling our factorial algorithm. Uses
     * the old standard established earlier for calling into our utility class.
     * @param n
     * @return
     */
    public static BigInteger factorial(int n)
    {
        if (singleton == null) {
            // Use default constructor which uses default algorithm
            singleton = new FactorialUtil();
        }
        return singleton.doFactorial(n);
    }

    /**
     * New mechanism which allows us to instantiate individual factorial
     * utilitiy classes and invoke customized factorial algorithms directory.
     * @param n
     * @return
     */
    private BigInteger doFactorial(int n)
    {
        // Defer to our algorithm
        return algorithm.factorial(n);
    }
}

And we have to modify our CachedFactorialImplementation and LoopedFactorialImplementation to include static class initializers which register those classes with my factory:

public class CachedFactorialImplementation implements FactorialAlgorithm
{
    static HashMap<Integer,BigInteger> cache = new HashMap<Integer,BigInteger>();
    
    static {
        FactorialAlgorithmFactory.registerAlgorithm("cachedAlgorithm", CachedFactorialImplementation.class);
    }
    
    @Override
    public BigInteger factorial(int n)
    {
        BigInteger ret;
        
        if (null != (ret = cache.get(n))) return ret;
        ret = BigInteger.valueOf(n).multiply(factorial(n-1));
        cache.put(n, ret);
        return ret;
    }
}

and

public class LoopedFactorialImplementation implements FactorialAlgorithm
{
    static {
        FactorialAlgorithmFactory.registerAlgorithm("loopedAlgorithm", LoopedFactorialImplementation.class);
    }
    @Override
    public BigInteger factorial(int n)
    {
        BigInteger ret = BigInteger.ONE;
        for (int i = 1; i <= n; ++i) ret = ret.multiply(BigInteger.valueOf(i));
        return ret;
    }
}

The beauty of this architecture, of course, is that we can even add in our own custom algorithm and plug it into the singleton underlying FactorialUtil. We simply create our new FactorialAlgorithm implementation, registering our class with the FactorialAlgorithmFactory class during static class initialization:

public class RecursiveFactorialImplementation implements FactorialAlgorithm
{
    static {
        FactorialAlgorithmFactory.registerAlgorithm("recursiveAlgorithm", RecursiveFactorialImplementation.class);
    }

    @Override
    public BigInteger factorial(int n)
    {
        if (n == 0) return BigInteger.ONE;
        return BigInteger.valueOf(n).multiply(factorial(n-1));
    }
}

and in our main routine, we make sure our class is loaded, then we set the property to specify we use our new algorithm.

    public static void main(String[] args)
    {
        try {
            Class.forName("com.chaosinmotion.factorial.RecursiveFactorialImplementation");
        }
        catch (ClassNotFoundException e) {
            // if this fails, no matter; we'll still use the default implementation.
        }
        System.getProperties().setProperty("com.chaosinmotion.factorialalgorithm", "recursiveAlgorithm");
        System.out.println("5! = " + FactorialUtil.factorial(5));
    }

No problems! And the architecture even lends itself to our plugging in more ingenious solutions, such as the algorithms documented here.

separator.png

Now I’m sure at this point there are a number of Java programmers reading this who are nodding their heads at how cool all of this mechanism is. There are a few, of course, who are about to hit the comment button and say “gosh, instead you should wire up the properties this way, or that way…” For example, I could have put the initializer for the different class mappings into a properties file that is part of the package, or as an XML file. Or perhaps I could have set the property to accept the class name explicitly; that way I could have the FactorialAlgorithmFactory do the ‘forName’ call and instantiate the class directly without having to look it up in two hash maps.

And I’m sure there are a few programmers out there taking notes or even cutting and pasting the above blocks of code. (Yes, I tested it all; it works on my system, though I don’t claim to have done the cut and paste properly in all cases.)

But here’s my point.

It’s all crap.

Every last line of it.

Sure, there are circumstances where a pluggable architecture would be desirable or even necessary–but that comes up so rarely it’s not even funny. About 99% of the time I see code like this, it’s completely and utterly useless. It’s obscuring the purpose of the code–replacing a two or three line utility (max!) with dozens or even hundreds of lines of self-important masturbatory Java bullshit. Sure, it may make you feel good–but it makes an ugly mess of it that future developers will have to clean up, or more likely just avoid like the plague.

And, worse, in the entire discussion, did you notice something?

We never handled negative numbers.

separator.png

The smart Java developer would know when to stop. Life is too short to build castles in the clouds. He’d know that a simple looped solution is more than sufficient, and of course he handles negative numbers. (Note that in our recursive solutions, a negative number results in an endless loop.)

The really smart Java developer figures out the domain of the problem set, knowing (for example) that factorial is actually a special subset of the Gamma function. Perhaps the right answer isn’t any of the code above; perhaps the right answer is using Gergo Nemes’s approximation to Stirling’s approximation to the Gamma Function:

    static double Gamma(double z)
    {
        double tmp1 = Math.sqrt(2*Math.PI/z);
        double tmp2 = z + 1.0/(12 * z - 1.0/(10*z));
        tmp2 = Math.pow(z/Math.E, z); // ooops; thanks hj
        tmp2 = Math.pow(tmp2/Math.E, z);
        return tmp1 * tmp2;
    }

But it depends on the domain: a domain we only second-guessed in all of our factory creation/algorithm interface bullshit above.

separator.png

The biggest complaint I have with many Java developers is that they develop a whole bunch of really bad habits. Specifications are unclear, or they think someday the code may need to be extended into a different direction. So they write a whole bunch of overblown architectural nonsense, sight unseen, thinking that the additional crap someday will help out and make things easier. And Java as a language lends itself to doing this very quickly and easily, so that (as the theory goes) it’s easy for us to build architectures that will someday make it easier on us in the future.

But the future never gets easier, does it?

Because a year from now they wade through all this excess baggage written at a time when they thought they understood the domain of the problem (but clearly didn’t), instead of having simple code (like the very first example of factorial above) that they can revise as needed, they wind up with the most over-engineered pile of nonsense that no-one can possibly understand.

And rather than wade into the mess to untangle the knot, or at least understand the mechanism that was put into place, they just plaster over the problem in a classical example of the lava flow anti-pattern. Perhaps they don’t understand how to create a pluggable algorithm, so instead they override the FactoryUtil class, or they simply create a new FactoryUtil class instead–and add hundreds more lines of (ill-conceived) code to plaster over the lack of understanding of the hundreds of existing lines of (ill-conceived) code.

separator.png

So please, do us all a favor: if you have the urge to add complexity because “someday we’ll need it, I just know it!”, or because “it’s not sufficiently flexible enough” or “we need reusability in our code” or (God help us!) because it’s “cool”–just go home early. Watch some cartoons. Rent Inception on DVD.

And stop creating extra work for us in the future for no good reason.

This entry was posted in Java. Bookmark the permalink.

51 Responses to How (not) to write Factorial in Java.

  1. Jens says:

    > if (null != (ret = cache.get(n))) return ret;

    Ouch. Never. So much for elegant, beautiful coode…

  2. Don’t like early returns?

  3. werebear says:

    I almost started crying when you added the singleton – your code looks exactly like what our code at work looks like. The most fun thing is how everything gets slower and slower (and stopped working on 32-bit Java as we require more than 4GB of RAM by now given all those HashMaps with millions of objects in them) but they find that completely normal and just buy more and more expensive machines to run the software.

  4. Can I scare you?

    Now a new developer comes along and he doesn’t understand the class loading or static class initializer structures used in the above code. Perhaps he wants a version of factorial(n) which does not use quite as much memory, but he doesn’t know how to use the existing utility class, because it wasn’t well documented or he’s on a deadline. So what does he do?

    The lava flow anti-pattern.

    First, he creates the new algorithm class:

    public class NewFactorialAlgorithm implements FactorialAlgorithm
    {
        @Override
        public BigInteger factorial(int n)
        {
            BigInteger ret = BigInteger.ONE;
            for (int i = 1; i < = n; ++i) ret = ret.multiply(BigInteger.valueOf(i));
            return ret;
        }
    }
    

    Then, because he wants to keep the singleton pattern but doesn't know how to plug into it, he creates a new subclass of the factorial class, which manages it's own singleton:

    public class NewFactorialUtil extends FactorialUtil
    {
        static NewFactorialUtil singleton = new NewFactorialUtil();
        
        private NewFactorialUtil()
        {
            super(new NewFactorialAlgorithm());
        }
    
        public static FactorialUtil getSingleton()
        {
            return singleton;
        }
        
        public static BigInteger factorial(int n)
        {
            return getSingleton().doFactorial(n);
        }
    }
    

    Which, of course, requires reaching into the original FactorialUtil class and changing the protection scope of doFactorial to 'protected'.

    I've seen this in the field! Drives me bats, and no-one thinks it's a problem. In fact, I've encountered Java programmers who think this is a *good thing*...

  5. @Jens:

    By the way, if all you’re complaining about is a compound assign and compare, with an early return, you’re missing the bigger point: the routine as written, if passed a negative number, will go into an infinite loop, crashing with a stack overflow.

    The fact that many Java programmers are so damned concerned with form they fail to notice the function is completely broken boggles my mind.

  6. hj says:

    The approximation algorithm contains a small error. It should be as follows.

    static double Gamma(double z)
    {
    double tmp1 = Math.sqrt(2*Math.PI/z);
    double tmp2 = z + 1.0/(12 * z – 1.0/(10*z));
    tmp2 = Math.pow(tmp2/Math.E, z);
    return tmp1 * tmp2;
    }

  7. Slaps forehead. Corrected in the text. Thanks.

  8. fffffffffffffff says:

    http://en.wikipedia.org/wiki/Strawman use evidence next time please.

  9. f*n: Really?

    That’s all your fuzzy little brain could fucking come up with? I mean, do you even know what I’m arguing? And you think it’s a God-damned “straw man”?

    Here are my points, summarized so you can deconstruct the logic and pick a better logical fallacy from here: http://onegoodmove.org/fallacy/toc.htm

    (1) Until the first ‘sun’ separator, I show some working code evolving from a simple function to a complex “enterprisy” style Java application, where the word “enterprisy” is an opinionated but otherwise non-descriptive tag associated as a matter of opinion.

    Where is the fallacy here?

    (2) Later I said “Now I’m sure at this point…”, clearly expressing opinion. I also say “And I’m sure…”, again expressing opinion.

    How the fuck is opinion a logical fallacy?

    I also note that the routines above didn’t handle negative numbers–the first point of fact that is offered above. And the fact can be verified by plugging the code into Eclipse and passing a negative number.

    Did I make a mistake? Are negative numbers not causing the recursive routine to go into an infinite loop, only to crash with a stack overflow?

    (3) I say “The smart Java developer would know…”–again, clearly expressing opinion. Further, I note “The really smart Java developer figures out…”–opinion, once again.

    (4) After the next divider, I start with “The biggest complaint I have”–opinion, once again. And I note that the pattern, in my experience (which is an opinion with battle scars), is subject to the lava flow anti-pattern.

    (5) Then I start with “So please, do us all a favor:…”–opinion once again.

    Dude, where did I make a logical argument (aside from the one observation that the routines at the start of the article contain a bug) other than expressing opinion? And how is having an opinion a logical fallacy?

    Or did my opinions hit a little close to your own fucking home?

  10. dimenwarper says:

    Agreed, and the most horrifying part of it all is that this is what is taught in schools as ‘best practices’. At least in my college, software engineering classes were designed to make you a ‘software architect’, trying to convince you that the architect mindset should be always on.
    Always remember that 99% of the code you write will eventually have to be rewritten.

  11. attila says:

    Sheesh… FactorialAlgorithmFactory should’ve really used the JAR Service Provider Mechanism to register implementations: http://download.oracle.com/javase/1.4.2/docs/guide/jar/jar.html#Service%20Provider

  12. Tom says:

    bruhaha very good article,
    it is really working in the practice like this,
    in my current place somebody invented a security module that should check if user has rights to perform a certain operation.
    Now this module is like the factorial calculation in the article. It has been “evolved”, now there are several factories, proxies, abstract factories interfaces and framework components involved. Everything is configurable in several layers through xml setup files, boot starters and so on. However at certain points (in order to make it simpler) assumption was made that a certain type of security check has to be there, if not then comes a null pointer exception. Haha

  13. synap says:

    This is exactly the shit that they tried force feeding me while in university, I debated, fought, pulled hair out of my head, started to doubt the sanity I thought I gained from coding since years before my teenage years. I even gave in for a while, gained the believe that it was in fact I who was wrong about these matters, that it was my young stubbornness that made me refuse the wisdom of something I now see as a disease called overdesignitus.

    If the people around you can only talk about code in terms of design patterns (which they interpret mistakingly too at the same time) want to refactor a ‘for’ into an overblown enumerator, a ‘switch’ into a strategy that is both generic and loads its strategies from external assemblies, if they in fact spend more time drawing up UMLs charts instead of writing the code. Then you know why JWZ (XScreenSaver fame, o wait and something that had to do with this ‘netscape’ thing?) left the software industry and started a night club ;-).

    My rant is complete! Also on UML (of which I see many uses but also a tool that is overused to the max in my opinion): I work within a small team on software (not some crack web 2.0 stuff) that is a few 100KLOC in size, the code is structured, easy to understand for us all. And guess what it is created without UML or any other charts at all. It is created by the sheer force of sanity and simplicity.

    Cheers.

  14. tom.k.cook says:

    Good article, I needed a few laughs. Sad, indeed, that some will see this code as best practice.

  15. AdiLev says:

    This make be remember that I once wrote a simple report in JSP that show status of TO DO.
    Then my boss want something that it more complex and gave it to a young developer to write it. From 3 classes it become over 24 classes and nothing worked. The developer used the same nonsense of factory of factory with no reason and then left it as skeleton only. My boss ask me if I can make it work, I said I don’t intend to use it as it was just the same crap…

    I think if you need different implementation you can used Spring framework… This at least hide some of the factories.

  16. Amusing article!! Sadly we do see this sort of architectural gold plating all of the time. But is that really the fault of the language? Or the developer?

    I’d argue that the developer isn’t following TDD/YAGNI principles, which is a trap in any language, let alone Java.

    Still this article should be held up as a great example of Java ant-patterns – I’m impressed you went through all of the steps :)

  17. maxant says:

    If I were in a position able to offer you a job or contract, I would do it based on this article alone – you hit the nail on the head – spot on!

    A pragmatic solution is in 90% of cases all that is required, so long as the architecture and general design is good.

    Another argument against building for the future is a financial one, which I’ve blogged here: http://blog.maxant.co.uk/pebble/2006/09/19/1158677220000.html and here: http://blog.maxant.co.uk/pebble/2006/03/17/1142633880000.html

    Ant

  18. kev.lee.wright says:

    Almost there!

    You forgot to pull all those factories into an XML configuration file so the choice of implementation can be changed after the application is deployed.

    All that remains then is to justify the overhead by calling it a “framework”, and then name this atrocity after a season of your choosing…

  19. LucianoQ says:

    Excellent post – thanks.

    I would also like to add that sometimes the boss, or tech lead, is to blame as well. I find that this problem occurs more often with the junior, less experienced programmers – veterans are more likely to do the absolute minimum that meets the needs (and they are more likely to have heard of test-driven development, or other useful approaches).

    The reason why the boss might be to blame is that often they do not give sufficient direction to their staff about what is important.

    An article I read several decades ago described an experiment where they got a lot of programmers and gave them all the identical task – but the programemrs also had to meet a particular objective. There were 4 objectives, but each programmer only had to worry about one. (Of course they all had to produce a properly functioning program).

    The objectives were:
    - the program is to be as fast as possible
    - the program has to be as small as possible
    - the program has to be completed as quickly as possible
    - the output of the program must be as neat and logical and comprehensible as possible.

    The result was that the programmers did achieve their objectives. The programmers who were to produce fast programs delivered faster programs than the others. The smaller programs were delivered by those who set out to do that. And so on.

    But, when comparing the programs against the other objectives, something very interesting came out.

    Those who produced the fastest programs ended up producing the largest programs, took longer than everybody else, and their output was the ugliest. But, can you criticise them? No – they achieved their objectives.

    And, even more interesting is that one group scored second for all the other objectives – which one? – the high quality output group. Their programs were the second fastest, the second smallest, etc.

    The moral? Most programmers will achieve objectives – but the boss has to spell them out. If the boss is quiet, they will invent their own objectives, and then no one can predict what they will do.

  20. LucianoQ says:

    Ah – here is a reference – I had forgotten what it is called.

    “You ain’t gonna need it” (or YAGNI for short) is the principle in extreme programming that programmers should not add functionality until it is necessary.

    http://en.wikipedia.org/wiki/You_ain%27t_gonna_need_it

    Cheers

  21. bebbo says:

    #1 – only create classes if required!

    As long there is no gain by using different configurable implementations, don’t implement crap like this.

    #2 – A HashMap is not a cache!

    A HashMap never drops values on its own. A cache does and thus frees memory.

    Never use: “HashMap cache …”
    It’s like: “Madoff yourMoneyIsSafe …”

    What about: “LRUCache cache …”?

    #3 most of the example code is acceptable, but not

    public BigInteger factorial(int n)
    {
    BigInteger ret;

    if (null != (ret = cache.get(n))) return ret;
    ret = BigInteger.valueOf(n).multiply(factorial(n-1));
    cache.put(n, ret);
    return ret;
    }

    Writing it better readable costs neither speed nor code lines:

    public BigInteger factorial(int n)
    {
    BigInteger ret = cache.get(n);

    if (ret != null) return ret;
    ret = BigInteger.valueOf(n).multiply(factorial(n-1));
    cache.put(n, ret);
    return ret;
    }

    #4 stick to the requirements

    I don’t know the requirements, but it’s obious that the gamma method yields different values than the original method.

    Maybe this is ok, maybe not. If the exact values are required, provide them.

    Bebbo

  22. filpizlo says:

    Clearly, what will make this all better, is a formal specification of what it means to compute a factorial. Using formal logic. So that an automatic theorem prover can verify that your factorial algorithm loadable modules actually compute factorials. Because without that you won’t be able to Reason about the code and it might not be Correct.

    The above would almost be funny if there wasn’t an entire field of wankers working on exactly such things.

  23. carfield says:

    You missed the multithread version

  24. jmann says:

    Became a registered user to simply say, “Thank You”.
    ’nuff said.

  25. @bebbo:

    (1) Of course.

    (2) Again, of course: WeakHashMap would be preferable in a situation where the associations are potentially unbounded or at least potentially huge. By quickly implementing a HashMap I was implementing a sort of dynamic programming, whereby each subproblem of the overall problem is ideally only solved once.

    (3) What strikes me funny about this comment is that, in telling me the preferred way to write factorial(), your function is functionally incorrect: there is no lower bound on the recursion. That comment was made earlier as well, with the other commentator not noticing that negative numbers are also unbounded.

    Code that works trumps code that is broken (or dangerously broken, as my earlier example deliberately was, where negative numbers may not be tested in a typical developer-written unit test).

    Further, I see the difference between:

    if (null == (r = cache.get(n))) return r;
    

    and

    r = cache.get(n);
    if (null == r) return r;
    

    as the difference between writing simple sentence, and writing run-on sentences. Run-on sentences are not recommended, but are often used–such as this one, where the secondary clause is being used to drive my point home.

    In general you should favor simpler statements, sure. But in my opinion, on occasion compound statements, if they represent a common idiom, helps with readability since they represent a compact idea in the code. For example, if we constantly applied the rule above, then:

    assertNotNull(thing = callOne());
    assertNotNull(thing2 = callTwo());
    
    assertNotNull(thing3 = callThree());
    

    would be unacceptable. But troublingly enough, someone who just blindly comes along and rewrites this as:

    thing = callOne();
    thing2 = callTwo();
    thing3 = callThree();
    
    assertNotNull(thing);
    assertNotNull(thing2);
    assertNotNull(thing3);
    

    Well, guess what: this changes the semantic meaning of the previous code. Perhaps callTwo relies on a side effect of callOne by design–and calling into callTwo if callOne fails and returns null could do bad things–in which case, to achieve a narrow idea of readability, you’ve introduced a potentially fatal defect. On the other hand, the semantically similar:

    thing = callOne();
    assertNotNull(thing);
    thing2 = callTwo();
    assertNotNull(thing2);
    thing3 = callThree();
    assertNotNull(thing3);
    

    Is this really superior? Is this more readable? Worse, a white space (which I see a similar to a paragraph break) would change the apparent meaning: we no longer have the grouping of callOne and callTwo in the original, which hints at the idea that perhaps callOne and callTwo are somehow related. And:

    thing = callOne();
    assertNotNull(thing);
    thing2 = callTwo();
    assertNotNull(thing2);
    
    thing3 = callThree();
    assertNotNull(thing3);
    

    This may capture the same grouping, but the interspaced assertNotNull in my opinion does not aid comprehension.

    Remember: all of these rules serve to help the poor son-of-a-bitch who has to maintain our code once we’ve moved on to more interesting projects. So like writing English, there are rules: never begin a sentence with ‘so’, never write run-on sentences, always use ‘and’ in the third clause of a list of items. But we can break them if we know what we’re doing: no-one is going to shoot me for writing a compound sentence beginning with a conjunction.

    Bottom line, though, is that:

    public BigInteger factorial(int n)
    {
        BigInteger ret;
    
        if (n <= 0) return BigInteger.ONE;
        if (null != (ret = cache.get(n))) return ret;
        ret = BigInteger.valueOf(n).multiply(factorial(n-1));
        cache.put(n, ret);
        return ret;
    }
    

    is preferable to:

    public BigInteger factorial(int n)
    {
        BigInteger ret = cache.get(n);
    
        if (ret != null) return ret;
        ret = BigInteger.valueOf(n).multiply(factorial(n-1));
        cache.put(n, ret);
        return ret;
    }
    

    because it doesn’t crash.

    (4) The problem with sticking to the requirements is that we don’t always have requirements to stick to. In the above example, I observed that the really smart developer goes and susses out what the requirements actually are, realizing that there are alternatives out there.

    In the above example, I noted the mathematical fact that for integers, gamma(n+1) == factorial(n).

    My point here is that a really good programer thinks beyond the loose requirements they’ve been told, and looks outside the box to find out what the code should be for the project.

    The reason for this should be obvious on reflection: many project managers believe they’re software architects, and they’ll manipulate the requirements thinking about how they think the code should be written. Ideally project managers provide the interface between business managers and development managers, and leave the software architecture to the people with the college degree and years of experience.

    But they don’t.

    In my example above, I deliberately didn’t say what the requirements were, and in fact, I alluded to the idea that we don’t know what the precise requirements are. That’s the way it is in the real world. And for all we know the project manager on this project needs the gamma function because we’re building a statistics package as part of a larger product that needs to evaluate relative complexity of picking items from a larger pool of items, but the values we’re getting from a higher stats package are fractional values. And perhaps our project manager cum mathematics “expert” and software “architect” decided during the development of the requirements that higher up, we should round the output of our statistics package to an integer because he didn’t know about the gamma function.

    It’s why it’s important to not just know the project requirements, but to also have some insight into how they were developed, and to talk to the project manager developing them–because you may have knowledge (such as gamma(n+1) == factorial(n)) that the project manager may not.

    Clearly, of course, this looking outside the box should be lightly done, and balanced with the corporate need to actually get something out the door that works and doing it on time: and a really good Java programmer understands this. But when he sees a loosely defined requirement like this, and he knows something obscure that seems applicable, it’s good to ask questions to find out if perhaps a different approach should be used.

  26. idutta says:

    This is an example how you can bloat your software in quest of utter completeness and consistency. For example, use of BigDecimal gives you a false sense of completeness and consistency. BigDecimal simply does not make this routine cover all possible positive integers. Because you are always limited by your computer’s memory.

    Cheers
    Indra

  27. Bodo says:

    Am I a complete idiot or is there a bug in the first Factorial class that uses the singleton? Perhaps I’m using the class wrong? How/where is the singleton set such that it uses the non-default algorithm?

  28. @Bodo:

    Oh, please dear God don’t use any of the code I wrote above!

    Yes, there is a bug in the first singleton version of the Factorial class. Actually, there are a couple: first, it’s not a singleton, properly speaking. Yes, there is a component of the class which will create a singleton when you call the static version of factorial(), but it can also be instantiated as a stand-alone (non-singleton) class with a custom algorithm. And yes, you’re right: there is no mechanism to set the algorithm.

    I “fix” this second bug with the second version of the singleton, which uses a property set in the System properties structure in order to determine which instance of the algorithm class will be used in the singleton instantiation.

    If you want to make FactorialUtil a proper singleton, then none of the constructors should be public.

  29. rebelBodhi says:

    Awesome article.. one of the best dev-related posts I’ve read in a while..

    I think many won’t get this – the whole ‘software craftsmanship’ ‘movement’ going on in the .NET community is beginning to echo this mindset more and more. I worked in .NET for years, and recently have begun to move to Java, so have some perspective on both.

    The same problem is definitely alive in many .NET shops (not the drag-and-drop ones, of course ;) ). I’ve worked on projects where extensibility/pluggability is a real (even primary) requirement – however inevitably this is the not the case for *every single aspect of the application*.

    When one tries to abstract/decouple/make runtime swappable every single f’n class, method, type, etc.. eventually you end up with something so general, so abstract, so awkward to implement and evolve.. that it’s useless. Or, more importantly, you spend a year or more on ‘framework’ and theory, without even implementing the real business problem.

    We write code to solve problems.. We should always have the business/practical/mathematical problem in mind as we write every line of code. Otherwise, as you so eloquently put it, we’re masturbating.. which is fine too, as long as you know that’s what you’re doing :)

  30. ewalshe says:

    Many thanks, that was a good read.

    Suggestions on how to improve the plugin design miss the point. I’ve never seen a minimalist design produced through the use of TDD evolving into something that needs a plugin. I have seen systems that used plugins where only one specialization of an interface existed or ever would exist – a terrible waste of effort. Any system I’ve encountered that used a plugin architecture was the result of too much up-front design.

  31. One thing that strikes me about the current “refactor by adding complexity” memes in the Java world and that (apparently) seems to be spreading into the .NET world is how often we do things (like create factories and extensible plugins) compared to how seldom these things actually need to be done.

    I strongly believe that, in a developers’ entire professional career, he may encounter an actual valid need to build an extensible framework like the one I sketched above perhaps once in his entire life.

    Once.

    And we do it all the time.

    (Shakes head.)

  32. Bodo says:

    Oh, don’t worry, I’ll never use the code :-). I was just working through what you did is all, and when I saw what I thought was a bug, I wanted to test it. Hence my question about using the class improperly.

  33. jennings says:

    Beautiful post. When I started reading it I was thinking to myself “I bet this guy thinks he’s a really good programmer” then “oh my God! this guy thinks more complexity is better” then… bam. the punchline. Awesome. I too started coding on the TRS-80, for me it was the model III in 1980. Maybe this is why I’m loving your blog. Great stuff. Thanks.

  34. I wanted the Model III when I was growing up; it just seemed like such a cool all-in-one box. Me, I had a Model I and an expansion box, which always seemed like such a cobbled together kludge. (I even went and soldered an extra 1kx1 RAM chip onto the circuit board to get lower-case characters.)

  35. chenrici says:

    Nice article, but still left me quite unsatisfied, if not frustrated.
    Why?
    For me the article brilliantly misses the point altogether and is sadly typical for programmers in generell:
    What is the ratio the write any code?
    You – probably quite rightly – are bashing “over engineers”, who miss the domain requirements alltogether. And you came up with 7 Liner, with 1 coding error. For me a couple of lines too much and on the other hand the most important code missing. We all know that Testcode can easily exceed the functional code. But i really do not understand why you would want to implement any of the Gamma functions family. There are enough mature and tested implementations readidly avaible. From my experience the use case for using a Gamma function, is the important part for a company, which may lead to lead to quite other problems not adressed here at all.
    So the question here should be, why write the code at all. An more often then not, the answer should be: Do not!Your code prefered code is the best example for that. Be humble and do not unterestimate the (complexity) of problem domain.
    On the other hand. If you really think you need to write code, because you think there is a business case, or that are in the unhappy situation, that no one else has adressed your problem. Then neither a simple Factory “bashing” would do, nor a “simplistic” domain analysis.
    So my simple question: too what kind of people is this blog adressed?

  36. But i really do not understand why you would want to implement any of the Gamma functions family.

    The point of that part of the essay was to point out that sometimes the right solution is not the obvious one–and sometimes you may need to think outside of the box to find it.

    Not many people know that factorial(n) == gamma(n+1) for n as an integer–it’s a relatively obscure fact. And in our rush to build complex factories and algorithms interfaces and dynamically configurable singletons: known “complexity”–we often miss the more subtle complexities that are important.

    In other words, we ignore the real problems and instead invent problems for which we know how to solve–and create a spaghetti tangled mess instead, thinking that our ‘invented’ problem is more interesting. We then even go on to congratulate ourselves for solving the “fake” problem, leaving the real problem alone.

    For Christ’s sake, I’ve seen people promoted because they delivered a 40- or 50-class solution where a 6-class solution would have done the job, because the solution they offered was so overly engineered of course they must be superior programmers who deserve a promotion. And when I rewrote their 50-class solution as a six class solution instead, I got disdain from some of my co-workers for making the solution too simple and easy to understand.

    We don’t reward simplicity anymore.

    So my simple question: too what kind of people is this blog adressed?

    Nobody.

    I just write about things that I find interesting or annoying. If people happen to read it, that’s great. If not, that’s fine too.

  37. lalborno says:

    Agree.

    I’ve seen this before and it’s not fun at all. Few years ago our team had to rewrite 100 thousands of code because of that. The code had every pattern applied to it… it was beautiful, some said “Perfect” but it didn’t work.

    A friend of mine, says that it’s because java lost it’s way. It was meant to be simple, but now is over patterned/layered/designed.

  38. thatguyer says:

    Hi,

    Great article — I’ve been feeling for some time now that this programming style will result in a lot of code that simply collapses under its own weight.

    I’m a compiler/VM hacker guy, so I decided to type in these different versions and time them. I realize that it’s beside the point, but I was curious.

    One initial observation is that the factory code is very brittle. I used my own package names, which screwed up the algorithm registry. If you don’t put the right class names in the factory’s class initializer then the VM doesn’t call the individual algorithm class initializers. As a result the factory silently fails and chooses the default algorithm every time. It took some debugging to figure out what was wrong and fix it.

    Another observation is that for benchmarking purposes, the factory version is actually pretty nice. I could easily write a single test harness and run it on all the different algorithms.

    Finally, all versions are relatively fast. I had to create a very work-intensive test harness in order to see any measurable differences. Each run repeats the following computation 1000 times: compute all factorial values from 1! to 300!. Note that the regular “int” version overflows, but it still performs the work.

    I ran on a Linux box with an Intel Core 2 quad running at 2.66GHz with 8GB RAM. Just for grins, I also wrote C++ versions of the basic iterative and recursive algorithms:

    Results:

    Iterative (int): 0.065s
    Recursive (int): 0.150s
    loopedAlgorithm: 11.492s
    recursiveAlgorithm: 6.387s
    cachedAlgorithm: 0.114s
    Iterative C++: 0.001s
    Recursive C++: 0.001s

    Note that my Java benchmarking methodology was relatively sloppy: I just typed “java FactorialUtil cachedAlgorithm”. What can you do.

    The most surprising result, IMHO, is that the recursive BigInteger version is faster than the iterative BigInteger version. I suspect this might have to do with memory behavior (allocation? GC?).

    Naturally, the C++ is about 100X faster (maybe more).

  39. “A friend of mine, says that it’s because java lost it’s way. It was meant to be simple, but now is over patterned/layered/designed.”

    I have a personal theory about that.

    Java is simple to program. With a good IDE like Eclipse, Java is a pleasure to write programs in. It’s also quite easy to write some very complicated algorithms, and building a client/server stack is absolutely trivial.

    But programmers like complexity. We like things that are complicated: the flashing complicated lights on the control panels on Star Trek, or the joys of figuring out a complicated calculator with nearly a hundred buttons or playing with cell phones that have dozens of modes and menus and buttons. Programmers are puzzle solvers, and we love puzzles. We love taking things apart, putting things together again: many of us are the ones who were punished at 8 for disassembling the family computer or television set or radio, despite the fact that we nearly put it together in working order.

    We love complexity.

    So we, almost as if it were a primal instinct like hunger or sex, crave complexity when things seem too simple. And we create that complexity, looking for problems that don’t exist (like not knowing which version of factorial() would be best), and we create complicated solutions (like a flexible configurable singleton with pluggable architectures) to address these non-problems.


    What’s worse is when programmers who think “complexity is good” go off and manage things or build things. The person who created the unworkable and unmanageably complex (but “perfect”) system is promoted while the person with the simple (but easy to prove correct and easy to maintain) system does not. We design products with buttons coming out of the sides, front, and back like a little porcupine, cramming in features because we think the complexity of the additional features is a bonus. (I read an essay that suggested the iPhone would never sell in Japan because it wasn’t complicated enough and the Japanese market liked complicated, obtuse things. They were wrong, natch.)

    And we create more and more complex things with unmanageable systems backing them–because we think “well, if they can’t keep up with the complexity, they’re just lusers.”

  40. Lucas Ward says:

    That was a great post. I especially liked how the code effectively showed the evolution of a simple problem to a complex architecture. It really got me thinking about why developers do these things:

    http://www.lucasward.net/2011/02/speculative-generality.html

  41. ivan says:

    I wonder what this blog would read like if the author would omit the cynicism and overcome the constant need to be righteous. As is, it reads more like a bitter-sweet story of a Java developer who has been hurt by careless Java programmers in his life.
    (“So please, do us all a favor:”… who is “us” and who is “all”.. is there an army? who can join?)

    Emotional crap aside… point well made.

  42. I wonder what this blog would read like if the author would omit the cynicism and overcome the constant need to be righteous.

    Really?

    You went through the trouble to register, set up an account, and comment on a stranger’s blog just so you could tell him that he’s a douchbag?

    Dude; I already knew I was a douchbag. I didn’t need your reaffirmation.

  43. fj says:

    a) Use Spring. Problem solved.
    b) This problem is not unique to Java.
    c) This problem is not unique in any way. Developers have been making code overly complicated since the dawn of programming in the name of future extensibility.
    d) This is a double-edged sword and programmers are just as good at screwing up simple code bases as they are complicated ones. I’ve been part of many software projects where the answer was always to build the simplest and most straightforward solution possible, and the outcome in every case was years of pain and suffering when the system use cases scaled to the point that we had to slide in a real architecture. This is the difference between ideologues and pragmatics. Ideologues see a two-dimensional world where there is a “right way” to do things and then there is everything else – and everything else is stupid, idiotic, and moronic. The dualists end-up write angry blogs about how dumb everyone else is and the realists have long careers building real-world systems.

  44. Use Spring. Problem solved.

    *Hallow laughter*

    Spring is the epitome of the overly-engineered.

    I’ve been part of many software projects where the answer was always to build the simplest and most straightforward solution possible, and the outcome in every case was years of pain and suffering when the system use cases scaled to the point that we had to slide in a real architecture.

    The real problem is this: it’s easy to make something more complicated. It’s pretty God-damned hard to make something more simple.

    So the real solution is to make something as simple as possible–and no simpler. The trick, of course, is the “no simpler” part: clearly one should not use the same software techniques for a piece of software designed to handle 1 transaction/day (say) than for software designed to handle 1 transaction/second. And more sophisticated techniques clearly are needed if you are handling 1 transaction/millisecond.

    Same thing with reliability: if you need (say) 90% uptime, you can use different techniques than if you need (say) 99.999% uptime.

    Of course real genius is understanding how one goes from 90% uptime on 1 transaction/day to 99.999% uptime on 1 transaction/millisecond, to understand enough of the latter problems that you design the former solution so it can be evolved–but without introducing the complexity for the former until you really need to up the reliability and performance. That’s what they pay software architects to do–or rather, used to, since for some reason or another, managers and developers no longer think this is a solvable problem without subscribing to whatever faddish tool set or technique was sold last year at Java One.


    The real problem I’ve seen, and the problem I’m alluding to in my little essay, goes towards the problem of premature optimization: engineering solutions to problems that we do not currently have nor currently need to solve–and engineering those solutions wrong because we don’t understand them because we haven’t encountered them yet.

    It’s why I hate Spring. For the large scale engineering problems it’s designed to solve, Spring is in fact the right answer. But it often gets used in places where Spring is not needed at–such as an internal reporting web site designed to run on one machine which only needs 90% uptime, which handles perhaps 1-5 transactions/minute (yes, minute).

    I hate Spring just as I hate all other overly complicated solutions because it becomes the defacto answer to every possible question–including “how should I wire my RPC” (since, clearly, no-one bothers to read the docs for java.rmi.*) and “how can I dynamically swap in individual code modules for testing purposes” (Can’t you just use a test harness run inside a good IDE debugger?)

    What really bothers me about Spring, though, is not Spring at all–but the mentality of developers who immediately go for the defacto “correct” solution without thinking, who can’t then be bothered to read through the documentation on how to really use Spring within a development and debugging context.

    Because the real problem is this: things like Spring are brought in without thinking how it’s going to fit into the development process, by developers who think that it (or whatever other large scale engineering solution for their small scale project) is so vastly important that we can dispense with the core tools of development: discoverability, debugability, compilability and flexibility.

    And so we wind up with a build environment that cannot be opened in an IDE, of a system that cannot be easily run and debugged end-to-end, using a configuration system which is impossible to trace or debug or even discover (thanks to AOP autowiring) which code module calls into what, with an architecture that is not well documented and using design patterns that look like they come from this list instead of this one, and then we wonder why we still have development death marches and two day bug hunts to find one simple, but obscure bug, and last minute all-nighter pushes to get software out the door.

    Or, worse, we no longer wonder why we do two day bug hunts, death marches and last minute all-nighters because that has become the norm.


    And yes, it can be far worse in C++, C, Assembly Language, Fortran, Cobol, or any other language. In fact, it gets far worse more quickly in some other programming languages, simply because discoverability tools in most languages are not as sophisticated as in Java (Grep? Really?). On the other hand, given Java’s rich assortment of tools, such as reflection, class introspection, annotations, proxy support and the like, it’s pretty damned easy to create something completely impossible to understand that is configured with a “configuration file” that is damned near Turing complete in it’s own right. (I’m looking at you, Spring…)

  45. David Conrad says:

    Try this on for size. Divide and conquer, faster for large factorials. :)

    import java.math.BigInteger;
    import static java.math.BigInteger.ONE;

    public class Factorial {
    private static final int MAX = 20;
    private BigInteger[] cache = new BigInteger[MAX+1];
    private BigInteger N = BigInteger.valueOf(MAX+1);
    private final BigInteger TWO = ONE.add(ONE);

    public Factorial() {
    BigInteger b = ONE;
    cache[0] = b;
    cache[1] = b;
    for (int i = 2; i <= MAX; ++i) {
    b = b.multiply(BigInteger.valueOf(i));
    cache[i] = b;
    }
    }

    public BigInteger calc(int x) {
    if (x < 0) throw new IllegalArgumentException("negative x");
    if (x <= MAX) return cache[x];
    BigInteger M = BigInteger.valueOf(x);
    return cache[MAX].multiply(multiply_range(N, M));
    }

    public BigInteger multiply_range(BigInteger n, BigInteger m) {
    if (n.equals(m)) return n;
    if (m.compareTo(n) < 1) return ONE;
    BigInteger half = n.add(m).divide(TWO);
    BigInteger next = half.add(ONE);
    return multiply_range(n, half).multiply(multiply_range(next, m));
    }
    }

  46. nurettin says:

    Wait, where are the injection and aspect oriented features for custom algorithms? What RPC framework are we going to use? What if I wanted to read the algorithm from a web service, signaling its ending and returning a value back to the client? Just lack of thought, man.

  47. mlant says:

    It is indeed sad that so many programmers (not just Java programmers) write code this way. On a project I was working on, a senior Java architect was brought in to help move the project along more quickly. He worked quickly and wrote code that worked well. He was on the project for about three months. The problem was that the code he wrote was impossibly complex. Large portions of the code are not understood by anyone on the team, and it is nearly impossible to maintain. For example, if a database column is renamed(not added, just renamed) there were 21 hard-coded references that all had to be changed. If a column was added, developers had to trace through the process in parallel with an existing column to ensure that the appropriate 21 references are added for the new column. The code he wrote is now being completely removed. A year later and there is still code in the system that he wrote that nobody understands and does not know how to remove or bypass without breaking the system.

    Complexity like this is a major pet peeve of mine. I blogged about it a while back in an article I wrote: Occam’s Razor and the Art of Software Design: http://michaellant.com/2010/08/10/occams-razor-and-the-art-of-software-design/

  48. ewilcox says:

    I can boil this whole thing down to a simple sentence – “Apply the KISS principle!”

    Complexity is a trap while simplicity results in elegant solutions, and, yes, I often find myself needing to re-write code because I got caught up in the development of some complex algorithm that – when I stop and take a look at what I am doing – I have forgotten why I was writing it in the first place! I hate when I do that, so I have learned to stop about once an hour, and take a look at my work.

    I started to learn programming in the late ’80s by writing assembly scripts to create DOS utilities that I assembled with debug. I quickly learned to write functions and routines in a generic manner so I could reuse them elsewhere. I also learned that not every function or routine should be reusable, in fact, not many that I wrote to be reusable ever got reused. That taught me another lesson. If I am going to write a code segment that I think sould be reused, I should first check to see if someone else has already done the work for me. That realization alone has saved me hundreds of hours of coding.

    I have anothe consideration for you all to consider. The code we write today may reside in the organization’s archives for years (or even decades) to come, long after we have moved on to greener pastures. My point here is, how do we want to be remembreed – as arrogant and hard to understand, or as the creator of simply brilliant elegance?

    My2Cents,
    ewilcox

  49. thecount says:

    It’s true that each language has its own set of common anti-patterns (and are therefor not all created equal). That said, it would be interesting to find a way of measuring this to see how languages stack up in this respect.

    After experimenting with many many languages I find that the one that stands out above the rest is Erlang. It seems to discourage and/or repel the kinds of programmers you despise. It has less religion around the application of design patterns and tends to force programmers to think about what patterns to use and why they should be used in the first place (because actors instead of classes are involved).

    New grads that I worked with said that it had greatly improved their skills as OOP developers to apply equivalent OOP patterns to the COP programming paradigm. I can testify to the fact that their code their code had in fact improved by an order of magnitude. I also noticed this change in Haskell programmers but I don’t have personal experience with it so I can’t comment further.

Leave a Reply