AWT Component and custom interface types: how to write good OOP code?

Tag: oop , design-patterns Author: qiaozhenghua Date: 2012-04-06

Let's say I have a Swing GUI that has to display a certain type of information in two different ways. From a design patterns perspective one would probably use the Strategy pattern here: create an interface that defines how the communication between the display component and the client works like this:

public interface Foo {
  void showData(Data bar)
}

The real action is then done by different components that implement Foo and can be created and plugged in for doing the real work.

Now, what happens, if the real components are java.awt.Components? As I see it, it results in a mess of type casts because Component is a class. Let's assume an implementation like this one:

public class Baz extends Component implements Foo {
  ...
}

If I want to pass objects of class Baz around, the methods can either use "Component" as the parameter type or "Foo". The problem is that some methods need objects that are both Component and Foo (e.g. because they add the object to a JPanel and then supply the data calling the interface method showData()).

As I see it I have some choices to make this happen:

  • I can pass the reference as Component and cast to Foo. Before, I have to check that the reference is an instance of Foo and I have to handle situations where this requirement is not met. Another problem is that I have to communicate to clients of the method that the Component passed also has to implement Foo, which is awkward and error-prone.
  • I can do the same thing with Foo
  • I can add a method "Component getComponent()" to the Foo interface and the implementation would always return "this". This boilerplate method could be put into an abstract sub-class of Component. This solution means an interface method I don't want and an additional sub-class I don't need.
  • I can pass two references, one Component and one Foo reference to the same object. Internally, I'd have to make sure, though, that both references belong to the same object. And I have to deal with situations in which this requirement is not met.
  • I can use an abstract sub-class of Component and define the interface using abstract methods. This would allow me to pass references in a type-safe manner, but break with good OOP practices: keeping interfaces and implementations separate and also the interface segregation principle.

So, all of these solutions are merely workarounds. Is there any solution I'm missing? What should I do?

Other Answer1

I would use the Strategy design pattern as you mentioned, but perhaps in a different context. The problem with trying to "shoe-horn" both Foo and Component into one class is that you could have combinations of implementations that would require duplicating code.

For example, imagine you have the following implementations of Component: (Its been too long since Ive used Swing, these classes may not exist)

  • JPanel
  • JButton
  • JMenu

And you also had the following implementations of Foo

  • MyFoo
  • HisFoo
  • OurFoo
  • WhatTheFoo

And Imagine all the combinations of those: that's whats called a class explosion. This is the classic justification for the Strategy pattern.

I would create a sort of container class that uses a HAS-A relationship for each of the needed classes instead of using the IS-A relationship as follows: (Im a c++ programmer, so you'll have to excuse the hybrid code :)

class ComponentFooHandler {
  Component component_;
  Foo fooImpl_;

  inline Foo getFoo()  {return fooImpl_;}
  void setFoo(Foo f)   {fooImpl_ = f;}

  Component getComponent()        {return component_;}
  void setComponent(Component c)  {component_ = c;}

  void doAction() {
    component_.someAction();
    fooImpl_.anotherAction();
  }
}

You would then have to create different implementations of Foo seperately. Then the Component and Foo implementations can be combined as needed with out having to duplicate Foo impl code. Notice also that you can call methods that like doAction() that can operate on both Foo and Component without knowing their details, similar to a Template Pattern.

To solve the issues with your original question:

  • When a Component is needed, call getComponent() on a handler instance
  • When a Foo is needed, call getFoo() on a handler instance
  • I would avoid creating methods that need both in one and split the method args into 2
  • Or just consider passing around a ComponentFooHandler

comments:

Okay, but my problem was not separating the Foo code from the Component code. My problem is that my Foo implementations are Components (e.g. JPanels) and even though I do know that I don't know how to put that knowledge in code so the Compiler can check this, too.
Ok, maybe you should consider if its necessary that your Foo impls are Components. If you're having a hard time trying to figure out how to use this design, maybe that's a sign that you should consider a different design.