-1

我有 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

4 回答 4

2

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

  • 模型 - 带有 getter/setter 的简单类,没有关于存储的逻辑或细节
  • 视图 - 以某种有组织的方式显示模型数据的类或页面
  • Controller - 接受来自 View 的操作、操作 Model 并将控制权转移到新 View 的对象(传递给它一个 Model)

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

因此,在您的情况下,您可能有一些员工视图,例如 NewEmployee、EmployeeDetails、EmployeeListing。然后你可能有一个像员工这样的模型。您应该能够在没有 SQL 的情况下构建这些视图。这样你就知道你做对了。然后,您引入 Controller,如 EmployeeController。然后,将视图中的按钮按下和单击事件与控制器上的操作方法联系起来。所以你可以添加这样的方法:

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

你的控制器应该是唯一与存储交互的组件,然后决定“下一个”视图应该是什么。这就是为什么它返回一个视图而不是模型。例如,该方法可能看起来像这样:

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

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

请注意,我还添加了一个“存储”对象,这样即使控制器也不知道使用了 SQL。这将引导您了解诸如 DAO 之类的概念,即如果您愿意,如何将实际存储细节从应用程序的其余部分中抽象出来。

于 2013-10-26T11:59:39.230 回答
1

模型视图控制器 (MVC) 是您可能想要使用的概念。更多关于这里的信息:如何正确实施摇摆- 请检查此链接中接受的答案。

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

于 2013-10-26T11:30:06.320 回答
1

首先将所有 SQL 代码分离到具有接口和实现的单独包中。您可以在没有 UI 的情况下测试那些并将它们放在一边。

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

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

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

这就是MVC。

于 2013-10-26T11:34:46.410 回答
0

根据我的阅读,我们应该将逻辑与设计分开。老实说,我不知道该怎么做,或者如何理解它。

您想要这样做的主要原因(除了通常的“更简洁的代码和架构”)以及背后的原则可以在一个示例中得到最好的解释。

假设,您有一个运行良好的桌面 UI 应用程序。现在您想要创建相同应用程序的 Web 版本。

当 GUI 应用程序将所有业务逻辑和所有 DB 访问放入表单(或任何等价物)中时,您就会遇到问题。因为所有东西都紧密耦合在一起,所以几乎不可能重用 GUI 应用程序中的任何东西。所以你开始复制你的代码,这真的很糟糕。这样,您最终会得到两个必须维护的代码库。

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

现在我们可以这样做:

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

为此,您必须做一些事情:

  1. 相应地拆分代码以具有独立的(代码)模块
  2. 删除从右手部分到左手部分的所有依赖项(即逻辑不应该知道关于 UI 细节的任何事情)

像 MVC 或 MVVM 或其他有条理的框架可以被视为最佳实践工具带,以支持您在逻辑和 UI 部分之间的接口处执行此操作。这些概念经过验证并在很长一段时间内成熟。严格来说,您不需要遵循这些概念,但强烈建议您这样做,因为它们不仅有助于架构决策,而且由于现有的框架实现(再次,这因使用的语言等而异)。

于 2013-10-26T11:52:05.077 回答