13 Replies Latest reply: Aug 9, 2010 8:18 AM by 800387 RSS

    Separation of Interface Logic and Business Logic in swing application

    843853
      Hi:
      I wonder how can separate the Interface logic(the ui for user,like the View in the concept of MVC) from the Bussiness logic(the Controll of MVC) in the desktop development(the swing).
      It maybe easy in some simple application,however if the user interface should be changed from different user choice,it is more or less complex.

      So I wonder if there is a good exmple ?
        • 1. Re: Separation of Interface Logic and Business Logic in swing application
          jduprez
          I sometimes point out [this old topic|http://forums.sun.com/thread.jspa?forumID=425&threadID=5403752] when discussing MVC in Swing. Often appreciated.
          See esp. Saish's reply #6 (the following posts were only there to force him to explicitate it :o)

          Edited by: jduprez on Aug 1, 2010 9:18 PM
          • 2. Re: Separation of Interface Logic and Business Logic in swing application
            843853
            Thanks, I read that topic,and I am still confusing with some details.\
            1)Separate the View and the Controller
            I used to use a controller class to controll the bussiness logic in my application.
            For the following example, I have a MainFrame,and it contain a top panel to put some content and a button panel to put the button.
            Codes:
            -----------------------------------
            import java.awt.*;
            import java.awt.event.*;
            import javax.swing.*;
            
            public class MainFrame extends JFrame {
            
                 private static final long serialVersionUID = 1L;
                 private JPanel jContentPane = null;
                 private JPanel topPanel = null;
                 private JPanel buttonPanel = null;
                 private JButton OKButton = null;
                 private JButton CancelButton = null;
                 private ViewController controller=null;
                 public MainFrame() {
                      super();
                      this.setSize(384, 260);
                      this.setContentPane(getJContentPane());
                      this.setTitle("JFrame");
                      this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                      
                      controller=new ViewController(this);
                 }
            
                 private JPanel getJContentPane() {
                      if (jContentPane == null) {
                           jContentPane = new JPanel();
                           jContentPane.setLayout(null);
                           jContentPane.add(getTopPanel(), null);
                           jContentPane.add(getButtonPanel(), null);
                      }
                      return jContentPane;
                 }
                 private JPanel getTopPanel() {
                      if (topPanel == null) {
                           topPanel = new CustomPanel();
                           topPanel.setBounds(new Rectangle(0, 0, 380, 180));
                      }
                      return topPanel;
                 }
                 private JPanel getButtonPanel() {
                      if (buttonPanel == null) {
                           buttonPanel = new JPanel();
                           buttonPanel.setBounds(new Rectangle(0, 180, 380, 45));
                           buttonPanel.add(getOKButton(), null);
                           buttonPanel.add(getCancelButton(), null);
                      }
                      return buttonPanel;
                 }
                 private JButton getOKButton() {
                      if (OKButton == null) {
                           OKButton = new JButton();
                           OKButton.setText("OK");
                           OKButton.addActionListener(new ActionListener() {
                                public void actionPerformed(java.awt.event.ActionEvent e) {
                                     controller.action_OK(e);
                                }
                           });
                      }
                      return OKButton;
                 }
            
                 private JButton getCancelButton() {
                      if (CancelButton == null) {
                           CancelButton = new JButton();
                           CancelButton.setText("Cancel");
                           CancelButton.addActionListener(new java.awt.event.ActionListener() {
                                public void actionPerformed(java.awt.event.ActionEvent e) {
                                     controller.action_cancel(e);
                                }
                           });
                      }
                      return CancelButton;
                 }
                 
                 public static void main(String[] args) {
                      new MainFrame().setVisible(true);
                 }
            
            } 
            class CustomPanel extends JPanel {
            
                 private static final long serialVersionUID = 1L;
                 private JComboBox typeComboBox = null;
                 private JLabel typeLabel = null;
                 private JPanel changedPanel = null;
                 public CustomPanel() {
                      typeLabel = new JLabel();
                      typeLabel.setBounds(new Rectangle(20, 20, 40, 20));
                      typeLabel.setText("Type:");
                      this.setSize(380, 180);
                      this.setLayout(null);
                      this.add(getTypeComboBox(), null);
                      this.add(typeLabel, null);
                      this.add(getChangedPanel(), null);
                 }
            
                 private JComboBox getTypeComboBox() {
                      if (typeComboBox == null) {
                           typeComboBox = new JComboBox();
                           typeComboBox.setBounds(new Rectangle(70, 20, 285, 20));
                           typeComboBox.addItemListener(new ItemListener() {
                                @Override
                                public void itemStateChanged(ItemEvent e) {
                                     // here,how to use the ViewController to controll this
                                     // combox
                                }
                           });
                      }
                      return typeComboBox;
                 }
            
                 private JPanel getChangedPanel() {
                      if (changedPanel == null) {
                           changedPanel = new JPanel();
                           changedPanel.setLayout(null);
                           changedPanel.setBounds(new Rectangle(22, 50, 340, 126));
                           changedPanel.setBorder(BorderFactory
                                     .createTitledBorder("This Panel should be changed according to the jcombox"));
                      }
                      return changedPanel;
                 }
            }
            class ViewController {
                 public ViewController(JFrame owner) {
                 }
            
                 public void action_OK(ActionEvent e) {
                 }
            
                 public void action_cancel(ActionEvent e) {
                 }
            }
            ---------------------------

            For the buttons in the frame I can throw the event handling to the ViewController directly since the MainFrame hold a reference of the ViewController.
            However,for the components in the CustomPanel(for example the Jcombox,and etc), their action event can not passed to ViewController easily.Because the CustomPanel should be indenpendent and reused, so I do not think it is a good idea to add a new ViewController reference to it.

            In a word,the action events of the component built directly in the MainFrame can be passed to a thirdParty class(the ViewController),how about these not built directly in the MainFrame(like the JCombox in the CustomPanel)?

            2)The distinguish of the Bussiness logic and the Interface logic.
            They can be easily distinguished from the literal meanings,however I found I am confusing with them.
            For example,I use a panel do display some icons. These icons should be aligned on the row and column. So, I have to calculate the position of each icon according the displayed icons and the size of the Panel, so these codes should be put in the Controller class or the View class?
            Generlly speaking, a Panel have the ability of display icons,so these codes can be put in the method called(maybe) disPlayIcons(Icons iconList) of the panel,
            however I think it is not very proper to put so many so said logic codes under the View(panel) class, in my opinion, the View class should contain the User Interface built codes only,so I used to put them under the Controller class.

            So I wonder which is better?
            And my indeed question:how to distinguish the Bussiness and Interface logic (which codes should put in the View and which in the Controller)?
            • 3. Re: Separation of Interface Logic and Business Logic in swing application
              800387
              I would do so by creating a dedicated listener for your custom panel. In the implementation below, the event is simply delegated to the original controller you had created.
              public class App {
              
                  private final ViewController controller = new ViewController();
              
                  private App() {
              
                      // Class only instantiated via main() method
                  }
              
                  public static void main(final String[] args) {
              
                      final App app = new App();
                      app.renderUi();
                  }
              
                  private void renderUi() {
              
                      final JFrame frame = new JFrame("App");
                      frame.setSize(384, 260);
                      frame.setContentPane(getJContentPane());
                      frame.setTitle("JFrame");
                      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                  }
              
                  private JPanel getJContentPane() {
              
                      final JPanel jContentPane = new JPanel();
                      jContentPane.setLayout(null);
                      jContentPane.add(getTopPanel(), null);
                      jContentPane.add(getButtonPanel(), null);
                      return jContentPane;
                  }
              
                  private JPanel getTopPanel() {
              
                      final JPanel topPanel = new CustomPanel(
                          new CustomPanelListener() {
                              public final void onTypeComboChanged(final ItemEvent e) {
                                  controller.action_typeCombo(e);
                              }
                          }
                      );
                      topPanel.setBounds(new Rectangle(0, 0, 380, 180));
                      return topPanel;
                  }
              
                  private JPanel getButtonPanel() {
              
                      final JPanel buttonPanel = new JPanel();
                      buttonPanel.setBounds(new Rectangle(0, 180, 380, 45));
                      buttonPanel.add(getOKButton(), null);
                      buttonPanel.add(getCancelButton(), null);
                      return buttonPanel;
                  }
              
                  @SuppressWarnings("synthetic-access")
              
                  private JButton getOKButton() {
              
                      final JButton oKButton = new JButton();
                      oKButton.setText("OK");
                      oKButton.addActionListener(new ActionListener() {
                          public void actionPerformed(final java.awt.event.ActionEvent e) {
                              controller.action_OK(e);
                          }
                      });
                      return oKButton;
                  }
              
                  @SuppressWarnings("synthetic-access")
              
                  private JButton getCancelButton() {
              
                      final JButton cancelButton = new JButton();
                      cancelButton.setText("Cancel");
                      cancelButton.addActionListener(new java.awt.event.ActionListener() {
                          public void actionPerformed(final java.awt.event.ActionEvent e) {
                              controller.action_cancel(e);
                          }
                      });
                      return cancelButton;
                  }
              }
              
              interface CustomPanelListener {
              
                  public abstract void onTypeComboChanged(final ItemEvent e);
              }
              
              class CustomPanel extends JPanel {
              
                  public CustomPanel(final CustomPanelListener listener) {
              
                      final JLabel typeLabel = new JLabel();
                      typeLabel.setBounds(new Rectangle(20, 20, 40, 20));
                      typeLabel.setText("Type:");
                      this.setSize(380, 180);
                      this.setLayout(null);
                      this.add(getTypeComboBox(listener), null);
                      this.add(typeLabel, null);
                      this.add(getChangedPanel(), null);
                  }
              
                  private JComboBox getTypeComboBox(final CustomPanelListener listener) {
              
                      final JComboBox typeComboBox = new JComboBox();
                      typeComboBox.setBounds(new Rectangle(70, 20, 285, 20));
                      typeComboBox.addItemListener(new ItemListener() {
                          public void itemStateChanged(final ItemEvent e) {
                              listener.onTypeComboChanged(e);
                          }
                      });
                      return typeComboBox;
                  }
              
                  private JPanel getChangedPanel() {
              
                      final JPanel changedPanel = new JPanel();
                      changedPanel.setLayout(null);
                      changedPanel.setBounds(new Rectangle(22, 50, 340, 126));
                      changedPanel.setBorder(BorderFactory.createTitledBorder("This Panel should be changed according to the jcombox"));
                      return changedPanel;
                  }
              }
              
              class ViewController {
              
                  public ViewController() {
              
                  }
              
                  public void action_OK(final ActionEvent e) {
              
                  }
              
                  public void action_cancel(final ActionEvent e) {
              
                  }
              
                  public void action_typeCombo(final ItemEvent e) {
              
                  }
              }
              - Saish
              • 4. Re: Separation of Interface Logic and Business Logic in swing application
                843853
                Fine, it delegat the event to the controller.
                BTW, is this pattern Appropriate? I mean use a extra controller class ?
                • 5. Re: Separation of Interface Logic and Business Logic in swing application
                  800387
                  It's a controller, but one that is tightly coupled to your CustomPanel. As such, the two go hand-in-hand. That allows you to keep the two classes together as a reusable component without having the other controller methods (e.g., related to the buttons) cluttering things.

                  - Saish
                  • 6. Re: Separation of Interface Logic and Business Logic in swing application
                    843853
                    Hi:
                    In my swing application, I always tried to split the Bussiness Logic and UI logic, however I often meet some same problems:The Null Point Exception.
                    This is the suitation:

                    When I build a UI,I will create a Controller to handle the realated action event and load some data to the UI. And often I hold a UI reference to the controller.For example:
                    public MainFrame extends JFrame {
                         MainFrameController controller;
                         public MainFrame {
                              initUI();
                              controller=new MainFrameController(this);
                         }
                    }
                    
                    public MainFrameController {
                         MainFrame view;
                         public MainFrameController(MainFrame view) {
                              this.view=view;
                         }
                    }
                    However I found sometimes the controller is null when using it in the UI class.
                    For example, in the View(UI class),when I create a JCombox, I will use the controller load some data from the Model, and add them to the combox itemlists. But since the UI class is under built(the JCombox is creating),so the controller is not initilized.
                    The NullPoint Exception occurs.

                    Here is a complete example, please have a check as possible:

                    -----------------------
                    package org.apache.test;
                    
                    import java.awt.Rectangle;
                    import java.awt.event.ActionEvent;
                    import java.awt.event.ActionListener;
                    import java.awt.event.ItemEvent;
                    import java.awt.event.ItemListener;
                    
                    import javax.swing.BorderFactory;
                    import javax.swing.JButton;
                    import javax.swing.JComboBox;
                    import javax.swing.JFrame;
                    import javax.swing.JLabel;
                    import javax.swing.JPanel;
                    
                    public class MainFrame extends JFrame {
                    
                         private static final long serialVersionUID = 1L;
                         private JPanel jContentPane = null;
                         private JPanel topPanel = null;
                         private JPanel buttonPanel = null;
                         private JButton OKButton = null;
                         private JButton CancelButton = null;
                         private ViewController controller = null;
                    
                         public MainFrame() {
                              super();
                              this.setSize(384, 260);
                              this.setContentPane(getJContentPane());
                              this.setTitle("JFrame");
                              this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    
                              controller = new ViewController(this);
                         }
                    
                         private JPanel getJContentPane() {
                              if (jContentPane == null) {
                                   jContentPane = new JPanel();
                                   jContentPane.setLayout(null);
                                   jContentPane.add(getTopPanel(), null);
                                   jContentPane.add(getButtonPanel(), null);
                              }
                              return jContentPane;
                         }
                    
                         private JPanel getTopPanel() {
                              if (topPanel == null) {
                                   topPanel = new CustomPanel(controller);
                                   topPanel.setBounds(new Rectangle(0, 0, 380, 180));
                              }
                              return topPanel;
                         }
                    
                         private JPanel getButtonPanel() {
                              if (buttonPanel == null) {
                                   buttonPanel = new JPanel();
                                   buttonPanel.setBounds(new Rectangle(0, 180, 380, 45));
                                   buttonPanel.add(getOKButton(), null);
                                   buttonPanel.add(getCancelButton(), null);
                              }
                              return buttonPanel;
                         }
                    
                         private JButton getOKButton() {
                              if (OKButton == null) {
                                   OKButton = new JButton();
                                   OKButton.setText("⊕");
                                   OKButton.addActionListener(new ActionListener() {
                                        public void actionPerformed(java.awt.event.ActionEvent e) {
                                             controller.action_OK(e);
                                        }
                                   });
                              }
                              return OKButton;
                         }
                    
                         private JButton getCancelButton() {
                              if (CancelButton == null) {
                                   CancelButton = new JButton();
                                   CancelButton.setText("Cancel");
                                   CancelButton.addActionListener(new java.awt.event.ActionListener() {
                                        public void actionPerformed(java.awt.event.ActionEvent e) {
                                             controller.action_cancel(e);
                                        }
                                   });
                              }
                              return CancelButton;
                         }
                    
                         public static void main(String[] args) {
                              new MainFrame().setVisible(true);
                         }
                    
                    }
                    
                    class CustomPanel extends JPanel {
                    
                         private static final long serialVersionUID = 1L;
                         private JComboBox typeComboBox = null;
                         private JLabel typeLabel = null;
                         private JPanel changedPanel = null;
                    
                         private PanelEventHandler handler;
                    
                         public CustomPanel(PanelEventHandler handler) {
                              this.handler = handler;
                              typeLabel = new JLabel();
                              typeLabel.setBounds(new Rectangle(20, 20, 40, 20));
                              typeLabel.setText("Type:");
                              this.setSize(380, 180);
                              this.setLayout(null);
                              this.add(getTypeComboBox(), null);
                              this.add(typeLabel, null);
                              this.add(getChangedPanel(), null);
                         }
                    
                         private JComboBox getTypeComboBox() {
                              if (typeComboBox == null) {
                                   typeComboBox = new JComboBox();
                                   typeComboBox.setBounds(new Rectangle(70, 20, 285, 20));
                                   typeComboBox.addItem("All");
                                   typeComboBox.addItem("Vector");
                                   typeComboBox.addItemListener(new ItemListener() {
                                        @Override
                                        public void itemStateChanged(ItemEvent e) {
                                             handler.action_CustomPanel_combox(e);
                                        }
                                   });
                              }
                              return typeComboBox;
                         }
                    
                         private JPanel getChangedPanel() {
                              if (changedPanel == null) {
                                   changedPanel = new JPanel();
                                   changedPanel.setLayout(null);
                                   changedPanel.setBounds(new Rectangle(22, 50, 340, 126));
                                   changedPanel.setBorder(BorderFactory
                                             .createTitledBorder("This Panel should be changed according to the jcombox"));
                              }
                              return changedPanel;
                         }
                    }
                    
                    interface PanelEventHandler {
                         public void action_CustomPanel_combox(ItemEvent e);
                    }
                    
                    class ViewController implements PanelEventHandler {
                         public ViewController(JFrame owner) {
                         }
                    
                         public void action_OK(ActionEvent e) {
                         }
                    
                         public void action_cancel(ActionEvent e) {
                         }
                    
                         @Override
                         public void action_CustomPanel_combox(ItemEvent e) {
                              if (e.getStateChange() == ItemEvent.SELECTED) {
                                   String select = e.getItem().toString();
                                   System.out.println(select);
                              }
                         }
                    }
                    -----------------------


                    And it is not simple to find the reason unless by debugging line by line. Expecially in some complex UI build class, for example, add some Customed Component, using inherit and etc.

                    So I wonder if there are any conventions to avoid this errors?
                    • 7. Re: Separation of Interface Logic and Business Logic in swing application
                      800387
                      While I enjoy helping people, I do insist they investigate first. Take a look at the stack trace. It will tell you exactly where the NPE is occurring. If after looking at the stack trace, stepping through your IDE's debugger, and doing some analysis of the problem, if after all that you are still stuck. Then post the stack trace, show the line that is failing, and we may be able to help you.

                      - Saish
                      • 8. Re: Separation of Interface Logic and Business Logic in swing application
                        843853
                        For example, in the View(UI class),when I create a JCombox, I will use the controller load some data from the Model, and add them to the combox itemlists. But since the UI class is under built(the JCombox is creating),so the controller is not initilized.
                        The NullPoint Exception occurs.

                        -------
                        I know the reason,but I have no idea to avoid it.
                        • 9. Re: Separation of Interface Logic and Business Logic in swing application
                          843853
                          lose patient?
                          I really know the reason, when the controller is passed , it is null since the view is not initlized.
                          I have no idea about it.
                          • 10. Re: Separation of Interface Logic and Business Logic in swing application
                            800387
                            What is the stack trace? Which line is actually throwing the NPE?

                            - Saish
                            • 11. Re: Separation of Interface Logic and Business Logic in swing application
                              843853
                              private JComboBox getTypeComboBox() {
                                        if (typeComboBox == null) {
                                             typeComboBox = new JComboBox();
                                             typeComboBox.setBounds(new Rectangle(70, 20, 285, 20));
                                             typeComboBox.addItem("All");
                                             typeComboBox.addItem("Vector");
                                        typeComboBox.addItemListener(new ItemListener() {
                                                   @Override
                                                  public void itemStateChanged(ItemEvent e) {
                                                       handler.action_CustomPanel_combox(e); // The NPE is throwed here, the handler is null
                                                  }
                                             });
                                        }
                                        return typeComboBox;
                                   }
                              private JPanel getTopPanel() {
                                        if (topPanel == null) {
                                             topPanel = new CustomPanel(controller);  ///////// [1]
                                             topPanel.setBounds(new Rectangle(0, 0, 380, 180));
                                        }
                                        return topPanel;
                                   }
                              [1]This is the where the handler is passed, when the CustomPanel is initilized, the controller is not built yet, so a Null object is passed.

                              Edited by: apachemaven on 2010-8-7 ??12:20
                              • 13. Re: Separation of Interface Logic and Business Logic in swing application
                                800387
                                Separate the initialization of the panel from rendering its components (e.g., create a renderUi() method or something similar, create the panel, pass an initialized controller, then call renderUi()).

                                - Saish