Если вы используете GitHub и еще не отправили коммит, GitHub Desktop легко решает эту проблему:
Я думаю, что лучше всего иметь единственный первичный конструктор , на который ссылаются перегруженные конструкторы, вызывая this ()
с соответствующими значениями параметров по умолчанию. . Причина этого в том, что он делает более ясным, что такое сконструированное состояние объекта - на самом деле вы можете думать о первичном конструкторе как о единственном реальном конструкторе , а остальные просто делегировать ему
Одним из примеров этого может быть JTable
- первичный конструктор принимает TableModel
(плюс модели столбцов и выбора), а другие конструкторы вызывают этот первичный конструктор.
] Для подклассов , в которых суперкласс уже имеет перегруженные конструкторы , я бы склонен предположить, что разумно рассматривать любой из родительских классов » s как первичный и считаю совершенно законным отсутствие единственного первичного конструктора. Например, при расширении исключения Exception
я часто предоставляю 3 конструктора, один из которых принимает только сообщение String
, один принимает причину Throwable
, а другой принимает оба. Каждый из этих конструкторов напрямую вызывает super
.
Если у вас очень сложный класс с множеством опций, из которых допустимы только некоторые комбинации, рассмотрите возможность использования Builder. Работает очень хорошо как с точки зрения кода, так и с логической точки зрения.
Builder - это вложенный класс с методами, предназначенными только для установки полей, а конструктор ComplexClass принимает только такой Builder в качестве аргумента.
Изменить: конструктор ComplexClass может гарантировать, что состояние в Builder является допустимым. Это очень сложно сделать, если вы просто используете сеттеры для ComplexClass.
Это действительно зависит от типа классов, поскольку не все классы созданы равными.
В качестве общего правила я бы предложил 2 варианта:
Хотя нет никаких «официальных рекомендаций», я следую принципу KISS и DRY. Сделайте перегруженные конструкторы настолько простыми, насколько это возможно, и самый простой способ - они будут вызывать только this (...). Таким образом, вам нужно только один раз проверить и обработать параметры.
public class Simple {
public Simple() {
this(null);
}
public Simple(Resource r) {
this(r, null);
}
public Simple(Resource r1, Resource r2) {
// Guard statements, initialize resources or throw exceptions if
// the resources are wrong
if (r1 == null) {
r1 = new Resource();
}
if (r2 == null) {
r2 = new Resource();
}
// do whatever with resources
}
}
С точки зрения модульного тестирования, будет легко протестировать класс, поскольку вы можете вложить в него ресурсы. Если у класса много ресурсов (или соавторов, как его называют некоторые специалисты по OO), рассмотрите одну из этих двух вещей:
public class SimpleParams {
Resource r1;
Resource r2;
// Imagine there are setters and getters here but I'm too lazy
// to write it out. you can make it the parameter class
// "immutable" if you don't have setters and only set the
// resources through the SimpleParams constructor
}
Конструктору в Simple нужно только либо разделить SimpleParams
параметр:
public Simple(SimpleParams params) {
this(params.getR1(), params.getR2());
}
… или сделайте SimpleParams
атрибутом:
public Simple(Resource r1, Resource r2) {
this(new SimpleParams(r1, r2));
}
public Simple(SimpleParams params) {
this.params = params;
}
Создайте фабричный класс, который инициализирует ресурсы для вас, что является благоприятным, если инициализация ресурсов является немного сложно:
public interface ResourceFactory {
public Resource createR1();
public Resource createR2();
}
Конструктор затем создается так же, как и с классом параметров:
public Simple(ResourceFactory factory) {
this(factory.createR1(), factory.createR2());
}
Да ... вы можете смешивать и сочетать оба способа в зависимости от того, что для вас проще в то время. Классы параметров и простые фабричные классы - это почти одно и то же, учитывая класс Simple
, что они используются одинаково.