This time, I was asked about OOP:
So when I first learned about OOP, it was the bees-knees; but, I did some research into OOP is js and I'm finding that now even in other non-prototype languages people are shying away from objects. Can you explain this to me why this is or would be? I mean EloquentJS dropped it from the book and I read many articles that said project managers and hiring managers are not really considering it as important anymore.
My answer was this:
This is an interesting question. I'll give you an answer in three different levels. Maybe not all three are equally interesting so feel free to skip ahead :)
On a very general level (non-technical reasons)
There are two reasons at this level. The first one is the generally accepted wisdom that “there's no silver bullet”, there's no single technique, methodology, approach, technology… that is the be-all-end-all, that solves all problems in all circumstances. This is obvious, and we all -programmers- know it, but still… Even though we know it, a fair number of them try to, in a way, ignore it. This produces discussions about how X or Y is “the correct way to do” things. There are shifts in these discussions and some times one gains more traction in the community, and other times another one does.
The second reason is programming and developing software are still very young disciplines. Maybe Computer Science is a bit more mature, but not much. This means we -as a profession, as a discipline- are still learning and looking for the best ways to do things. We try new things, give them time, see what sticks. Hopefully we learn from all this and the new upcoming ways draw upon the better parts of the old ones… because, well, there's no silver bullet but at the same time even things which didn't work may have some usable parts.
On a general level (technical reasons but concerning all languages)
Now, what about OOP specifically? Is it dead? Has it been surpassed?
Nah, not really. But throughout the years certain flavours of OOP have been tried and now that it is more or less mature, that he have seen what works and what ends up being a problem or an annoyance, it seems like it's time to look back at it and see where it stands.
There are problems with OOP. Problems which have been acknowledged and discussed. Some, to a certain point, may have been mitigated.
Probably among the most important problems with OOP we could find:
- Inheritance. Inheritance in itself has problems. The main reason held against inheritance is that the world we try to model doesn't really form in a hierarchical way. It's an artificial construct and it's too rigid. Not only that, but from a more practical p.o.v. once we define a hierarchy it limits us in the ways we can evolve it. Inserting a new object/class which doesn't exactly fit, may force us to re-do the whole tree and this would in turn force us to re-do major parts of the application that was built over such a tree. Even in heavily OOP languages this has been acknowledged and people suggest that we should favour composition over inheritance. ( there's still a lot of discussion going on about this. Wikipedia has this but you can find a lot of articles on the net by searching “composition over inheritance”) ( there's also some discussion about whether inheritance itself is or not part of what OOP originally meant. You can read some of Alan Kay's comments here and here. He invented the term “object oriented” ;) )
- Some things are not really objects. A sorter or a comparator, to use simple examples, feel too forced and artificially bound into an object. In OOP languages hundreds of such “non-object objects” are been forced to appear. In any medium to large Java, C++ or C# code-base you will inevitably find a lot of these. This problem is more generally known as the nouns and verbs problem, suggesting that OOP is all about nouns and too little about verbs. ( interesting classic article: Execution in the kingdom of nouns )
- State. One of the hardest parts of developing and maintaining a program is understanding how it all works. And one of the things that makes is harder is having to keep up with the state of things. You probably have been through this some time. Something behaves one way… except if some time in the past some other action occurred and then this other thing fired; if we are in this or this state such and such with behave differently. The problem with OOP in this regard is that it distributes state in a lot of separate places. All objects have their own particular bits of state, then the classes may have their bits of state too, then the objects and classes you inherit from or the ones you composed with, they will all carry their bits and pieces of state. If this is approached with caution and discipline, the problem may not be that big. It will be difficult to reason about it all but maybe somewhat manageable. But it's easy for little bits to slip by this discipline and little bits of state to leak here and there into places they aren't expected. ( to learn more about this, you may search for a couple of Rich Hickey's talks on the subject. Caution: Clojure (a lisp) will be probably involved xD but the talks are good anyway. there's one called “Simple made Easy” and at least one or two other ones where he talks about state. )
(There's a much longer list of arguments against OOP here )
Now, all that I've said before applies. More particularly, JS's prototypes are clearly not well suited for big inheritance trees. Probably not even medium ones. Small ones… they are maybe ok, but even with a couple of levels it all starts to be not really optimal at all.
I can tell you a bit about my own approach, which will probably not match yours or even be a good approach at all xD It's not set in stone and it changes from time to time.
I tend to use objects for data structure. That is encapsulation, grouping of related data. It's obvious that having a
Point object with coordinates
z is a good thing and almost always better than having the separated coordinates in different places. I also use encapsulation for name-spacing, to group together functions without polluting the global name-space. And finally I use encapsulation through closures ( think "revealing module pattern" ) mainly for developing widgets or modules which need to keep some degree of privacy. Not for the sake of privacy itself but because when you work in a team not everybody takes the same care and I've found this helps a bit as a deterrent in some occasions (it's a bit difficult to explain clearly, sorry, but I do like using closures and find this pattern to be useful and comfortable).
Other than that, I tend to favour using a more functional approach, coupled with a messaging/event system. This latter thing is particularly useful in JS, as most things are already “event-oriented”. We don't always need it because we do stuff that goes from simple pages where you may at most find some form validation, a couple of AJAX calls and little visual/interaction logic, to more complex internal company applications. A Pub/sub system has helped us in those more complex areas or when UX gives us very complex interactions to implement. I tend to go for a functional approach by keeping simple structures of objects and have functions that operate on them. I'm still fighting state but it's not always easy to get the team to follow on this, as not everybody is accustomed to this. Anyway, I don't want to bore you with our particular details xD
I do use prototypes from time to time. Never -at least never that I can remember now- with more than two levels of inheritance. And even two levels is rare.
I hope this answers your question at least in part. Ask anything and do read about these subjects to get some other opinions. The discussion about all this stuff is still ongoing.