views:

138

answers:

4

For a certain layout that I'm working on, I need to make my own LayoutManager.

It will lay out different components depending on what type they are: Labels in one way, Separators in another way, and everything else in a third way.

I can easily implement this and to determine the different positions for different types of components, if I make a method:

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

Is there a better way to do this, without using instanceOf?

(And no, I don't HAVE to make my own LayoutManager, but it makes things extremely easier if i do ;) )

thanks in advance

/B

A: 

The only other practical way is to use polymorphism and push the function calculatePositionForComponent() to the component interface/abstract class. This could in turn use data from the actual layout manager and then return the Dimensions. It might be impractical to use this approach in your particular case depending on what sort of data needs to be accessed by the calculatePositionForComponent() method.

A tip while working with multiple instanceof if clauses is to use if-else if-else if-else sort of styling. Then as per practicality move the most obvious choices up in the comparison hierarchy to make the if-block faster.

Sandy
A: 

It looks like instanceOf may be the most straightforward way of doing this. You cannot use method overloading, as method overloading is decided at compile time. This means that you cannot just make the following methods:

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

because you'll still have to use instanceOf and casts to get the correct method to be invoked at runtime.

Yes, using instanceOf is usually a code smell, and there may be a more OOP way of doing this. However, for this specific kind of code, I've seen many advanced developers use instanceOf. It's in the langauge for a reason, not just as a crutch. There are times when it's the best tool for the job. IMHO, this is one of those times.

Eddie
A: 

I'd also suggest some kind of interface/subclass solution for encapsulating the different layout calculations for different components. I also would add some registration mechanism in order to be flexible for future additions (if you want to implement another separate behaviour for new component types)

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);
  }
}

Now, you can register individual PositionCalculators for all of your components by putting

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

Of course, this solution might suffer from the following drawbacks:

  • It's probably slower than the original one.
  • It doesn't work with inherited classes: if you have a subclass from JLabel, you must register it separately. The solution could be adapted to this, but this would be at the cost of another performance loss...

On the other hand, the solution is pretty well extensible: You can define different layout behaviour without touching your MyLayoutManager class.

MartinStettner
Oh my, is it that bad? :-|
MartinStettner
I'd really appreciate a short hint to the reason behind a downvote...
MartinStettner
Yes, Id like to know why it was downvoted also, because it looks like quite a nice way of doing it (except possibly for a performance panalty?)
Brimstedt
+1  A: 

You might want to add the information to the constraints object. As a bonus/penalty, you get an extra layer of indirection.

My preference is for layout manager interfaces to add components to the container, rather than the default way of having the container add components to the layout manager. That allows you to construct the constraints object in a more natural way. In your case, you can have separate methods for adding JLabel, JSeparator, etc., so that you don't have to repeat the information and can have differing arguments.

Tom Hawtin - tackline