Removing Code Duplication Using Loops and Collections
Operations
Something that you see over and over in code is doing the same operation again and again but on a different variable. This is something needed in programming, so what is wrong with it? This article is going to get you to think in terms of data instead of operations. Instead of performing operations and passing data to them, perform an operation on a collection of data. This article will use building a UI (User Interface) as an example, but this applies anywhere in code.
Thinking in Terms of Operations
You are given the following code:
// Java code
final var cancel = new Button();
final var save = new Button();
final var export = new Button();
// ... setup buttons ...
You are then given the task of adding each button to a layout. After you are finished doing so, the code would look something like the following:
// Java code
final var cancel = new Button();
final var save = new Button();
final var export = new Button();
// ... setup buttons ...
layout.add(cancel);
layout.add(save);
layout.add(export);
This example performs the operation of adding a button to the layout three times. This is what you would expect to see, right?
The problem with this code is it has code duplication. This is probably not what you are thinking when you are thinking of code duplication. In this example, only the data is changing with each operation. This code is really a collection of data that an operation needs to be performed on. It is common to not think like this because the variables that you are working with are not in a collection. If you were given the same task and the code had a collection of buttons, how would you think about it then?
Thinking in Terms of Data
If you change your mindset and think about the example above and think in terms of a collection of data, your code will become simpler. The previous code example is working with a collection of data, but there is no collection defined. If you are repeating the same operation over and over, that is an indication you should be using a loop. If you need to use a loop, you need a collection. The previous example could be written with the following:
// Java code
final var cancel = new Button();
final var save = new Button();
final var export = new Button();
// ... setup buttons ...
List.of(
cancel,
save,
export
)
.forEach(layout::add);
This may seem odd at first, but it actually adds quite a bit of clarity to your code, and there is no longer duplication.
If you were cleaning up objects in a test, you could write it with the following:
// Java code
@Test
public void test() {
// ... test
List.of(
testUser1,
testUser2,
testUser3
)
.forEach(this::cleanUp);
// ...
}
If you were building a UI and all of your components were in a collection, think of how simple operations can become. If you had to disable all components, you could do it with something like the following:
// Java code
components.forEach(Component::disable);
You could reset all of the fields in a form with something like the following:
// Java code
fields.forEach(Field::clear);
If you have a collection of components and need to disable the buttons. You could do it with the following:
// Java code
components.stream()
.filter(c -> c.isButton())
.forEach(Component::disable)
This is similar in the way that you think about writing a query for a database and then working with the result set.
Whatever your UI would need, by keeping each component in a collection, when you have to perform operations, it will automatically update those fields. If you were to add a new component and they weren't in a collection, you would have to add a line of code for each of these operations to handle the new component. This opens up the possibility of a bug because it was forgotten somewhere.
Some of these examples might make you think that you should always put your data in a collection; that isn't what is being said. Put data in a collection when you are performing the same operation more than once, and it is really the data changing.
Conclusion
When working with a collection of data that isn't in a collection, move into a collection. This allows you to do the same operation for the whole dataset, reduce potential bugs, and not duplicate code.