泛型 java_使用Java泛型的模板方法模式示例

泛型 java

泛型 java

如果您发现除了某些部分外,您的许多例程完全相同,那么您可能需要考虑使用Template Method来消除容易出错的代码重复。 这是一个示例:下面是两个做类似事情的类:

  1. 实例化并初始化Reader以从CSV文件读取。
  2. 阅读每一行并将其分解为令牌。
  3. 将每行中的令牌解组到一个实体(产品或客户)中。
  4. 将每个实体添加到集合中。
  5. 返回集合。

如您所见,只有在第三步中才有所不同–拆封到一个实体或另一个实体。 其他所有步骤均相同。 我已经突出显示了每个代码段中代码都不同的那一行。

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。 我们使用提取方法将行提取为自己的方法:

提取方法后的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;
    }
}

既然我们已经区分了哪些变化与哪些保持不变,我们将创建一个父类,该父类将保存两个类保持相同的代码。 我们将此父类称为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”方法,因此我们需要创建一个称为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);
}

现在,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> {

    @Override
    Product 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> {

    @Override
    Customer unmarshall(String[] tokens) {
        Customer customer = new Customer(Integer.parseInt(tokens[0]), tokens[1], 
                tokens[2], tokens[3]);
        return customer;
    }
}

就是这样! 没有更多重复的代码! 父类中的方法是“模板”,其中包含保持不变的代码。 更改的内容保留为抽象方法,这些方法在子类中实现。 请记住,重构时,应该始终进行自动化的单元测试,以确保不破坏代码。 我将JUnit用于我的。 您可以在Github存储库中找到我在此处发布的代码以及其他一些“设计模式”示例。 在开始之前,我想简单介绍一下模板方法的缺点。 模板方法依赖于继承,而继承则存在脆弱的基类问题。 简而言之,脆弱的基类问题描述了基类的更改如何被子类继承,从而经常导致不良后果。 实际上,在GoF书籍开始时发现的基本设计原则之一就是“偏重于继承而不是继承”,许多其他设计模式都说明了如何避免代码重复,复杂性或其他容易出错的代码,而减少了依赖性关于继承。 请给我反馈,以便我可以继续改进我的文章。

翻译自: https://www.javacodegeeks.com/2014/07/template-method-pattern-example-using-java-generics.html

泛型 java

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值