博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
菜鸟译文(二)——使用Java泛型构造模板方法模式
阅读量:6463 次
发布时间:2019-06-23

本文共 6212 字,大约阅读时间需要 20 分钟。

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

  1. 实例化并初始化一个Reader来读取CSV文件;
  2. 读取每一行并解析;
  3. 把每一行的字符填充到Product或Customer对象;
  4. 将每一个对象添加到Set里;
  5. 返回Set。

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

 

ProductCsvReader.java

public class ProductCsvReader {     Set
getAll(File file) throws IOException { Set
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
getAll(File file) throws IOException { Set
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 after Extract Method

public class ProductCsvReader {     Set
getAll(File file) throws IOException { Set
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)相同的部分。我们把它定义为一个抽象类,因为我们不需要实例化它。然后我们将使用重构这个父类。

 

AbstractCsvReader.java

abstract class AbstractCsvReader {    Set
getAll(File file) throws IOException { Set
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
getAll(File file) throws IOException { Set
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
{ Set
getAll(File file) throws IOException { Set
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
{ @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
{ @Override Customer unmarshall(String[] tokens) { Customer customer = new Customer(Integer.parseInt(tokens[0]), tokens[1], tokens[2], tokens[3]); return customer; }}

 

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

原文地址;

翻译的不好,欢迎拍砖!

转载地址:http://wahzo.baihongyu.com/

你可能感兴趣的文章
新书问答:Software Wasteland
查看>>
Mozilla发布Servo浏览器每日构建版
查看>>
MySQL 8支持文档存储,并带来性能和安全方面的改进
查看>>
百度发布智能电视伴侣,并公布短视频计划
查看>>
作者问答:解密硅谷
查看>>
Ooui:在浏览器中运行.NET应用
查看>>
LFE将Lisp编程带到Erlang虚拟机上
查看>>
移动互联网下半场,iOS开发者如何“高薪”成长?
查看>>
Java的序列化特性将要退出历史舞台了
查看>>
同事反馈环:如何实现持续改进的文化
查看>>
企业IT部门主管告诉你,DevOps给我们带来了这些变化
查看>>
AWS开源Firecracker,一种运行多租户容器服务的新虚拟化技术
查看>>
何时该用无服务器,何时该用Kubernetes?
查看>>
Google Chrome 51与交叉观察者和证书管理等API一起更新
查看>>
Hadoop真的要死了吗?
查看>>
Apache Kylin在绿城客户画像系统中的实践
查看>>
2019 SRE 调查报告:事故处理是主要工作,SRE 压力山大
查看>>
IBM发表论文:可能已找到处理量子计算退相干的方法
查看>>
苏宁的Node.js实践:不低于Java的渲染性能、安全稳定迭代快
查看>>
回顾我的2016
查看>>