A matter of style
Thank you for reading and taking time to respond. Really appreciated it. You made a very valid point that `class` affords class inheritance.
Different school of thoughts
I feel that we are in different school of thoughts, and it is in my opinion very similar to a management problem in general — choosing a technology stack.
At where I currently work, we discussed about this a lot.
Let’s say, on one end is the narrow gate. We have something like Haskell or Lisp or Erlang.
A language with many unheard concepts (like cofunctors or applicatives), and concept everyone knows (mutations, inheritance) are nonexistent.
A language that gives you virtually unlimited choices that you don’t even know what’s the best way to do something.
A language that just a few people know, but once you master them (which probably takes years), you’d be able to do in one day what would take a team of developers one week to do in Java or Go.
On the other end are the languages that almost everyone knows. The languages with easy-to-swallow concepts, based on how objects in real-life interact.
The languages that people can get started within a few days and start making things, and can write similar-looking code. A language with one, definitive way of doing things. A language like Go.
As a team of Haskell noobs, it would be unwise to go ahead and write a production code in Haskell. If we do that, it’s probably going to be like writing Go code in Haskell, and would take us twice the time, and once we have more experience we’ll lament over what a messy code we wrote.
From the management perspective these weird languages seem like a bad choice as well. If we do it in Go and someone leaves, it’d be very easy to find a replacement.
But if we were all a bunch of Haskell experts, it’d be a very different story.
For me, it’s no different than to use semicolons or not to use semicolons.
You encourage the play-it-safe way. An in that case “don’t use`class` is a very good advice” for non-experts.
However people may not fully appreciate why using class is so harmful. They just don’t use classes because they are told it’s best not to.
However I’m more like a learn-it-the-hard-way kind of guy.
A lot of my projects became unmaintainable. I stopped maintaining it and move on to the next one. I failed numerous times and I learned to prefer pure functions and compositions.
And that’d be the way I’d encourage people to learn a programming language. Use them. Make stuff with them. Stumble and fall, fail hard, learn a lesson, and move on.
If, say, in a job interview, I ask the candidate about their opinion on class inheritance, and they say they don’t do it because some wise people told them not to, I can’t tell much from the answer, except that they read stuff.
But if they starts explaining how inheritance gave them a bad, bad time in their prior work, I know I can stop probing them.
Use classes the correct way for the correct reason, and you can be more expressive and productive.
Now, let’s see how you can use class and do nice things.
The Mouse Factory
You don’t need to put the mouse factory as a static method. You can put it in an entirely different repo, and have people `require()` it and use it.
Just use `new Animal` instead of `Object.create(animal)` and you’re done.
I learned not to put too many stuff in the constructor, and prefer factory functions instead.
A constructor function is not a replacement for a factory function.
Given this, the `new` keyword is no longer a problem. Use `new` where you’d normally use `Object.create`. Use a factory otherwise.
Here’s an example:
A Collection of Musical Notes
I wrote a library that deals with musical scores. There is a popular file format for that and I based my library on top of it, but I learned not to put the logic of parsing that file into the constructor.
A constructor should do just the minimum work of constructing a valid object, given the parameters.
With that in mind, your constructor can be very simple:
And then you can safely export that constructor.
Now I want to support loading from a GarageBand file, but I don’t want to clutter my MusicalScore with code that handles every single file format.
So I create another repository for working with GarageBand files.
Method delegation and coordination
You also made a valid point that `obj.on()` is more convenient. In that case, I just write the forwarding code explicitly.
By looking at this class, it is very clear what methods will be available to the user.
Now back to my musical example. Actually, I’m developing a web-based music game in my spare time. A music game requires a lot of coordination between the screen, the timer, and the music, and each part has its own representation of time.
For the screen, it’s pixels (where to display notes). For the game, it’s the number of seconds since the I start playing. For the musical score, it’s the number of beats.
So I wrote different classes to deal with each aspect.
In the composing class, I delegated each method to respective objects, so it becomes very easy to use:
Not every method in every composed objects are used. So I only expose the ones that are actually used.
The above may look a bit verbose and unnecessary, but it gets more beautiful when, in addition to delegation, there’s coordination:
It’s also very predictable because there are no mutable states involved — the musical score itself is immutable. Every mutable thing (such as your current score) are encapsulated in its own mutable classes.
When multiple objects collaborate in a simple and predictable way, that’s some good OOP.
Unfortunately, not many people have coded enough (or read enough) to gain this level of experience when they graduate. Not many people cared enough to challenge every dogma they see.
Playing with fire
But in my opinion, this is not the only way.
With a real expert in your team, with your team members care about code as a craft, with everyone wanting to become a better programmer every day, and with a careful code review and coaching, I believe we can all become ninjas.