Google Code Prettify
2015年11月8日 星期日
2015年11月7日 星期六
Practice
在實務上遇到的一些問題及提出來的解決辦法,分享出來,希望有鄉民可以提供更好的建議。
項目 | 日期 | |
---|---|---|
1 | 重構: Duplicated Code (重複的程式碼) |
2015/11/06
|
2 | Producer-Consumer Pattern |
2015/10/17
|
3 | Tibco RV - fault tolerance |
2015/07/10
|
4 | 陽春版內容管理系統 |
2015/10/25
|
5 | 報表結果要不要關連到組織主檔? |
2015/05/28
|
6 | 剖析固定長度欄位的訊息字串 |
2014/08/09
|
7 | 即時更新程式設定 |
2014/06/15
|
8 | Journal Comments |
2015/05/16
|
9 | BCD pack & unpack |
2015/05/10
|
10 | 在 WebSphere 8.5.5.x 上遇到的 jar 檔版本和衝突問題 |
2017/03/30
|
11 | JWT 認證在 RESTful service 上的應用 |
2017/08/03
|
12 | 台股: 手續費與交易稅的計算 |
2016/09/03
|
13 | 台股: 配息之已實現損益及未實現損益的計算 |
2016/09/12
|
14 | 移除 VirtualBox 產生的「網路連線」介面卡 |
2021/02/16
|
15 | 解決 eclipse 2021-09 + lombok 產生的錯誤 |
2021/09/19
|
16 | IPv4 內部 IP |
2021/09/28
|
17 | jasypt 加解密 |
2022/07/06
|
標籤:
台股,
重構,
design pattern,
JWT,
refactoring,
REST,
TibcoRV,
WebSphere
2015年11月6日 星期五
重構: Duplicated Code (重複的程式碼)
Martin Fowler 大師於將近 20 年前出版的名著「Refactoring: Improving The Design of Existing Code」(重構 - 改善既有程式的設計),提出許多重構的手法,這些方法在當時是相當新穎的觀念與技術,現在已經是許多程式員每天寫程式一定會用到的。
在書中 3.1 節是談論重複的程式碼,這當然是程式的壞味道,是重構的標的物,底下將舉出我實際遇到的例子,以及我在重重限制下進行的重構。
這是一個有上百個 tables 的資料庫 web 系統,用 java 開發,沒有採用任何 MVC 的 framework,程式也沒有將 view、model 等區隔開,所以整個系統絕大多數的程式碼是寫在 jsp 裡,在這些 jsp 裡會看到大量的 html、javascript、jquery、css、java 摻雜,連接資料庫直接使用 JDBC API,底下的例子就是一個將 JDBC API 相關的程式,經過重構減少重複程式碼的例子。
底下是重構一書 3.1 節的原文,有興趣的可以看看,沒看過這本書的人,建議買一本,絕對值得!
在書中 3.1 節是談論重複的程式碼,這當然是程式的壞味道,是重構的標的物,底下將舉出我實際遇到的例子,以及我在重重限制下進行的重構。
這是一個有上百個 tables 的資料庫 web 系統,用 java 開發,沒有採用任何 MVC 的 framework,程式也沒有將 view、model 等區隔開,所以整個系統絕大多數的程式碼是寫在 jsp 裡,在這些 jsp 裡會看到大量的 html、javascript、jquery、css、java 摻雜,連接資料庫直接使用 JDBC API,底下的例子就是一個將 JDBC API 相關的程式,經過重構減少重複程式碼的例子。
PreparedStatement pstmt;
String sql;
sql = "UPDATE FoudItem "
+ " SET "
+ " name = NVL(?,' '),"
+ " type = ?, "
+ " rate = ? , "
+ " empNo = NVL(?,' '), "
+ " mtime = SYSTIMESTAMP "
+ " WHERE fundNo = RPAD(?,3) "
;
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, fundName);
pstmt.setString(2, fundType);
pstmt.setDouble(3, rate);
pstmt.setString(4, empNo);
pstmt.setString(5, fundNo);
pstmt.executeUpdate();
像這樣存取資料庫的程式碼在整個系統中有數百個,全部是同樣邏輯,先有一段用 string 一直相加產生的 sql statement,在產生 PreparedStatement 後,傳入參數並執行。我是後續接手維護的人,也被告知不能動大架構,所以我只簡單的如下重構,以去除重複的程式碼。
- 將 sql statement 移到 xml 檔
<?xml version="1.0" encoding="UTF-8"?>
<sql>
<statement id="update">
<![CDATA[
UPDATE FoudItem "
SET "
name = NVL(?,' '),"
type = ?, "
rate = ? , "
empno = NVL(?,' '), "
mtime = SYSTIMESTAMP "
WHERE fundno = RPAD(?,3)
]]>
</statement>
</sql>
- 自 xml 中讀取 sql statement
private static String getSqlStatement(String name, String id) {
File file = new File(path + name + ".xml");
SAXReader reader = new SAXReader();
Document document;
try {
document = reader.read(file);
Element sqlElmt = document.getRootElement();
Iterator<Element> iter = sqlElmt.elementIterator("statement");
while (iter.hasNext()) {
Element elmt = iter.next();
if (id.equals(elmt.attributeValue("id"))) {
return elmt.getText();
}
}
} catch (DocumentException e) {
logger.error(e.getMessage(), e);
}
return StringUtils.EMPTY;
}
這段程式碼很簡單的讀取指定目錄下 xml 檔,以取得程式需要的 sql statement。
- 傳入參數到 PreparedStatement 並執行
public static int execute(Connection conn, String name, String id, Object... params) throws SQLException {
String sql = getSqlStatement(name, id);
PreparedStatement pstmt = conn.prepareStatement(sql);
for(int i=1; i<=params.length; i++) {
Object param = params[i-1];
if (param instanceof String) {
pstmt.setString(i, (String) param);
}
else if (param instanceof Long) {
pstmt.setLong(i, (Long) param);
}
else if (param instanceof Double) {
pstmt.setDouble(i, (Double) param);
}
else if (param instanceof BigDecimal) {
pstmt.setBigDecimal(i, (BigDecimal) param);
}
else {
throw new SQLException("field's type is invalid");
}
}
return pstmt.execute();
}
- 改寫原 JDBC 相關的程式
SQLExecutor.execute(
conn, "fund", "update",
fundName, fundType, rate, empNo, fundNo
);
改寫完了之後,一大段的程式最後只剩一行指令! (從命名為 fund.xml 的檔案裡,讀取 id = update 的 sql statement,產生 PreparedStatement 後,將相關參數傳入執行。)
底下是重構一書 3.1 節的原文,有興趣的可以看看,沒看過這本書的人,建議買一本,絕對值得!
Duplicated Code
Number one in the stink parade is duplicated code. If you see the same code structure in more than one place, you can be sure that your program will be better if you find a way to unify them.
The simplest duplicated code problem is when you have the same expression in two methods of the same class. Then all you have to do is Extract Method and invoke the code from both places.
Another common duplication problem is when you have the same expression in two sibling subclasses. You can eliminate this duplication by using Extract Method in both classes then Pull Up Field. If the code is similar but not the same, you need to use Extract Method to separate the similar bits from the different bits. You may then find you can use Form Template Method. If the methods do the same thing with a different algorithm, you can choose the clearer of the two algorithms and use Substitute Algorithm.
If you have duplicated code in two unrelated classes, consider using Extract Class in one class and then use the new component in the other. Another possibility is that the method really belongs only in one of the classes and should be invoked by the other class or that the method belongs in a third class that should be referred to by both of the original classes. You have to decide where the method makes sense and ensure it is there and nowhere else.
訂閱:
文章 (Atom)