Наследование Java, используя шаблон построителя

У меня есть 3 класса:

  • Ошибка
  • ShellError
  • WebError

где

ShellError extends Error 

и

WebError extends Error

В ShellError есть поля, некоторые из которых являются необязательными, а другие - обязательными. Я создаю объект следующим образом:

shellError = new ShellError.Builder().setFile(filePattern)
.setHost(host).setPath(path).setSource(file.isSource())
.setJobName(p.getJobName()).build();

Так как ShellError продолжается Error, я также:

shellError.setDescription(msg.toString());
shellError.setExceptionClass("MyEvilException");
shellError.setExceptionMessage("Some clever error message");
shellError.setStacktrace(stack);

Итак... зачем беспокоиться с Builder? Мне нравится тот факт, что my build(), среди прочего, удобно проверяет, что все поля установлены соответствующим образом и т.д.

Мне бы это понравилось, если бы я мог.. build() ShellError и добавить к нему поля из класса Error.

Что я делал.

  • Вопрос в следующем:

Есть ли лучший способ, или это имеет смысл, что я сделал?

- EDIT

Я обновил Builder(), чтобы принять некоторые параметры, которые ранее были в классе Error. Теперь у меня

shellError = new ShellError.Builder(exception, "Some description").setFile(filePattern).setHost(host)
.setPath(path).setSource(file.isSource()).
setJobName(p.getJobName()).build();

Что ты скажешь? лучше? Хуже?

Ответ 1

На основе функций, на которые вы ссылались, это явно не стандартный класс java.lang.Error. Как правило, строители используются для обеспечения возможности простого создания неизменяемого объекта или для обеспечения функциональности, аналогичной "именованным параметрам" в случаях, когда имеется множество параметров конфигурации/построения.

В этом конкретном случае было бы более разумным, если бы класс Error был неизменным после построения, и если бы эти дополнительные функции setter были на строителе, а не в классе ошибок. Я не знаю, какой контроль у вас есть над любым из этих классов, но если вы можете их изменить, я бы предложил сначала сделать поддержку строителей одними и теми же сеттерами, чтобы вы могли выполнить всю конфигурацию в построителе. Затем, если это возможно, вы можете попробовать удалить эти методы setter и вместо этого разрешить их настройку из конструктора. Если у вас нет никакого контроля над ними, вы можете потенциально расширить класс строителя другим, который поддерживает эти дополнительные методы.

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

Ответ 3

Как уже было сказано, шаблон компоновщика не является тем, что может органично вписаться в существующую политику инициализации объектов Java. Существует несколько подходов для достижения требуемого результата. Хотя, конечно, всегда лучше избегать любых неоднозначных практик, это не всегда возможно. Мой взлом основан на API отражения Java с дженериками:

abstract public class AbstractClass {

    public static class Builder {

        public <T extends AbstractClass> T build(Class<T> implementingClass) {
            try {
                Constructor<T> constructor = implementingClass
                        .getConstructor(new Class[]{Builder.class});
                return constructor.newInstance(this);
            } catch (NoSuchMethodException e) {
                // TODO handle the exception
            } catch (InvocationTargetException | InstantiationException |
                     IllegalAccessException  e) {
                // TODO handle the exception
            }
        }
    }

    protected AbstractClass(Builder builder) {

    }
}

public class ImplementingClass extends AbstractClass {

    public ImplementingClass (Builder builder) {
        super(builder);
    }
}

Инициализация:

ImplementingClass instance = new AbstractClass.Builder()
                    .build(ImplementingClass.class);