java 泛型 重构_菜鸟译文(二)——使用Java泛型构造模板方法模式

如果你发现你有很多重复的代码,你可能会考虑用模板方法消除容易出错的重复代码。这里有一个例子:下面的两个类,完成了几乎相同的功能:

实例化并初始化一个Reader来读取CSV文件;

读取每一行并解析;

把每一行的字符填充到Product或Customer对象;

将每一个对象添加到Set里;

返回Set。

正如你看到的,只有有注释的地方是不一样的。其他所有步骤都是相同的。

ProductCsvReader.java

public classProductCsvReader {

Set getAll(File file) throwsIOException {

Set returnSet = new HashSet<>();try (BufferedReader reader = new BufferedReader(newFileReader(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();

}

}returnreturnSet;

}

}

CustomerCsvReader.java

public classCustomerCsvReader {

Set getAll(File file) throwsIOException {

Set returnSet = new HashSet<>();try (BufferedReader reader = new BufferedReader(newFileReader(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();

}

}returnreturnSet;

}

}

对于本例来说,只有两个实体,但是一个真正的系统可能有几十个实体,所以有很多重复易错的代码。你可能会发现Dao层有着相同的情况,在每个Dao进行增删改查的时候几乎都是相同的操作,唯一与不同的是实体和表。让我们重构这些烦人的代码吧。根据GoF设计模式第一部分提到的原则之一,我们应该“封装不同的概念“ProductCsvReader和CustomerCsvReader之间,不同的是有注释的代码。所以我们要做的是,把相同的放到一个类,不同的抽取到另一个类。我们先开始编写ProductCsvReader,我们使用Extract Method提取带注释的部分:

ProductCsvReader.java after Extract Method

public classProductCsvReader {

Set getAll(File file) throwsIOException {

Set returnSet = new HashSet<>();try (BufferedReader reader = new BufferedReader(newFileReader(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();

}

}returnreturnSet;

}

Product unmarshall(String[] tokens) {

Product product= new Product(Integer.parseInt(tokens[0]), tokens[1],new BigDecimal(tokens[2]));returnproduct;

}

}

现在我们已经把相同(重复)的代码和不同(各自特有)的代码分开了,我们要创建一个父类AbstractCsvReader,它包括两个类(ProductReader和CustomerReader)相同的部分。我们把它定义为一个抽象类,因为我们不需要实例化它。然后我们将使用Pull Up Method重构这个父类。

AbstractCsvReader.java

abstract classAbstractCsvReader {

Set getAll(File file) throwsIOException {

Set returnSet = new HashSet<>();try (BufferedReader reader = new BufferedReader(newFileReader(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();

}

}returnreturnSet;

}

}

ProductCsvReader.java after Pull Up Method

public class ProductCsvReader extendsAbstractCsvReader {

Product unmarshall(String[] tokens) {

Product product= new Product(Integer.parseInt(tokens[0]), tokens[1],new BigDecimal(tokens[2]));returnproduct;

}

}

如果在子类中没有‘unmarshall’方法,该类就无法进行编译(它调用unmarshall方法),所以我们要创建一个叫unmarshall的抽象方法。

AbstractCsvReader.java with abstract unmarshall method

abstract classAbstractCsvReader {

Set getAll(File file) throwsIOException {

Set returnSet = new HashSet<>();try (BufferedReader reader = new BufferedReader(newFileReader(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();

}

}returnreturnSet;

}abstractProduct unmarshall(String[] tokens);

}

现在,在这一点上,AbstractCsvReader是ProductCsvReader的父类,但不是CustomerCsvReader的父类。如果CustomerCsvReader继承AbstractCsvReader编译会报错。为了解决这个问题我们使用泛型。

AbstractCsvReader.java with Generics

abstract class AbstractCsvReader{

Set getAll(File file) throwsIOException {

Set returnSet = new HashSet<>();try (BufferedReader reader = new BufferedReader(newFileReader(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();

}

}returnreturnSet;

}abstractT unmarshall(String[] tokens);

}

ProductCsvReader.java with Generics

public class ProductCsvReader extends AbstractCsvReader{

@Override

Product unmarshall(String[] tokens) {

Product product= new Product(Integer.parseInt(tokens[0]), tokens[1],new BigDecimal(tokens[2]));returnproduct;

}

}

CustomerCsvReader.java with Generics

public class CustomerCsvReader extends AbstractCsvReader{

@Override

Customer unmarshall(String[] tokens) {

Customer customer= new Customer(Integer.parseInt(tokens[0]), tokens[1],

tokens[2], tokens[3]);returncustomer;

}

}

这就是我们要的!不再有重复的代码!父类中的方法是“模板”,它包含这不变的代码。那些变化的东西作为抽象方法,在子类中实现。记住,当你重构的时候,你应该有自动化的单元测试来保证你不会破坏你的代码。我使用JUnit,你可以使用我帖在这里的代码,也可以the Fragile Base Class Problem。简单的说就是,修改父类会对继承它的子类造成意想不到的不良影响。事实上,基础设计原则之一的GoF设计模式提倡“多用组合少用继承”,并且许多其他设计模式也告诉你如何避免代码重复,同时又让复杂或容易出错的代码尽量少的依赖继承。欢迎交流,以便我可以提高我的博客质量。

翻译的不好,欢迎拍砖!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值