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

Запуск метода после конструктора любого производного класса

Скажем, у меня есть класс Java

abstract class Base {
    abstract void init();
    ...
}

и я знаю, что каждый производный класс должен будет вызвать init() после его создания. Я мог бы, конечно, просто вызвать его в конструкторах производных классов:

class Derived1 extends Base {
    Derived1() {
        ...
        init();
    }
}

class Derived2 extends Base {
    Derived2() {
        ...
        init();
    }
}

но это сильно нарушает принцип "не повторяй себя" (и будет много подклассов Base). Конечно, вызов init() не может войти в конструктор Base(), так как он будет выполнен слишком рано.

Любые идеи, как обойти эту проблему? Я был бы очень рад увидеть решение Scala.

ОБНОВЛЕНИЕ: Ниже приведен общий вариант метода метода Factory:

interface Maker<T extends Base> {
    T make();
}

class Base {
    ...
    static <T extends Base> T makeAndInit(Maker<T> maker) {
        T result = maker.make();
        result.init();
        return result;
    }
}

ОБНОВЛЕНИЕ 2: Этот вопрос в основном "как вы используете метод шаблонов для конструкторов"? И ответ, кажется, "Ты можешь, но это плохая идея". Поэтому я могу сделать Template Factory (Template Method + Abstract Factory).

4b9b3361

Ответ 1

Что происходит в init()? Вероятно, лучший дизайн может полностью исключить этот метод или, по крайней мере, ослабить требование, которое оно выполняет после конструктора подкласса. Убедитесь, что init() не создает видимый объект для любых других потоков до завершения конструктора, поскольку создает ошибки concurrency.

В качестве (уродливой) альтернативы абстрактный метод может быть реализован подклассами в качестве псевдоконструктора:

abstract class Base {
  Base() {
    ctor();
    init();
  }
  abstract void ctor();
  abstract void init();
}

Ответ 2

Избегайте этого. Если вы это сделаете, любой класс, который расширяет ваш класс DerivedX, может решить также вызвать init(), оставив объект в несогласованном состоянии.

Один подход заключается в том, чтобы метод init() вызывался вручную клиентами вашего класса. Имейте поле initialized и бросайте IllegalStateExcepion, если любой из них, требующий инициализации, вызывается без него.

Лучшим подходом было бы использовать статический factory метод вместо конструкторов:

public Derived2 extends Base {
    public static Derived2 create() {
       Derived2 instance = new Dervied2();
       instance.init();
       return instance;
    }
}

Обновление. Как вы можете предложить в своем обновлении, вы можете передать Builder статический метод factory, который вызовет init() в экземпляре. Если ваших подклассов немного, я думаю, что это слишком сложное упражнение.

Ответ 3

В дополнение к рекомендации Bozho контейнер приложения отлично подходит для этой задачи.

Отметьте свой метод init() аннотацией javax.annotation.PostConstruct, а правильно настроенный контейнер EJB или Spring выполнит этот метод после завершения инъекций зависимостей, но до того, как объект может быть использован приложением.

Пример метода:

@PostConstruct
public void init() { 
    // logic..
}

В корпоративном приложении вы можете открывать ресурсы, например, для файловой системы в методе init(). Эта инициализация может вызывать исключения и не должна вызываться из конструктора.

Ответ 4

если бы у Java это было, мы не увидели бы все вызовы метода init() в дикой природе.

"окружающий дочерний конструктор с чем-то" - это невозможно сделать в чистой java. Слишком плохо, потому что могут быть очень интересные приложения, особенно с анонимным блоком инициализации экземпляра класса +.

factory и контейнер - они могут быть полезны, когда native new не выполняет работу; но это тривиально и скучно и не будет работать с анонимными классами.

Ответ 6

Если вы по какой-то причине отрицательно относитесь к использованию фабрик, вы можете использовать следующий трюк:

trait RunInit {
    def init():Unit
    init()
}

class Derived1 extends Base with RunInit {
    def init() = println("INIT'ing!")
}

Это приведет к запуску init() перед конструктором Derived1/body.