Java - заставить подклассы вызывать супер метод после конструктора - PullRequest
0 голосов
/ 25 июня 2018

Я хочу, чтобы группа подклассов вызывала супер-метод после завершения конструктора следующим образом:

public abstract class Superclass {

    ...

    public Superclass(...) {
        ...    // do stuff before initializing subclass
    }

    protected void dispatch() {     //method to be called directly after creating an object
        doStuff();
        ...
    }

    public abstract void doStuff();
}

public class Subclass extends Superclass {

    ...

    public Subclass(...) {
        super(...);     //has to be the first line
        ...             //assign variables etc.
        dispatch();     //has to be called after variables are assigned etc.
    }

    public void doStuff() {
        //do stuff with assigned variables etc.
    }
}

Функция dispatch() содержит ряд вещей, которые нужно сделать с объектом после его создания, который должен применяться ко всем подклассам. Я не могу переместить эту функцию в супер-конструктор, так как она вызывает методы из подклассов, которые требуют уже назначенных переменных. Но так как super() требует быть первой строкой суб-конструктора, я не могу устанавливать переменные до тех пор, пока не будет вызван супер-конструктор.

Это будет работать так, как сейчас, но я считаю плохой концепцией вызывать dispatch() в конце конструктора каждого подкласса. Есть ли более элегантный способ решить эту проблему? Или я должен даже полностью переосмыслить свою концепцию?

Ответы [ 4 ]

0 голосов
/ 25 июня 2018

Это будет работать так, как сейчас, но я считаю плохой концепцией вызывать dispatch () в конце конструктора каждого подкласса.Есть ли более элегантный способ решить эту проблему?Или я должен даже полностью переосмыслить свою концепцию?

Как подчеркнул Тимоти Тракл, ваша логика конструктора слишком сложна.

Вы можете сделать вещи намного проще и достичь своей цели, используя метод шаблона для инициализации экземпляра подкласса.Обратите внимание, что вы уже использовали этот шаблон с doStuff().
Конструктор подкласса - действительно ваша проблема: вы хотите уменьшить обязательные котельные плиты, требуемые в каждом подклассе, а также улучшить их читаемость и техническое обслуживание.
Итак, представьтеновый шаблонный метод в суперклассе и вызовите его из конструктора суперкласса.
Этот метод будет делать то же самое, что и конструктор, но его можно вызывать более гибким способом.
dispatch(), то естьискусственный метод, введенный только для уловки, также не требуется.
Вся логика может быть организована из конструктора суперкласса.

Суперкласс может выглядеть следующим образом:

public abstract class Superclass {

    ...

    public Superclass(...) {
        ...    // do stuff before initializing subclass
        init();
        doStuff();
    }

    public abstract void init();

    public abstract void doStuff();
}

А в подклассе заменить:

public Subclass(...) {
    super(...);     //has to be the first line
    ...             //assign variables etc.
    dispatch();     //has to be called after variables are assigned etc.
}

на:

public Subclass(...) {
    super(...);   // let the super constructor to orchestrate the init logic  
}

public void init(){
    // move the constructor logic here
}

Результат намного проще, потому что этот проект объединяет обязанности, связанные с «алгоритмом» инициализации подкласса в одном месте: конструктор суперкласса.


О вашем комментарии:

Это действительно выглядит более элегантно, чем я.Спасибо!РЕДАКТИРОВАТЬ: только что заметил, это не работает с подклассами, имеющими разные параметры конструктора.Есть идеи, как решить эту проблему?

С таким требованием, чтобы сделать вещи простыми и понятными, вы должны сделать вещи в два этапа:

  • создать экземпляр объекта
  • вызовите по ссылке метод init().

Это может выглядеть так:

SuperClass o = new Subclass(argFoo, argBar); 
o.init();

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

С оболочкой это может выглядеть следующим образом:

SuperClass o = new MyWrapper(new Subclass(argFoo, argBar));

Где MyWrapper является подклассом SuperClass и содержит экземпляр объекта SuperClass:

public class MyWrapper implements SuperClass{

   private SuperClass wrapped;

   public MyWrapper (SuperClass wrapped){
       this.wrapped = wrapped;
       this.wrapped.init();
   }

   // then delegate each superclass method to the wrapped object
   public void doStuff(){
       this.wrapped.doStuff();
   }

  // and so for...

}
0 голосов
/ 25 июня 2018

Ваш запрос нарушает несколько лучших практик Java, например ::10000

  • Не выполняйте сложную настройку в конструкторе, заполняйте только закрытые (конечные) переменные-члены и выполняйте только самые базовые проверки согласованности (если они вообще есть).

  • Не вызывать non private или non final методы из конструктора, даже косвенно.

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

0 голосов
/ 25 июня 2018

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

public abstract class SuperClass{
    private boolean instantiated;

    public SuperClass(...){
        ...
    }

    public abstract void doStuff();

    private void dispatch(){
        if(!instantiated){
            instantiated = true;
            doStuff();
        }
    }

    public static void executeActionOnSuperClass(SuperClass s){
        s.dispatch(); // call instantiation if not already done
        s.executeAnAction();
    }
}

И подкласс:

public class SubClass extends SuperClass{
    public SubClass(...){
        super(...);
    }

    public void doStuff(){
         ...
    }
}

Который затем может быть выполнен следующим образом:

SuperClass.executeAnActionOnSuperClass(new SubClass(...));

Хотя это, в основном, анти-паттерн и его следует использовать отдельно.

0 голосов
/ 25 июня 2018

Lorelorelore является верным, если вам нужно выполнить инстанцирование подкласса для завершения при вызове метода.В противном случае вы можете делать то, что у вас есть.Я бы предложил разместить адекватные комментарии, если другим понадобится использовать код.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...