0

I have a "standard" JPanel with two panels inside. I was trying to create a kind of template class and then extend it and implement the content. The question is about which would be the way to implement it.

The code below is what I'm trying to make it work but I've just started to read the Effective Java book and I'm not familiar with static factory method. Specially trying to abstract them.

Some tips from the book that i'm trying to follow specially are

  • Consider static factory methods instead of constructors
  • Favor composition over inheritance
  • Prefer interfaces to abstract classes

but I couldn't find out a good solution respecting these points (and without them :P).

public abstract class CentralPage {

    static JPanel getInstance() {
        JPanel container = new JPanel();
        container.setBackground(Color.white);
        container.setBorder(
                BorderFactory.createCompoundBorder(new LineBorder(Color.BLACK), new EmptyBorder(10, 10, 10, 10)));
        container.setMinimumSize(new Dimension(960, 400));
        container.setPreferredSize(new Dimension(960, 400));
        container.setLayout(new BoxLayout(container, BoxLayout.Y_AXIS));

        JPanel up = getUp(container);
        container.add(up);
        JPanel down = getDown(container);
        container.add(down);
        return container;
    }

    abstract JPanel getDown(JPanel container);

    abstract JPanel getUp(JPanel container);

}

Feel free to ask if you need more information about other parts of code.

Umuril Lyerood
  • 185
  • 1
  • 1
  • 9
  • What [*problem*](http://meta.stackoverflow.com/q/66377/163188) are you trying to solve – trashgod Aug 21 '16 at 12:37
  • I think, I have an abstract class with a default static factory method but it's not possible to add static methods on abstract classes. So I'm trying to find the standard work around. Better? – Umuril Lyerood Aug 21 '16 at 12:44
  • The basic idea of providing static factory methods instead of constructors is to hide the implememtations. The implementations are hidden somewhere in private or anonymous classes. You approach looks more like abstract factory methods. You should provide a general factory interface which then can be used to customize your panels. Nonetheless you can still provide 'default' implementations of the factory methods as static methods if needed. – kaetzacoatl Aug 21 '16 at 13:14
  • @kaetzacoatl I think I don't understand your solution. How can i provide a default implementation with an interface? Please create an answer with your proposal. – Umuril Lyerood Aug 21 '16 at 13:37

2 Answers2

3

A Java static method cannot be abstract -- longer discussion here.

Now let's break down your construction: your end result should be a JPanel with two children, also JPanels, whose own construction depends on the parent JPanel. You would like this construction to be done in static factory method.

If that is correct, this could be a solution:

public interface UpDown{
    public JPanel getUp(JPanel parent);
    public JPanel getDown(JPanel parent);
}

public class CentralPage{
    static JPanel getInstance(UpDown components){
        JPanel container = new JPanel();
        container.setBackground(Color.white);
        container.setBorder(
                BorderFactory.createCompoundBorder(new LineBorder(Color.BLACK), new EmptyBorder(10, 10, 10, 10)));
        container.setMinimumSize(new Dimension(960, 400));
        container.setPreferredSize(new Dimension(960, 400));
        container.setLayout(new BoxLayout(container, BoxLayout.Y_AXIS));

        JPanel up = components.getUp(container);
        container.add(up);
        JPanel down = components.getDown(container);
        container.add(down);
        return container;
    }
}

Another solution closer to your original proposal would be like this:

public abstract class CentralPage{

    private static CentralPage page;

    protected JPanel container;

    protected CentralPage(){
        container = new JPanel();
        container.setBackground(Color.white);
        container.setBorder(
               BorderFactory.createCompoundBorder(new LineBorder(Color.BLACK), new EmptyBorder(10, 10, 10, 10)));
        container.setMinimumSize(new Dimension(960, 400));
        container.setPreferredSize(new Dimension(960, 400));
        container.setLayout(new BoxLayout(container, BoxLayout.Y_AXIS));

        JPanel up = getUp(container);
        container.add(up);
        JPanel down = getDown(container);
        container.add(down);
    }

    static JPanel getInstance(){
        if(page==null){
            page=new CentralPage();
        }
        return page.getContainer();
    }

    abstract JPanel getDown(JPanel container);

    abstract JPanel getUp(JPanel container);

    protected JPanel getContainer(){
         return this.container;
    }
}

The downside to this (rather anti-pattern) approach is that you need to remember to create a constructor on your concrete class that calls super();

Community
  • 1
  • 1
Deroude
  • 909
  • 6
  • 22
  • And how should i "call" it on "child" class? (class that implements UpDown) `static JPanel getInstance() { return CentralPage.getInstance(this); }` ? – Umuril Lyerood Aug 21 '16 at 13:56
  • That will not be possible, because `this` has no meaning in a `static` method. But why would you want to construct a parent in a child class? You might have a bigger scope (like your UI application) where you would call `JPanel mainPanel=CentralPage.getInstance(new UpDown(){/*implement the two methods*/});` – Deroude Aug 21 '16 at 14:35
0

My congratulations for reading "Effective Java" and trying to put it into practice. It will bring more usability and clearness to your code.

Now, let's see:

1.In the first place, if your abstraction CentralPage just needs two Panel objects in the moment of construction, the simplest way would be a non-abstract class with two parameters in its constructor:

public class CentralPage
{
  public CentralPage(Panel up, Panel dn)
  {
      ...
  }
}

2.If, appart from receiving parameters at construction, there is some behaviour which is not known to the CentralPage and it must be delegated to a third party at any time within the object's lifecycle (after the constructor ended), the proper pattern would be an abstract class with one abstract method for each required behaviour:

public abstract class CentralPage
{
  protected CentralPage(...)
  {
      ...
  }

  protected abstract return-type myBehaviour1(parameters...)
  {
      ...
  }

  protected abstract return-type myBehaviour2(parameters...)
  {
      ...
  }
}

And, of corse, each non-abstract subclass must provide each required behaviour implemeting its corresponding method.

3.Static factory methods are aimed to non-abstract classes. Its purpose is to decide if an object has to be created (maybe an existing object could be reused, like in the singleton pattern), and eventually to decide which class must be instantiated (maybe the owner class itself, or maybe some subclass).

kaetzacoatl
  • 1,329
  • 14
  • 26
Little Santi
  • 7,940
  • 2
  • 14
  • 40