程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Groovy >> Groovy 探索之閉包 二

Groovy 探索之閉包 二

編輯:Groovy

自從編程進入面向對象時代,大家都在孜孜不倦的同面向過程作斗爭。想想 看,我們大家都愛使用、並且奉為經典的Bang of Four的模式,有多少是針對if …else…這樣的語句進行優化的。但是,即使我們做了如上述的大量努力,一些 面向過程的語言仍然站在那裡嘲笑著我們的無能。

一.解決經典的try…catch問題

不錯,try…catch就是我們經常碰到的經典問題。看一看下面的一段代碼:

  publicstatic List getDataFromExcel(String fileName,int beginRow,int endRow,ModelIntf model,String[] inputCols,List messages)
{
    List retn = new ArrayList();
    InputStream fs = null;
    Workbook wb =null;
    try
    {
      fs = new FileInputStream(fileName);
      wb = Workbook.getWorkbook(fs);
      Sheet sh = wb.getSheet(0);
      if (logger.isDebugEnabled()) {
        logger.debug("SheetName = " + sh.getName());
      }
      int len = inputCols.length;
      String[] values = new String[len];
      ……
    }
    catch(Exception e)
    {
      if (logger.isDebugEnabled()) {
        logger.debug("ExcelReader", e);
      }
      e.printStackTrace();
    }
    finally
    {
      try {
        if(fs!=null)
        {
          fs.close();
        }
        if(wb!=null)
        {
          wb.close();
        }
      } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      }
    }
   return retn;
}

上面的代碼是我從使用JXL API讀取Excel文檔的一段代碼,為了關注我們正 在談論的問題,我將其中一段代碼刪掉。這樣,我們可以清楚的看到代碼中的 try…catch語句,以及它們所起的作用。

在我的這段讀取Excel文檔的代碼中,有五六個類似這樣的方法。它們都要使 用這樣的try…catch語句,而且它們除了在try語句段裡面做的事情不同以外, 在catch語句段和finally語句段裡做的事情都是一樣的。

難道我們都要把相同的catch語句段和finally語句段拷貝到各個方法裡去嗎 ?如果需要維護catch語句段和finally語句段的時候,是不是每個方法裡都去修 改?

看到這裡,你一定想起更為經典的JDBC API的使用,並且拿出經典的Spring 對JDBC的解決方案:JdbcTemplate。

不錯,JdbcTemplate解決方案使用了Java內部類,的確是個不錯的解決方案 ,我們使用起來感覺也相當的不錯。但是,我前面在《Groovy探索之閉包 一》 中說,Java內部類使用起來相當晦澀和繁瑣,實際上並不好用。看到這裡,Java 的扇子們不要忙著給我扔臭雞蛋,看看閉包的解決方案先。

下面的代碼是我在Grails平台使用Gsql操作存儲過程的一段代碼,具體用來 控制存儲過程的事務:

def doCallWithTransaction(Closure closure) throws DataInTransactionException
{
   def conn = dataSource.getConnection()
   conn.setAutoCommit(false)
   try
   {
    StoredProcedureDao dao = new StoredProcedureDao(conn)
    closure.call(dao)
    conn.commit()
   }
   catch(Exception e)
   {
    conn.rollback()
    thrownew DataInTransactionException(e)
   }
   finally
   {
    conn.close()
   }
}

可以看到,這段代碼除了給出了相同的catch代碼段和finally代碼段,而將 不同的代碼執行都交給了一個閉包對象closure。

closure.call(dao)

那麼,我們怎麼使用這個方法呢?

//inputList is two or more inputs
def callWithTransaction(String procName,List inputList) throws DataInTransactionException
{
   this.doCallWithTransaction{
    dao ->
      inputList.each{
        dao.call(procName,it)
     }
   }
}

看看上面的代碼,當使用一個domain對象的集成對存儲過程進行調用的時候 ,我們需要考慮該操作的事務。通過調用doCallWithTransaction,我們輕松就 控制了存儲過程的事務。這樣的編碼的確既簡單又直觀。

下面我們來看看怎麼解決上面的操作Excel文檔的問題。

//closure對象包裝的是一段代碼段,它具體對Excel文檔進行操作

def getDataFromExcel(Closure closure)
{
    List retn = new ArrayList();
    InputStream fs = null;
    Workbook wb =null;
    try
    {
      fs = new FileInputStream(fileName);
      wb = Workbook.getWorkbook(fs);
      Sheet sh = wb.getSheet(0);
      if (logger.isDebugEnabled()) {
        logger.debug("SheetName = " + sh.getName());
      }

//上面的代碼在每一個操作Excel的方法中都是相同的,可以照寫

//下面是每一個操作Excel的方法中不同的代碼,就把它封裝在閉包裡

retn = closure.call(sh)

//注意:由於閉包封裝的代碼段需要操作Sheet對象,所以必須把sh對象傳遞 給閉包對象。

    }
    catch(Exception e)
    {
      if (logger.isDebugEnabled()) {
        logger.debug("ExcelReader", e);
      }
       e.printStackTrace();
    }
    finally
    {
      try {
        if(fs!=null)
        {
          fs.close();
        }
        if(wb!=null)
        {
          wb.close();
        }
      } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      }
    }
   return retn;
}

就這樣,我們就輕松的完成了對操作Excel文檔的try…catch代碼的封裝。下 面使用這個方法:

//這是我們最初看到的原始方法,我們使用了閉包對try…catch代碼段進行 封裝以後,就可以在這個方法中使用它。

public static List getDataFromExcel(String fileName,int beginRow,int endRow,ModelIntf model,String[] inputCols,List messages)
{
   getDataFromExcel
{
   sh ->
      int len = inputCols.length;
      String[] values = new String[len];
      //開始使用sh對Excel文檔進行操作
      ……
   //在最後記得返回一個List對象
}
}

這樣,我們就可以在該類的四五個與“getDataFromExcel(String fileName,int beginRow,int endRow,ModelIntf model,String[] inputCols,List messages)”功能相同的方法中使用上面的閉包了。

二.靈活的閉包

閉包不但在解決諸如try…catch這樣的面向過程的語句中顯得更為簡單和易 懂;同樣,它還有比內部類更為靈活的一面。因為閉包可以定義在代碼的很多地 方,甚至是一個方法體內,這樣使得它方法本身的變量,從而不必需要向閉包對 象傳遞很多的變量。下面試舉一例來看看這個問題。

我們在寫標簽的時候,標簽經常有一些必填和非必填的屬性。對於非必填的 那些屬性,如“id”和“alt”等等,我們需要判斷它們是否有值。

def id = attrs["id"]
    if(id!=null&&!(id.trim()==''))
    {
       sb.append(" id=\"")
       sb.append(id)
       sb.append("\"")
}
    def size = attrs["size"]
    if(id!=null&&!(id.trim()==''))
    {
       sb.append(" size=\"")
       sb.append(size)
       sb.append("\"")
}

遇到了這樣的代碼,大家都知道使用閉包了。如下:

def setProperty = {
       propertyName ->
           def propertyValue = attrs["${propertyName}"]
                             if(propertyValue!=null&&!

(propertyValue.trim()==''))
           {
              sb.append(" ${propertyName}=\"")
              sb.append(propertyValue)
              sb.append("\"")
           }
}

可以看到,該閉包只能一個輸入參數,但實際上還有“attrs”和“sb”兩個 參數沒有通過閉包的輸入參數獲得。這是因為我們的setProperty閉包直接定義 在該標簽實現體內,使得閉包可以直接使用這兩個變量。下面是對該閉包的調用 :

  def propertyNames = ['disabled','size','maxlength','readonly',
'tabindex','alt','id','width','height',
'onChange','onClick']
   propertyNames.each{
      setProperty.call(it)
}

怎麼樣,閉包的使用是不是靈活得多?

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved