让构造函数在封装参数之前对其进行预处理的指南

76 阅读1分钟

让构造函数在封装参数之前对其进行预处理似乎是不好的做法。 然而,很多时候,我们有必要这样做:对作为参数提供的对象进行一些操作,然后才将它们分配给构造对象的属性。为此,我建议使用预构造器,它可以是方法或独立的对象。

Huge in France (2019) by Gad Elmaleh et al.

Gad Elmaleh等人的《Huge in France》(2019)。

说,这是你的代码:

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

唯一的构造函数期望有一个标题列表,它被封装为this.titles ,以便将来使用。通过JDK的装饰器,它还可以防止任何意外的修改。unmodifiableList到目前为止,一切都很好。现在,我们想让我们的类更聪明一点,让它不仅接受List ,而且接受一个字符串数组。

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;
  }
}

这段代码有什么问题?那些读过我早期关于OOP的博文的人,最清楚答案。首先,有两个主构造函数,这是另一种不好的做法。 其次,在第二个构造函数中有代码,这也是一个坏主意

下面是我通常重构这段代码的方法,以解决上述两个问题:

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;
  }
}

我把这个新的静态方法toList() ,称为预构造器:它只在构建对象的时候使用,而且只从第二构造器中使用。

一个更好的设计方法是建立一个新的类ToList ,它将做同样的事情,但以一种更加声明性和懒惰的方式:

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 来自Cactoos的预函数就是这样一个完美的例子。