This post has been more than 4 years since it was last updated.
A Word About Dynamic
In dynamically typed languages types are checked during runtime, which means that the language does no checking to ensure that the objects being passed around have any particular class ancestry. The only thing that matters is that an object actually respond to the message that its clients want to send. Object Oriented applications are made up of objects and how they interact. The way for thoses objects to talk with each other is done by sending a message. As you can see latter message expectation turn out to be the core concept of object oriented design in dynamically typed language. In this post we'll be using Ruby for code example, but it can be apply to other dynamic languages as well.
When in Rome, do as the Romans do
People who are used to programming in statically typed languages often wonder how all of this lack of type checking possibly work and will certainly lead to disaster, with programs constantly crashing as they try to make number 23 to connect to a database. All of this fear in using objects result in the following code.
def empty?(s) if s.is_a?(String) s == '' elsif s.is_a?(Array) s.size == 0 end end
The problem with the following code is that it tends to couple your system together much more tightly than necessary. It just doesn't go well with the easy nature of dynamic language. Now considering the new version, Instead of checking for the type explicitly what we are interested in is what can the object do and in this case it respond to size message. It works with String, Array or Range and in fact it will work with any object that respond to size. This kind of coding style is what we called programming to an interface and is one of the five most well known principles of object-oriented design SOLID.
def empty?(s) s.size == 0 end
When working with dynamically typed language like Ruby, people usually don't check for the type of an object. If you found yourself writing this kind of code it is a bad sign that there is something wrong with your design and you might want to consider thinking thing over.
All About Finding Your Ducks
As you can see in the previous example, we just expect s to respond to size message regardless of whatever its class is. This "I am what I am" approach to typing has been called duck typing. That is "If it looks like a duck and quacks like a duck then it is a duck.". Your one true path to become proficient in OO design in dynamic language is all about finding the duck. The key is to recognize that empty? serves a single purpose, its arguments arrive wishing to collaborate to accomplish a single goal. They arrive here for the same reason and that reason is unrelated to the argument's underlying class. Instead of getting bound by the fact that you know what the argument's class does, think instead about what empty? needs from its argument. The common coding pattern indicate the presence of a hidden duck is follow.
- Case statements that switch on class
- kind_of? and is_a?
Programmers who understand that they should not depend on class names but who haven’t yet sync into the uses of duck types are tempted to replace kind_of? with responds_to?; while this may reduce the number of dependencies, it's still bound to class.
Trust in Your Ducks
Use of kind_of?, is_a?, respond_to? and case to switch on class effectively saying "I know who you are and because of that I know what you do" which exposes a lack of trust between your objects collaboration. It introduces depedencies and make code difficult to change. It indicates that you are missing an object whose public interface has yet to be discovered and that object is your duck. Object Oriented Applications are built on trust; the trust between your object collaboration. It is your job to find a duck type one with consistent public interface, implement that interface and trust thoses implementers to do their job correctly.
Conquering a Fear of Duck Typing
While using duck typing can enable you to create a flexible and extentable application, it is hard to read and understand the code that rely on abstraction compare to code that rely on concretion. In this case documenting your duck can be help. Good unit tests can also serve as good documentation as well as help you make up for type checking that static language offfers.
All Rights Reserved