Класс композиции с бесконечной рекурсией

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

abstract class Phase{
  protected String phaseName;
  protected char[] keys;
  protected String[] commands;
  protected Phase[] phases;
  protected StringBuilder pattern;

}

class RemotePhase extends Phase{
  private static RemotePhase remotePhase;

  protected RemotePhase(){
    phaseName="Remote.";
    commands=new String[]{"Lock/unlock windows", "Toggle door", "Select dog menu"};
    setPattern();

    //Just below here starts an infinite loop
    phases=new Phase[]{FixWindows.getFixWindows(), ToggleDoor.getToggleDoor(), SelectDogPhase.getSelectDogPhase()};

  }

  public static RemotePhase getRemotePhase(){
    if(remotePhase==null){
      remotePhase=new RemotePhase();
    }
    return remotePhase;
  }
}

final class FixWindows extends Phase{
  private static FixWindows windows;
  private RemotePhase remotePhase;

  private FixWindows(){

    //execution keeps coming here as FixWindows object is never constructed
    remotePhase=RemotePhase.getRemotePhase();

  }

  public static FixWindows getFixWindows(){
    if(windows==null){
      windows=new FixWindows();
    }
    return windows;
  }
}

Я попытался сделать RemotePhase статическим классом, а FixWindows использовал его для своих членов, но столкнулся с ошибками, пытаясь переопределить нестатические методы абстрактного класса и пытаясь вызвать их из FixWindows в нестатическом контексте. Однако я предпочитаю не делать его статическим, потому что мне пришлось бы создать дополнительный класс только для ссылки на RemotePhase.

Любые способы заставить эту работу работать. Спасибо


person Danny P.    schedule 07.08.2016    source источник
comment
Это бесконечная рекурсия, а не бесконечный цикл.   -  person Dici    schedule 07.08.2016
comment
Измените конструктор FixWindows и передайте RemotePhase (это), чтобы построить его в getFixWindows, или просто установите remotePhase в getFixWindows. Старайтесь избегать вызова конструктора в другом конструкторе. Ваши статические методы хороши для установки таких вещей   -  person Ebrahim Pasbani    schedule 07.08.2016
comment
Можете ли вы показать это, пожалуйста   -  person Danny P.    schedule 07.08.2016
comment
См. этот ответ для простого способа реализации синглтона.   -  person Bohemian♦    schedule 07.08.2016
comment
спасибо, напомнило, что я видел эту стратегию в статье   -  person Danny P.    schedule 07.08.2016


Ответы (3)


Зачем вам хранить ссылку на синглтон, если вы всегда можете получить к нему доступ с помощью статического геттера? Это разрешит ленивый доступ к RemotePhase из FixWindows и устранит циклическую зависимость. Следовательно, самое чистое исправление — просто не вызывать геттер внутри конструктора FixWindows.

final class FixWindows extends Phase{
  private static FixWindows windows;

  private FixWindows(){
      // does nothing but preventing external classes to instantiate it
  }

  public static synchronized FixWindows getFixWindows(){
    if(windows==null){
      windows=new FixWindows();
    }
    return windows;
  }

  public void methodThatRequiresTheRemotePhase(){
    doSomeStuff(RemotePhase.getRemotePhase());
  }
}

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

person Dici    schedule 07.08.2016
comment
@ДэнниП. Я отредактировал свой пост с примером и предупреждением о безопасности потоков. - person Dici; 07.08.2016
comment
Хорошо, теперь я понимаю лучше... Пока я писал код для настройки автоматического выполнения фаз, мне пришлось хранить массив фаз для каждой фазы. - person Danny P.; 07.08.2016
comment
Я не знаю, что вы делаете, но я бы рекомендовал дважды подумать об использовании такого количества синглетонов. Обычно вы используете синглтоны в основном, чтобы избежать многократной загрузки или сериализации очень больших ресурсов (или пулов потоков и тому подобного), но, похоже, вы этого не делаете, поэтому мне интересно, полезна ли такая сложность. - person Dici; 07.08.2016
comment
Хорошо, я не использую его для этого, у меня просто есть карта, возвращаемая другой картой, которая устанавливается путем перебора этих массивов: имена, ключи и фазы. Я хотел использовать только один объект для фаз и других классов и отслеживать. - person Danny P.; 07.08.2016

Это ответ на вопрос, как разорвать цикл инициализации. Однако Ответ @Dici лучше, так как вам не нужно хранить одноэлементные ссылки на RemotePhase в первую очередь.< /эм>

Ваша проблема в том, что одиночные инициализаторы взаимозависимы.

Один из способов нарушить это — назначить статическое одноэлементное поле RemotePhase до завершения инициализации RemotePhase. Затем это гарантирует, что при выполнении инициализации и создании объекта FixWindows singleton он сможет найти (частично) инициализированный объект RemotePhase singleton.

Итак, случайный код выглядит следующим образом:

private RemotePhase() {
    phaseName = "Remote.";
    commands = new String[] { "Lock/unlock windows",
                              "Toggle door",
                              "Select dog menu" };
    setPattern();
}

private void init() {
    phases = new Phase[] { FixWindows.getFixWindows(),
                           ToggleDoor.getToggleDoor(),
                           SelectDogPhase.getSelectDogPhase() };
}

public static RemotePhase getRemotePhase() {
    if (remotePhase == null) {
        remotePhase = new RemotePhase(); // assigns partially initialized object
        remotePhase.init();              // completes initialization
    }
    return remotePhase;
}
person Andreas    schedule 07.08.2016

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

Я показываю эту идею для FixWindows, вы можете использовать то же самое для других подклассов

abstract class Phase{
  protected String phaseName;
  protected char[] keys;
  protected String[] commands;
  protected Phase[] phases;
  protected StringBuilder pattern;

}

class RemotePhase extends Phase{
  private static RemotePhase remotePhase;

  protected RemotePhase(){
    phaseName="Remote.";
    commands=new String[]{"Lock/unlock windows", "Toggle door", "Select dog menu"};
    setPattern();

    //Just below here starts an infinite loop
    phases=new Phase[]{FixWindows.getFixWindows(this), ToggleDoor.getToggleDoor(), SelectDogPhase.getSelectDogPhase()};

  }

  public static RemotePhase getRemotePhase(){
    if(remotePhase==null){
      remotePhase=new RemotePhase();
    }
    return remotePhase;
  }
}

final class FixWindows extends Phase{
  private static FixWindows windows;
  private RemotePhase remotePhase;

  private FixWindows(){

    //execution keeps coming here as FixWindows object is never constructed
    //remotePhase=RemotePhase.getRemotePhase(); //shoud be deleted

  }

  public static FixWindows getFixWindows(remotePhase){
    if(windows==null){
      windows=new FixWindows();
      windows.setRemotePahse(remotePhase);
    }
    return windows;
  }
}
person Ebrahim Pasbani    schedule 07.08.2016