Magic Scaling Sprinkles

Just another WordPress.com weblog

Why I love everything you hate about Java

with 37 comments

If you’re one of those hipster programmers who loves Clojure, Ruby, Scala, Erlang, or whatever, you probably deeply loathe Java and all of its giant configuration files and bloated APIs of AbstractFactoryFactoryInterfaces. I used to hate all that stuff too. But you know what? After working for all these months on these huge pieces of Twitter infrastructure I’ve started to love the AbstractFactoryFactories.

Let me explain why. Consider this little Scala program. It uses “futures”, which are a way to schedule computation to be done in parallel from the main flow of a program. They are sometimes a natural way of modeling the most efficient scheduling of program execution. Usually you schedule in advance some expensive work that can be done in parallel and then you do something else in the meantime. Only when you really need the result of the original computation do you block and wait (and hopefully only very briefly since you scheduled the work way in advance!). Here is a “typical” Java-ish Futures library used from Scala:

    private val executor = new ThreadPoolExecutor(
      poolSize, maxPoolSize,
      keepAlive.inSeconds, TimeUnit.SECONDS,
      new LinkedBlockingQueue[Runnable],
      new NamedPoolThreadFactory(name))

    val future = new FutureTask {
      doSomeWork
    }

    executor.execute(future)

If you come from a dynamic language like Ruby or Python you will probably have a visceral reaction like “Yeck! Look at all that horrible boilerplate. Convention over configuration!” Wouldn’t it be nice if you could just do something like:

    val future = new Future {
      doSomeWork
    }

It seems nice but its nicety is just an illusion. All that boilerplate is really important when you work at massive scale and where efficiency really matters. These magic numbers like the thread pool size and the kind of queue you use to schedule work can vastly impact the performance of your application. And the “right” configuration depends entirely on the nature of the problem you’re solving and how callers of this code behave. What all of this weird boilerplate provides is a way to configure the behavior of the system; it doesn’t assume there’s one right way of doing things. And that is precisely how modular software behaves: modular code is code designed to grow past the assumptions of just one user. Modularity really matters when your software isn’t a little throw-away program.

Twitter recently open-sourced Querulous, a minimal database querying library for Scala. We use it in several projects in Twitter, but it was designed principally to meet the extreme demands FlockDB, our distributed, fault-tolerant graph database. FlockDB demands extremely low-latency (sub millisecond) response times for individual queries. Any excessive indirection from an ORM would be unacceptable. Furthermore, because FlockDB processes tens of thousands of queries per second across dozens of shards, FlockDB must collect extensive statistics on the performance and health of the various shards in order to direct traffic to the most efficient place.

So Querulous was designed for querying databases at low latency, massive scale, and with easy operability. It has flexible timeouts, extensive logging, and rich statistics. But as FlockDB became more mature and sophisticated, the demands grew greater. We needed different health-check and timeout strategies in different contexts. It became clear that Querulous would need to be made extremely modular and extremely configurable to work at all.

So we set about to re-write Querulous using my favorite modularity techniques: Dependency Injection, Factories, and Decorators. In other words, everything you hate about Java.

The design patterns of modularity

In order for code to be modular it must have few hard-coded assumptions. In Object-Oriented software this means something very particular since the essence of an Object-Oriented program is that its structure is organized around the types of objects. Therefore, the most fundamental, anti-modular assumption in Object-Oriented software is the concrete type of objects. Any time you write new MyClass in your code you’ve hardcoded an assumption about the concrete class of the object you’re allocating. This makes it impossible, for example, for someone to later add logging around method invocations of that object, or timeouts, or whatever isn’t anticipated a priori.

In a very dynamic language like Ruby, open classes and method aliasing (e.g., alias_method_chain) mitigate this problem, but they don’t solve it. If you manipulate a class to add logging, all instances of that class will have logging; you can’t take a surgical approach and say “just objects instantiated in this context”. (Update: some people are asking “what about metaclasses? Metaclasses do not solve this problem at all because if you do not have control over the caller of Foo.new then you cannot later add new behavior to the metaclass; it has to be hardcoded at the site of manufacture. The point of this technique is to avoid knowing in advance what behavior you will add in, to make it configurable!)

There are standard design patterns to mitigate this, namely Dependency Injection, Factories, and Decorators. By injecting a Factory (a function that manufactures objects) as a parameter to a function that needs to create objects, you allow a programmer to later change his mind about what Factory to inject; and this means the programmer can change the concrete types of objects as his heart desires. And by using Decorators, the programmer can mix and match functionality easily, stack one thing on top of another like so many legos. Let’s look at an example.

Here I have a Query object, with methods like #execute(). I want to add timeouts around all queries. I start by creating a QueryProxy that routes all method invocations through an over-ridable method: #delegate:

    abstract class QueryProxy(query: Query) extends Query {
      def select[A](f: ResultSet => A) = delegate(query.select(f))
      def execute() = delegate(query.execute())
      def cancel() = query.cancel()

      protected def delegate[A](f: => A) = f
    }

Then, to implement timeouts, I create a Query Decorator:

    class TimingOutQuery(timeout: Duration, query: Query) extends QueryProxy(query) {
      override def delegate[A](f: => A) = {
        try {
          Timeout(timeout) {
            f
          } {
            cancel()
          }
        } catch {
          case e: TimeoutException =>
            throw new SqlTimeoutException
        }
      }
    }

This Decorator delegates to the underlying query object the execution of the query, but it wraps that execution in a Timeout.

As an aside, it is interesting to note that the Decorator pattern is just the Object-Oriented equivalent of function composition in a functional language. Scala makes this especially explicit since everything is both an Object and a Function (it is a function if it is an object that responds to the method #apply()). A Decorator around an object that only implements #apply() is pure Function-composition as you would see in Haskell, ML, and so forth. I might phrase this as: function composition is a degenerate case of the Decorator pattern.

The implementation of the Timeout function is shown for the curious. It uses threads and is weird but cool.

    object Timeout {
      val timer = new Timer("Timer thread", true)

      def apply[T](timeout: Duration)(f: => T)(onTimeout: => Unit): T = {
        @volatile var cancelled = false
        val task = if (timeout.inMillis > 0) Some(schedule(timeout, { cancelled = true; onTimeout })) else None
        try {
          f
        } finally {
          task map { t =>
            t.cancel()
            timer.purge()
          }
          if (cancelled) throw new TimeoutException
        }
      }

      private def schedule(timeout: Duration, f: => Unit) = {
        val task = new TimerTask() {
          override def run() { f }
        }
        timer.schedule(task, timeout.inMillis)
        task
      }
    }

(An alternative implementation of Timeout could use Futures, but that’s a subject for another blog post)

Modularity and testing techniques

One of the principal advantages of (or stated another way, one of the principal motivations for) writing Decorator-oriented code is how easy it is to write isolated unit tests of that code. To test the timeout functionality of the TimingOutQuery we don’t need to interact with a database at all. We can write behavioral/mockish tests like this:

    val latch = new CountDownLatch(1)
    val query = new FakeQuery(List(resultSet)) {
      override def cancel() = { latch.countDown() }

      override def select[A](f: ResultSet => A) = {
        latch.await(2.second.inMillis, TimeUnit.MILLISECONDS)
        super.select(f)
      }
    }
    val timingOutQuery = new TimingOutQuery(query, timeout)

    timingOutQuery.select { r => 1 } must throwA[SqlTimeoutException]
    latch.getCount mustEqual 0

If the timeout functionality was just inlined into the #select() method of the source code of the Query class, or “bolted on” as an alias_method_chain in Ruby (or added as “advice” in some AOP shit) you could not write this test without talking to the database and somehow finding a query that takes long enough that it will actually hit the timeout. Because we instead use Decorators, to test the code we can use a fake query that implements the Query interface but that doesn’t talk to the database at all. Here we use a CountDownLatch to “halt” execution for a bounded amount of time, thus triggering the timeout.

Tying it together with Factories

Back to our original mission. So now we have a way of layering on timeout functionality on top of a Query object. But how do we ensure that Timeouts get used when we want them to? The thing that glues this all together is to make sure that everybody that needs to instantiate a Query object never ever calls new Query directly. We provide instead a Factory as a parameter to the method that needs to manufacture the object. The programmer chooses which Factory to provide at runtime. Here is a Factory that makes TimingOutQueries:

    class TimingOutQueryFactory(queryFactory: QueryFactory, timeout: Duration) extends QueryFactory {
      def apply(connection: Connection, query: String, params: Any*) = {
        new TimingOutQuery(queryFactory(connection, query, params: _*), timeout)
      }
    }

Since TimingOutQueries are decorators around regular Queries, to manufacture a TimingOutQuery you have to first manufacture a regular Query. In this example, the TimingOutQueryFactory takes another Factory as an argument. This could be a simple QueryFactory or something more complex–allowing Factories to be composed indefinitely. With this we stack together timeouts, logging, statistics gathering, and debugging like so many pieces of legos. This smacks of the oft-ridiculed Java AbstractFactoryFactoryInterface. But let me put it bluntly: AbstractFactoryFactoryInterface's are how you write real, modular software–not little fart applications.

This seems like a bit of a mind-fuck because we here have Factory Decorators that take Decorated Factories that make Decorated Queries. It’s so meta! (Actually, “meta” in Greek means nothing like “meta” in English. “Meta” plus the accusative means “after” so Aristotle’s Metaphysics is actually just a book “after [the book on] physics”. Anyway.) So all these crazy FactoryFactoryDecorators sound kind-of scary at first but it is just the kind of abstraction on top of abstraction and closure under composition that allows complex software to be made simple. Manage complexity by taking many things and re-conceiving of them as just one thing; this one thing is then combined with many other things and the process is repeated up the ladder of abstraction until you reach the Godhead.

Taking this to the next level

To take this even further, let’s add a new feature: per-query timeouts. At one point in the history of FlockDB, there was a global 3-second timeout. This was really stupid given that our most common query has a latency of 0.5ms and a standard deviation of 2ms. If you have a global timeout you must set your timeout around your most expensive query not your most common query (otherwise, your most expensive query will always timeout!). But for a production system, cheap frequent queries, if they start exceeding 2 standard deviations, can take down your site. So a sensible timeout for these frequent queries is like 5ms. But we had it set to 3,000 ms!! Yikes. So let’s change it!

    class PerQueryTimingOutQueryFactory(queryFactory: QueryFactory, timeouts: Map[String, Duration])
      extends QueryFactory {

      def apply(connection: Connection, query: String, params: Any*) = {
        new TimingOutQuery(queryFactory(connection, query, params: _*), timeouts(query)) // YAY
      }
    }

That’s it. We’ve now implemented a new Timeout strategy in one line of code! And to wire it all together it is a piece of cake! Querulous makes no assumptions about how best to implement a timing-out strategy, it doesn’t even assume you’ll want timeouts (in fact, there are some cases you don’t want any timeouts). Querulous achieves modularity by providing an “injection point” for the programmer to layer on custom functionality. It takes QueryFactories as a parameter to the method, which can return arbitrarily decorated Queries.

I love this example because it’s so simple but yet it’s no toy. It also emphasizes the value of Dependency Injection more generally than just with Factories. We could have written the TimingOutQuery with a static global constant (probably the most common programming technique):

    class TimingOutQuery(query: Query) extends QueryProxy(query) {
      val TIMEOUT = 3.seconds

But intead it is injected as a parameter to the constructor to the TimingOutQuery:

     class TimingOutQueryFactory(queryFactory: QueryFactory, timeout: Duration) extends QueryFactory {
        def apply(connection: Connection, query: String, params: Any*) = {
          new TimingOutQuery(queryFactory(connection, query, params: _*), timeout)
        }
      }

This enables the TimingOutQueryFactory to invoke a function to choose the appropriate timeout for this query. In this case, we just look some shit up in a hash table (timeouts(query)) and we’re done.

Yes, all this FactoryFactory bullshit is exactly what you hate about Java. But it’s amazing not how just short this code is but that it could be configured by any programmer anywhere, regardless of whether they have access to the source code that actually instantiates and executes queries. Any user of Querulous can decide if she want timeouts or not, and she can decide if they also want debugging, stats gathering, and so forth–Querulous hard-codes no assumptions. So, yay modularity.

Postscript:

Check out Querulous.

Written by nkallen

February 8, 2010 at 11:56 pm

Posted in Uncategorized

37 Responses

Subscribe to comments with RSS.

  1. functional programming will still be here when you’re ready to come back to it. :)

    But seriously, does this actually have anything to do with java? It’s no harder to write in this style in another language. And leaving behind a set of default, sane choices for the mere mortals on your way to the Godhead lets programmers who are not deeply familiar with your system have at least a fighting chance of writing code that works, in a reasonable amount of time.

    matt knox

    February 9, 2010 at 12:44 am

  2. Simplicity rules (IMHO). In every Java application I’ve worked on, there have been so many layers after layers that makes the core logic hard to follow. Sure, you can build all that complexity in PHP or whatever, but the tendency is that you don’t. Maybe it’s because you don’t have big companies like IBM, Sun, etc. who shove so many java systems and components down your throat. Whatever the reason, you have a much cleaner object-oriented code in PHP that do the job compared to java-based systems. I love the language, but I can’t stand the complex systems that you end up needing to do simple things.
    my 2 cents…

    Ramin

    February 9, 2010 at 2:05 am

  3. Nicely written article–though I might have gone without the “linkbait” title. ;) As Matt Knox said previously, I don’t really see the connection to Java except that Java developers tend to have a better (or at least, more outwardly expressed) understanding of object oriented design patterns. All modern developers should understand design patterns as they are very helpful no matter what [OO] language you are writing.

    Let’s also not forget that not all software needs to be written to handle the scale of something like Twitter. I’d guess that 90% or more of software will never need to scale to that level.

    Brian Crescimanno

    February 9, 2010 at 2:05 am

  4. great article. but in my opinion it’s all about choosing the right tools: if you want to prove that something is working in a conceptual way – just use the tools that take you there – fast!

    and if it’s working it is legitimate to throw everything away and do a proper software job (when did twitter switch from ruby to scala :-)

    Sebastian Deutsch

    February 9, 2010 at 3:17 am

  5. @brian: special cases such as rapid prototyping aside, just because software doesn’t need to scale doesn’t mean it shouldn’t be written to scale. good design is good design no matter how many users you have.

    ed ceaser

    February 9, 2010 at 3:20 am

  6. I too like to look at decorators as a crufty way to do function composition in an OO world.

    An even clearer analogy is seeing factories as partial (curried) function application.

    You can think of your timeout query as a function from (timeout, connection, queryString, params) -> Query. After applying just the timeout argument, you get a function (connection, queryString, params) -> Query, which is the signature of the factory interface.

    Or, which is closer to the way things actually work in your decorator pattern, you are just composing a function (connection, queryString, params) -> Query with a function (timeout, Query) -> Query, only the latter is partially applied to the timeout argument. This too, when composed, will result in the signature (connection, queryString, params) -> Query; the factory interface.

    Both ways of looking at factories are actually identical, and in the end they are nothing more than function application.

    David D

    February 9, 2010 at 5:51 am

  7. @David if WordPress had a “Like” feature for comments I would have “Like’d” your comment. Totally agree (though I don’t think Decorators are Crufty).

    nkallen

    February 9, 2010 at 5:55 am

  8. The first comment mirrors my initial reaction: this isn’t about Java; this is about modularity, extensibility, and so on. There’s no boilerplate code in your first example–you’re just setting parameters. For prototyping, you might not need to set the same parameters. For production, you might. Having the capability to do *either* is the right answer.

    Dave

    February 9, 2010 at 1:11 pm

  9. Maybe a better title would have been “Why everything you love about your non-java language doesn’t matter to me.” It seems like what is really exposed here is that convention doesn’t help in every specialized situation. As someone who has to swing the java sledgehammer a lot I’ve come to realize that maybe 5% of what I do would feel less cumbersome with a different language or conventions. The challenging 95% of the work is domain specific.

    Carson McDonald

    February 9, 2010 at 1:50 pm

  10. An excellent article on the value of writing modular code. Lengthier response on my own blog: http://avdi.org/devblog/2010/02/09/everything-you-love-about-java-is-everything-i-love-about-good-design/

    avdi

    February 9, 2010 at 2:04 pm

  11. Thank you for putting so eloquently what I know instinctively but couldn’t put into worlds. I’ve just finished reworking a java system and removed almost all new MyClass calls. Its incredibly powerful.

    Benjamin Darfler

    February 9, 2010 at 2:26 pm

  12. @ed

    I agree completely that engineers should be striving for good design no matter what their application–I only meant to point out that scaling up to something the size of Twitter has special challenges–even beyond good design.

    Brian Crescimanno

    February 9, 2010 at 2:43 pm

  13. As has been pointed out on Reddit and elsewhere, you misunderstand what people dislike about Java.

    Extensibility and modularity is great. The problem is that it lacks sensible defaults.

    One can spend just 10 minutes with Scala and see that its designers have taken the time and effort to think through reasonable defaults. Type inference is an obvious example. If you use a variable in an integer context, it presumes that the variable is of type integer. Similarly, if you write a one-line program, Scala executes it. It doesn’t complain that it must be wrapped in a static public main.

    Scala demonstrates that “scaling up” (supporting different use cases) and “scaling down” (having reasonable defaults) are not mutually exclusive. A language or API designer can put in an effort in both directions and maximize programmer’s total productivity.

    Paul Prescod

    February 9, 2010 at 4:42 pm

  14. Disagree!

    Let’s look at your very first example. I just changed it a tiny bit.

    private val executor = new ThreadPoolExecutor(
    poolSize, maxPoolSize, TimeUnit.SECONDS, keepAlive.inSeconds,
    new LinkedBlockingQueue[Runnable],
    new NamedPoolThreadFactory(name))

    Now I’ve introduced a bug into the code – hard to see even with the correct code to compare it to! and yet the code might well work without trouble until I make a tiny change elsewhere.

    (Hint: suppose the keepalive were 3 seconds and you changed it to 2…)

    That whole style of constructors with huge signatures is error-prone and verbose.

    It also completely obscures which parameters are important, key parameters that you established with a lot of thought, and which ones are “default” values that are obvious.

    If you change the constructor, the code is broken everywhere – unless you use default parameters, which are a classic source of error.

    Attempting to overload constructors is another source of error!

    In a lighter language like Python, you might write this as:
    factory = NamedPoolThreadFactory(name)
    executor = ThreadPoolExecutor(factory)
    executor.maxPoolSize = 40000
    # Set more parameters here.

    Note that only the parameters that are important are set! Note that each parameter has a name, and order is not significant.

    It might be that your use case is such that you can’t change maxPoolSize after your executor is created! One way to make this happen would be to throw an exception when you try to set maxPoolSize – but that’s inelegant.

    Nicer is to introduce an intermediate dumb data class that’s used to constructor the executor – like this:

    factory = NamedPoolThreadFactory(name)
    executor_desc = ExecutorDesc(factory)
    executor_desc.maxPoolSize = 40000
    # Set more parameters here.
    executor = ThreadPoolExecutor(executor_desc)

    This has a lot of advantages too! For example, you can construct a single ExecutorDesc that you keep immutable, and use it every time you need to construct a ThreadPoolExecutor.

    tl; dr: positional parameters are a known anti-pattern and should be avoided.

    tomswirly

    February 9, 2010 at 4:49 pm

  15. Like Tomswirly, I disagree with your initial poke at Python.
    The Python idiom would be to supply defaults to a call to ThreadPoolExecutor that would work in a lot of cases as-is. You would be able to change the defaults at invocation, something like:

    executor = ThreadPoolExecutor(factory, maxPoolSize=40000)

    - Paddy.

    Paddy3118

    February 9, 2010 at 6:24 pm

  16. Agree with Paddy – and in fact the clever parameter passing of Python lets you create the descriptor as a dictionary and then pass it in as named arguments.

    One of the thing I love about Python is that at a certain point I realized that at least half the classes I created simply didn’t need to exist at all – they could just be replaced by a dictionary or a tuple!

    Writing code is easy but debugging is hard. And there seem to be a constant percent chance that any given line is buggy, no matter what the language. So if you write in a language that takes a third the code to express your ideas, you have a third of the bugs… as well as saving your hands from carpal tunnel… :-D

    tomswirly

    February 9, 2010 at 7:05 pm

  17. To tomswirly, that’s an excellent point. No single source of cruft in widespread programming languages is nearly so harmful as the insistence on naming *everything*. Smart programmers can decide when building a named abstraction is a good idea… and when it’s clearer to expose the implementation directly rather than making people learn the meaning of yet another name.

    That’s another side of the same point that many are making here. It’s not that these choices shouldn’t be available… it’s that you shouldn’t be forced to write code to handle ten gazillion concerns that you don’t care about. When you do care about them, you can start writing code to deal with them.

    Chris Smith

    February 9, 2010 at 8:28 pm

  18. I understand your point of view, and as a Java developer it makes perfect sense. I’ve worked with Java for a long time.

    As far as prodding at functional languages or Ruby though (and some of your concerns with them as compared to Java), they have different mechanisms that may not at first seem like the right tool for the job. Once the nuances and application are fully understood you learn to approach the problem in a different way.

    As for using Java, its pretty time tested so far. Most approaches you discussed were good practices for modularity (but again, this could be accomplished in other ways in, say, Ruby), and should be followed closely. As a C programmer I reject the notion of using Exceptions for typical program flow, but otherwise its easy to see your experience with it.

    Good article!

    Scott Conner

    February 10, 2010 at 5:26 am

  19. Great article, I have just one small addition I’d like to offer.

    If my memory serves me correctly Bertrand Meyer asserts that a good api should have sensible defaults to facilitate entry level use of an api. I believe he then states it must be possible to override such defaults as the developer using the api becomes more of an expert their use of the api.

    Looking at the two examples of ‘future work’ at the top of the article it appears to me that they could easily be the same api (one snippet showing entry level use, one snippet showing expert level use).

    Pete Tyler

    February 10, 2010 at 4:54 pm

  20. @ed

    At enormous scales, the curves of space-time are perfectly smooth. At tiny scales, the universe is a gravely quantum “foam”. Einstein couldn’t find a unified theory of everything, and you, certainly, can’t find a unified theory of software design.

    As you manipulate the parameters of a software project — how many end-users? how many rows? how many developers? — you’ll encounter state changes that dramatically alter the behavior of the project as a whole. Applying the same strict design rules to every project regardless of those parameters is seldom the most efficient strategy.

    Granted, for many successful projects, some of the parameters change dramatically throughout the lifecycle of the software. Frequently, this results in frustrating “back to the drawing board” moments, but that’s just the life of a software developer. If the lesson you take away from difficult re-designs is that any good project will have two interfaces for each concrete class, you are likely to stifle the creativity that made your last project a success in the first place.

    Will Conant

    February 11, 2010 at 8:35 pm

  21. @Will

    Good points; I’d go a step further though, and suggest that for those very reason its good if you can use a framework that will adapt with you. That’s one reason for the boom in popularity of frameworks like Seam – they allow people to develop relatively simple applications with very little configuration, but all of the power of the framework is still there waiting for you if and when you need it. Website never gets popular? Stick with the defaults. Suddenly you need to scale up to 50 servers and include distributed persistent messaging? Its right there waiting for you – tweak and tune the important bits, keep on churning out the other stuff.

    Richard Stanford

    February 12, 2010 at 3:08 pm

  22. I don’t hate the decorators. I hate the fact thay the concept needs a name. Look at your TimingOutQueryFactory: How much of the code actually deals with the decoration, and how much is just tedious repetition of the base class? With some form of currying this (the TOQF) may not only be easy to use but also easier to implement.

    As they say: Java is a tax form.

    Andreas Krey

    February 14, 2010 at 5:02 pm

  23. Your comments on concrete types reminded me of Bill Cook’s article on Objects vs. Abstract Data Types, referenced here:

    http://lambda-the-ultimate.org/node/3668

    He points you can use “pure object-oriented style” in Java by obeying a few rules, one of which is:

    “Classes only as constructors” – a class name may only be used after the keyword ‘new’.

    Yay modularity!

    Dan B.

    February 15, 2010 at 11:37 pm

  24. [...] read my blog post on modularity techniques in Scala and various responses (in Clojure, in Ruby, in Ruby [...]

  25. When I see this kind of code, I often want to beat it with the YAGNI sledgehammer. The problem I have with AbstractFactoryFactoryHandlerManager-type code is that the generality obscures the meaning of the code.

    If you’re writing library/framework code that will be used by many other developers that will not want to edit your source code (or might even have access to it), by all means make it very generic. The problem is that most code does not have those requirements. It is used in a single project, and every developer has access to the source code and can change whatever they want. There is not as much need for modularity in this case, but in the Java culture, “good” OO is typically regarded more important than simple and clear code.

    Martin Vilcans

    February 16, 2010 at 8:09 am

  26. Martin: I’m pretty sure I agree with you, but want to quibble about your choice of words.

    “Generic” has a specific meaning in this sort of context – it means, in essence, “duck typing”, where you use a specific method or function by name and don’t worry about inheritance.

    C++’s STL is generic, Python probably uses generic programming styles more often than object-oriented.

    I think the word you’re looking for is “abstract” – where there’s an abstract base class or interface and the concrete classes are implemented on top of that.

    Generic is much lighter than abstract because you don’t create that interface or base class – you just “define a method here, use it there”.

    If that’s what you mean, I agree with you. When I’m writing in strongly-typed languages like C++ or Java, I’m careful to avoid abstracting until I’m forced to – until I create the second derived class!

    The other big advantage of that strategy is you don’t usually have the interface correct until you’re obliged to add the second class into the hierarchy…

    Since we started this thread, I have actually started a moderate-sized C++ project. Now, I do love C++, and for these purposes (DSP) it’s about the only possible tool; but I keep being reminded as I write volumes of boilerplate how much smaller this’d be in Python, how much faster it’d be, how much easier the testing would be (indeed, since this is inside a large framework with neither mocks nor tests, I’m wondering if there will ever be unit tests! :-( )

    I’m sure I’d feel the same way in Java. I love to type and yet I find Java so very tiresome. Automatic completion is the only way and yet that’s still not as fast as just banging out fast, short code from the keyboard.

    The trouble with Java is it still doesn’t have a really good reason to exist except for allowing moderately talented programmers the ability to formulaically glue together premade modules semi-automatically. If I want to write compact code fast, I’ll use Python. If I want to write fast code that’s compact in memory, I’ll use C++.

    (I’ve written a lot of Java and I do like the language! And having a huge invested base is a great reason to keep going. I’m just not sure that I’d ever start a new project in Java again…)

    Tom Human

    February 16, 2010 at 6:18 pm

  27. To all those complaining about the verbosity of the constructor invocation and/or positional parameters, Scala 2.8 has named and default arguments: http://www.scala-lang.org/node/2075

    Alex Cruise

    February 17, 2010 at 5:22 am

  28. @Tom

    Obviously generic was a bad word choice. Perhaps I should have used the word general instead. They are synonyms in English, but the word generic has a very specific meaning in the context of the generics feature of some programming languages (C++ and Java for instance). What I meant was generic in the sense “not specific or particular”. And generality is typically implemented with abstractions.

    What I was talking about was the notion that code should be able to adapt to varying conditions. If code should be able to adapt without being rewritten, it must be general. The problem is that general code is more complicated than code that just implements the requirements without regard to being able to adapt *without changes* when the requirements change.

    The notion that code should be able to adapt without changes is what I’m objecting against. In most systems you can just change the code when the requirements change, instead of making it very general to begin with. (Or generic, whatever.) This is the essence of YAGNI.

    Martin Vilcans

    February 19, 2010 at 9:37 am

  29. ENTERPRISE QUALITY

    Seriously, seeing code like in this article drives me insane. A perfect example is from “Evolution of a Python Programmer” [1], in the “Enterprise programmer” segment. You should take a look at that and compare the approach demonstrated there for factorials to how you did things in your article.

    References:
    [1] http://gist.github.com/289467

    bergerus

    March 3, 2010 at 2:05 am

  30. I checked out Queruous. There is an import for com.twitter.xrayspecs.Duration do you know where I can download this package. This is probably not the right forum but I didn’t really know where else to post the question.

    sjcarroll6

    March 6, 2010 at 3:15 pm

  31. Perhaps I’m being dense, but why can’t you just set a delegate slot on the Query object to accomplish inversion of control? If the delegate slot is set, you send it a message asking it what to do. It could implement a timeout, logging … etc. If it isn’t set, the execute operation proceeds normally. Does this assume that you can’t modify (or monkey patch) the implementation of Query.execute?

    Rich Collins

    March 13, 2010 at 8:35 am

  32. What you can do in java you can do in C++ too. To hate java is to hate C++ and vice versa.

    iresha

    March 16, 2010 at 10:59 am

  33. “function composition is a degenerate case of the Decorator pattern.”

    It is good to see functional programming alive and well…

    “Manage complexity by taking many things and re-conceiving of them as just one thing; this one thing is then combined with many other things and the process is repeated up the ladder of abstraction until you reach the Godhead.”

    This is how you design confusing and hard to work with software. Abstractions aren’t just some carpet you sweep annoying complexity under they are the language you end up solving problems with and how you communicate to programmers.

    Users probably expect some simple query proxy object that has the ability to do timeouts built in. You don’t need to factor it into this complicated system in the article to please them. You should design your interfaces around the simple abstractions people expect while optionally adding a set of lower level interfaces.

    This article is the reason I hate java and the mind set it breeds. It funnels you into attacking problems in a far more complicated way than they need to be to solve them.

    I do like functional composition though.

    Andrew

    March 31, 2010 at 11:30 pm

  34. My problem with Java isn’t the language itself ( simplified C++ :) my problem is the code written by the general Java community. I just finished screaming at the excessively complex home-rolled framework my current contract requires me to use. So many patterns, so little functionality. I hate being forced to write 10 classes where 2 will do, and then having to implement core things like change detection or key generation.

    8 of the last 10 corporate Java contracts I’ve done have involved some huge, overly complex custom framework. Only 2 had leads committed to using standard tools like Hibernate or Spring. ( of course Log4j and JUnit is everywhere.. big deal. )

    Java DI, ORM and core libraries are fine and good, but Java pattern freaks have a belief, a faith, that their complex patterns will make maintenance easier. I’ve yet to see or hear of one real world case where those patterns helped a major refactoring. All I see are carefully created cases that never seem to arise in reality.

    OK, I know, there are a lot of bad programmers out there. Most of them seem to settle in at some large corporation and decide to write a huge framework. What fun. For them. Every time I’ve started a big framework I stopped at some point and realized what a horror it will become, scrap it and just get the job done.

    It seems to me that Java people are forever fighting the very static typing they say they love. ( starting with Sun’s own Swing property change regime. ) Hashmaps to avoid beans with typed fields, etc. I’ve gone as far as I care to with reflection and Sun’s nifty Proxy class to get dynamic. What a struggle.

    One of the thing I love about Python is that at a certain point I realized that at least half the classes I created simply didn’t need to exist at all – they could just be replaced by a dictionary or a tuple!

    I had the same evolution. Also Python first-class functions, they can do much of what Java people write full classes for. Now I can spot a Java-wise Python newbie in a flash; it’s all class-this and class-that. Silly rabbit!

    I just wish there were some decent Python contracts in this town.

    grubert

    June 24, 2010 at 1:23 am

  35. The article reads well, but I’m not persuaded. Perhaps I misunderstood you? Your argument for boilerplate code is “but these values are really important in X environment” is completely answered by “convention over configuration”. Perhaps you misunderstand convention to mean “lock-in” to those defaults? (or perhaps I misunderstand convention to mean sane, but configurable, defaults).

    Regarding Design Patterns, what Rich Hickey reverberates as true to me: “Design Patterns are for when you run out of language”.

    Tim Harper

    July 19, 2010 at 8:42 pm

  36. “Design Patterns are for when you run out of language”.

    You invariably will once you create nontrivial software.

    albedo

    November 23, 2010 at 3:44 am

  37. “If you come from a dynamic language like Ruby or Python you will probably have a visceral reaction like “Yeck! Look at all that horrible boilerplate. Convention over configuration!””

    I think you’re taking the Ruby-on-Rails manifesto and assuming that this is what dynamic languages are about. “Convention over configuration” is a relatively new concept (did DHH coin the phrase?), while dynamic languages are anything but.

    Another Ruby-ism you’re applying to all dynamic languages is performance. I like Ruby as a language, but its runtime is still one of the slowest I’ve ever used, and the language provides very minimal support for letting the programmer tell you how to make it go fast. Pick a mature dynamic language with a native optimizing compiler, like OCaml or Common Lisp, and you can get C-like performance (and Python/Ruby-like flexibility, or better) without too much trouble.

    In this case, my visceral reaction to your code was: “Yeck! Look at all that horrible boilerplate. This is exactly why closures and special variables were invented.” (Java seems much more concerned with letting you say who *can’t* use your classes and methods, than providing or letting you build good higher-level abstractions.)

    The problem that us dynamic-language programmers have with Java is not its flexibility (ha!). It’s the verbosity, and the *lack* of flexibility. You can be flexible and performant without being nearly so verbose. The problem that us dynamic-language programmers have with pop-hits like Rails is that they make Java programmers think that’s all that dynamic languages are for. :-)

    (Yes, I realize the title is total linkbait, and I fell for it. There are many things that *everyone* hates about Java. :-)

    jon

    December 2, 2010 at 4:52 pm


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.