Java-конструктор "неизменяемого класса" со многими полями со значениями по умолчанию?
У меня есть класс JAVA с большим количеством полей. Они должны в основном устанавливаться на этапе конструктора и никогда не меняться. Семантически класс тогда является неизменным.
public class A{
final int a;
final short b;
final double e;
final String f;
final String g;
//and more
}
Проблема в том, что обычно эти поля имеют значения по умолчанию, поэтому я не хочу всегда обременять пользователя конструктором со всеми из них. В большинстве случаев им просто нужно установить пару из них. Есть несколько способов решить эту проблему:
- Мне понадобится много конструктора с другой подписью.
- Создайте кучу заданного метода этого поля и установите только те значения, которые не являются значениями по умолчанию. Но это как-то указывает на другую семантику, отличную от неизменной.
- Создайте новый класс параметров, который изменен и использует этот класс как конструктор.
Ничто из этого не является полностью удовлетворительным. Есть ли другой подход? Благодарю.
Один из способов
Ответы
Ответ 1
Я бы использовал комбинацию класса параметров и свободного API-интерфейса для создания параметра:
public class A {
private final int a;
private final short b;
private final double e;
private final String g;
public static class Aparam {
private int a = 1;
private short b = 2;
private double e = 3.141593;
private String g = "NONE";
public Aparam a(int a) {
this.a = a;
return this;
}
public Aparam b(short b) {
this.b = b;
return this;
}
public Aparam e(double e) {
this.e = e;
return this;
}
public Aparam g(String g) {
this.g = g;
return this;
}
public A build() {
return new A(this);
}
}
public static Aparam a(int a) {
return new Aparam().a(a);
}
public static Aparam b(short b) {
return new Aparam().b(b);
}
public static Aparam e(double e) {
return new Aparam().e(e);
}
public static Aparam g(String g) {
return new Aparam().g(g);
}
public static A build() {
return new Aparam().build();
}
private A(Aparam p) {
this.a = p.a;
this.b = p.b;
this.e = p.e;
this.g = p.g;
}
@Override public String toString() {
return "{a=" + a + ",b=" + b + ",e=" + e + ",g=" + g + "}";
}
}
Затем создайте экземпляры A следующим образом:
A a1 = A.build();
A a2 = A.a(7).e(17.5).build();
A a3 = A.b((short)42).e(2.218282).g("fluent").build();
Класс A неизменен, параметры являются необязательными, а интерфейс свободен.
Ответ 2
Две вещи, которые вы можете сделать:
- Многие перегрузки конструктора
- Используйте объект .
Ответ 3
Это только полусерьезное предложение, но мы можем изменить ответ mikera, чтобы быть типичным.
Скажем, что у нас есть:
public class A {
private final String foo;
private final int bar;
private final Date baz;
}
Тогда напишем:
public abstract class AProperty<T> {
public static final AProperty<String> FOO = new AProperty<String>(String.class) {};
public static final AProperty<Integer> BAR = new AProperty<Integer>(Integer.class) {};
public static final AProperty<Date> BAZ = new AProperty<Date>(Date.class) {};
public final Class<T> propertyClass;
private AProperty(Class<T> propertyClass) {
this.propertyClass = propertyClass;
}
}
и
public class APropertyMap {
private final Map<AProperty<?>, Object> properties = new HashMap<AProperty<?>, Object>();
public <T> void put(AProperty<T> property, T value) {
properties.put(property, value);
}
public <T> T get(AProperty<T> property) {
return property.propertyClass.cast(properties.get(property));
}
}
Aficionados из усовершенствованных шаблонов дизайна и/или неясных трюков Java распознает это как разнородный разнородный контейнер. Просто благодарю, что я не использовал getGenericSuperclass()
.
Затем вернитесь в целевой класс:
public A(APropertyMap properties) {
foo = properties.get(AProperty.FOO);
bar = properties.get(AProperty.BAR);
baz = properties.get(AProperty.BAZ);
}
Все это используется следующим образом:
APropertyMap properties = new APropertyMap();
properties.put(AProperty.FOO, "skidoo");
properties.put(AProperty.BAR, 23);
A a = new A(properties);
Только для lulz мы можем даже дать карте свободный интерфейс:
public <T> APropertyMap with(AProperty<T> property, T value) {
put(property, value);
return this;
}
Что позволяет абонентам писать:
A a = new A(new APropertyMap()
.with(AProperty.FOO, "skidoo")
.with(AProperty.BAR, 23));
Есть много небольших улучшений, которые вы могли бы сделать. Типы в AProperty
можно обрабатывать более элегантно. APropertyMap
может иметь статический factory вместо конструктора, позволяющий использовать более свободный стиль кода, если вы занимаетесь такими вещами. APropertyMap
может вырастить метод build
, который вызывает конструктор A
, по существу превращая его в конструктор.
Вы также можете сделать некоторые из этих объектов более универсальными. AProperty
и APropertyMap
могли иметь общие базовые классы, которые выполняли функциональные биты с очень простыми A
-специфическими подклассами.
Если вы чувствуете себя особенно предпринимателем, а ваши объекты домена были объектами JPA2, то вы можете использовать атрибуты метамодели в качестве объектов свойств. Это приводит к тому, что map/builder делает немного больше работы, но это все еще довольно просто; У меня есть общий строитель, работающий в 45 строках, с подклассом на объект, содержащий один однострочный метод.
Ответ 4
Один интересный вариант - создать конструктор, который принимает вход Map<String,Object>
, который содержит значения, которые пользователь хочет указать.
Конструктор может использовать значение, указанное на карте, если оно присутствует, или значение по умолчанию в противном случае.
EDIT:
Я думаю, что случайные downvoters полностью пропустили точку - это не всегда будет лучшим выбором, но это полезный метод, который имеет несколько преимуществ:
- Это кратким и избегает необходимости создавать отдельные классы конструкторов/строителей
- Это позволяет легко программировать конструкцию наборов параметров (например, если вы создаете объекты из разобранного DSL)
- Это техника, которая часто используется и доказана для работы на динамических языках. Вам просто нужно написать приличные тесты (которые вы должны делать в любом случае!)
Ответ 5
Наличие многих полей может быть признаком того, что один класс делает слишком много.
Возможно, вы можете разбить класс на несколько неизменяемых классов и передать экземпляры этих классов конструкторам других классов. Это ограничило бы число конструкторов.