Looks Like a Duck, Quacks Like a Duck
If every object trusts all others to be what it expects at any given moment, and any object can be any kind of things, the design possibilities are infinite. These possibilities can be used to create flexible designs that are marvels of structured creativity or, alternatively, to construct terrifying designs that are incomprehensibly chaotic.
- Sandi Metz, Practical Object-Oriented Design in Ruby, Chapter 5
As you are reading this, please accompany my blog post with this song.
Duck types are interfaces that are not associated with classes, thereby reducing dependencies on classes and moving toward dependencies on messages. A type refers to the type of data (e.g. string, boolean, array, object); knowing the data’s category gives an application certain expectations about how a variable’s contents will behave. Duck typing allows communication with various methods and is one way of achieving polymorphism, which refers to the ability of various objects to respond to the same message. We’ll explore how this fits into tic-tac-toe in an example below.
Consequences of Duck Typing
Duck-typed code is more abstract, which is initially harder to grasp, but easier to extend and amend. Metz states that being able to “tolerate ambiguity about the class of an object is the hallmark of a confident designer” #codergoals. Being able to recognize the need for a duck type, and abstracting it, is critical. The ability to discern when to use one seems to be shaped by timing and experience: the longer you spend on a single application, the more familiar you are with its architecture. Likewise, on a macro-level, perhaps the more applications you develop, the more aware you are of the need for certain patterns and design decisions.
There are several ways to determine the need for duck typing, or recognize a duck type that already exists (but is not apparent) within the code. Duck types will typically respond to difference in classes; one way to identify duck typing, whether explicit or not, is in the use of case statements and messages that check and respond to classes (particularly found in methods like `is_a?` or `kind_of?`). Duck typing is found in languages that are not strongly typed, such as Ruby; however, it is argued that this technique is not necessarily tied to dynamic languages.
The Case for Dynamic Typing
I was introduced to programming incidentally, having sat in on a few Java classes for a mobile development program in Queens. I loved the feeling of successfully executing a program through a few lines of code. And then I learned about Ruby: no curly braces, no type declaration, no compiling, and no verbosity. Its English-like syntax and simplicity won me over. And it became my first language into the wonderful world of coding.
While it seems that there are arguments for statically typed languages, I will outline the case for dynamic typing in an effort to demonstrate the advantages of duck typing. Static languages have a compile/make cycle, which some argue will save programmers from accidental type errors; Metz argues otherwise and claims that runtime failures will still occur. According to Metz, having the ability to cast a variable to a new type leaves a program vulnerable to errors–and that both static and dynamic type systems will inevitably have errors. Tests are the best guardian against type inconsistencies. Metaprogramming, which is the act of writing code that writes code, is also easier in languages like Ruby. All in all, dynamic typing forgoes type checking, which arguably has high cost but little value, and is much more efficient. (Of course, this last statement is up for debate).
Example of Duck Typing
Here is a snippet of pseudo-code that exemplifies duck typing. In this scenario, both `ComputerPlayer` and `HumanPlayer` are ducks that respond to `request_move`. Both classes will have methods with a shared name that have completely different operations.
class Game
def request_moves(players)
# During the lifecycle of the game, switch between players
# and invoke `request_move`.
end
endclass ComputerPlayer
def request_move(game)
# ...
end
endclass HumanPlayer
def request_move(game)
# ...
end
end
Clearly, this code block will need to be more fleshed out, but the basic premise of duck typing is apparent. This example also reinforces the need for objects to define their roles and trust other objects to do their part, as previously stated. Along with single-responsibility classes and dependency injection, Learning about techniques while concurrently developing a game is an opportunity to apply some of these lessons. Recognizing and implementing design patterns, all the while being mindful and critical of the lessons at hand, is an extremely unique learning opportunity. I feel fortunate to be able to be given the time and resources to learn about software development on a deeper level.
With that said, it’s a wrap (for this week)! Thanks for having me, 8th Light. Week one down and I have learned so much already.