Scala的啟發(fā):代碼本質(zhì)與過(guò)度包裝的平衡
本文來(lái)自王在祥先生的博客(blogspot),原標(biāo)題為《重溫 scala》。對(duì)于代碼簡(jiǎn)潔化與可讀性矛盾的解決,Scala語(yǔ)言無(wú)疑是一種啟發(fā)。以下為原文:
51CTO編輯推薦:Scala編程語(yǔ)言專(zhuān)題
#t#最近,在閱讀項(xiàng)目組的代碼時(shí),再次陷入了苦思:基于Java、Hibernate的商業(yè)應(yīng)用開(kāi)發(fā)確實(shí)陷入到了一個(gè)很痛苦的境界,這個(gè)問(wèn)題實(shí)際上正在進(jìn)行開(kāi)發(fā)的大部分開(kāi)發(fā)人員都不會(huì)感覺(jué)到,因?yàn)榇蠹叶加X(jué)得這就是正常的程序員生活。再說(shuō),幾乎所有的框架都在力捧Hibernate這樣的ORM工具能夠極大的簡(jiǎn)化程序的開(kāi)發(fā),要不,你去使用使用 JDBC試試。
在Java中基于JDBC編程,確實(shí)有些匯編語(yǔ)言的感覺(jué),摘抄一份實(shí)際項(xiàng)目的代碼:
- public void updateTerminalStatus(String timeout, String transtime) throws Exception {
- //更新超時(shí)終端的狀態(tài)值(9為超時(shí))
- String updateTerminal = "update T_Terminal set Status=9 "
- + " where TerminalID in (select TerminalID from T_Terminal where TO_CHAR(latestDate, 'yyyymmddHH24miss')<=? and (status=0 or status=2) ) ";
- //查詢(xún)超時(shí)的終端信息,且該終端號(hào)在故障表中沒(méi)有未處理的超時(shí)記錄,(包含該終端號(hào)在故障表中沒(méi)有超時(shí)記錄的情況)。
- String queryTimeOutTerm = "SELECT te.TerminalID FROM T_TERMINAL te WHERE te.status=9 ";
- String queryTerm = "SELECT terminalid FROM T_TROUBLELOG tr " +
- " WHERE tr.DeviceID='COMMMODULE' AND tr.TroubleID='CommTimeOut' " +
- " AND tr.SolveDate IS NULL GROUP BY terminalid ";
- //向故障表中添加一條新的故障記錄。
- String insertTrouble = "insert into T_TroubleLog (LogID, TerminalID, DeviceID, TroubleID, HappenDate) "
- + " values (?, ?, 'COMMMODULE', 'CommTimeOut', TO_DATE(?,'yyyymmddHH24miss')) ";
- //更新故障表中的終端超時(shí)記錄,該終端號(hào)的狀態(tài)已經(jīng)不超時(shí),則超時(shí)故障處理完畢。
- String updateTrouble = "UPDATE T_TROUBLELOG tl SET tl.SolveDate=TO_DATE(?,'yyyymmddHH24miss') " +
- " WHERE terminalid IN" +
- " (SELECT tm.terminalid FROM T_TERMINAL tm,T_TROUBLELOG tl " +
- " WHERE tm.Status != 9 AND tm.TerminalID=tl.TerminalID )" +
- " AND tl.DeviceID='COMMMODULE' AND tl.TroubleID='CommTimeOut' AND tl.SolveDate IS NULL ";
- Connection conn = null;
- PreparedStatement pstmt = null;
- PreparedStatement pstmtInsert = null;
- ResultSet rs = null;
- List terlist = new ArrayList();
- List noterlist = new ArrayList();
- try {
- conn = transDataSource.getConnection();
- conn.setAutoCommit(false);
- pstmt = conn.prepareStatement(updateTerminal);
- pstmt.setString(1, timeout);
- pstmt.executeUpdate();
- pstmt.close();
- pstmt = conn.prepareStatement(updateTrouble);
- pstmt.setString(1, transtime);
- pstmt.executeUpdate();
- pstmt.close();
- log.debug("更新完成");
- //生成32位的隨機(jī)ID,使用Hibernate中的UUID算法。
- Properties props = new Properties();
- props.setProperty("separator", "");
- IdentifierGenerator gen = new UUIDHexGenerator();
- ( (Configurable) gen ).configure(Hibernate.STRING, props, null);
- pstmtInsert = conn.prepareStatement(insertTrouble);
- pstmt = conn.prepareStatement(queryTerm);
- rs = pstmt.executeQuery();
- while(rs.next()){
- String termid = rs.getString(1);
- terlist.add(termid);
- }
- log.debug("查詢(xún)終端號(hào)在故障表中沒(méi)有未處理的超時(shí)記錄完成");
- rs.close();
- rs = null;
- pstmt.close();
- pstmt = null;
- pstmt = conn.prepareStatement(queryTimeOutTerm);
- rs = pstmt.executeQuery();
- while (rs.next()) {
- String term = rs.getString(1);
- noterlist.add(term);
- }
- log.debug("查詢(xún)超時(shí)的終端信息完成" + noterlist.size());
- for(int j = 0; j < noterlist.size(); j++){
- String terminalid = noterlist.get(j).toString();
- if(terlist.contains(terminalid)){
- continue;
- }
- pstmtInsert.setString(1, (String) gen.generate(null, null));
- pstmtInsert.setString(2, terminalid);
- pstmtInsert.setString(3, transtime);
- pstmtInsert.addBatch();
- }
- pstmtInsert.executeBatch();
- conn.commit();
- log.debug("向故障表中添加新的故障記錄完成");
- } catch (Exception ex) {
- try{
- conn.rollback();
- }catch (Exception e) {
- }
- throw ex;
- } finally {
- if (rs != null) {
- try {
- rs.close();
- } catch (Exception ex) {
- }
- }
- if (pstmt != null) {
- try {
- pstmt.close();
- } catch (Exception ex) {
- }
- }
- if (pstmtInsert != null) {
- try {
- pstmtInsert.close();
- } catch (Exception ex) {
- }
- }
- if (conn != null) {
- try {
- conn.close();
- } catch (Exception ex) {
- }
- }
- }
- }
這份代碼長(zhǎng)達(dá)120行,要去理解它還是得花一些時(shí)間的,如果使用 scala來(lái)寫(xiě)的話,可以怎么寫(xiě)呢?
- implicit val conn: Connection = transDataSource.getConnection()
- transaction {
- update("""update T_Terminal set Status=9 where TerminalID in
- (select TerminalID from T_Terminal where TO_CHAR(latestDate, 'yyyymmddHH24miss')<= ? and (status=0 or status=2))
- """, timeout);
- update("""UPDATE T_TROUBLELOG tl SET tl.SolveDate=TO_DATE(?,'yyyymmddHH24miss') WHERE terminalid IN
- (SELECT tm.terminalid FROM T_TERMINAL tm,T_TROUBLELOG tl WHERE tm.Status != 9 AND tm.TerminalID=tl.TerminalID )
- AND tl.DeviceID='COMMMODULE' AND tl.TroubleID='CommTimeOut' AND tl.SolveDate IS NULL
- """, transtime);
- val terlist: List[String] = List()
- query("""SELECT terminalid FROM T_TROUBLELOG tr
- WHERE tr.DeviceID='COMMMODULE' AND tr.TroubleID='CommTimeOut' AND tr.SolveDate IS NULL
- GROUP BY terminalid
- """).foreach { (row) =>
- terlist += row("terminalid")
- }
- log.debug("查詢(xún)終端號(hào)在故障表中沒(méi)有未處理的超時(shí)記錄完成");
- val noterlist: List[String] = List()
- query("SELECT te.TerminalID FROM T_TERMINAL te WHERE te.status=9").foreach { (row)=>
- noterlist += row("terminalid")
- }
- log.debug("查詢(xún)超時(shí)的終端信息完成" + noterlist.size());
- val psInsert = conn prepareStatement """insert into T_TroubleLog (LogID, TerminalID, DeviceID, TroubleID, HappenDate)
- values (?, ?, 'COMMMODULE', 'CommTimeOut', TO_DATE(?,'yyyymmddHH24miss'))"""
- for(val terminalId <- noterlist){
- psInsert << uuid() << terminalid << transtime <<!
- }
- }
這是一個(gè)直譯的版本,代碼函數(shù)為34行,瘦身到25%左右。不僅代碼行數(shù)更短,而且新的代碼的可讀性也更高得多。
最近一直在思考,我們對(duì)JDBC是否做了過(guò)度的包裝?包括事務(wù)處理,DAO等從EJB1.0時(shí)代產(chǎn)生的設(shè)計(jì)模式,到后續(xù)的O-R-M框架,看上去代碼是越發(fā)簡(jiǎn)潔,實(shí)際上已經(jīng)遠(yuǎn)遠(yuǎn)的遠(yuǎn)離代碼的本質(zhì)。這種基于Scala的JDBC簡(jiǎn)單封裝,即便于我,也還遠(yuǎn)不如原始的4GL的簡(jiǎn)潔,但相比傳統(tǒng)的jdbc或者后續(xù)的orm、spring+dao等等,則要簡(jiǎn)化得多。
scala到底能不能投入實(shí)際項(xiàng)目應(yīng)用?開(kāi)發(fā)人員的學(xué)習(xí)成本有多高?這個(gè)問(wèn)題一直讓我痛苦,最近,看到 dcaoyuan 先生的netbeans scala ide,讓我重新點(diǎn)燃對(duì)scala的欲望,或許,在我們的平臺(tái)中,接下來(lái)就可以嘗試 scala了:至少在我看來(lái),scala可以有效的消除我們目前代碼中不必要的 DAO 模式以及復(fù)雜的事務(wù)處理模型等。如果能夠在有限的范圍內(nèi)進(jìn)行嘗試,進(jìn)而進(jìn)行推廣。或許能夠在接下來(lái)能進(jìn)一步的提高工作效率。