zoukankan      html  css  js  c++  java
  • 新秀翻译(两)——使用Java通用配置模板方法模式

    假设你发现你已经非常重码,你可能会考虑使用模板的方法来消除easy重复错误代码。下面是一个示例:以下两类,他完成了几乎相同的功能:

    1. 实例化并初始化一个Reader来读取CSV文件。
    2. 读取每一行并解析;
    3. 把每一行的字符填充到Product或Customer对象;
    4. 将每个对象加入到Set里;
    5. 返回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> {
    
        @Override
        Product 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> {
    
        @Override
        Customer 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


    翻译的不好。欢迎拍砖。



  • 相关阅读:
    HTML元素解释
    Java命名规范
    HDU 1058 Humble Numbers(DP,数)
    HDU 2845 Beans(DP,最大不连续和)
    HDU 2830 Matrix Swapping II (DP,最大全1矩阵)
    HDU 2870 Largest Submatrix(DP)
    HDU 1421 搬寝室(DP)
    HDU 2844 Coins (组合背包)
    HDU 2577 How to Type(模拟)
    HDU 2159 FATE(二维完全背包)
  • 原文地址:https://www.cnblogs.com/mfrbuaa/p/4657272.html
Copyright © 2011-2022 走看看