This is the part-II in the continuation of the “Crushing Design Patterns” series (Part-I) where we debunk the idea of having to use the design patterns to design the system.
They are either just so natural that we have been using them unconsciously or there are better ways in which we don’t have to focus on ceremoniously writing them.
Here we go.
1. Builder Pattern:
The intent of the Builder design pattern is to separate the construction of a complex object from its representation.
Meaning in plain english: A way to create objects with a flexibility to use any number of parameters for its creation.
Question: what is wrong with the constructors?
Counter question: How many would you make? To answer that, I’ll have show off my high school mathematics:
If there are n optional parameters/fields, there will be 2^n possible combinations of them, that means we’ll have to create those many constructors.
Turns out it will be 64 for our 6 optional veggies. Oh God, Java will make me die unhealthy!
Here comes the Builder pattern for our rescue,
But hey, this is 2019 and remember, we don’t need to do it like this! Behold, Java has a saviour now, lombok! Let’s rewrite our lombok-y Sandwich class:
That is it! Heck, we didn’t even need write the getters and setters ourselves, lombok does it all for you ! How do we use it, if you ask. Simple:
Pro Tip: If you are a Java dev, you should definitely check out the Project Lombok, it offer dozens of cool modern features with simple annotations. All the lombok annotated code simply de-sugars at the compile time into the ugly Java we don’t wanna write!
Verdict: Builder Pattern is unnecessary in Java due to lombok! Some languages provide features like named and default arguments (Scala, Clojure, Python etc) making the object building easier.
2. Iterator Pattern:
The iterator pattern is a design pattern in which an iterator is used to traverse a container and access the container’s elements. The iterator pattern decouples algorithms from containers.
Meaning: It’s a pattern where an abstract container providing a next() method and in some cases hasNext() method as well.
At this point, if you are a programmer, your brain should be screaming right now. We already have those in every mainstream programming language!!.
- Java has Iterator<T> interface.
- Python objects provides __next__() method.
- Scala and other functional languages provide sequences and lazy sequences.
3. Command Pattern:
Here, object is used to encapsulate all information needed to perform an action or trigger an event at a later time.
Meaning: Every command implementation does what it is meant for!
You got the point right! Now, I want to you to pay attention and see that “we only care about the encapsulated execute() method”. So, in a nutshell, the Command pattern is just a function/method!
This becomes very intuitive with Java-8 lambdas, we don’t need to create LoginCommand and LogoutCommand wrapper implementations of the Command interface:
BTW, this is very easy in functional languages, let’s take Scala for example:
4. Strategy Pattern:
Strategy pattern (also known as the policy pattern) enables selecting an algorithm at runtime. Instead of implementing a single algorithm directly, code receives run-time instructions as to which in a family of algorithms to use.
Meaning: Injecting the algorithm/strategy for some computation at runtime.
For example, suppose we are running a country named LaLaLand and are running out of cash. What would be the best way to fill up our stash?
We start making a taxing strategy, let’s tax a quarter of the income of everybody.
# quarter-taxing-strategy:tax = user.income / 4
But , when we think about the good people running a kitten orphanage NGO, we feel bad for them. Also, we want to tax more the black money holders now. We need more strategies!
# non-profit-taxing-strategy:tax = 0# half-taxing-strategy:tax = user.income / 2
Here, the algorithm is the taxing strategy to compute the taxes from users. More strategies mean, more algorithms (functions). Let’s start with good old Java way:
For every new strategy, we need to create another implementation class! Usage:
Smart ones among you might already have guessed that we don’t need the wrapper implementation classes, we just need functions as strategies and pass them around arguments!
Let’s do it in functional way (no need to create multiple implementations of Strategy interface):
In functional languages (Scala):
We have discussed 4 pattern in this post: Builder, Iterator, Command and Strategy. Each one can be used without writing any pattern, simply by just intuitively using language features (even in Java). Each pattern has been right in front of us hiding in the language!
- Builder — Lombok’s Builder annotation in Java, named and default parameters in other languages (Scala, Clojure, Python etc).
- Iterator — Readily available interfaces/abstractions in many languages.
- Command — Just pure functions.
- Strategy — passing functions as strategy at runtime.