Skip to main content

Eapen G

Eapen G's Public Library

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.

OOP functional programming scala

May
16
2012

Over the last two or three years we have been using MongoDB in some mid-size projects.

Now it is time to say goodbye to MongoDB for a variety of technical reasons:

the currently memory model of MongoDB based on memory-mapped files is brain-dead. Leaving memory management to the operating is nice idea - in reality it does not scale and does not play very well. There is no single way to control  the memory usage using system tools except maintaining mongod instances on dedicated virtual machines without running further services. There are numerous complaints from people about this stupid architectural decision from various side and 10gen is doing nothing to change this brain-dead memory model.
Locking: a global server lock for a scalable database solution is a no-go - especially since MongoDB  supports  only atomic operations. Now there is relief in the making with more granular locking or the temporary yielding of the lock during long-running write operations. But this is more a workaround than a solid and scalable solution.
Query engine: the query engine of MongoDB still can only make use of one index per query. How insane is this? There is no obvious reason why this limitation exists. The index model of MongoDB is very similar to relational databases - in fact: it borrows a lot of concepts and ideas from relational database. Having worked on indexes and search enginew myself for more than a decade I can not recognize any particular reason why the query engine can not use multiple indexes per query - the query engine appears poorly implemented.
Query language: using JSON as a query language was a bad decision. The current JSON query language works for standard queries but the functionality of the operators is limited. It is still not possible to express arbitrary queries like in SQL using JSON. One would argue: not need - but in reality there are always cases where you need more complex queries. The only way around is to implement something client-side or use the server-side JS code execution (single-threaded, slow). Having no option to perform an operation comparable to UPDATE table SET foo=bar WHERE.... (which is possibly a low-hanging fruit). There are various odds and ends with the query language and its implementation. E.g. why don't you get an error message when using the $and operator with MongoDB version that does not support it? Why does not MongoDB complain here about an inappropriate operator usage? Look at the mailing list and discover such flaws all day long in various posting. Silently discarding error is a worse thing. If there is a problem then raise the issue and don't hide it under the carpet.
Map-Reduce: Map-reduce in MongoDB feels like a useless appendix added at some point to MongoDB. Same problem as with server-side code execution: it blocks.  Now instead of fixing a bad implementation or fixing the underlaying architectural issues, 10gen seems to address the MR limitations by supporting Hadoop for the MR part - either they don't trust their own MR implementation or they won't/can't fix it. No, we do not need more tools for doing map-reduce - there are already too many moving parts in a setup for scalable applications. Either fix MR inside MongoDB or throw it out completely.
Sharding: yet another misfeature of MongoDB. Going from a single server installation to a partitioned setups is *huge*. You need at least two replica sets for the shards, three config servers and the load balancers. That's like building a skyscraper beside a small town-house.
Data-center awareness: yet another feature that has been tinkered together. Replica sets only support one primary with multiple secondaries. Write can only go one secondary. Running a replica set across multiple datacenter is doable but write can only go to one secondary in one data-center. Assuming have a replica set with nodes in Europe, US and Asia with the current master being locates in US: all write from US and Asia need to be performed against the master in US and replicated back to the secondaries in Europe and asia - insane and not scalable.
The "safe" mode is off by default: who made this idiotic decision? Many reports why people about data los have been seen - just for the reason that "safe" is off by default. Although this is documented here and there: does such a decision bring trust to MongoDB? Safe mode must be enabled by default - people should be able to turn it off for performance reasons and with the understanding that turning it off may lead to data loss unless they perform explicit error checking client-side.
Journaling: MongoDB pre-allocates 3 GB of data for journaling - independent of the actual database size(s) - insane for small installations.
The social component: 10gen and MongoDB are trying to make the impression that building scalable large systems is now much easier - no, it is not. Building large-scale systems has been complex in the past and it is complex nowadays. Building large-scale systems requires knowledge, experience and expertise. Instead lots of people with zero or little database knowhow feel blessed building scablable database applications. The clear message is: building mid-size and large-size systems using MongoDB is nothing for low-performances or zero-achievers. Solid know-how is still necessary. My essage to companies building applications on top of MongoDB: assigned smart people to MongoDB and don't leave the database work to people that can hardly spell their name or that can just count to three. Yes, this paragraph is harsh and does not comply with diversity but it is true and reality. The number of people that should not do any database related work, people without reasonable background, people lacking basic skills in understanding databases is extraordinary high. Just because a database appears as means does not mean that it should be used by everyone feeling blessed for whatever reason doing database related work.

MongoDB is currently more about marketing and hype than it deserves. The primary goal of 10gen is currently running through the world in order to tell the world how cool MongoDB is. The reason is clear: 10gen is trying to play all other databases in the same market against the wall with the funding they received from their investors. It is a legitimate goal of 10gen but the technical foundation is shaky. Many things like the query language and query processor are half-baked since MongoDB 1.2 (my first version I used) - and no significant improvement have been made since. Many people said that the MongoDB 2.0 version should have been 1.0 - and I agree with that. Yes, MongoDB is an emerging technology (with potential) but MongoDB is hyped by 10gen as a new enterprise-level database (and perhaps want to position it against Oracle & friends). The truth is that many things are half-baked or some more iterations in order to make them usable for public consumption.

mongodb

Learning To Speak Object Oriented
=================================

In this exercise I'm going to teach you how to speak "object oriented". What
I'll do is give you a small set of words with definitions you need to know.
Then I'll give you a set of sentences with holes in them that you'll have to
understand. Finally, I'm going to give you a large set of exercises that you
have to complete to make these sentences solid in your vocabulary.


Word Drills
-----------

* class : Tell Python to make a new kind of thing.
* object : Two meanings: the most basic kind of thing, and any instance of some thing.
* instance : What happens when you tell Python to create a class.
* def : How you define a function inside a class.
* self : Inside the functions in a class, self is a variable for the instance/object being accessed.
* inheritance : The concept that one class can inherit traits from another class, much like you and your parents.
* composition : The concept that a class can be composed of other classes as parts, much like how a car has wheels.
* attribute : A property classes have that are from composition and are usually variables.
* is-a : A phrase to say that something inherits from another, as in a Salmon is-a Fish.
* has-a : A phrase to say that something is composed of other things or has a trait, as in a Salmon has-a mouth.

Alright, take some time to make flash cards for those and memorize them. As
usual this won't make too much sense until after you're done with this
exercise, but you need to know the base words first.


Phrase Drills
-------------

Next I have a list of Python code snippets on the left, and the English
sentences for them:

1. class X(Y) : "Make a class named X that is-a Y."
2. def __init__(self, J) : "class X has-a __init__ that takes self and J parameters."
3. def M(self, J) : "class X has-a function named M that takes self and J parameters."
4. foo = X() : "Set foo to an instance of class X."
5. foo.M(J) : From foo get the M function, and call it with parameters self, J."
6. foo.K = Q : "From foo get the K attribute and set it to Q."

In each of these where you see X, Y, M, J, K, Q, and foo you can treat those
like blank spots. For example I can also write these sentences as:


1. "Make a class named ____ that is-a Y."
2. "class ____ has-a __init__ that takes self and ____ parameters."
3. "class ____ has-a function named ____ that takes self and ____ parameters."
4. "Set foo to an instance of class ____."
5. From foo get the ____ function, and call it with self=____ and parameters ____."
6. "From foo get the ____ attribute and set it to ____."

Again, write these on some flash cards and drill them. Put the Python code
snippet on the front and the sentence on the back. You *have* to be able to
say the sentence exactly the same every time whenever you see that form. Not
sort of the same, but exactly the same.


Combined Drills
---------------

The final preparation for you is to combine the words drills with the phrase
drills. What I want you to do for this drill is this:

1. Take a phrase card and drill it.
2. Flip it over and read the sentence, and for each word in the sentence that is in your words drills, get that card.
3. Drill those words for that sentence.
4. Keep going until you are bored then take a break and do it again.


A Reading Test
--------------

I now have a little Python hack that will drill you on these words you know in
an infinite manner. For now, go get the code at http://codepad.org/2L0MQUa6
and try it out. What you do is run it, and type the answer in, then hit ENTER
and see if you get it right. It doesn't actually check, it just prints it out
so you can see if you are close enough.

You should also change this script so that it does the inverse by giving you a
phrase and then you have to write the python code for it.

oops language programming objects

1 - 20 of 3314 Next › Last »
Showing 20 items per page

Diigo is about better ways to research, share and collaborate on information. Learn more »

Join Diigo
Move to top