How Behavior Parameterization Helps You Write Adaptable Code for Changing Requirements

As software engineers, no matter what we do, user requirements will always change. The real challenge is writing code that adapts without becoming messy.

For example, imagine an application designed to help a librarian manage inventory. At first, the librarian may want a feature to find all books by the same genre. But the next day, he might say, "I also want to find all books priced under $20." Two days later, the librarian comes back with another request: "It would be nice to find all books by the same genre that are also priced under $20."

The question is: how can you cope with these constantly changing requirements? Ideally, you’d like to implement new features quickly, easily, and in a maintainable way.

Let’s walk through an example and progressively improve it to make our code more flexible and maintainable.

First iteration: filtering Horror books

In the context of a library-inventory application, you have to implement a function to filter horror books from a list. Sounds easy, right?

Article content

At first glance, this looks fine. But imagine now the librarian changes his mind and wants to also filter science fiction books. What can you do? A naive solution could be to duplicate your method and rename it filterSciFiBooks and change the if condition. However, this approach doesn't cope well with the changes.

Second iteration: parameterize the genre

To parameterize the genre and be more flexible to changes, what you could do is add a parameter to your method:

Article content

Seems simple, right? Let’s complicate the matter a bit. The librarian comes back to you and asks you to filter books by price (less than $20).

Article content

This is somewhat disappointing because it breaks the DRY (don’t repeat yourself) principle of software engineering, because you have to duplicate most of the implementation for traversing the inventory and applying the filtering criteria on each book.

Third iteration: filtering with every possible attribute

An ugly attempt to merge all attributes might be as follows:

Article content

This solution is extremely bad. First, the client code looks terrible. What do true and false mean? In addition, this solution doesn’t cope well with changing requirements. What if the librarian asks to filter with more attributes?

Fourth iteration: use Behavior parameterization

You saw in the previous section that you need a better way than adding lots of parameters

to cope with changing requirements. Let’s find out a better level of abstraction: behavior parameterization. The core idea Instead of writing a method that does one fixed thing, you make it flexible by letting the caller decide how it should behave.

Article content

A Predicate is a simple "functional interface" that takes an object and returns true or false. It is used to define custom filtering logic.

The interface contains one abstract method, test(T t), which evaluates the given input against a condition.

You have to admit this code looks a lot cleaner than our previous attempts!

Conclusion

Behavior parameterization allows you to pass behavior (logic) as a parameter instead of hardcoding it inside methods.

This makes your code more flexible, reusable, and easier to extend when requirements change.

To view or add a comment, sign in

More articles by Amine Asli

Others also viewed

Explore content categories