分离逻辑和GUI

时间:2013-10-26 11:14:33

标签: java swing

我有Java Swing应用程序,它是在糟糕的架构中设计的。 GUI,SQL语句......等等,都是一个类。 防爆。 NewEmployee.java具有GUI,SQL插入,更新,删除&在这个类中选择,没有分离。 根据我的阅读,我们应该将逻辑与设计分开。 说实话,我不知道该怎么做,或者如何理解它。

我需要知道如何分解我的项目所以我得到了:模型,视图和&控制器,但我需要知道每个人的意思和每个人应该如何与其他人合作。

你可以帮忙分开这个:

    public class PendingOInvoices extends CFrame {

    private static final long serialVersionUID = 1L;
    private JToolBar toolBar = new JToolBar();
    private JPanel panel_1 = new JPanel();
    private JLabel label3 = new JLabel();
    private CText cSearch = new CText();
    private JLabel label2 = new JLabel();
    private CDCombo cSearchBy = new CDCombo();
    private CBGeneral cMakeBill = new CBGeneral();
    private Component component5_1 = Box.createHorizontalStrut(3);
    private CBRefreshE cRefresh = new CBRefreshE();
    private CBCloseE cClose = new CBCloseE();
    private Component component5_3 = Box.createHorizontalStrut(3);
    private JLabel label1 = new JLabel();
    private CDate cTDate = new CDate();
    private MyOutBillingModel model1 = new MyOutBillingModel();
    private JPVTableView table1 = new JPVTableView(model1);
    private JLabel label = new JLabel();
    private CDate cFDate = new CDate();
    private CBNewE cNew = new CBNewE();
    private CBModifyE cModify = new CBModifyE();
    private Component component5 = Box.createHorizontalStrut(3);
    private Component component5_2 = Box.createHorizontalStrut(3);
    private JLabel label4 = new JLabel();
    private CDCombo cFilter = new CDCombo();

    public PendingOInvoices () {

        setTitle("Out Patients - Pending Encounters");
        setFrameIcon("opdbilling");

        try {
            jbInit();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

  private void jbInit() throws Exception {
      setSize(new Dimension(980, 546));
      getContentPane().setLayout(new BorderLayout());      
      getContentPane().add(panel_1, BorderLayout.NORTH);
      panel_1.setMaximumSize(new Dimension(0, 44));
      panel_1.setMinimumSize(new Dimension(0, 44));
      panel_1.setLayout(null);
      panel_1.setPreferredSize(new Dimension(0, 44));

      panel_1.add(label3);
      label3.setText("Search engine:");
      label3.setBounds(790, 0, 170, 19);

      panel_1.add(cSearch);
      cSearch.addKeyListener(new CSearchKeyListener());
      cSearch.setBounds(790, 20, 170, 23);

      panel_1.add(label2);
      label2.setText("Search by:");
      label2.setBounds(662, 0, 127, 19);

      panel_1.add(cSearchBy);
      cSearchBy.addActionListener(new CSearchByActionListener());
      cSearchBy.setBounds(662, 20, 127, 23);

      cSearchBy.addItem("ID No");
      cSearchBy.addItem("File No");
      cSearchBy.addItem("Patient Name (EN)");
      cSearchBy.addItem("Patient Name (ع)");
      cSearchBy.addItem("Encounter No");

      toolBar.setBounds(0, 0, 264, 45);
      panel_1.add(toolBar);
      toolBar.setFloatable(false);

      toolBar.add(cRefresh);     
      toolBar.add(component5_1);
      toolBar.add(cNew);
      cNew.addActionListener(new CNewActionListener());
      toolBar.add(component5_3);
      toolBar.add(cModify);      
      cModify.addActionListener(new CModifyActionListener());
      toolBar.add(component5);
      toolBar.add(cMakeBill);
      cMakeBill.setText("Make Bill");

      cRefresh.addActionListener(new CRefreshActionListener());
      cMakeBill.setIcon(SwingResourceManager.getIcon(PendingOInvoices.class, "/images/small/billmaker.png"));
      cMakeBill.addActionListener(new CMakeBillActionListener());

      toolBar.add(component5_2);
      toolBar.add(cClose);
      cClose.addActionListener(new CCloseActionListener());

      panel_1.add(label1);
      label1.setText("To Date:");
      label1.setBounds(382, 0, 115, 19);

      panel_1.add(cTDate);
      cTDate.addTextListener(new CTDateTextListener());
      cTDate.addKeyListener(new CTDateKeyListener());
      cTDate.setBounds(382, 20, 115, 23);

      getContentPane().add(table1);
      table1.getJTable().addMouseListener(new table1JTableMouseListener());
      table1.getJTable().addKeyListener(new Table1JTableKeyListener());
      cSearch.setHorizontalAlignment(SwingConstants.CENTER);

      panel_1.add(label);
      label.setText("From Date:");
      label.setBounds(266, 0, 115, 19);

      panel_1.add(cFDate);
      cFDate.setText("01/01/"+cTDate.getText().substring(7));
      cFDate.addTextListener(new CFDateTextListener());
      cFDate.addKeyListener(new CFDateKeyListener());
      cFDate.setBounds(266, 20, 115, 23);

      panel_1.add(label4);
      label4.setText("Filtering Options:");
      label4.setBounds(498, 0, 163, 19);

      panel_1.add(cFilter);
      cFilter.addActionListener(new CFilterActionListener());
      cFilter.setBounds(498, 20, 163, 23);

      cFilter.addItem("--- Choose ---");
      cFilter.addItem("Guarantors Shares Only");
      cFilter.addItem("Cash Patients Only");
      cFilter.addItem("Patients Got Discount Only");

      setWidths();
      fillEncounters();
  }

  public void updateMe(String encno){
      fillEncounters();
      table1.getTable().requestFocus();
      table1.getTable().setFocusCell(table1.search(encno, 1),1);
  }

  private void setWidths() {

      model1.setColumnCount(9);
      model1.setRowCount(0);

      table1.getColumn(0).setPreferredWidth(75);
      table1.getColumn(1).setPreferredWidth(90);
      table1.getColumn(2).setPreferredWidth(350);
      table1.getColumn(3).setPreferredWidth(80);
      table1.getColumn(4).setPreferredWidth(80);
      table1.getColumn(5).setPreferredWidth(80);
      table1.getColumn(6).setPreferredWidth(80);
      table1.getColumn(7).setPreferredWidth(80);
      table1.getColumn(8).setPreferredWidth(20);

      table1.getColumn(0).setHeaderValue("Date");
      table1.getColumn(1).setHeaderValue("Encounter No");
      table1.getColumn(2).setHeaderValue("Patient Name");
      table1.getColumn(3).setHeaderValue("Total");
      table1.getColumn(4).setHeaderValue("Guarantors");
      table1.getColumn(5).setHeaderValue("Discount");
      table1.getColumn(6).setHeaderValue("Paid");
      table1.getColumn(7).setHeaderValue("Balance");
      table1.getColumn(8).setHeaderValue("");

      table1.setColumnType(0, JPVTable.DATE, null);
      table1.setColumnType(1, JPVTable.DATE, null);
      table1.setColumnType(2, JPVTable.TEXT, null);
      table1.setColumnType(3, JPVTable.DOUBLE, null);
      table1.setColumnType(4, JPVTable.DOUBLE, null);
      table1.setColumnType(5, JPVTable.DOUBLE, null);
      table1.setColumnType(6, JPVTable.DOUBLE, null);
      table1.setColumnType(7, JPVTable.DOUBLE, null);
      table1.setColumnType(8, JPVTable.BOOLEAN, null);

      CTableConfig mc = new CTableConfig();
      mc.newConfigureTable(table1,8,0,true);

      table1.getTable().getColumnModel().getColumn(8).setHeaderRenderer(new CHGeneral(new MyItemListener()));
  }

  class MyItemListener implements ItemListener {
      public void itemStateChanged(ItemEvent e) {
        Object source = e.getSource();
        if (!(source instanceof AbstractButton)) return;
        boolean checked = e.getStateChange() == ItemEvent.SELECTED;
        int rows = model1.getRowCount();
            for(int x = 0; x < rows; x++){
                model1.setValueAt(checked,x,8);
        }
      }
  }

  private String getSearchColumn(){
      if(cSearchBy.getSelectedIndex() == 0){
          return "patients.PatID";
      }else if(cSearchBy.getSelectedIndex() == 1){
          return "encounters.Enc_Patient";
      }else if(cSearchBy.getSelectedIndex() == 2){
          return "concat(patients.PatFirst,' ', patients.PatFather,' ',patients.PatMiddle,' ',patients.PatFamily)";
      }else if(cSearchBy.getSelectedIndex() == 3){
          return "concat(patients.PatFirstA,' ', patients.PatFatherA,' ',patients.PatMiddleA,' ',patients.PatFamilyA)";
      }else if(cSearchBy.getSelectedIndex() == 4){
          return "encounters.Enc_No";
      }
      return "";
  }

  private void fillEncounters(){
      String currentValue = "";
      int r = table1.getTable().getFocusRow();

      cFDate.setFormat(2);
      cTDate.setFormat(2);

      String sql = "SELECT \n"
          +"Date_Format(Enc_Date,'%d/%m/%Y'), \n"
          +"encounters.Enc_No, \n"
          +"CONCAT(' ',patients.PatFirst,' ',patients.PatFather,' ',patients.PatMiddle,' ',patients.PatFamily), \n"
          +"FORMAT((Enc_Clinics+Enc_Labs+Enc_Rads+Enc_MED),2), \n"
          +"FORMAT(Enc_Guarantor,2), \n"
          +"FORMAT(Enc_Discount,2), \n"
          +"FORMAT((Enc_Receipt-Enc_Payment),2), \n"
          +"FORMAT(Enc_Balance,2), \n"
          +""+Boolean.FALSE+", \n"
          +"encounters.Enc_Patient, \n"
          +"VERSION \n"
          +"FROM \n"
          +"encounters \n"
          +"INNER JOIN encsummary ON encounters.Enc_No = encsummary.Enc_No \n"
          +"INNER JOIN patients ON encounters.Enc_Patient = patients.PatNo \n"
          +"WHERE Enc_Date Between '"+cFDate.getText()+"' AND '"+cTDate.getText()+"' AND Enc_Billed = 'false' \n"
          +"AND (Enc_Clinics+Enc_Labs+Enc_Rads+Enc_MED) > 0 \n";

                if(cFilter.getSelectedIndex() == 1){
                    sql+="and Enc_Guarantor > 0 \n";
                }else if(cFilter.getSelectedIndex() == 2){
                    sql+="and Enc_Guarantor = 0 \n";
                }else if(cFilter.getSelectedIndex() == 3){
                    sql+="and Enc_Discount > 0 \n";
                }
                if(cSearch.getText().trim().length() > 0){
                   sql+= "and "+getSearchColumn()+" LIKE '%"+cSearch.getText()+"%' \n";
                }

                sql+="Order By encounters.Enc_No DESC";
                      cFDate.setFormat(1);
                      cTDate.setFormat(1);
                      model1.setData(CDeclare.dataAccessor.getData(sql));
                try{
                  currentValue = table1.getValueAt(r, 1).toString();
                }catch(Exception ex){}
                int i = table1.search(currentValue, 1);
                table1.getTable().scrollRectToVisible(new Rectangle(table1.getTable().getCellRect(i, 1, true)));
                table1.getTable().changeSelection(i, 1, false, false);
  }

  private class CSearchByActionListener implements ActionListener {
    public void actionPerformed(ActionEvent arg0) {
        cSearchBy_actionPerformed(arg0);
    }
  }
  private class table1JTableMouseListener extends MouseAdapter {
    public void mouseClicked(MouseEvent arg0) {
        table1JTable_mouseClicked(arg0);
    }
  }
  private class CMakeBillActionListener implements ActionListener {
    public void actionPerformed(ActionEvent e) {
        cMakeBill_actionPerformed(e);
    }
  }
  private class CSearchKeyListener extends KeyAdapter {
    public void keyReleased(KeyEvent e) {
        cSearch_keyReleased(e);
    }
  }
  private class CTDateKeyListener extends KeyAdapter {
    public void keyReleased(KeyEvent e) {
        cTDate_keyReleased(e);
    }
  }
  private class CRefreshActionListener implements ActionListener {
    public void actionPerformed(ActionEvent e) {
        cRefresh_actionPerformed(e);
    }
  }
  private class CTDateTextListener implements TextListener {
    public void textValueChanged(TextEvent e) {
        cTDate_textValueChanged(e);
    }
  }
  private class CCloseActionListener implements ActionListener {
    public void actionPerformed(ActionEvent e) {
        cClose_actionPerformed(e);
    }
  }
  private class CFDateKeyListener extends KeyAdapter {
    public void keyReleased(KeyEvent e) {
        cFDate_keyReleased(e);
    }
  }
  private class CFDateTextListener implements TextListener {
    public void textValueChanged(TextEvent e) {
        cFDate_textValueChanged(e);
    }
  }
  private class CNewActionListener implements ActionListener {
    public void actionPerformed(ActionEvent e) {
        cNew_actionPerformed(e);
    }
  }
  private class CModifyActionListener implements ActionListener {
    public void actionPerformed(ActionEvent e) {
        cModify_actionPerformed(e);
    }
  }
  private class Table1JTableKeyListener extends KeyAdapter {
    public void keyPressed(KeyEvent e) {
        table1JTable_keyPressed(e);
    }
  }
  private class CFilterActionListener implements ActionListener {
    public void actionPerformed(ActionEvent e) {
        cFilter_actionPerformed(e);
    }
  }

    protected void cSearchBy_actionPerformed(ActionEvent arg0) {
        if(cSearchBy.getSelectedIndex() == 3){
              cSearch.applyComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
        }else{
              cSearch.applyComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
        }
        cSearch.setText(null);
    }

    protected void table1JTable_mouseClicked(MouseEvent e) {
        int c = table1.getTable().getSelectedColumn();
        if(e.getClickCount() >= 2 && c != 8){
            cModify_actionPerformed(null);
        }
    }

    private void insertInvoice(){

         double total = 0,guarantors = 0,grandtotal = 0;
         CDate datg = new CDate();
         String encno = "",invno = "",version = "";

         for(int i = 0; i < model1.getRowCount();i++){
             if(model1.getValueAt(i, 8).equals(Boolean.TRUE)){
                 if(datg.getFormat() != 1){
                     datg.setFormat(1);
                 }
                 encno = model1.getValueAt(i, 1).toString();
                 version = model1.getValueAt(i, 10).toString();

                 if(!CDeclare.SAMEVERSION("encounters","Enc_No",encno,version)){
                     return;
                 }
                 CDeclare.dataAccessor.UpdateDB("update encounters set VERSION = (VERSION+1) WHERE Enc_No = '"+encno+"'");
                 CDeclare.doubleValue.setValue(model1.getValueAt(i,3));
                 total = CDeclare.doubleValue.getDouble();

                 CDeclare.doubleValue.setValue(model1.getValueAt(i,4));
                 guarantors = CDeclare.doubleValue.getDouble();

                 grandtotal = total-guarantors;

                 invno = CDeclare.newNumber.getLastYearMonthNo(datg, "LastTemp");
                 datg.setFormat(2);

     String sql = " Insert into invoice(Inv_No,Inv_Entry_Date,Inv_Kind,Inv_ClientKind,Inv_ClientSubKind,Inv_SubRefNo,Inv_Name,Inv_Date," +
                  "Inv_VatPercent,Inv_SubTotal,Inv_OthersTotal,Inv_Total,EMPIII) values (" +
                  "'" + invno + "'," +
                  "'" + CDeclare.getServerDateMySSQL() + "'," +
                  "'" + "S" + "'," +
                  "'" + "P" + "'," +
                  "'" + "OP" + "'," +
                  "'" + encno + "'," +
                  "'" + model1.getValueAt(i, 9) + "'," +
                  "'" + datg.getText() + "'," +
                  "'" + CDeclare.VAT + "'," +
                  "'" + total + "'," +
                  "'" + guarantors + "'," +
                  "'" + grandtotal + "'," +
                  "'" + CDeclare.EMPNO + "'" + ")";

         CDeclare.dataAccessor.InsertDB(sql);
         insertInvDetails(encno,invno);

             }
         }
    }

    private void insertInvDetails(String encno,String invno){
         CCurrency vat = new CCurrency();

         String invDetSql = "SELECT \n"
                           +"Sec_Account, \n"
                           +"Sec_Department, \n"
                           +"Ch_Kind, \n"
                           +"FORMAT((SUM(Ch_Total)/("+(1+CDeclare.VAT)+")),2), \n"
                           +"FORMAT(SUM(Ch_Total),2) \n"
                           +"FROM \n"
                           +"enccharges \n"
                           +"INNER JOIN medicalsections ON Ch_Section = Sec_No \n"
                           +"WHERE \n"
                           +"Ch_EncNo = '"+encno+"' \n"
                           +"GROUP BY Ch_Kind,Sec_Account,Sec_Department for update \n";
         Vector<?> data = CDeclare.dataAccessor.getData(invDetSql);
         for(int i = 0; i < data.size(); i++){
         Vector<?> v = (Vector<?>) data.elementAt(i);

         CDeclare.myDouble.setValue(v.elementAt(4));
         CDeclare.doubleValue.setValue(v.elementAt(3));

         vat.setDouble(vat.getDouble()+CDeclare.myDouble.getDouble()-CDeclare.doubleValue.getDouble());

         String insSql = "Insert into invoicedetails(Inv_No,Inv_Account,Inv_Department,Inv_Kind,Inv_Total,Inv_TaxTotal) values (" +
                         "'" + invno + "'," +
                         "'" + v.elementAt(0) + "'," +
                         "'" + v.elementAt(1) + "'," +
                         "'" + v.elementAt(2) + "'," +
                         "'" + CDeclare.doubleValue.getDouble() + "'," +
                         "'" + CDeclare.myDouble.getDouble()+ "'" + ")";

          CDeclare.dataAccessor.InsertDB(insSql);
         }
         CDeclare.dataAccessor.UpdateDB("update invoice set Inv_Vat = '"+vat.getDouble()+"' where Inv_No = '"+invno+"'");

         String sqlUpdateEncounter = " Update encounters set \n" +
                                     " Enc_Billed = 'true',\n" +
                                     " Enc_Status = 'D',\n" +
                                     " Enc_BillNo = '" + invno + "'\n " +
                                     " where Enc_No = '" + encno + "'";

         CDeclare.dataAccessor.UpdateDB(sqlUpdateEncounter);
    }

    protected void cMakeBill_actionPerformed(ActionEvent e) {

        int option = (new CConfirms()).getSelection('S',"This operation will generate temporary invoices for selected encounters \n Are you sure ?!!!");
        if(option != 0){
            return;
        }

        CDeclare.dataAccessor.initAutoCommit();
        CDeclare.dataAccessor.beginTransaction();

        insertInvoice();

        if(CDeclare.exCounter == 0){
           CDeclare.dataAccessor.endTransaction();
           fillEncounters();
        }else{
           CDeclare.dataAccessor.rollBack();
           new CAlerts('D');
        }
    }

    protected void cSearch_keyReleased(KeyEvent e) {
        fillEncounters();
    }
    protected void cTDate_keyReleased(KeyEvent e) {
        if(cTDate.getText().length() == 10){
            fillEncounters();
        }
    }

    protected void cRefresh_actionPerformed(ActionEvent e) {
        fillEncounters();
    }

    protected void cTDate_textValueChanged(TextEvent e) {
        if(cTDate.getText().length() == 10){
           fillEncounters();
        }
    }

    protected void cClose_actionPerformed(ActionEvent e) {
        dispose();
    }
    protected void cFDate_keyReleased(KeyEvent e) {
        if(cFDate.getText().length() == 10){
           fillEncounters();
        }
    }
    protected void cFDate_textValueChanged(TextEvent e) {
        if(cFDate.getText().length() == 10){
           fillEncounters();
        }
    }
    protected void cNew_actionPerformed(ActionEvent e) {
        NewEncounter newr = new NewEncounter(getTitle() + " - "+cNew.getText());
        newr.insertMode();
        newr.setOwner(this);
        newr.setVisible(true);
    }
    protected void cModify_actionPerformed(ActionEvent e) {
        int r = table1.getTable().getFocusRow();
        String encno = model1.getValueAt(r,1).toString();

        NewEncounter newr = new NewEncounter(getTitle() + " - "+cModify.getText());
        newr.modifyMode();
        newr.setOwner(this);
        newr.fillEncounter(encno);
        newr.setVisible(true);
    }
    protected void table1JTable_keyPressed(KeyEvent e) {
        if(e.getKeyCode() == KeyEvent.VK_ENTER){
            e.consume();
            cModify_actionPerformed(null);
        }
    }
    protected void cFilter_actionPerformed(ActionEvent e) {
        fillEncounters();
    }
}

class MyOutBillingModel extends PVTableModel {
    private static final long serialVersionUID = 1L;
    public boolean isCellEditable(int iRow, int iCol) {
      try{
          if(iCol == 8){
              return true;
          }
      }catch(Exception ex){}
      return false;
    }

4 个答案:

答案 0 :(得分:2)

如果你知道MVC是什么,你就走在正确的轨道上。您可能还想查看MVVM,因为它具有相同的目标。让我们分解一下:

  • 模型 - 具有getter / setter的简单类,没有关于存储的逻辑或细节
  • 视图 - 以某种有条理的方式显示模型数据的类或页面
  • Controller - 从View接受操作,操作Model并将控件转移到新View(将其传递给Model)的对象

一般来说,我们要做的是首先将数据“建模”为没有业务逻辑,UI概念或存储依赖关系(如SQL)的类。它们是带有数据的getter / setter的简单对象。然后设计“视图”类/页面,以便他们知道如何读取这些模型对象并以任何有意义的方式显示它们。但是视图不知道这些模型对象的存储位置和方式。将视图和模型放在一起的粘合剂是“控制器”,这就是它具有该名称的原因。通常,用户与视图交互以获取/更改数据,然后视图使用“get-employee”之类的“操作”调用控制器。控制是唯一知道动作意味着什么和做什么的事。在简单的情况下,控制器将从存储中获取数据,然后决定下一个视图应该为用户提供什么。它从存储中加载模型对象,构造视图,并将模型传递给呈现它的视图。这里的关键点是第一个视图不知道控制器将要做什么,也不知道下一个视图将是什么。这就是控制器的全部业务。

因此,在您的情况下,您可能会有一些员工视图,例如NewEmployee,EmployeeDetails,EmployeeListing。然后你可能有一个类似员工的模型。您应该能够在没有SQL的情况下构建这些视图。这就是你知道你做得对的方式。然后,您将像EmployeeController一样引入Controller。然后,将按钮和按钮中的事件绑定到Controller上的操作方法。所以你可以添加这样的方法:

View listEmployees()
View createEmployee(Employee e)
View getEmployee(long id)
View deleteEmployee(long id)

然后,您的控制器应该是与存储交互的唯一组件,然后决定“下一个”视图应该是什么。这就是它返回View而不是Model的原因。例如,该方法可能如下所示:

public class EmployeeController {
  . . .
  public View listEmployees() {
    List<Employee> employees = storage.getAllEmployees();
    return new EmployeeListing(employees); 
  }
}

这是基本概念以及分离的工作原理。在大多数真正的MVC / MVVC框架中,围绕动作和视图的映射更为复杂。

注意我还添加了一个“存储”对象,这样即使Controller也不知道使用了SQL。这将引导您进入像DAO这样的概念,如果您愿意,可以将实际存储详细信息从应用程序的其余部分抽象出来。

答案 1 :(得分:1)

模型视图控制器(MVC)是您可能想要使用的概念。有关详情,请访问:How to correctly implement swing - 请在此链接中查看已接受的答案。

在您的情况下,除非必须,否则不要破坏正常工作的代码......请记住,这样的设计更改可能会花费更多的成本,而不是正在运行的系统。

答案 2 :(得分:1)

首先将所有SQL代码分离为具有接口和实现的单独包。你可以在没有用户界面的情况下测试那些并将它们放在一边。

然后我建议让所有Swing类成为另一个独立的包。不要在Swing类中调用new来创建和附加监听器。相反,提供一种通过构造函数或setter传递它们的机制。注入这些依赖项。

模型类应该代表您在没有UI或数据库的情况下解决的问题。看看他们是否这样做。

最后让控制器包具有实例化侦听器和视图的类,为视图提供所需的侦听器,并通过操作模型和持久性对象来实现用例。

那是MVC。

答案 3 :(得分:0)

  从我读到的内容中我们应该将逻辑与设计分开。   说实话,我不知道该怎么做,或者如何理解它。

主要原因(超出通常的“清洁代码和架构”)为什么你想要这样做,背后的原则可以在一个例子中得到最好的解释。

假设您有一个很好的工作桌面UI应用程序。现在,您要创建完全相同的应用程序的Web版本。

当GUI应用程序已将所有业务逻辑和所有数据库访问放在表单中(或等效的任何内容)时,您遇到了问题。 因为所有内容都紧密地耦合在一起,所以几乎不可能重复使用GUI应用程序中的任何内容。所以你开始复制你的代码,这真的是坏事。这样你就得到了两个必须维护的代码库。

为了充分利用重用游戏,您希望(至少)将UI与基础业务逻辑分开。虽然我们在这里,但再次分割抽象级别并提取数据模型也不是一个坏主意。

现在我们可以这样做:

+-----------+
|  Web UI   |<<------+
+-----------+        |
                     |       +-----------+        +------------+
                     +----->>| Biz Logic |<<---->>| Data Model |
                     |       +-----------+        +------------+
+-----------+        |
|    GUI    |<<------+
+-----------+

要实现这一目标,您必须做某些事情:

  1. 相应地拆分代码以具有独立(代码)模块
  2. 从右手部分删除所有依赖关系到左手部分(即逻辑不应该知道关于UI细节的任何信息)
  3. 有条理的框架(如MVC或MVVM或其他)可以被视为最佳实践工具带,以支持您在逻辑和UI部件之间的接口上执行此操作。这些概念已经过验证,并且已经成熟了很长时间。严格来说,您不需要遵循这些概念,但强烈建议使用,因为它们不仅有助于架构决策,而且使日常编码工作变得更加容易,因为现有的框架实现(再次,根据使用的语言等不同而不同)。