Monday, 30 April 2012

Life Without Objects

Over the last few years I have been learning and experimenting with a wider range of programming languages. In particular I have started using Scala as my primary development language, adopting a more and more functional style. I have also become very interested in Haskell (a pure functional language) and Clojure (a modern lisp dialect).

I have therefore been moving away from the object-oriented development principles that have made up the bulk of my 17 year career to date. More and more I am beginning to feel that objects have been a diversion away from building concise, well structured and reusable software.

As I pondered on this topic, I realised that this isn’t a sudden switch in my thinking. The benefits of objects have been gradually declining over a long period of time. The way I use objects today is very different to how I used them when they were new and shiny. In this post I explore this change in my thinking about object-oriented development.

The Promise of Objects

Back in the 1990s, objects were new and exciting. The promise of being able to create reusable classes built around common design patterns was seductive. The ability to then combine these classes into reusable and configurable business components seemed like the Mecca of software development. New languages like C++ and then, slightly later, Java held the promise of a new way of building great software.

Business Components Aren’t Reusable
It didn’t take us long to discover that the ability to create reusable business components was just a giant fallacy. Each business is significantly different from another, even in the same industry. Each similar project has very different business rules.

The only way to build reusable business components at this level is to make them hyper-configurable by adding such things as rules engines and embedded scripting languages. Hardly a component model and more like a bloatware model. This promise gone, people either buy into the big bloatware systems (suckers) or build their custom business objects on a project by project basis.

Patterns Don’t Build Well Structured Software
The next thing we learnt was that excessive reliance on design patterns doesn’t lead to the good software structure. Instead it leads to software that is overly complex, hard to understand and difficult to maintain. Some patterns even turned out to be anti-patterns (the singleton pattern makes software almost impossible to unit test, for example).

We soon learnt to use patterns judiciously. More often than not it’s just cleaner to code the software as you understand the model rather than try to abstract it into a more generalised pattern.

Frameworks for Class and Component Reuse Give Few Benefits
Another early promise of objects was rich, tightly coupled frameworks of classes which when used together would make building applications from reusable component a breeze by hiding all the technical complexity and implementation plumbing. Think EJB and similar. Experience soon showed that these just did not work. They were just too restrictive and cumbersome for what people were trying to achieve.

These heavy-weight frameworks soon died out to be replaced with more lightweight libraries and toolkit type approaches. Collections of more loosely coupled classes that you can pull together as needed are now the preferred way to build software. Take just what you need and nothing more.

Inheritance Creates Brittle Software
The ability to support interface and implementation inheritance was one of the key tenets of object oriented software development. We could spot common code and behaviour and push this down into a shared base class so that future abstractions could benefit from having this shared code available to build on.

Sadly, this just didn’t work out well. Each sub-class turns out to be subtly different from its peers, resulting in lots of overrides of base class behaviour or making the base classes even more generic. The net result was super-fragile software, where any small changes to a common base class would break most, if not all, of the sub-class implementations.

These days we don’t use inheritance much, and especially not for creating technical base classes. Its use is pretty much restricted to interface inheritance to indicate an object supports a certain behaviour or to occasional domain models where there is a true inheritance relationship. Other than that we tend to extract commonality in to separate ‘mixin’ type classes and compose them together through an aggregation approach.

Violation of Encapsulation
Another key feature of the object-oriented model was the ability to encapsulate state and then expose behaviours (via methods) that access and update this state. Unfortunately it turns out that there are a large number of cases, where we are actually interested in the vales of the state rather than the behaviour.

For example, asking an object to render itself as HTML turns out to be a pretty poor approach. Knowledge of HTML rendering gets spread across the code base and a small change in approach causes us to change many, many classes. Instead we tend to pass the object to a dedicated HTML rendering/template component, which pulls the data values from the object.

Anti-patterns have even emerged around this to allow us to have light-weight objects that just encapsulate state without behaviour (Java Beans, Data Transfer Objects and so on). If we are doing this, then why not just work directly with first-class structured data as opposed to objects?

Mutable State Causes Pain

Another perceived benefit of encapsulation was the ability to mutate the state of an object instance without impacting on the clients that use that object. However, anyone who has built a significant sized object-oriented system can tell you stories of trawling through many files of code to find the location that mutated the state of an object to an unexpected value that happened to make your software blow up in a completely different place (usually where you output or store the state of that object).

More and more we now favour immutable state and stateless services so that these problems do not occur. There’s also the additional benefit that immutable state is a much better model for building highly concurrent systems and for getting the most out of modern multi-core hardware. It’s also far easier and less error prone than trying to work with threads, locks and concurrency safe data structures.

Behavioural Interfaces Cause Code Complexity and Bloat
One of the things we do frequently in object-oriented languages is create small marker interfaces that have just a single method. Any class wanting to support this behaviour extends the interface and implements the method. We can also declare anonymous implementations for ad-hoc use.

However, we have found that neither of these approaches are particularly good. Implementing the marker interfaces often pollutes classes with implementations that are not their direct concern (thus violating the single responsibility principle). Anonymous classes are just unnecessary bolierplate that makes our code more difficult to understand and maintain.

Life Without Objects

So, is it possible to go back on 17 years of experience and contemplate a life without objects? I’m not sure that I’m 100% there just yet, but using a multi-paradigm language like Scala is allowing me to overcome many of the limitations of the object-oriented approach.

For example, Scala’s support for mixin traits makes it almost unnecessary to ever use implementation inheritance. It’s rich collections framework plus the ability to use case classes to create data structure like concepts obviates working around encapsulation issues. A recommendation to use immutable data and collections makes code easier to debug and reason about. The ability to use functions as general abstractions and type classes to extend behaviour while maintaining single responsibilities makes it much easier to build well structured, reusable software.

In fact, what I find I am doing more and more is using simple objects in the form of case classes to represent data structures, with a few behavioural methods to simplify working with this data. Then I’m just using mixin traits as a modular approach for grouping related functions together. Then I’m combining these together to form components in which I compose together various functions that transform data from one for into another.

Perhaps I’m further away from the pure object-oriented approach than I’d thought. I’m certainly building smaller, cleaner and better structured software than I ever was before.

50 comments:

  1. You can certainly get away without any objects. I don't know how suitable Scala is for this, but the idea is this: You write data types and combinators for them. A combinator arises whenever you identify two algorithms to be structurally similar. You then make a combinator that captures the structure of the algorithm. This is where folds come from, for example.

    ReplyDelete
    Replies
    1. Thanks for your comment. I don't think it's possible (or perhaps even desirable) to eliminate objects entirely from Scala programming. There's some fundamental concepts such as low level data structures (case classes) and implementation of type classes that depend heavily on and object approach. However, I'm finding more and more that I tend to use Scala's traits as a way of grouping related functions and that I then use these to compose behaviour together rather than using them as a way of supporting multiple inheritance type approaches. This is an area I am planning to experiment much more with in the near future.

      Delete
    2. Case classes are basically just algebraic datatypes in disguise. Maybe in Scala, it's hard to imagine making those not be objects, and that's fine, but there are very closely related analogues in Haskell, which has no *explicit* support for object oriented programming.

      Though if you ask me, Haskell actually does a really decent job at capturing the true spirit of OOP where you really want it.

      What is an object really? If you ask me, the concept which is at the heart of it isn't inheritance or subtyping or any of that. It's about pieces of data being distinguished not by their internal state, but by their responses to a collection of messages, or methods.

      Well, in Haskell (or pick your favourite typed language with immutable structures and first class functions), we can encode an object literally as a record of functions from the parameters of each method to the resulting response to that method. If the method has no parameters, then it turns into a simple piece of data (this is more realistic to do in a lazy language -- you might want to give them a dummy parameter like an empty tuple in a strict one).

      Methods which would have modified the object will simply produce a new object in response.

      For example, instead of having an algebraic sum:

      data GameObject = Missile Point Vector Double | Spaceship ... | ... litany of other cases ...

      and a bunch of functions which pattern match on those cases and do different things accordingly, like draw to the screen, step forward in time, and so on, we turn it around:

      data GameObject = GO { draw :: IO (), step :: DeltaTime -> GameObject, ... }

      and then we define our cases like this:

      missile :: Point -> Vector -> Double -> GameObject

      missile centre velocity radius = GO { .. } -- using wildcard notation
      where { draw = drawCircle centre radius
      ; step dt = missile ((dt *^ velocity) ^+. centre) velocity radius
      ; ... other methods ...
      }

      (I'm leaving in the semicolons and whatnot because I have no idea what your blog software is going to do to this.)

      So "private member variables" more or less become function parameters to the function which takes the place of our object constructor, and we construct objects recursively instead of updating them.

      This is like OOP without any subtype polymorphism. But note that much of what the subtype polymorphism was used for in popular OOP languages was just making up for the fact that method implementations couldn't be first class, and so you *needed* separate subclasses just in order to define your methods differently, even if the interface was identical.

      Overall, this "coalgebraic" approach makes it harder to add new primitive methods (because we have to update all the object implementations), but makes it easy to define new constructors (GameObjects in my example). I think this is really the heart of object oriented programming which will be what has some measure of staying power in how we write programs in the future.

      The "algebraic" (disjoint sum of Cartesian products) approach on the other hand, by laying bare all the internal structure of the data and not specifying a finite list of methods from the outset, makes it easy to define new methods in the future, but harder to add new cases (because all the method implementations have to be updated to handle the new case).

      Delete
  2. Marius Andersen1 May 2012 18:52

    "Object orientation makes code understandable by encapsulating moving parts. Functional programming makes code understandable by minimizing moving parts." - Michael Feathers

    ReplyDelete
    Replies
    1. An excellent quote, thanks. My experience tends to be that it is very difficult to build object-oriented systems that don't leak knowledge of the moving parts out to other objects. In particular, patterns such as Java Beans tend to encourage this leaking of encapsulation. Also, a significant percentage of developers I have come across in my career really struggle with the concepts of encapsulation on object-oriented systems. Best to minimise the moving parts I'm coming to believe.

      Delete
  3. I started a wordy reply here, but grew it into a blog article - Life With Objects

    ReplyDelete
  4. Chris, I'm trying to understand you but I can't, let me explain.

    The very serious problem of our industry is the dramatic reinvention of the wheel, most of this reinvention is not because of there are better wheels, is just because most of our colleagues are very young programmers and hardly can distinguish when something "new" smells good and is a real improvement instead of another even more clumsy way to do the same.

    So then when I read a text from an experienced guy like you I get alarmed.

    In a world where MOST OF DEVELOPERS DON'T KNOW HOW TO MODEL A OOP SYSTEM bashing OOP is like saying to them "hey guy your plain unstructured crap is fine and trendy".

    Let me rebate your points:

    * Business Components Aren’t Reusable

    "This promise gone, people either buy into the big bloatware systems (suckers) or build their custom business objects on a project by project basis"

    Yes, I agree, and fortunately this is NOT BAD, this is reason of every project is unique and why software development is not an production line automatized by bots, this is why SOFTWARE DEVELOPMENT IS MORE LIKE CRAFT!!!

    Business objects are highly reusable inside your concrete project, yes the OOP promise works in the small.

    And reusable OOP also works in the service level, think in a any framework or library you use in a day by day basis, yes it works.

    Fortunately "external" object reusing hardly work in data models and business objects related, yes reusing the class "User" provided by some external framework is not a good idea. But be sure, Amazon have modeled "User" shared on the whole company, this "User" is not your "User" way of thinking and IT IS FINE!!

    * Patterns Don’t Build Well Structured Software

    "rather than try to abstract it into a more generalised pattern"

    Pursuing universal generalization, abstraction for reusing is a silver bullet and most of the time the generalization purposed is not valid for you or the generalization is valid in the cost of clumsy and costly configurations/adaptations where usually the benefit of reusing is lost...

    And is it bad? NO!!! this is why software is fucking exciting!! is the eternal typical race of reusing the work of others trying to fit it into my problem against doing myself. This has NOTHING TO DO WITH OOP.

    Java collections are plenty of patterns and they are a good example of how universal generalization some times amazingly can work. Of course if you follow the path of higher generalization levels using other external frameworks your experience must be different.

    By the way, patterns are not dogmas just "good practices recommended by many" if a concrete pattern is bad for you or for your problem then is not a pattern for you.

    Almost nobody use a Gang Of Four framework and no need of that but many people are consciously or unconsciously applying the same patterns again and again.

    ReplyDelete
    Replies
    1. Thanks for your detailed comments. I'll try to answer a few. Firstly, wanted to comment on your opening statement...

      "The very serious problem of our industry is the dramatic reinvention of the wheel, most of this reinvention is not because of there are better wheels, is just because most of our colleagues are very young programmers and hardly can distinguish when something "new" smells good and is a real improvement instead of another even more clumsy way to do the same.

      So then when I read a text from an experienced guy like you I get alarmed.

      In a world where MOST OF DEVELOPERS DON'T KNOW HOW TO MODEL A OOP SYSTEM bashing OOP is like saying to them "hey guy your plain unstructured crap is fine and trendy".
      "

      I'd like to make an interesting observation on this. I don't see any reinvention of the wheel going on here. My relationship with obects has changed over time. I've now noticed that it has moved towards more functional concepts. What is interesting is that those functional and lisp like concepts that I have moved towards tend to pre-date the object-oriented approach, or at least have been developing in parallel. So perhaps less reinventing the wheel, but perhaps discovering that other wheels that already exists might actually have been more than adequate for the task at hand?

      I would very much agree that we are in a world where a significant proportion of developers cannot model an oop system. However, I like to hope that my experience shows that I can do this and I've found my models drifting further from a pure oo approach as I've learnt more and gained more experience. I'm however not bashing oo or advocating 'plain unstructured crap'. I'm just tending to lean towards accepting that there are other ways to create well structured software that may exist outside of objects or be some hybrid model somewhere in between. I don't quite see how looking at something, realising it's weaknesses and looking for potential answers can be classes as 'bashing'. We should be doing this every day as part of our day-to-day work.

      Delete
  5. This is the second part, sorry for the previous anonymous post.

    * Frameworks for Class and Component Reuse Give Few Benefits

    Umm really? Tried JPA?

    These days I'm working with Android, Android Java API is brutally based on implementation inheritance, any decent desktop-like toolkit is similar... Can you imagine the code I would need to make something like a CalendarView or to add some simple enhancement instead of inheriting from it?

    http://developer.android.com/reference/android/widget/CalendarView.html

    * Inheritance Creates Brittle Software

    Try to think this class avoiding implementation inheritance, and try to provide an alternative way of extending this class for end developers (most of Android classes are designed for implementation inheritance).

    http://developer.android.com/reference/android/preference/PreferenceActivity.html

    "resulting in lots of overrides of base class behaviour"

    Inheriting Cat from Tiger produces this kind of problems.

    "or making the base classes even more generic"

    If you class tree is too complicated sure you are coupling several orthogonal concepts maybe the path is breaking in several simpler class trees.

    * Violation of Encapsulation

    Encapsulation is not dogma, the encapsulation principle applied like a dogma to a data model is stupid, a data model by definition is designed to be manipulated in many different ways alongside the system.

    Yes in 80's everybody tried to put anything into the class User and inheriting User from some kind of ViewablePersistent monster class, just for the shake of encapsulation and the "beauty" of pure OOP.

    Fortunately these days are gone in spite of some people want to resurrect them with something like "rich data models", yes back to 80's.

    "If we are doing this, then why not just work directly with first-class structured data as opposed to objects?"

    Yes, data models are like simple structures, but they are almost the ONLY exception, service objects (DAOs, parsers, network bridges etc) can have all the beauty of OOP, and a more general view of encapsulation can take place on service objects.

    * Mutable State Causes Pain

    "More and more we now favour immutable state and stateless services so that these problems do not occur. There’s also the additional benefit that immutable state is a much better model for building highly concurrent systems and for getting the most out of modern multi-core hardware"

    I don't see the problem with OOP, you can make very complex stateless service objects like a parser with implementation inheritance, encapsulation and many methods some public and some private.

    Again the dogma of "everything and the kitchen-sink into the data model class"?

    * Behavioural Interfaces Cause Code Complexity and Bloat

    I don't understand your point, anyway closures in Java would alleviate the noise of anonymous classes, just noise reduction not a revolution, nothing fundamental changes.

    * Life Without Objects

    I hardly can see a life without objects, oh yes I can see, take a look to some piece of "average" JavaScript code using jQuery and try to think on a big complex system made of this kind of stuff beyond the fancy visual movement of a div :)

    ReplyDelete
    Replies
    1. Business Objects:
      "But be sure, Amazon have modeled "User" shared on the whole company, this "User" is not your "User" way of thinking and IT IS FINE!!"

      Interestingly, most of the large organisation that I have developed software with have many different abstractions for something like a User. Perhaps Amazon is different, I don't know. My experience has always been that while there might be reuse within a single project or within a group working on a tightly grouped of products, reuse in the wider business is more of a myth. Some may have achieved it, but the vast majority haven't.

      Patterns:
      I totally agree with you on patterns. My experience from amny years ago was that people tended to go to town with trying put as many patterns into their software as possible and to try to generalise everything into these patterns. My observation is not that patterns are bad but that we now use them more judiciously and in the correct context (such as generic collection classes) rather than embedded across the entire code base.

      Frameworks:
      "Umm really? Tried JPA?"

      Yes, I've use JPA and I find it somewhat frustrating having to bend my object models to match the ORM approach. However, I class JPA as being one of the newer light-weight libraries where we can select elements and use as much or as little as we wish. This is good. I was really talking about what came before JPA, namely EJB1 & EJB2 in particular. These were bad solutions that required you to buy into the entire framework in order to use any part of it. I'm glad those days are behind us.

      "...based on implementation inheritance, any decent desktop-like toolkit..."

      Okay, I agree that implementation inheritance works for UI toolkits and building desktop applications. I was trying to generalise across the whole gambit of software development, from web applications to data processing solutions to games to whatever. However, my experience with Swing and being involved early on with what then went on to become Apache Wicket still makes me feel that it might be nicer to build a UI toolkit around aggregation and composition, but I don't have any real evidence to support this feeling.

      Encapsulation:
      Again, my point was not that encapsulation is bad, but that we have moved from the rich model of encapsulation where we push everything deep in to objects and then try to access it only through behaviour. Instead we now adopt a mix of richer objects and lighter-weight objects. The lighter-weight objects tend to be closer to algebraic data types, which was my particular point. Encapsulation for richer objects is probably still a good idea, although functional programming has some alternative approaches for externalising state and passing it into functions.

      Life Without Objects:
      "I hardly can see a life without objects, oh yes I can see, take a look to some piece of "average" JavaScript code using jQuery and try to think on a big complex system made of this kind of stuff beyond the fancy visual movement of a div :)"

      I totally agree with you, the "average" JavaScript code using jQuery is not the best model for building maintainable software. However, that's not what I'm advocating in my post. I'm looking more at languages like Scala, Haskell and Clojure which provide a much more structured development model, just one where the structure is somewhat different to the object one - although as I observe, perhaps not quite as far apart from modern object-oriented practices as I had expected.

      Delete
  6. This comment has been removed by a blog administrator.

    ReplyDelete
  7. Ugg sorry for repetitions, Wordpress delays makes me crazy :(

    ReplyDelete
    Replies
    1. No problem. Blogger decided for some reason that your second post was spam and decided not to publish it. Thanks for your excellent points. I largely agree with you and feel that perhaps more has been extracted from my post than I intended to be. I'll answer some of your specific points a bit later when I get an uninterrupted period to focus on it,

      Delete
  8. I don't agree. While it's true that some OOP precepts (like inheritance) are overused and some junior programmers create complex class relationships without question, OOP languages like C++ and ObjectiveC do make wonderful use of code re-use; GUI frameworks like Qt provide pre-made window and GUI elements with common behaviors that would be chore over-wise - everyone expects buttons and windows to behave the same way and implementing event-driven behavior procedurally or with dumb structs would be a chore.

    What you're referring to is business logic which is hard to nail down in a persistent design. It's like quantum mechanics - just studying a business model may well have the effect of altering it!

    In that case, OOP's use is limited, as are design patterns. But any programmer worth his paycheck will attempt to commonalize his code, if only to document and *test* it. In which case simple objects and well-written free functions are the way to go.

    ReplyDelete
    Replies
    1. C++ is not an OOP language, although it has some OOP feature(and those OOP features seem to get the most focus). In fact, the C++ standard is not even OO. The container classes and iterators don't use inheritance at all. And all the algorithms are provided as free functions(to avoid bloated classes) and higher-order functions(much like in functional languages). It has been this way since the 90s, yet people still program C++ like its in the 80s(which is wasn't even standardized then).

      Delete
    2. Regarding code re-use, I'm not talking about re-use at the micro level. That tends to work well, especially for technical libraries like collections, persistence, gui and so on. Where it works less well is at the macro level - such as reusing business components.

      My point is really that early on in objects we thought reusable business components might have been a possibility, we soon found this was not the case and instead got our re-use from more loosely coupled, lower-level libraries. Interestingly, many of these behave very much like function libraries and type classes in languages such as Scala and Haskell.

      Delete
  9. Steve Naidamast2 May 2012 15:33

    Well said!

    Though I enjoy developing with objects\classes and find it a superior way to modularize and organize one's code all of the other promoted benefits often seem to be of little use or develop into overly complex frameworks as was noted.

    In fact, a major paper was written I believe back in the late 1990s on the failure of OOP to provide the benefits of re-use based on project research the analyst had performed.

    Given the ease with which code can be organized with objects\classes I doubt we would want to return to a more procedural approach of modularizing our development efforts but the actual development of clean code and efficient systems is more or less a result of superior professionals doing the work and not the tools and frameworks used.

    It should be noted that not all patterns are bad. The best example of an implemented pattern\framework is ASP.NET MVC. It provides a good structure to build a web application with and if followed properly will yield a very clean implementation. That being said, like any pattern, ASP.NET MVC can easily be abused turning the result into a mess if one were to rely on all of the implemented helpers and tools instead of using more easily understandable methodologies of processing requests and data.

    On the other hand, as the author notes such reliance on patterns can be taken to the extreme, which I found with Microsoft's Prism framework for implementing MVVM with WPF and Silverlight. Unlike the clean infrastructure of ASP.NET MVC, Prism appears to be nothing less than a nightmare to deal with in comparison. This is due to the basis of WPF, which has no inherent support for such a framework as ASP.NET does for MVC. Unfortunately, Prism is the best and most complete of such frameworks for those who want to design desktop applications in a similar style to web apps...

    ReplyDelete
    Replies
    1. Thanks for some excellent comments and observations Steve.

      I totally agree that objects and classes are a great way to modularise and organise code. Scala's support for mixing traits makes this work very well. I've personally found that over time I'm gradually using this capability to organise groups of related functionality rather than treating them as pause object constructs.

      As you say, patterns are great when used appropriately and judiciously. It's when people go overboard with them that things tend to go awry.

      Delete
    2. Doug Warner7 May 2012 01:02

      It's unfortunate that most OOP languages don't have constructs that separate modules from objects and classes. In Python I find that a module frequently provides just what I need without writing a class. Jack Diederich's PyCon US 2012 presentation "Stop Writing Classes" addresses this point.

      Delete
  10. I apologize in advance for the super silly sarcasm but I've got to get this off my chest:

    Correctly dealing with mutable state *is* painful exactly because coordinating sets of mutable state *is* difficult to perform correctly, but that's OK because that's what we do! We're programmers and the machines our programs run on are stateful layer upon beautiful stateful layer of mutable state including (but probably not limited to): disk sectors, banks of RAM, microprocessor registers, stacks and i/o ports (those evil b4stards at Intel, how dare they!), database object definitions and their accursed rows and columns, screen pixels, keyboard and mouse key/button state, mouse location and wheel state, a certain percentage of disk media, external port connector plug state, amplitude per time slice of both speaker and microphone volume, and, horror of bloody horrors: even our beloved programs have had this unholy SDLC abomination foisted upon its precious construction, deforming with wave after wave of corruption until it is unrecognizable even to its own maker.

    Actually, upon further review, I'm tempted to believe that maybe the reason no one appears to be anywhere near solving the "software is just fscking *AWFUL* crisis" is that the best and brightest -- err, well, at least the most tenured and endowed (with *grants*, people!) -- have decided that Earth's best hope lies in a model of computation that prescribes the implementation of a function that modifies a single array element's value to be by basically cloning the array, element-by-element, in such a way that the element to be modified is carefully substituted for the element it is to replace after which the new, cloned array is returned which will be used to create a new let-binding with the same name as the original array's let-binding thereby reducing the ref-count of the original array whose unattached value will, at some future time, be detected by the background garbage collector process that will recursively (and most carefully in our multi-core world) traverse the old array's elements trying to free any individual elements (which as a first approximation seems to be exactly 1) that have no references save the soon-to-be wiped array head itself.

    If ever a concept deserved a stupid-ugly super run-on-sentence, it's that big ball of steaming mud that can be quite simply and effectively be implemented via this time-tested algorithm:

    1. copy the new value's memory bytes over top of the memory bytes of
    the ith element of the array
    2. optionally reduce the array's allocated size (b/c we're fancy)

    May the record show that the least of a C programmer's problems are related to their use of constants; as a result, the petitioning citizen is requesting a permanent injunction on further mention of 'immutability' in any discussion about how to attack the problem that the 'percentage of programs that are crap' is clearly outpacing Sturgeon's Law by at least 9%.

    And let it also be said that having immutable objects *is* surely a nice way to ease the pain of designing concurrent systems; however, it's also kind of a cop-out ultimately because all (?) of the fundamental concurrency primitives (semaphores, cmp-n-xchg, monitors, locks, ...) are *defined* by how they rigidly control their mutability or prevention thereof. One must ask if the final system cost won't be far greater after paying the final bill that includes all the deferred costs from using immutable objs.

    And because the horse is not dead enough: If it ain't changing, it ain't living.

    Robert W. McCall
    execNext.com
    "Lasting peace and happiness for *ALL* human beings."

    ReplyDelete
    Replies
    1. Oh snap! Right on brother!

      Delete
    2. Thanks for the comments Robert. I completely get where you are coming from. I've had some great successes with immutible state, but I can see that there is a mismatch between these and the underlying hardware.

      However, my observation over many years of development is that there is a real lacking in ability in many programmers to write good quality mutable code that works in concurrent environments. I've probably spent weeks of my career to-date debugging these sort of issues where state is not what it was expected to be or has become inconsistent. Writing good quality concurrency code is really hard, adding mutability makes it harder.

      Therefore, my feeling is that we should be looking to specialists in this area of coding to provide the libraries that can then be used by non-specialist developers to build solutions that scale and function efficiently in concurrent environments. Look as something like the Akka framework and you will see what I mean.

      As devs we are never going to get fully away from having to deal with concurrency and change of state, but anything we can use to help get it right must be beneficial.

      Delete
  11. Inheritance, interfaces and statics (such as for singletons) are overused: they lead to most of the problems you've identified. After all, what we're trying to achieve is "low coupling, high cohesion": and you don't get more highly coupled than inheriting from something (and interfaces basically add a layer of inheritance).

    But don't throw the baby out with the bathwater: the world is composed of objects, which have functions; and we're in the business of modelling the real world. OOD/P is the best way of doing this.

    So my recommendation would be: avoid inheritance unless it is clearly correct; avoid interfaces unless you're writing an external API; avoid statics at all times. But stick with OOD and OOP.

    ReplyDelete
    Replies
    1. I think that's largely what I said. Back at the beginning we used inheritance, interfaces, statics and the like with abandon. Gradually our relationship to objects have changed and we have moved away from these concepts. I'm fine with that.

      My observation was that as we move in this direction there becomes a much greater similarity to non-object approaches like functional programming or lisps. So, perhaps there is a different approach that works well, or as Scala has done, some hybrid approach that attempts to blend the different approaches together.

      I don't think I was advocating throwing out any babies, just opening minds to other possibilities that come in to play given out current thinking on how objects can be used.

      Delete
  12. I completely agree with your article. Like you, after developing with OOP for many years, I realized that it never even came close to fulfilling its promises. Nowadays, the dogmatic clinging to the OO paradigm has risen to the level of a religion, where innovators and independent thinkers are often dismissed out of hand for questioning the central tenets of OO. I hope the ideas in your article continue to move the industry in a healthier direction.

    ReplyDelete
  13. I was trained to program through the application of a system analysis method. It distinguished carefully between assets (things whose behavior we can specify) and values (things that we hope will evolve to a desired end state through interaction with the assets).

    Given that orientation, when people talk about "encapsulation", I think about "library" or "machine shop", and am astonished that it should be at all controversial that behaviors should be manifested in a organized context. That is my sense of an "object".

    Many of the problems described in the post are endemic to programming. People have always rewritten applications to adapt to changing realities, even before OO. The fundamental challenge is trying to evolve a linear representation of system behavior (text) to respond to changes in a multi-dimensional reality. The linear representation is an inadequate representation of the operational model that it implements, and so mitigates against adaptation.

    The enthusiasm of the OO zealots for Christopher Alexander's work is telling. Alexander evolved a GRAPHICAL method for analysis of the coupling between design features, and a discipline for projecting that into the design of physical places. The software industry has not yet attained a similar discipline.

    I learned OO through Rumbaugh, and there is another aspect of the paradigm that has not been addressed here: Rumbaugh's method was OOAD - "Object-Oriented Analysis and Design". Jacobsen's original method included a rigorous practice of systems analysis. One of the stated objectives of both was to ensure that developers and customers shared terminology to facilitate negotiation of requirements. That's worth remembering.

    ReplyDelete
  14. I did functional programming before the advent of OO and I have seen the use and abuse of both. It's refreshing to read an article like this. As with anything, moderation and consideration of application for the task at hand are key. If you try to solve any problem with an open mind and no agenda or bias towards satisfying OO or functional programming for the sake of it (including patterns etc.) then you'll come up with the best solution.

    Just because I bought a great hammer, doesn't mean I need to use it every time for every job. Sometimes I can push the pin in with my thumb.

    ReplyDelete
  15. I just wanted to add a couple of clarifications to this article. I have seen some tweets going around casting this posting as an "objects considered harmful" type attack on object-oriented development. That couldn't be further from the truth and if that is the conclusion that people have taken from reading my post then I clearly haven't communicated my intent clearly enough.

    The post is about my changing relationship with the object approach. Things always change over time and approaches that we thought were best practice 10 to 15 years ago have proved to be less successful than we has assumed/expected them to be. I still use objects and I expect I will continue to do so for many years to come. However the way I use them now is very different to how I used them before and will undoubtedly be different to how I use them in the future. That's not to say that at any point along the continuum that it's not a sound approach when used correctly. Things change, people learn and experiment, new ideas prove more successful than old ones (or not).

    However, in my forays into Scala, Haskell and Clojure I have certainly noticed that the approaches that I use now when building object systems are much closer in nature to the way non-object system are constructed. It was this that got me to thinking whether my future software development may continue outside of an object-oriented view of the world. I can only quote from experience gained over the two years that I have accumulated so far programming enterprise type applications using both object, non-object and mixed approaches concurrently. That experience shows for the most part that the non-object or mixed solutions that I create tend to be smaller, simpler and easier to maintain while still being well structured. Your experiences may vary.

    ReplyDelete
  16. Chris is interesting to note that Scala is a step forward in OOP when talking about traits.

    Show a Scala class with several traits applied to a C++ developer, and the first comment will be something like "oh interesting Scala also has some kind of multiple implementation inheritance" :)

    ReplyDelete
    Replies
    1. Interesting point. Scala traits are indeed a powerful concept. However, with great power comes great responsibility ;-)

      The observation I would observe is that abstracting into one or two traits and then mixing them in works just fine. Adding more and it starts to get more complex. Then you get to the point of starting to have dependencies between traits. Scala supports this through self types. You then get to something known in Scala circles as the 'cake pattern', where many inter-dependent traits are inherited into a component. If you don't take great care, this soon becomes a horrible tangled mess (the bakery of doom as some people call it!).

      Personally I tend to use traits more as a way of grouping a set of related functions. When all these functions need some state that is not used elsewhere then I tend to encapsulate it in the trait. Where there is state or data that is shared across multiple traits then I tend to treat that as a more functional data construct and pass that to/return it from those functions rather than encapsulate it in an object fashion. This approach is working out well for me so far, but I'm still experimenting lots to find the exact blend that works well - and this may be different depending on the project or what the trait is actually doing. The jury is still out.

      Delete
    2. After reading this comment, I was shocked for a while whether I forgot how trait works since I'm still new. As luck would have it, noted in my reference and comment, trait is a beautiful feature for debugging purpose and make code compact.

      Delete
  17. I enjoyed reading this post and all the comments immensely. :) I too have been doing 15+ years of OO Java mostly focused on the client side w/ a real time & game dev focus and am well on my way to moving on from the classical approach. However, I found one last niche to exploit before jumping the shark to a more pure or even mixed (Scala) functional approach. I have a long running framework started in '03 to accomplish client oriented tasks and it has evolved greatly since conception. While modularity always has been the main concern things kicked off in the traditional OOP fashion relying heavily on inheritance / explicit composition and overused OO patterns re singleton. I was able to get away with this for quite some time. The first real breaking of the seams came from my entity system for game dev. In creating a generalized entity system applicable to any game the nastiness of the blob anti-pattern attacked in earnest in addition to a fracturing of storing state necessitating pushing various state sub areas into explicitly composed implementations. The final breaking point where I made the full plunge to move away from inheritance as the de-facto organizational pattern came when I finished the initial port of my efforts to Android (finished April '09 / Android 1.1). As jmarranz noted the Android Java SDK is "brutally based on implementation inheritance". It was clear to me in the transition from Android 1.0 to 1.1 that this was the case and it isn't going to be pretty or IMHO end well for modularity and targeting the larger ecosystem. This is very evident today and I can't say the Android team has taken a judicious approach to API development (it seems like anything goes really); the last straw was drawn and a new way was necessary. As things go though I can't jump to a pure functional approach due to existing code base to the client side / real time app / game dev applications which as things go just aren't as efficient with immutable data structures on the desktop let alone Android.

    The key insight that I came across is a focus on implicit composition. The explicit variant being an object containing another object member variable. The implicit form being a lightweight container (IE component manager / IComponentManager) where one can store and retrieve aggregated components. The goal is no longer to encapsulate data and logic in one object, but to concretely split implementation between data and system / logic oriented components. There has been a bit of discussion on this approach for becoming known as the entity system architecture, but I generalized the approach for use any any context including the generalized entity system. A quick example is:

    entity.getAs(DATA, Position.class);

    DATA is a statically included import for IData.class. Data components certainly can also be publicly scoped so one can do this: entity.getAs(DATA, Position.class).x;

    You can store anything in a component manager and add data / system components dynamically. One can also define any other component type too as necessary.

    ReplyDelete
  18. There is a lot more there to discuss and I will be delivering a lot of this content as a presentation (Performance Java for Android: Tricks for the Troubles") at AnDevCon III in a couple of weeks.

    I must admit since I had an existing medium sized SDK / runtime framework level codebase ~150k SLOC the jump to implicit composition and rewriting each of the core SDK / runtime areas has been staggering. It took me almost 1.5 years mostly full time+ to rework my efforts in this direction before everything compiled and ran again; a very incremental restructuring from the core to the periphery. I might mention that the runtime and SDK layers are now composed of 700+ independent IDE projects with minimal dependencies between them.

    For handling state I have embraced extensible enums, well, extensively and the solution I came up with for storing unrelated state is the ExtEnumSet / Map which is an extension of the EnumSet/Map collections. Extensible enums also made decoupling state between IDE modules / separate components possible to a level I never achieved before.

    Re Chris T:
    "However, my experience with Swing and being involved early on with what then went on to become Apache Wicket still makes me feel that it might be nicer to build a UI toolkit around aggregation and composition, but I don't have any real evidence to support this feeling."

    This is one of the things I have done for Android not only with the View / graphics2D API, but along with the rest of the Activity lifecycle and many fundamental SDK areas for Android dev. Of course the same techniques apply for any other graphics API; OpenGL / ES, JavaFX, Java2D, etc. I can tell you it works well and I am very excited to release my efforts imminently. I hope you don't mind me mentioning this effort: TyphonRT.

    I suppose might also mention that Scala is next on my list to explore once I finish reworking my efforts at the Java runtime and SDK layers. I posit and have a feeling that applying the direction I have taken with Java dev will work quite well with integrating Java w/ Scala without inheritance.

    Anyway.. cheers everyone... I'm glad to see independent confirmation / discussion of directions I too have embraced whole hog.

    ReplyDelete
  19. OO is a analysis method, it can cut a big one into fragment ones, another article: Tell Above, and Ask Below - Hybridizing OO and Functional Design(http://michaelfeathers.typepad.com/michael_feathers_blog/2012/03/tell-above-and-ask-below-hybridizing-oo-and-functional-design.html) maybe help you.

    all pains in OO practice all are from "OO language" that is not true OO, in ture OO, Objects interact with each other by async messages/events, I can do it in Java with jdon framework, if you are interesting in it.

    ReplyDelete
  20. Maybe you could subtitle this post - Or, Towards a Life Without Complexity - because I think that you and the other comments touch on a deeper issue: How increasing and unnecessary complexity has polluted software development.

    I started coding in C/UNIX in the 90's. If you were lucky you had computer with a few MB of RAM and a few 10s of MHz CPU. We didn't write large complex code, because you couldn't run it if you tried.

    Instead we built synergistic ecosystems of small responsive apps communicating via messaging. We wrote code as simple as possible, but not simpler (to quote Einstein).

    And it worked; we had electrical utilities running multiuser billing systems for 1000s of customers on computers with less power than an iPhone.

    As you ably describe in your post, something has gone wrong in the past two decades. Somehow complexity has crept in, seemingly for complexity's sake. I'm as guilty as anyone; I've built a whole generation of systems swarming with SOAP web services that only send a few bytes of actual payload. Layers of interfaces and classes to save a simple data structure to a table. ASP.NET webforms that abstract away and bear no relation to the HTML sent to the browser. Years debugging monolithic systems with layer upon layer of code implementing whatever pattern/framework trendy at the time. 3GLs, 4GLs, OOP, now FP.

    But for the average-Joe enterprise programmer (like me) has any of this really improved what is actually important (and why we get paid)? Ie: timely, cost-effective and responsive systems that meet users' needs and are adaptive to change, that don't cost an arm and a leg to run and maintain? That the poor sucker that inherits your code in a few years doesn't have to spend weeks with a debugger just to find out what it does?

    Fortunately over the last several years we have seen a push-back against this creeping complexity, leading to rise of the various opinionated development frameworks (eg Grails, Rails) and 'pragmatic' languages such as Groovy, Ruby, and now Scala.

    KISS. Just like we used to, but seem to have forgotten. Focus on what the client wants - reliable software they can use without needing a degree, delivered as quickly as possible, to last only as long as they need it. We don't need to write software as though it must stand the sands of time. I've been coding for 20 years, and I have never seen a system last unchanged/unscathed for more than a couple of years.

    The people paying you don't care that you have abstracted away layers of code, 'just in case' you might one day move away from SQL to NoSQL. They don't care if you are using a share nothing, massively concurrent. functional, DSL-enhanced, platform-agnostic framework.

    They care that the screen they enter data on is simple, obvious, and fast. And that the data magically comes back next time they search for it, super fast. And if things change fast, that you can quickly code another system that keeps on helping them make or save money.

    Pick an opinionated development framework. One that whoever has to maintain your code in 5 years time can pick up and run with in a few days. Make sure that the language is pragmatic, not dogmatic, and allows you to express the problem at hand in code naturally with DSLs, closures, maps. Build ecosystems of applications that exchange data through simply, preferably via a message broker or REST/JSON apis.

    My 2 bobs worth: It doesn't matter if you stick with OOP (it's never going away) or launch into FP (shows promise) or sit on the fence (probably the best bet). Just make sure that what ever you do, Keep It Simple Stupid. Those who come after you will thank you.

    ReplyDelete
  21. Very interesting points you guys make! However, it seems to me that most of them have more to do with 'poor design' than failure in OOP methodologies. Recently had my 'textbook' OOP programming style come under scrutiny by an older developer - one who sits in a dark room coding with a text editor, and I must say its been difficult for me to explain 'why' we should be using OOP. Having said that, they also don't understand relational databases nor the structure that exist naturally between data entities. Anyway, as a result I found myself simplifying my applications & more specifically the 'abstracted model' i use to program against inside the application. Shallow inheritance & static classes of functionality that can manipulate objects are mostly the order of the day at the moment. Its OK to use all the principles we have at our disposal but not just using them for the sake of it, its more a case of 'is it really necessary' for what we are trying to accomplish at the time of going to press? Have worked on many projects where we tried to get the full 'generic' implementation going in order to maximise code re-use and in hindsight i must say, any modifications mostly required some form of coding exercise anyway. There is a happy medium but finding it is the real craft, possibly made easier with experience.

    ReplyDelete
  22. Dunno, I'm with the others saying that the problem isn't caused by OOP, but by the people using it.
    I have limited knowledge about functional programming, although I'm gonna work on this in the upcoming future, but I've worked on a lot of different projects, using several languages, several approaches and methodologies, and feel correct OOP is a clear step in the right direction for me.
    For example, I've recently had to rewrite an old VB6 app, at first I kept most of its procedural style, it worked flawlessly, way better than before, however, since more changes were needed, I refactored all code to use OOP, there was a certain part of the application that has always been badly thought that has been somewhat a pain to use proper OOP, but in the end performance and memory usage have barely changed, the app is more scalable, flexible, the code is clearer and more concise, etc. In summary, what OOP promises.
    Of course, I've seen horrors caused by OOP misuses, for example, I know of certain application currently being sold, and constantly updated, that has tons of projects, all highly coupled, which highly depends of certain files when it shouldn't, compiling it takes half an hour, etc.
    I agree that mutable states cause pain, but also, I wouldn't want to program certain things without it.

    ReplyDelete
  23. I'm a bit shocked at the amount of push back you're getting on this blog post - but it does seem that daring to say maybe "traditional" OOP is not the best solution gets some people pretty wound up...

    I learned plain ol' procedural coding and functional programming pretty much side-by-side in the early 80's. I liked SASL and Miranda - but they were niche languages and my day job was writing C. Then in the early 90's, I moved to C++ - and I liked Haskell, but it also remained a niche language. I was on the ANSI C++ Standards committee for most of the 90's. In the late 90's, I moved to Java, still wishing functional programming was more mainstream but doing my best to be a good little OOP worker bee since that was the common wisdom. I had hoped that generic programming and collections in the C++ Standard Template Library would gain more traction, especially with generics coming to Java, but that also seemed a niche style of development...

    I remember when design patterns exploded onto the scene. I'd been programming commercially for a decade before the Gang of Four book appeared. I used to attend the Object Technology conferences back in the 90's. Everyone was trying to find better ways to do things and there were lots of new ideas buzzing around (the Oxford Programming Research Group spent quite a bit of effort looking at how to blend this "new" OOP approach with the previous best practices in the functional world, for example).

    Over time, that seemed to settle down and everyone just followed the OOP tablets of stone, churning out ever more complex systems, with more and more abstraction and The Nouns took over, almost obliterating Verbs in the representations of all our models.

    In the last decade, I moved from a serial monoglot to a true polyglot, mixing and matching multiple languages on projects. Increasingly a functional approach became more and more feasible in mainstream languages. Over the last four years, working with Groovy, then Scala and then Clojure, I've been able to adopt functional programming more and more in my applications and it really has simplified a lot of things. I find I reuse more code - small functions are easier to reuse than classes; I find I can do more with my data - maps and vectors and more amenable to general data analysis and transformation techniques than data hidden inside classes; and I find I can make larger changes more easily in response to new / changing requirements - the lack of side-effects and a more generic approach to data helps keep code more loosely coupled.

    OOP was definitely an improvement over raw procedural thinking but in a world of increased concurrency, the mutable state associated with the traditional OOP approach is becoming more of a liability and we need something different if we're to continue tackling ever more complex systems in any sane way. Functional programming - once considered so niche and specialized - just happens to fit well with that concurrency-laden world and, as we can see, it's certainly not a _new_ idea. It's just been waiting for its time.

    I think we'll see more hybrid approaches, OOP where it still makes sense (and GUI-building seems to be a good fit, as well as for modeling systems that truly require shared mutable state) combined with a functional approach to data at large that frees us up to think about the business logic being implemented without getting bogged down in the housekeeping associated with encapsulated mutable state.

    Great to see blog posts like this and I hope the "haters" will, over time, come to see some of the benefits of the "new" functional programming approach :)

    ReplyDelete
  24. Just a thought: isn't this just the old "if all you have is a hammer, then all problems will look like a nail" discussion again ?

    I don't mean to underestimate your ideas here - exactly the opposite! I think you have some good points, especially about mutable states and macro re-usability.

    But then again the way you express your ideas does sound a bit on the radical side (and I want to be clear on this: the WAY seems radical, not your ideas). So you get many replies of the "hey - so true!" or "hey - all crap!" kind. Radical replies.

    I think the fact that in the last few years, like you and others here point out too, functional programming concepts have re-emerged and have been included in many mainstream (and traditionally imperative) programming languages, is just an expression of your same ideas and concerns.

    The point once again, IMHO, is simple: use the right tool (technology) for the right job. Of course OOP is not helping you much with business logic. And of course it's great for UI frameworks and other similar cases. It all just stands to reason.

    What you say about patterns is just the perfect example: patterns are good, but only when applied to the right cases and in the right way.

    Nice post. Maybe just a bit radical-sounding, as I said. :)

    ReplyDelete
  25. Hi,

    very interesting points.

    Just some questions that came to my mind:

    As you've said there is no common understanding how to use patterns, inheritance, polymorphism and the like. Additionally the unexperienced developer will always do pretty nasty things, correct.

    1. Do you think it is easier to start with functional languages than with the others? I don't think there is less complexity on functional languages. I also don't have an idea how to build complex UI without having predefined controls that can be inherited from. But as in JS (e.g. Extjs) I really like the approach of mixins. That does make things easier and reduces a lot of the boilerplate code that just has to be written.

    2. Moving from procedural languages (assembler) to oop (c#) to "more" functional (f#, js) it all has its benefits and drawbacks. E.g. moving around functions that use functions that work on functions is not more transparent than having predefined patterns that are somehow visible - or at least understandable architecture that fits the tasks not the predefined best practices. What you think about that?

    hiro.

    ReplyDelete
  26. I am feeling that things have been a interruption away from developing brief, well arranged and recyclable software.

    photoshop courses sydney

    ReplyDelete
  27. This comment has been removed by the author.

    ReplyDelete
  28. Certainly not every good program is object-oriented, and not every object-oriented program is good.

    Bjarne Stroustrup

    ReplyDelete
  29. same thoughts can be found at existentialtype.wordpress.com

    ReplyDelete
  30. the disadvantage of the article is that it is possible to be understood by those only who already experienced the described troubles and made the same conclusion.

    best regards,
    silly-sad

    ReplyDelete
  31. Like all the info... Thanks you share it.

    ReplyDelete
  32. Like all the info... Thanks you share it.


    Church Software

    ReplyDelete
  33. I recently released the availability calendar source, but thought it would be useful to release the bulk email credential script I made for the client.

    ReplyDelete
  34. I already know that the government is supposed to have evidence of a crime BEFORE indictment or arrest.
    They are not supposed to count on squeezing a defendant that pled guilty into making some shit up against another defendant,
    which would be the result of this


    books
    chick lit
    romance novels
    life
    romance

    ReplyDelete
  35. I have 20+ years of OO with C++ then Java, J2EE etc so yes I have seen distasteful amounts of complexity and layering frameworks which bloat memory usage and distract from the users problem but no I dont want to live without objects.
    OO Analysis and Design and Use Cases are definitely helpful for all stakeholders. GOF Design Patterns help to make solutions communicable as they provide shared language and concepts.
    When your OO design needs to be implemented, then an OO language provides the most direct mapping. The strongly typed procedural sequential and synchronous nature of Java is sometimes a pain but a few classes (TreeSet, FutureTask,PriorityBlockingQueue) and design patterns (Whiteboard) can usually make it tolerable to overcome. I am investigating other languages such as Scala and computing models such as ROC (NetKernel) and deployment modules (OSGI)

    ReplyDelete