使用Java泛型的模板方法模式示例
如果發(fā)現(xiàn)除了某些部分外,您的許多例程完全相同,那么您可能需要考慮使用Template Method來消除容易出錯的代碼重復 。 這是一個示例:下面是兩個做類似事情的類:
正如您所看到的,只有在第三步中才有所不同–將編組到一個實體或另一個實體。 其他所有步驟均相同。 我已經突出顯示了每個代碼段中代碼都不同的那一行。
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;} }在此示例中,只有兩個實體,但是實際系統(tǒng)中可能有數(shù)十個實體,因此有很多容易出錯的重復代碼。 對于DAO,您可能會發(fā)現(xiàn)類似的情況,其中每個DAO的選擇,插入,更新和刪除操作將執(zhí)行相同的操作,僅適用于不同的實體和表。 讓我們開始重構這個麻煩的代碼。 根據(jù)GoF設計模式書第一部分中的一種設計原則,我們應該“封裝變化的概念”。 在ProductCsvReader和CustomerCsvReader之間,突出顯示的代碼有所不同。 因此,我們的目標是將變化的內容封裝到單獨的類中,同時將保持不變的內容移動到單個類中。 讓我們首先開始編輯一個類,即ProductCsvReader。 我們使用提取方法將行提取到自己的方法中:
提取方法后的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 = 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;} }既然我們已經分離出了哪些變化與哪些保持不變,我們將創(chuàng)建一個父類,該父類將保存兩個類保持相同的代碼。 我們將此父類稱為AbstractCsvReader。 讓我們使其抽象,因為沒有理由單獨實例化該類。 然后,我們將使用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
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”方法,因此我們需要創(chuàng)建一個稱為unmarshall的抽象方法。
使用抽象解組方法的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;}abstract Product unmarshall(String[] tokens); }現(xiàn)在,AbstractCsvReader將成為ProductCsvReader的出色父級,而不是CustomerCsvReader的父級。 如果從AbstractCsvReader擴展,CustomerCsvReader將不會編譯。 為了解決這個問題,我們使用泛型。
具有泛型的AbstractCsvReader.java
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
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與泛型
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;} }就是這樣! 沒有更多重復的代碼! 父類中的方法是“模板”,其中包含保持不變的代碼。 更改的內容保留為抽象方法,這些方法在子類中實現(xiàn)。 請記住,重構時,應該始終進行自動化的單元測試,以確保不破壞代碼。 我將JUnit用于我的。 您可以在Github存儲庫中找到我在此處發(fā)布的代碼以及其他一些“設計模式”示例。 在開始之前,我想簡單介紹一下模板方法的缺點。 模板方法依賴于繼承,而繼承則存在脆弱的基類問題 。 簡而言之,脆弱的基類問題描述了基類的更改如何被子類繼承,并經常導致不良后果。 實際上,在GoF本書開始時發(fā)現(xiàn)的基本設計原則之一就是“偏向于繼承而不是繼承”,許多其他設計模式都說明了如何避免代碼重復,復雜度或其他容易出錯的代碼,而減少了依賴關于繼承。 請給我反饋,以便我可以繼續(xù)完善我的文章。
翻譯自: https://www.javacodegeeks.com/2014/07/template-method-pattern-example-using-java-generics.html
總結
以上是生活随笔為你收集整理的使用Java泛型的模板方法模式示例的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 清炒猴头菇 猴头菇怎么清炒
- 下一篇: Java并发教程–线程之间的可见性