假設你發現你已經非常重碼,你可能會考慮使用模板的方法來消除easy重復錯誤代碼。下面是一個示例:以下兩類,他完成了幾乎相同的功能:
- 實例化并初始化一個Reader來讀取CSV文件。
- 讀取每一行并解析;
- 把每一行的字符填充到Product或Customer對象;
- 將每個對象加入到Set里;
- 返回Set。
正如你看到的,僅僅有有凝視的地方是不一樣的。其它全部步驟都是同樣的。
ProductCsvReader.java
public class ProductCsvReader {Set<Product> getAll(File file) throws IOException {Set<Product> returnSet = new HashSet<>();try (BufferedReader reader = new BufferedReader(new FileReader(file))){String line = reader.readLine();while (line != null && !line.trim().equals("")) {String[] tokens = line.split("\\s*,\\s*");//不同Product product = new Product(Integer.parseInt(tokens[0]), tokens[1], new BigDecimal(tokens[2]));returnSet.add(product);line = reader.readLine();}}return returnSet;}
}
CustomerCsvReader.java
public class CustomerCsvReader {Set<Customer> getAll(File file) throws IOException {Set<Customer> returnSet = new HashSet<>();try (BufferedReader reader = new BufferedReader(new FileReader(file))){String line = reader.readLine();while (line != null && !line.trim().equals("")) {String[] tokens = line.split("\\s*,\\s*");//不同Customer customer = new Customer(Integer.parseInt(tokens[0]), tokens[1], tokens[2], tokens[3]);returnSet.add(customer);line = reader.readLine();}}return returnSet;}
}
對于本例來說,僅僅有兩個實體,可是一個真正的系統可能有幾十個實體,所以有非常多反復易錯的代碼。
你可能會發現Dao層有著同樣的情況。在每個Dao進行增刪改查的時候差點兒都是同樣的操作。唯一與不同的是實體和表。讓我們重構這些煩人的代碼吧。依據GoF設計模式第一部分提到的原則之中的一個,我們應該“封裝不同的概念“ProductCsvReader和CustomerCsvReader之間,不同的是有凝視的代碼。所以我們要做的是。把同樣的放到一個類。不同的抽取到還有一個類。我們先開始編寫ProductCsvReader,我們使用Extract Method提取帶凝視的部分:
ProductCsvReader.java after Extract Method
public class ProductCsvReader {Set<Product> getAll(File file) throws IOException {Set<Product> returnSet = new HashSet<>();try (BufferedReader reader = new BufferedReader(new FileReader(file))){String line = reader.readLine();while (line != null && !line.trim().equals("")) {String[] tokens = line.split("\\s*,\\s*");Product product = unmarshall(tokens);returnSet.add(product);line = reader.readLine();}}return returnSet;}Product unmarshall(String[] tokens) {Product product = new Product(Integer.parseInt(tokens[0]), tokens[1], new BigDecimal(tokens[2]));return product;}
}
如今我們已經把同樣(反復)的代碼和不同(各自特有)的代碼分開了,我們要創建一個父類AbstractCsvReader,它包括兩個類(ProductReader和CustomerReader)同樣的部分。我們把它定義為一個抽象類。由于我們不須要實例化它。然后我們將使用Pull Up Method重構這個父類。
AbstractCsvReader.java
abstract class AbstractCsvReader {Set<Product> getAll(File file) throws IOException {Set<Product> returnSet = new HashSet<>();try (BufferedReader reader = new BufferedReader(new FileReader(file))){String line = reader.readLine();while (line != null && !line.trim().equals("")) {String[] tokens = line.split("\\s*,\\s*");Product product = unmarshall(tokens);returnSet.add(product);line = reader.readLine();}}return returnSet;}
}
ProductCsvReader.java after Pull Up Method
public class ProductCsvReader extends AbstractCsvReader {Product unmarshall(String[] tokens) {Product product = new Product(Integer.parseInt(tokens[0]), tokens[1], new BigDecimal(tokens[2]));return product;}
}
假設在子類中沒有‘unmarshall’方法,該類就無法進行編譯(它調用unmarshall方法),所以我們要創建一個叫unmarshall的抽象方法。
AbstractCsvReader.java with abstract unmarshall method
abstract class AbstractCsvReader {Set<Product> getAll(File file) throws IOException {Set<Product> returnSet = new HashSet<>();try (BufferedReader reader = new BufferedReader(new FileReader(file))){String line = reader.readLine();while (line != null && !line.trim().equals("")) {String[] tokens = line.split("\\s*,\\s*");Product product = unmarshall(tokens);returnSet.add(product);line = reader.readLine();}}return returnSet;}abstract Product unmarshall(String[] tokens);
}
如今。在這一點上,AbstractCsvReader是ProductCsvReader的父類,但不是CustomerCsvReader的父類。假設CustomerCsvReader繼承AbstractCsvReader編譯會報錯。為了解決問題我們使用泛型。
AbstractCsvReader.java with Generics
abstract class AbstractCsvReader<T> {Set<T> getAll(File file) throws IOException {Set<T> returnSet = new HashSet<>();try (BufferedReader reader = new BufferedReader(new FileReader(file))){String line = reader.readLine();while (line != null && !line.trim().equals("")) {String[] tokens = line.split("\\s*,\\s*");T element = unmarshall(tokens);returnSet.add(product);line = reader.readLine();}}return returnSet;}abstract T unmarshall(String[] tokens);
}
ProductCsvReader.java with Generics
public class ProductCsvReader extends AbstractCsvReader<Product> {@OverrideProduct unmarshall(String[] tokens) {Product product = new Product(Integer.parseInt(tokens[0]), tokens[1], new BigDecimal(tokens[2]));return product;}
}
CustomerCsvReader.java with Generics
public class CustomerCsvReader extends AbstractCsvReader<Customer> {@OverrideCustomer unmarshall(String[] tokens) {Customer customer = new Customer(Integer.parseInt(tokens[0]), tokens[1], tokens[2], tokens[3]);return customer;}
}
這就是我們要的!
不再有反復的代碼。父類中的方法是“模板”,它包括這不變的代碼。那些變化的東西作為抽象方法。在子類中實現。記住,當你重構的時候,你應該有自己主動化的單元測試來保證你不會破壞你的代碼。
我使用JUnit,你能夠使用我帖在這里的代碼,也能夠在這個Github庫找一些其它設計模式的樣例。在結束之前,我想說一下模板方法的缺點。模板方法依賴于繼承。患有?the Fragile Base Class Problem。簡單的說就是,改動父類會對繼承它的子類造成意想不到的不良影響。其實,基礎設計原則之中的一個的GoF設計模式提倡“多用組合少用繼承”。而且更多設計模式也告訴你怎樣避免代碼反復,同一時候又讓復雜或easy出錯的代碼盡量少的依賴繼承。歡迎交流,以便我能夠提高我的博客質量。
原文地址。Template Method Pattern Example Using Java Generics
翻譯的不好。歡迎拍磚。
本文轉自mfrbuaa博客園博客,原文鏈接:http://www.cnblogs.com/mfrbuaa/p/4657272.html,如需轉載請自行聯系原作者