亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > Java > 正文

Java代碼重用之功能與上下文重用

2024-07-14 08:40:47
字體:
來源:轉載
供稿:網友

我幾乎不需要討論為什么重用代碼是有利的。代碼重用通常使得程序開發更加快速,并使得 BUG 減少。一旦一段代碼被封裝和重用,那么只需要檢查很少的一段代碼即可確保程序的正確性。如果在整個應用程序中只需要在一個地方打開和關閉數據庫連接,那么確保連接是否正常則容易的多。但我確信這些你已經都知道了。

有兩種類型的重用代碼,我稱它們為重用類型:

  • 功能重用(Action Reuse)
  • 上下文重用(Context Reuse)

第一種類型是功能重用,這是最常見的一種重用類型。這也是大多數開發人員掌握的一種。即重用一組后續指令來執行某種操作。

第二種類型是上下文重用,即不同功能或操作代碼在相同上下文之間,將相同上下文封裝為重用代碼(這里的上下文指的是一系列相同的操作指令)。雖然它在控制反轉中越來越受歡迎但它并不常見。而且,上下文重用并沒有被明確的描述,因此它并沒有像功能重用一樣被系統的使用。我希望你看完這篇文章之后會有所改變。

功能重用

功能重用是最常見的重用類型。它是一組執行某種操作指令的重用。下面兩個方法都是從數據庫中讀取數據:

public List readAllUsers(){  Connection connection = null;  String sql = "select * from users";  List users = new ArrayList();  try{    connection = openConnection();    PreparedStatement statement = connection.prepareStatement(sql);    ResultSet result = statement.executeQuery();    while(result.next()){      // 重用代碼      User user = new User();      user.setName (result.getString("name"));      user.setEmail(result.getString("email"));      users.add(user);      // END 重用代碼    }    result.close();    statement.close();    return users;  }  catch(SQLException e){    //ignore for now  }  finally {    //ignore for now  }}public List readUsersOfStatus(String status){  Connection connection = null;  String sql = "select * from users where status = ?";  List users = new ArrayList();  try{    connection = openConnection();    PreparedStatement statement = connection.prepareStatement(sql);    statement.setString(1, status);    ResultSet result = statement.executeQuery();    while(result.next()){      // 重用代碼      User user = new User();      user.setName (result.getString("name"));      user.setEmail(result.getString("email"));      users.add(user);      // END 重用代碼    }    result.close();    statement.close();    return users;  }  catch(SQLException e){    //ignore for now  }  finally {    //ignore for now  }}

對于有經驗的開發人員來說,可能很快就能發現可以重用的代碼。上面代碼中注釋“重用代碼”的地方是相同的,因此可以封裝重用。這些是將用戶記錄讀入用戶實例的操作??梢詫⑦@些行代碼封裝到他們自己的方法中,例如:

// 將相同操作封裝到 readUser 方法中private User readUser(ResultSet result) throws SQLException {  User user = new User();  user.setName (result.getString("name"));  user.setEmail(result.getString("email"));  users.add(user);  return user; }

現在,在上述兩種方法中調用readUser()方法(下面示例只顯示第一個方法):

public List readAllUsers(){  Connection connection = null;  String sql = "select * from users";  List users = new ArrayList();  try{    connection = openConnection();    PreparedStatement statement = connection.prepareStatement(sql);    ResultSet result = statement.executeQuery();    while(result.next()){      users.add(readUser(result))    }    result.close();    statement.close();    return users;  }  catch(SQLException e){    //ignore for now  }  finally {    //ignore for now  }}

readUser()方法也可以在它自己的類中使用修飾符private隱藏。

以上就是關于功能重用的內容。功能重用是將一組執行特定操作的指令通過方法或類封裝它們來達到重用的目的。

參數化操作

有時,你希望重用一組操作,但是這些操作在使用的任何地方都不完全相同。例如readAllUsers()和readUsersOfStatus()方法都是打開一個連接,準備一條語句,執行它,并循環訪問結果集。唯一的區別是readUsersOfStatus()需要在PreparedStatement上設置一個參數。我們可以將所有操作封裝到一個readUserList()方法。如下所示:

private List readUserList(String sql, String[] parameters){  Connection connection = null;  List users = new ArrayList();  try{    connection = openConnection();    PreparedStatement statement = connection.prepareStatement(sql);    for (int i=0; i < parameters.length; i++){      statement.setString(i, parameters[i]);    }    ResultSet result = statement.executeQuery();    while(result.next()){      users.add(readUser(result))    }    result.close();    statement.close();    return users;  }  catch(SQLException e){    //ignore for now  }  finally {    //ignore for now  }}

現在我們從readAllUsers()readUsersOfStatus()調用readUserList(...)方法,并給定不同的操作參數:

public List readAllUsers(){  return readUserList("select * from users", new String[]{});}public List readUsersWithStatus(String status){  return readUserList("select * from users", new String[]{status});}

我相信你可以找出其他更好的辦法來實現重用功能,并將他們參數化使得更加好用。

上下文重用

上下文重用與功能重用略有不同。上下文重用是一系列指令的重用,各種不同的操作總是在這些指令之間進行。換句話說,重復使用各種不同行為之前和之后的語句。因此上下文重用通常會導致控制風格類的反轉。上下文重用是重用異常處理,連接和事務生命周期管理,流迭代和關閉以及許多其他常見操作上下文的非常有效的方法。

這里有兩個方法都是用 InputStream 做的:

public void printStream(InputStream inputStream) throws IOException {  if(inputStream == null) return;  IOException exception = null;  try{    int character = inputStream.read();    while(character != -1){      System.out.print((char) character); // 不同      character = inputStream.read();    }  }  finally {    try{      inputStream.close();    }    catch (IOException e){      if(exception == null) throw e;    }  }}public String readStream(InputStream inputStream) throws IOException {  StringBuffer buffer = new StringBuffer(); // 不同  if(inputStream == null) return;  IOException exception = null;  try{    int character = inputStream.read();    while(character != -1){      buffer.append((char) character); // 不同      character = inputStream.read();    }    return buffer.toString(); // 不同  }  finally {    try{      inputStream.close();    }    catch (IOException e){      if(exception == null) throw e;    }  }}

兩種方法與流的操作是不同的。但圍繞這些操作的上下文是相同的。上下文代碼迭代并關閉 InputStream。上述代碼中除了使用注釋標記的不同之處外都是其上下文代碼。

如上所示,上下文涉及到異常處理,并保證在迭代后正確關閉流。一次又一次的編寫這樣的錯誤處理和資源釋放代碼是很繁瑣且容易出錯的。錯誤處理和正確的連接處理在 JDBC 事務中更加復雜。編寫一次代碼并在任何地方重復使用顯然會比較容易。

幸運的是,封裝上下文的方法很簡單。 創建一個上下文類,并將公共上下文放入其中。 在上下文的使用中,將不同的操作指令抽象到操作接口之中,然后將每個操作封裝在實現該操作接口的類中(這里稱之為操作類),只需要將該操作類的實例插入到上下文中即可??梢酝ㄟ^將操作類的實例作為參數傳遞給上下文對象的構造函數,或者通過將操作類的實例作為參數傳遞給上下文的具體執行方法來完成。

下面展示了如何將上述示例分隔為上下文和操作接口。StreamProcessor(操作接口)作為參數傳遞給StreamProcessorContext的processStream()方法。

// 流處理插件接口public interface StreamProcessor {  public void process(int input);}// 流處理上下文類public class StreamProcessorContext{  // 將 StreamProcessor 操作接口實例化并作為參數  public void processStream(InputStream inputStream, StreamProcessor processor) throws IOException {    if(inputStream == null) return;    IOException exception = null;    try{      int character = inputStream.read();      while(character != -1){        processor.process(character);        character = inputStream.read();      }    }    finally {      try{        inputStream.close();      }      catch (IOException e){        if(exception == null) throw e;        throw exception;      }    }  }}

現在可以像下面示例一樣使用StreamProcessorContext類打印出流內容:

FileInputStream inputStream = new FileInputStream("myFile");// 通過實現 StreamProcessor 接口的匿名子類傳遞操作實例new StreamProcessorContext().processStream(inputStream, new StreamProcessor(){  public void process(int input){    System.out.print((char) input);  }});

或者像下面這樣讀取輸入流內容并添加到一個字符序列中:

public class StreamToStringReader implements StreamProcessor{  private StringBuffer buffer = new StringBuffer();  public StringBuffer getBuffer(){    return this.buffer;  }  public void process(int input){    this.buffer.append((char) input);  }}FileInputStream inputStream = new FileInputStream("myFile");StreamToStringReader reader = new StreamToStringReader();new StreamProcessorContext().processStream(inputStream, reader);// do something with input from stream.reader.getBuffer();

正如你所看到的,通過插入不同的StreamProcessor接口實現來對流做任何操作。一旦StreamProcessorContext被完全實現,你將永遠不會有關于未關閉流的困擾。

上下文重用非常強大,可以在流處理之外的許多其他環境中使用。一個明顯的用例是正確處理數據庫連接和事務(open - process - commit()/rollback() - close())。其他用例是 NIO 通道處理和臨界區中的線程同步(lock() - access shared resource - unlock())。它也能將API的已檢查異常轉換為未檢查異常。

當你在自己的項目中查找適合上下文重用的代碼時,請查找以下操作模式:

  • 常規操作之前(general action before)
  • 特殊操作(special action)
  • 常規操作之后(general action after)

當你找到這樣的模式時,前后的常規操作就可能實現上下文重用。

上下文作為模板方法

有時候你會希望在上下文中有多個插件點。如果上下文由許多較小的步驟組成,并且你希望上下文的每個步驟都可以自定義,則可以將上下文實現為模板方法。模板方法是一種 GOF 設計模式?;旧?,模板方法將算法或協議分成一系列步驟。一個模板方法通常作為一個單一的基類實現,并為算法或協議中的每一步提供一個方法。要自定義任何步驟,只需創建一個擴展模板方法基類的類,并重寫要自定義的步驟的方法。

下面的示例是作為模板方法實現的 JdbcContext。子類可以重寫連接的打開和關閉, 以提供自定義行為。必須始終重寫processRecord(ResultSet result)方法, 因為它是抽象的。此方法提供不屬于上下文的操作,在使用JdbcContext的不同情況下的操作都不相同。這個例子不是一個完美的JdbcContext。它僅用于演示在實現上下文時如何使用模板方法。

public abstract class JdbcContext {  DataSource dataSource = null;  // 無參數的構造函數可以用于子類不需要 DataSource 來獲取連接  public JdbcContext() {  }  public JdbcContext(DataSource dataSource){    this.dataSource = dataSource;  }  protected Connection openConnection() throws SQLException{    return dataSource.getConnection();  }  protected void closeConnection(Connection connection) throws SQLException{    connection.close();  }  // 必須始終重寫 processRecord(ResultSet result) 方法  protected abstract processRecord(ResultSet result) throws SQLException ;  public void execute(String sql, Object[] parameters) throws SQLException {    Connection    connection = null;    PreparedStatement statement = null;    ResultSet     result   = null;    try{      connection = openConnection();      statement = connection.prepareStatement(sql);      for (int i=0; i < parameters.length; i++){        statement.setObject(i, parameters[i]);      }      result = statement.executeQuery();      while(result.next()){        processRecord(result);      }    }    finally {      if(result   != null){        try{          result.close();        }        catch(SQLException e) {          /* ignore */        }      }      if(statement != null){        try{          statement.close();        }        catch(SQLException e) {          /* ignore */        }      }      if(connection != null){        closeConnection(connection);      }    }  }}

這是擴展 JdbcContext 以讀取用戶列表的子類:

public class ReadUsers extends JdbcContext{  List users = new ArrayList();  public ReadUsers(DataSource dataSource){    super(dataSource);  }  public List getUsers() {    return this.users;  }  protected void processRecord(ResultSet result){    User user = new User();    user.setName (result.getString("name"));    user.setEmail(result.getString("email"));    users.add(user);  }}

下面是如何使用 ReadUsers 類:

ReadUsers readUsers = new ReadUsers(dataSource);readUsers.execute("select * from users", new Object[0]);List users = readUsers.getUsers();

如果ReadUsers類需要從連接池獲取連接并在使用后將其釋放回該連接池,則可以通過重寫openConnection()closeConnection(Connection connection)方法來插入該連接。

注意如何通過方法重寫插入操作代碼。JdbcContext的子類重寫processRecord方法以提供特殊的記錄處理。 在StreamContext示例中,操作代碼封裝在單獨的對象中,并作為方法參數提供。實現操作接口StreamProcessor的對象作為參數傳遞給StreamContext類的processStream(...)方法。

實施上下文時,你可以使用這兩種技術。JdbcContext類可以將實現操作接口的ConnectionOpener和ConnectionCloser對象作為參數傳遞給execute方法,或作為構造函數的參數。就我個人而言,我更喜歡使用單獨的操作對象和操作接口,原因有兩個。首先,它使得操作代碼可以更容易單獨進行單元測試;其次,它使得操作代碼在多個上下文中可重用。當然,操作代碼也可以在代碼中的多個位置使用,但這只是一個優勢。畢竟,在這里我們只是試圖重用上下文,而不是重用操作。

結束語

現在你已經看到了兩種不同的重用代碼的方法。經典的功能重用和不太常見的上下文重用。希望上下文的重用會像功能重用一樣普遍。上下文重用是一種非常有用的方法,可以從 API 的底層細節(例如JDBC,IO 或 NIO API等)中抽象出代碼。特別是如果 API 包含需要管理的資源(打開和關閉,獲得并返回等)。

persistence/ORM API、Mr.Persister 利用上下文重用來實現自動連接和事務生命周期管理。 這樣用戶將永遠不必擔心正確打開或關閉連接,或提交或回滾事務。Mr.Persister 提供了用戶可以將他們的操作插入的上下文。 這些上下文負責打開,關閉,提交和回滾。

流行的 Spring 框架包含大量的上下文重用。 例如 Springs JDBC 抽象。 Spring 開發人員將其使用上下文重用作為“控制反轉”。 這不是 Spring 框架使用的唯一一種控制反轉類型。 Spring 的核心特性是依賴注入 bean 工廠或“應用程序上下文”。 依賴注入是另一種控制反轉。

以上所述是小編給大家介紹的Java代碼重用之功能與上下文重用,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對VeVb武林網網站的支持!


注:相關教程知識閱讀請移步到JAVA教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
精品视频在线播放色网色视频| 国产欧美一区二区三区在线| 97在线视频观看| 久久九九全国免费精品观看| 57pao国产成人免费| 欧美午夜片欧美片在线观看| 日韩福利在线播放| 日韩激情av在线免费观看| 性欧美在线看片a免费观看| 欧美性猛交xxxx久久久| 欧美日韩亚洲高清| 一本一本久久a久久精品牛牛影视| 国产精品视频区| 国产精品丝袜视频| 日韩国产高清视频在线| 亚洲精品国产综合久久| 午夜免费久久久久| 91av在线网站| 国产91在线高潮白浆在线观看| 中文字幕9999| 亚洲视频777| 不卡毛片在线看| 91国产精品视频在线| 国产一区二区三区毛片| 九九九久久久久久| 成人黄色在线观看| 亚洲网站在线观看| 日本欧美国产在线| 欧美精品videossex性护士| 国产99在线|中文| www高清在线视频日韩欧美| 91a在线视频| 国产成人精品免高潮在线观看| 欧美特黄级在线| 高清欧美电影在线| 亚洲天堂视频在线观看| 97色在线播放视频| 欧美黄网免费在线观看| 欧美午夜激情视频| 91精品国产高清自在线看超| 久久久国产精品免费| 91黑丝在线观看| 成人av电影天堂| 亚洲精品国产福利| 亚洲天堂日韩电影| 国产精品九九久久久久久久| 久久久久久久爱| 亚洲精品av在线播放| 亚洲精品wwwww| 深夜福利日韩在线看| 青草青草久热精品视频在线观看| 欧美大片网站在线观看| 国产亚洲精品91在线| 国产日韩欧美在线视频观看| 国产精品亚洲片夜色在线| 国产精品电影在线观看| 亚洲无限av看| 日韩美女免费观看| 欧美成人午夜剧场免费观看| 最近日韩中文字幕中文| www.日韩av.com| 亚洲free嫩bbb| 欧美三级xxx| 亚洲国模精品私拍| 成人免费网站在线看| 国产日韩欧美视频| 国产精品爱久久久久久久| 成人精品久久一区二区三区| 国产日韩av高清| 国产精品久久久久久久久久ktv| 亚洲欧美另类自拍| 亚洲国产精品va在看黑人| 亚洲精品国产成人| 欧美亚洲视频一区二区| 欧美精品在线播放| 亚洲国产另类久久精品| 欧洲成人在线观看| 久久久精品视频在线观看| 91禁外国网站| 日韩国产在线看| 8090理伦午夜在线电影| 国产亚洲视频在线观看| 亚洲国产精品一区二区三区| 国产日产久久高清欧美一区| 国产97在线亚洲| 日韩欧美中文免费| 亚洲精品久久久久久久久久久久| 成人激情免费在线| 欧美日韩中文在线观看| 精品国产精品三级精品av网址| 美女视频黄免费的亚洲男人天堂| 精品国产乱码久久久久久婷婷| 国产成人精品电影久久久| 欧美高清在线观看| 日韩一区二区福利| 九色91av视频| 精品福利在线看| 色偷偷综合社区| 国产精品一二三视频| 欧美黄网免费在线观看| 亚洲人成人99网站| 国产免费一区二区三区香蕉精| 日韩专区在线播放| 国产日韩欧美日韩| 国产91九色视频| 亚洲国产精品va在线看黑人动漫| 高清一区二区三区四区五区| 国模gogo一区二区大胆私拍| 麻豆国产精品va在线观看不卡| 国产美女直播视频一区| 亚洲精品国产suv| 欧美日韩免费在线| 欧美老女人性视频| 91久久久久久| 精品国产老师黑色丝袜高跟鞋| 亚洲男人第一av网站| 日韩最新av在线| 欧美精品在线播放| 91精品久久久久久久久| 国产午夜精品免费一区二区三区| 欧美日韩免费区域视频在线观看| 亚洲综合视频1区| 97在线精品国自产拍中文| 欧日韩在线观看| 中文字幕欧美亚洲| 国产日本欧美一区二区三区| 欧美理论电影在线观看| 久久精品视频在线| 免费不卡在线观看av| 欧美孕妇孕交黑巨大网站| 91精品视频在线看| 欧美电影免费观看大全| 日本欧美在线视频| 91精品久久久久久久久| 久久久成人精品视频| 亚洲中国色老太| 91精品国产电影| 色无极影院亚洲| 91日本在线观看| 亚洲午夜久久久影院| 法国裸体一区二区| 欧美老肥婆性猛交视频| 亚洲福利视频免费观看| 美女久久久久久久久久久| 亚洲理论片在线观看| 精品久久久久久久久久| 97精品视频在线观看| 欧美一区二区大胆人体摄影专业网站| 亚洲人成电影网站| 成人免费在线视频网站| 国产精品看片资源| 国产成人91久久精品| 欧美电影免费观看电视剧大全| 亚洲性猛交xxxxwww| 日韩美女av在线| 九九久久久久99精品| 91在线播放国产| 亚洲综合最新在线| 美女视频黄免费的亚洲男人天堂| 97精品久久久中文字幕免费| 57pao国产精品一区| 91成人在线播放| 久久精品国产96久久久香蕉| 中文字幕久精品免费视频|