Java Refactor привел к циркулярной ссылке - PullRequest
3 голосов
/ 08 ноября 2010

У меня есть такой код в настольном приложении

Это просто JPanel, который содержит кнопки и тому подобное.

class ApplicationPanel {

   private Listener listener;

   public ApplicationPanel(){
      this.listener = new Listener(this);
   }
}

Это добавляет события к элементам управления в JPanel выше.

class Listener {

   private ApplicationPanel panel;

   public Listener(ApplicationPanel panel){
      this.panel = panel;
   }
}

Код вызывающего абонента будет выглядеть так

public void main(String[] args){
   ApplicationPanel panel = new ApplicationPanel();
}

Если я попытаюсь применить внедрение зависимостей И фабрики (позже будут заменены на Guice)

class ApplicationPanel {

   private Listener listener;

   public ApplicationPanel(){
      this(new Listener());
   }

   public ApplicationPanel(Listener listener){
      this.listener = listener;
   }
}

class Listener {

   private ApplicationPanel panel;

   public Listener(){
      this(new ApplicationPanel());
   }

   public Listener(ApplicationPanel panel){
      this.panel = panel;
   }
}

Код вызывающего абонента будет таким Как видите, в этом коде есть циклическая зависимость Какой лучший способ решить эту проблему?

public void main(String[] args){
   Listener listener = new Listener(panel);
   ApplicationPanel panel = new ApplicationPanel(listener);

}

Ответы [ 3 ]

3 голосов
/ 08 ноября 2010

Проверьте это сообщение в блоге по теме;guice достаточно умен, учитывая привязки, которые вы предоставляете в вашем модуле guice, чтобы обнаружить циклическую ссылку, а затем использовать временный прокси, чтобы разрешить инъекцию.

2 голосов
/ 08 ноября 2010

Проблема в том, что вы не можете выполнить юнит-тест ApplicationPanel с фальшивкой Listener, потому что Listener принимает ApplicationPanel (если вы используете конструктор по умолчанию для Listener, тогда у слушателя будет поле, ссылающееся на вещественное ApplicationPanel).Вы можете использовать макет Listener с фреймворком, но в этом случае я думаю, что циклические зависимости могут указывать на запах кода.

Реальная проблема с этой циклической зависимостью заключается в том, что Listener получает ссылкудо ApplicationPanel до завершения строительства ApplicationPanel.Это может вызвать проблемы безопасности потока.Даже если путь к коду не является многопоточным, Listener может вызвать ApplicationPanel, который вызывает отправку события, до того, как ApplicationPanel инициализирует список прослушивателей!

Вместо этого передайте данные слушателю, когда он получает события:

public interface Listener {

  void onApplicationChanged(ApplicationPanel panel){
  }
}

Более традиционный способ добавить слушателя к объекту, отправляющему события, - вызвать метод:

public class ApplicationPanel {
  private List<Listener> listeners = new CopyOnWriteArrayList<Listener>();

  public void addListener(Listener listener) {
    listeners.add(listener);
  }
}

Если вы хотите, чтобы класс, отправляющий события, использовал конструктор для указания слушателей, и вы хотите использовать Guice, посмотрите на Multibinder .ApplicationPanel будет выглядеть так:

public class ApplicationPanel {
  private Set<Listener> listeners;

  @Inject
  public ApplicationPanel(Set<Listener> listeners) {
    listeners = new HashSet<Listener>(listeners);
  }
}
2 голосов
/ 08 ноября 2010

Общее решение: при использовании внедрения зависимостей всегда используйте конструкторы и установщики по умолчанию.

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

...