Как я могу реализовать свой LayoutManager без instanceOf? - PullRequest
1 голос
/ 01 апреля 2009

Для определенного макета, над которым я работаю, мне нужно сделать собственный LayoutManager.

Он будет располагать различные компоненты в зависимости от того, какого они типа: Label s одним способом, разделители другим способом и все остальное третьим способом.

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

private Dimension calculatePositionForComponent(Component component) {
  if (component instanceOf JLabel)
     ...
  if (component instanceOf JSeparator)
     ...
}

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

(И нет, я НЕ ДОЛЖЕН делать свой собственный LayoutManager, но это очень упрощает, если я делаю;))

спасибо заранее

/ B

Ответы [ 4 ]

1 голос
/ 01 апреля 2009

Возможно, вы захотите добавить информацию к объекту ограничений. В качестве бонуса / штрафа вы получаете дополнительный уровень косвенности.

Я предпочитаю, чтобы интерфейсы менеджера компоновки добавляли компоненты в контейнер, а не способ по умолчанию, когда контейнер добавлял компоненты в диспетчер компоновки. Это позволяет вам построить объект ограничений более естественным образом. В вашем случае вы можете использовать отдельные методы для добавления JLabel, JSeparator и т. Д., Чтобы вам не приходилось повторять информацию и иметь разные аргументы.

1 голос
/ 01 апреля 2009

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

public interface PositionCalculator {
  Dimension calculatePositionForComponent(MyLayoutManager manager, Component component);
}

class JLabelCalculator implements PositionCalculator {
  public Dimension calculatePositionForComponent(MyLayoutManager manager, Component component) {
    // ...
  }
}

class JRadioButtonPosCalculator impements PositionCalculator {
  public Dimension calculatePositionForComponent(MyLayoutManager manager, Component component) {
    // ...
  }
}

// More classes ...


class MyLayoutManager extends LayoutManager {
  private static HashMap<Class, PositionCalculator> calculators = new HashMap<Class, PositionCalculator>();
  public static registerPositionCalculator(Class c, PositionCalculator p) {
    calculators.put(c, p);
  }
  private static PositionCalculator defaultCalculator = new DefaultPositionCalculator(); // Not shown here ...
  // ...
  private Dimension calculatePositionForComponent(Component c) {
    PositionCalculator calc = calculators.get(c.getClass());
    if (calc == null)
      calc = defaultCalculator;
    return calc.calculatePositionForComponent(this, c);
  }
}

Теперь вы можете зарегистрировать отдельные PositionCalculator для всех ваших компонентов, набрав

MyLayoutManager.registerPositionCalculator(JLabel.class, new JLabelCalculator());
MyLayoutManager.registerPositionCalculator(JRadioButton.class, new JRadioButtonCalculator());
// ...

Конечно, это решение может страдать от следующих недостатков:

  • Это, вероятно, медленнее, чем оригинал.
  • Он не работает с унаследованными классами: если у вас есть подкласс от JLabel, вы должны зарегистрировать его отдельно. Решение может быть адаптировано к этому, но это будет за счет другой потери производительности ...

С другой стороны, решение довольно хорошо расширяемое: вы можете определить другое поведение макета, не касаясь вашего класса MyLayoutManager.

0 голосов
/ 01 апреля 2009

Похоже, instanceOf может быть самым простым способом сделать это. Вы не можете использовать перегрузку метода, поскольку перегрузка метода решается во время компиляции. Это означает, что вы не можете просто сделать следующие методы:

private Dimension calculatePositionForComponent(JLabel component) {
    ....
}
private Dimension calculatePositionForComponent(JSeparator component) {
    ....
}
....

потому что вам все равно придется использовать instanceOf и приведение типов, чтобы получить правильный метод, который будет вызываться во время выполнения.

Да, использование instanceOf обычно является запахом кода, и для этого может быть более ООП-способ. Однако, для этого специфического вида кода, я видел, что многие продвинутые разработчики используют instanceOf. Это в языке по какой-то причине, а не просто как костыль. Есть моменты, когда это лучший инструмент для работы. ИМХО, это один из тех времен.

0 голосов
/ 01 апреля 2009

Единственный другой практический способ - это использовать полиморфизм и передать функцию calcPositionForComponent () в интерфейс компонента / абстрактный класс. Это может, в свою очередь, использовать данные из фактического менеджера компоновки и затем возвращать Размеры. Возможно, было бы нецелесообразно использовать этот подход в вашем конкретном случае, в зависимости от того, к какому типу данных необходимо получить доступ с помощью метода CalculayPositionForComponent ().

Подсказка при работе с несколькими предложениями instanceof if заключается в использовании стиля if-else if-else if-else. Затем в соответствии с практикой переместите наиболее очевидные варианты в иерархию сравнения, чтобы ускорить блок if.

...