Подтвердить что ты не робот

Java-конструктор "неизменяемого класса" со многими полями со значениями по умолчанию?

У меня есть класс JAVA с большим количеством полей. Они должны в основном устанавливаться на этапе конструктора и никогда не меняться. Семантически класс тогда является неизменным.

public class A{
    final int a;
    final short b;
    final double e;
    final String f;
    final String g;
    //and more
}

Проблема в том, что обычно эти поля имеют значения по умолчанию, поэтому я не хочу всегда обременять пользователя конструктором со всеми из них. В большинстве случаев им просто нужно установить пару из них. Есть несколько способов решить эту проблему:

  • Мне понадобится много конструктора с другой подписью.
  • Создайте кучу заданного метода этого поля и установите только те значения, которые не являются значениями по умолчанию. Но это как-то указывает на другую семантику, отличную от неизменной.
  • Создайте новый класс параметров, который изменен и использует этот класс как конструктор.

Ничто из этого не является полностью удовлетворительным. Есть ли другой подход? Благодарю. Один из способов

4b9b3361

Ответ 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

Наличие многих полей может быть признаком того, что один класс делает слишком много.

Возможно, вы можете разбить класс на несколько неизменяемых классов и передать экземпляры этих классов конструкторам других классов. Это ограничило бы число конструкторов.