A poet called James Whitcomb Riley once said:
“When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck.”
Much probably referring to the Mallard (like the one of the above picture) that acts just like a duck, but isn’t a duck!
Well, in programing language we use the term “Duck Typing” to describe the “use a certain behaviour of an object, but defer the check to the runtime”. Ok, still confusing, let me try to simplify this.
A simple logger
Let’s suppose we’ve built a logger function in Node, which receives a target to output all the logs. It will call the “log” method, acting as a simple facade, doing the log calls for you.
The logger is a composed function that acts like a factory, build the log caller using the given target instance (storing it inside it’s context).
So, let’s see what happens if we create an instance which have some of the expected behaviour from the console:
And then, we just throw an instance of it inside our logger…
…and boom! We have “Hello world!” written on the log.txt file.
And here is the trick! The FileLogger don’t have anything more than the “log” behaviour from the console object. You can see that no “extends” were used, or any prototype shared too.
Ok, we still had to call the constructor and the close methods, but the good thing is, you can expose the “log” function to the entire application, without worrying what is being inside.
Now, thing on the possibilities, you can implement the info, warn, error and debug…
…and then, create a lot of different “targets”, other than “file”, like “websocket”, “printer”, “led terminal” or whatever you want. Use the strategy pattern to choose the right target for the right situation and you’re going to have a nice and powered log system for your app. Note again, the targets doesn’t share anything but the behaviour!
For sure a really simple example, but it was just to let the things more clear for you. Well, there’s another day-to-day use that I’ll use as example (and you probably already saw/done this before): Writing tests and mocking behaviours.
Tests and dependency injection
When writing tests, I often create/modify functions and methods to work using dependency injection. Why?
Why the function doSomethingGreat needs to know how to build a SomeDep instance? This attaches a unecessary logic that isn’t it responsibility (check the SOLID principles). When we remove the dependency instantiation from inside, it doesn’t need to know where this instance comes from, but just how to use it. So instead of doing the above code, I normally do this:
When I mean dependency injection, I wasn’t talking about module resolvers, instances managers, and all that fancy thing. I’m just talking about something else having the responsibility to instantiate the dependencies and giving it to the one who requires. But let’s abstract that for now, the good thing is now we’re able to do this:
Look at the alwaysGreat and neverGreat, they’re two literal objects which have the doSomething behaviour, and only that. As literal objects, they don’t have ‘class’/constructors like myDep instance have SomeDep. They also don’t extend class/share prototype with SomeDep, or even implements all the methods from it.
Now think how more complex would be to test doSomethingGreat function if we kept the first example. Of course all the test frameworks provide us with ways to mock the libraries, but if you need to change the used lib. you’re going to rewrite your tests.
When you architect the code for dependency injection, you can just use the power of Duck Typing to “control” the function behaviour, making all the tests more simple and readable and reducing the probability of rewriting the tests.
Functions, objects and methods which are highly generic can be designed with the duck typing in mind. This helps a lot when you do the tests and when refactoring is needed, but if it goes more and more specific, trying to build with this design can raise the code complexity, so don’t go creating adapters for everything just to use duck typing, remember each case is different, sometimes worth to do, sometimes don’t!
Thanks for reading this article! If you liked, give it some 👏 and show your support! Also, check the follow up of this article, React and Duck Typing, where I do more “visual” examples using React.