Let’s Build!

Anar Khalilov
Dev Genius
Published in
5 min readJan 22, 2021

--

Photo by 贝莉儿 DANIST on Unsplash

Consider you own a website where people can buy/sell cars. Needless to say, you must have a search page where users can specify numerous search criteria. Similar to the page below from cars.com:

How do you model this requirement?

Well, the first idea that comes to mind is to create and fill a search criteria instance and pass it to a method that will return results according to this criteria. Something like this:

Some issues with this approach:

  1. The most obvious issue here is the number of arguments. According to the research made by Susan Weinschenk PhD, limiting the information you give to people to four items is nearly optimal. Anything more than that and we, as humans, tend to forget or at least get stressed/feel uncomfortable about it. This and more in her brilliant book 100 Things Every Designer Needs to Know About People which I highly recommend to everyone who develops software. After all, we — software developers are designers.
  2. Another issue with methods that have a long list of arguments, is that you may easily lose track of which value is what. In the example above, by the time you come to the 8th parameter and see zero there, you may easily get confused as to what this value is. Is that a minimum mileage, a minimum price or even a binary value specifying whether the car has a sunroof or not. If you as a developer look at the code and get confused, it means that it was not designed properly. A well designed product is nonambiguous in the sense that it is self-explanatory or intuitive, leaves a pleasing experience and is simply beautiful.
  3. What if a user specifies only body style and wants to list all cars with Sedan body style. What to we do? We just fill the first parameter and set everything to its default value like below:

I am sure most of you will agree that it is quite ugly. A bunch of code with no added value and with a lot of question marks. A code with no added value is not only useless, but also harmful; it needs to perish.

I am not going to explain why we shouldn’t create and then call a new constructor for this case; I think it is quite obvious why it is not such a brilliant idea.

4. And last, but not least, I will leave you with the famous quote from Robert C. Martin’s must read book Clean Code:

  • The ideal number of arguments for a function is zero (niladic).
  • Next comes one (monadic) followed closely by two (dyadic).
  • Three arguments (triadic) should be avoided where possible.
  • More than three (polyadic) requires very special justification — and then shouldn’t be used anyway.

Even though at first he seems to emphasize the number of parameters, maybe the more important issue here is about software testing. If you have written any unit tests in your life or have a slight idea about it, you must be knowing that each new introduced parameter of a method requires you to create more and more test cases as the total number of parameters increase. The more variables a method has, the more complex its business logic is inclined to be.

So, how do we solve all these issues while at the same time keeping everything clean and simple? Well, as with every decision there are trade-offs. Changing the code will take our valuable time and energy but in the long run it will be well worth it. Both in terms of the product you are developing and your personal career/improvement.

Now compare these:

The latter looks like it was written by someone who cared. Cared about the product (s)he was developing. Cared about the company (s)he was working for. Cared about teammates. Cared about her/his own reputation. And finally cared about the people whom (s)he will never know — fellow programmers that will work on the code long after (s)he is gone.

Notice that we chain methods one after another in a fluent manner (you can Google “method chaining”, “fluent interface”, “fluent API” if you are curious). I am sure you already know this technique as method syntax in LINQ.

I can hear my lazy inner voice complaining: why bother with all extra code when we can simply set only the needed properties?

Well, fair enough. :) I recommend you not to ignore your lazy inner voice simply because you may miss out on some shortcuts and it may even make you successful. Sometimes the best solution is the most straightforward one.

I am afraid to tell you however, this time it isn’t. Proposed lazy solution brings issues like:

  • We need to avoid exposing all fields as public settable properties whenever possible and prefer constructors with read-only properties.
  • All input data is evil and needs to be validated. If we don’t have any restriction/validation, the object may transform to an invalid state. To give an example, assume there are two properties, namely MinYear and MaxYear. When we set MinYear to 2019 and MaxYear to 2018, the object switches to an invalid state and it becomes pointless to search with this search criteria instance. Of course you could add some validations in the method that actually makes the search, but I firmly believe the object shouldn’t be in the wrong state in the first place.

As you may have noticed from the Builder pattern code sample, there is a Build() method at the end. This method is the final link in the chain of methods and is a perfect place to add some rules like:

  • MinYear cannot be greater than MaxYear.
  • A coupê must always have exactly two doors.
  • The number of cylinders a car may have must be between 1 and 12 .

I don’t want to choke you with very long code blocks so I will leave a link where you can download all source code here.

To sum things up, we use the Builder pattern when we need to create highly configurable objects in a simple, clean and sustainable way.

--

--