2011年6月22日 星期三

Java Swing - Model-View-Controller (MVC) Structure


上圖是一個用Swing刻出來的簡單GUI, 目的是更新Database設定,Mailserver 設定....等等, 這邊GUI是依照Swing 的MVC 架構做出來的, Swing 的MVC包含了以下3個:

Model :包含了資料源和所有基於對這些資料的操作
View :Model 跟UI 介面的顯示
Control:在Model和View之間起到了溝通的作用,處理用戶在View上的輸入,並轉發給Model。這樣Model和View兩者之間可以做到鬆散耦合,甚至可以彼此不知道對方,而由Controller連接起這兩個部分。

在Oracle 的 Java SE Application Design With MVC 提到完整的MVC 細節如下:


  • Model - The model represents data and the rules that govern access to and updates of this data. In enterprise software, a model often serves as a software approximation of a real-world process.





  • View - The view renders the contents of a model. It specifies exactly how the model data should be presented. If the model data changes, the view must update its presentation as needed. This can be achieved by using a push model, in which the view registers itself with the model for change notifications, or apull model, in which the view is responsible for calling the model when it needs to retrieve the most current data.





  • Controller - The controller translates the user's interactions with the view into actions that the model will perform. In a stand-alone GUI client, user interactions could be button clicks or menu selections, whereas in an enterprise web application, they appear asGET andPOST HTTP requests. Depending on the context, a controller may also select a new view -- for example, a web page of results -- to present back to the user.

    整個GUI Project 結構如下:


    實作是參考 Model-View-Controller (MVC) Structure 而來的。
    首先是Model 的部份, 以GUI的Database Setting 來說是一個TabbedPanel裡的Item , 所以Model屬性會包含了所有相關元件的資料, 在這邊就是5種屬性, driverclassname,dburl,dbusername,dbuserpwd,databasetype, 我將這些封裝成一個JavaBean, 在DataBaseSettingModel裡面操作, DataBaseSettingModel針對DbSettingObject 做setter&getter 的動作,get時會從db拿值,set時會update db的資料。


    DbSettingObject.java
    package com.main.ui.dataobject;
    
    public class DbSettingObject {
     private String driverClassName;
     private String dbUrl;
     private String dbUserName;
     private String dbUserPwd;
     private String dataBaseType;
     
     public DbSettingObject(){
      this.driverClassName = "";
      this.dbUrl = "";
      this.dbUserName = "";
      this.dbUserPwd = "";
      this.dataBaseType =  "";
      
     }
     
     public String getDriverClassName() {
      return driverClassName;
     }
     public void setDriverClassName(String driverClassName) {
      this.driverClassName = driverClassName;
     }
     public String getDbUrl() {
      return dbUrl;
     }
     public void setDbUrl(String dbUrl) {
      this.dbUrl = dbUrl;
     }
     public String getDbUserName() {
      return dbUserName;
     }
     public void setDbUserName(String dbUserName) {
      this.dbUserName = dbUserName;
     }
     public String getDbUserPwd() {
      return dbUserPwd;
     }
     public void setDbUserPwd(String dbUserPwd) {
      this.dbUserPwd = dbUserPwd;
     }
     public String getDataBaseType() {
      return dataBaseType;
     }
     public void setDataBaseType(String dataBaseType) {
      this.dataBaseType = dataBaseType;
     }
     
    
     @Override
     public String toString(){
      StringBuffer tostring = new StringBuffer();
      
      tostring.append("driverClassName = "   driverClassName "\n");
      tostring.append("dbUrl = "   dbUrl "\n");
      tostring.append("dbUserName = "   dbUserName "\n");
      tostring.append("dbUserPwd = "   dbUserPwd "\n");
      tostring.append("dataBaseType = "   dataBaseType "\n");
      
      return tostring.toString();
     }
     
    }
    


    DbSettingModel.java
    package com.main.ui.model;
    
    import java.sql.SQLException;
    
    import com.main.dao.IQueryUiObjectDao;
    import com.main.dao.QueryDatabaseSettingDao;
    import com.main.ui.dataobject.DbSettingObject;
    
    public class DbSettingModel {
     
     private DbSettingObject dbObj;
     private IQueryUiObjectDao<DbSettingObject> queryDao;
     
     public DbSettingModel(){
      queryDao = new QueryDatabaseSettingDao();
     }
     
     public DbSettingObject getDbSettingObject(){
      try {
       this.dbObj = queryDao.getDataObject();
      } catch (SQLException e) {
       dbObj = new DbSettingObject();
      }  
      return dbObj;
     }
    
    
     
     public void setDbSettingObject(DbSettingObject obj){
      try {
       queryDao.setDataObject(obj);
      } catch (SQLException e) {
      }
      this.dbObj = obj;
     }
    }
    

    再來是View 的部份, DataBase Setting 的Panel設計如下
    一個View Object 是extends JPanel ,建立的相關的UI, 取得或修改UI的顯示,view 會提供Controller 相關的method 來操作。

    DbSettingView.java
    package com.main.ui.view;
    import java.awt.event.ActionListener;
    
    import javax.swing.JButton;
    import javax.swing.JLabel;
    import javax.swing.JPanel;
    import javax.swing.JSeparator;
    import javax.swing.JTextField;
    
    import com.main.ui.dataobject.DbSettingObject;
    import com.main.ui.model.DbSettingModel;
    
    public class DbSettingView extends JPanel{
     private JLabel passwd;
     private JLabel databaseType;
     private JTextField changeDbType;
     private JTextField changeUserName;
     private JTextField changePassword;
     private JTextField changeDbUrl;
     private JTextField changeDriverClassName;
     private JButton Save;
     private JSeparator jSeparator1;
     private JTextField passwordValue;
     private JTextField userNameValue;
     private JTextField dbUrlValue;
     private JTextField dbTypeValue;
     private JTextField driverClassNameValue;
     private JLabel dbUserName;
     private JLabel dbUrl;
     private JLabel DriverClassName;
     private DbSettingModel model ;
     
     public DbSettingView(DbSettingModel imodel){
      this.model = imodel;
      initGUI();
      initTextValues();
     }
     
     private void initGUI() {
      try {
       {
        this.setPreferredSize(new java.awt.Dimension(498, 222));
        this.setRequestFocusEnabled(false);
        this.setOpaque(false);
        this.setLayout(null);
        {
         DriverClassName = new JLabel();
         this.add(DriverClassName);
         DriverClassName.setBounds(12, 12, 119, 19);
         DriverClassName.setText("Driver Class Name:");
        }
        {
         dbUrl = new JLabel();
         this.add(dbUrl);
         dbUrl.setText("DB Url :");
         dbUrl.setBounds(12, 59, 95, 15);
        }
        {
         dbUserName = new JLabel();
         this.add(dbUserName);
         dbUserName.setText("UserName :");
         dbUserName.setBounds(12, 81, 95, 15);
        }
        {
         passwd = new JLabel();
         this.add(passwd);
         passwd.setText("Password :");
         passwd.setBounds(12, 102, 95, 15);
        }
        {
         databaseType = new JLabel();
         this.add(databaseType);
         databaseType.setText("DB Type :");
         databaseType.setBounds(12, 37, 95, 15);
        }
        {
         driverClassNameValue = new JTextField();
         this.add(driverClassNameValue);
         driverClassNameValue.setBounds(143, 11, 146, 22);
         driverClassNameValue.setEnabled(false);
        }
        {
         dbTypeValue = new JTextField();
         this.add(dbTypeValue);
         dbTypeValue.setBounds(143, 34, 146, 22);
         dbTypeValue.setEnabled(false);
        }
        {
         dbUrlValue = new JTextField();
         this.add(dbUrlValue);
         dbUrlValue.setBounds(143, 56, 146, 22);
         dbUrlValue.setEnabled(false);
        }
        {
         userNameValue = new JTextField();
         this.add(userNameValue);
         userNameValue.setBounds(143, 78, 146, 22);
         userNameValue.setEnabled(false);
        }
        {
         passwordValue = new JTextField();
         this.add(passwordValue);
         passwordValue.setBounds(143, 99, 146, 22);
         passwordValue.setEnabled(false);
        }
        {
         jSeparator1 = new JSeparator();
         this.add(jSeparator1);
         jSeparator1.setBounds(12, 133, 447, 7);
        }
        {
         Save = new JButton();
         this.add(Save);
         Save.setText("Save");
         Save.setBounds(394, 146, 65, 22);
        }
        {
         changeDriverClassName = new JTextField();
         this.add(changeDriverClassName);
         changeDriverClassName.setEnabled(true);
         changeDriverClassName.setBounds(313, 11, 146, 22);
        }
        {
         changeDbUrl = new JTextField();
         this.add(changeDbUrl);
         changeDbUrl.setEnabled(true);
         changeDbUrl.setBounds(313, 56, 146, 22);
        }
        {
         changePassword = new JTextField();
         this.add(changePassword);
         changePassword.setEnabled(true);
         changePassword.setBounds(313, 99, 146, 22);
        }
        {
         changeUserName = new JTextField();
         this.add(changeUserName);
         changeUserName.setEnabled(true);
         changeUserName.setBounds(313, 78, 146, 22);
        }
        {
         changeDbType = new JTextField();
         this.add(changeDbType);
         changeDbType.setEnabled(true);
         changeDbType.setBounds(313, 34, 146, 22);
        }
       }
      } catch(Exception e) {
       e.printStackTrace();
      }
     }
     
     private void initTextValues(){
      System.out.println("init text values , dbSettingView");
      if(this.model != null){
       DbSettingObject obj = this.model.getDbSettingObject();
       this.dbTypeValue.setText(obj.getDataBaseType());
       this.dbUrlValue.setText(obj.getDbUrl());
       this.userNameValue.setText(obj.getDbUserName());
       this.passwordValue.setText(obj.getDbUserPwd());
       this.driverClassNameValue.setText(obj.getDriverClassName());
       
      }
     }
     
     public void changeSetting(DbSettingObject object){
      this.dbTypeValue.setText(object.getDataBaseType());
      this.dbUrlValue.setText(object.getDbUrl());
      this.userNameValue.setText(object.getDbUserName());
      this.passwordValue.setText(object.getDbUserPwd());
      this.driverClassNameValue.setText(object.getDriverClassName());
     }
     
     public DbSettingObject getCurrentSettingObject(){
      DbSettingObject currentObject = new DbSettingObject();
      currentObject.setDataBaseType(this.dbTypeValue.getText());
      currentObject.setDbUrl(this.dbUrlValue.getText());
      currentObject.setDbUserName(this.userNameValue.getText());
      currentObject.setDbUserPwd(this.passwordValue.getText());;
      currentObject.setDriverClassName(this.driverClassNameValue.getText());
      
      return currentObject;
     }
     
     
     public void addChangeSettingListener(ActionListener mal){
      this.Save.addActionListener(mal);
     }
     
     public JLabel getPasswd() {
      return passwd;
     }
    
     public JLabel getDatabaseType() {
      return databaseType;
     }
    
     public JTextField getChangeDbType() {
      return changeDbType;
     }
    
     public JTextField getChangeUserName() {
      return changeUserName;
     }
    
     public JTextField getChangePassword() {
      return changePassword;
     }
    
     public JTextField getChangeDbUrl() {
      return changeDbUrl;
     }
    
     public JTextField getChangeDriverClassName() {
      return changeDriverClassName;
     }
    
     public JButton getSave() {
      return Save;
     }
    
     public JSeparator getjSeparator1() {
      return jSeparator1;
     }
    
     public JTextField getPasswordValue() {
      return passwordValue;
     }
    
     public JTextField getUserNameValue() {
      return userNameValue;
     }
    
     public JTextField getDbUrlValue() {
      return dbUrlValue;
     }
    
     public JTextField getDbTypeValue() {
      return dbTypeValue;
     }
    
     public JTextField getDriverClassNameValue() {
      return driverClassNameValue;
     }
    
     public JLabel getDbUserName() {
      return dbUserName;
     }
    
     public JLabel getDbUrl() {
      return dbUrl;
     }
    
     public JLabel getDriverClassName() {
      return DriverClassName;
     } 
    }
    

    再來建立Controller , 會傳入model 跟 view, 然後統過Constructor 新增view的listener , 在此操作Model 跟view 的相關動作。

    DbSettingController.java
    package com.main.ui.controller;
    
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    
    import com.main.ui.dataobject.DbSettingObject;
    import com.main.ui.model.DbSettingModel;
    import com.main.ui.view.DbSettingView;
    
    
    public class DbSettingController {
     private DbSettingModel model;
     private DbSettingView view;
     
     public DbSettingController(DbSettingModel iModel,DbSettingView iView ){
      this.model = iModel;
      this.view = iView;
      this.view.addChangeSettingListener(new ChangeDbSettingListener());
     }
     
     class ChangeDbSettingListener implements ActionListener {
    
      //Actionlistener for change database setting when "save" button  on click
      public void actionPerformed(ActionEvent e) {
       
       DbSettingObject newSettingobject = getNewSettingObject();   
       DbSettingObject currentSettingObj = view.getCurrentSettingObject();     
       changeNewSettingObjectValues(newSettingobject, currentSettingObj);
       
       model.setDbSettingObject(newSettingobject);//update object & database values   
       view.changeSetting(model.getDbSettingObject());//get object from database
      }
    
      private void changeNewSettingObjectValues(DbSettingObject newSettingobject,DbSettingObject currentSettingObj) {
       if(newSettingobject.getDriverClassName().length() <= 0){
        newSettingobject.setDriverClassName(currentSettingObj.getDriverClassName());
       }
       if(newSettingobject.getDbUrl().length() <= 0){
        newSettingobject.setDbUrl(currentSettingObj.getDbUrl());
       }
       if(newSettingobject.getDataBaseType().length() <= 0){
        newSettingobject.setDataBaseType(currentSettingObj.getDataBaseType());
       }
       if(newSettingobject.getDbUserName().length() <= 0){
        newSettingobject.setDbUserName(currentSettingObj.getDbUserName());
       }
       if(newSettingobject.getDbUserPwd().length() <= 0){
        newSettingobject.setDbUserPwd(currentSettingObj.getDbUserPwd());
       }
      }
    
      private DbSettingObject getNewSettingObject() {
       DbSettingObject newSettingobject = new DbSettingObject();   
       newSettingobject.setDataBaseType(view.getChangeDbType().getText());
       newSettingobject.setDbUrl(view.getChangeDbUrl().getText());
       newSettingobject.setDbUserName(view.getChangeUserName().getText());
       newSettingobject.setDbUserPwd(view.getChangePassword().getText());
       newSettingobject.setDriverClassName(view.getChangeDriverClassName().getText());
       return newSettingobject;
      }
      
      
      
    //  private DbSettingObject checkObjectContent(){
    //   
    //  }
      
     }
    }
    
    

    當我們的元系都設計好的時候,就可以建立起來
    ....
    ....
    DbSettingModel dbSettingmodel = new DbSettingModel();
    DbSettingView dbSettingPanel = new DbSettingView(dbSettingmodel);
    DbSettingController controller = new DbSettingController(dbSettingmodel,dbSettingPanel);        
    tabbedPane.addTab("DataBase Setting", icon, dbSettingPanel,"change db setting values");
    dbSettingPanel.setPreferredSize(new java.awt.Dimension(551, 211));
    ...
    ...
    



    Reference:

    Java SE Application Design With MVC
    Model-View-Controller (MVC) Structure
    A Swing Architecture Overview
    Building Graphical User Interfaces
    with the MVC Pattern

    Javaworld.com - MVC meets Swing
    Javaworld(TW) Swing MVC and Model

  • 沒有留言:

    張貼留言