PHPnews.io

Prestructors

Written by Yegor Bugayenko / Original link on Aug. 4, 2021

Making constructors pre-process the arguments before encapsulating them seems to be bad practice. However, very often it’s necessary to do exactly that: perform some manipulations with the objects provided as arguments and only then assign them to the attributes of the constructed object. For this purpose I suggest using prestructors, which could be methods or stand-alone objects.

huge-in-france.jpg
Huge in France (2019) by Gad Elmaleh et al.

Say, this is your code:

import java.util.List;
import java.util.Collections;
class Books {
  private final List<String> titles;
  Books(List<String> list) {
    this.titles = Collections.unmodifiableList(list);
  }
}

The only constructor expects a list of titles, which is being encapsulated as this.titles for some future use. It’s also protected against any accidental modifications, through the JDK decorator at unmodifiableList. So far, so good. Now, we want to make our class a bit smarter and let it accept not only the List but an array of strings:

class Books {
  private List<String> titles;
  Books(List<String> list) {
    this.titles = Collections.unmodifiableList(list);
  }
  Books(String... array) {
    final List<String> list = new ArrayList<>(array.length);
    for (final String title : array) {
      list.add(title);
    }
    this.titles = list;
  }
}

What’s wrong with this code? Those of you who have read my earlier blog posts about OOP most definitely know the answer. First, there are two primary constructors, which is another bad practice. Second, there is code in the second constructor, which is also a bad idea.

Here is how I usually refactor this code, to solve both mentioned problems:

class Books {
  private List<String> titles;
  Books(List<String> list) {
    this.titles = Collections.unmodifiableList(list);
  }
  Books(String... array) {
    this(Books.toList(array));
  }
  private static List<String> toList(String... array) {
    final List<String> list = new ArrayList<>(array.length);
    for (final String title : array) {
      list.add(title);
    }
    return list;
  }
}

I call this new static method toList() a prestructor: it is used only at the moment of object construction and only from the secondary constructor.

An even better way to design it would be to make a new class ToList, which would do exactly the same, but in a more declarative and lazy way:

class Books {
  private List<String> titles;
  Books(List<String> list) {
    this.titles = Collections.unmodifiableList(list);
  }
  Books(String... array) {
    this(new ToList(array));
  }
}
class ToList<T> implements List<T> {
  private final T[] array;
  ToList(T... items) {
    this.array = items;
  }
  // All required methods of the List interface
}

ListOf from Cactoos is a perfect example of such a prestructor.

oop calevans yegor256

« Ko-Fi Membership Tiers are available for everyone! - How to Easily Protect your Code Base from Commented PHP Code »