新秀翻译(两)——使用Java通用配置模板方法模式
假设你发现你已经非常重码,你可能会考虑使用模板的方法来消除easy重复错误代码。下面是一个示例:以下两类,他完成了几乎相同的功能:
- 实例化并初始化一个Reader来读取CSV文件。
- 读取每一行并解析;
- 把每一行的字符填充到Product或Customer对象;
- 将每个对象加入到Set里;
- 返回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
翻译的不好。欢迎拍砖。
新秀翻译(两)——使用Java通用配置模板方法模式的更多相关文章
- 折腾Java设计模式之模板方法模式
博客原文地址:折腾Java设计模式之模板方法模式 模板方法模式 Define the skeleton of an algorithm in an operation, deferring some ...
- Java设计模式之模板方法模式(Template Method)
一.含义 定义一个算法中的操作框架,而将一些步骤延迟到子类中.使得子类可以不改变算法的结构即可重定义该算法的某些特定步骤,不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现. 二 ...
- Java抽象类应用—模板方法模式
模板方法模式(Templete method) 定义一个操作中的算法的骨架,而将一些可变部分的实现延迟到子类中,模板方法模式使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定的步骤. 例: ...
- Java设计模式应用——模板方法模式
所谓模板方法模式,就是在一组方法结构一致,只有部分逻辑不一样时,使用抽象类制作一个逻辑模板,具体是实现类仅仅实现特殊逻辑就行了.类似科举制度八股文,文章结构相同,仅仅具体语句有差异,我们只需要按照八股 ...
- Java设计模式之模板方法模式(Template)
前言: 我们在开发中有很多固定的流程,这些流程有很多步凑是固定的,比如JDBC中获取连接,关闭连接这些流程是固定不变的,变动的只有设置参数,解析结果集这些是根据不同的实体对象“来做调整”,针对这种拥有 ...
- 从西天取经的九九八十一难来看Java设计模式:模板方法模式
目录 示例 模板方法模式 定义 意图 主要解决问题 适用场景 优缺点 西天取经的九九八十一难 示例 当我们设计一个类时,我们能明确它对外提供的某个方法的内部执行步骤, 但一些步骤,不同的子类有不同的行 ...
- Java/C++实现模板方法模式---数据库操作
对数据库的操作一般包括连接.打开.使用.关闭等步骤,在数据库操作模板类中我们定义了connDB().openDB().useDB().closeDB()四个方法分别对应这四个步骤.对于不同类型的数据库 ...
- java设计模式之模板方法模式
模板方法模式 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中. 模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤.通俗的说的就是有很多相同的步骤的,在某一些地方可能有一些差 ...
- 菜鸟译文(二)——使用Java泛型构造模板方法模式
如果你发现你有很多重复的代码,你可能会考虑用模板方法消除容易出错的重复代码.这里有一个例子:下面的两个类,完成了几乎相同的功能: 实例化并初始化一个Reader来读取CSV文件: 读取每一行并解析: ...
随机推荐
- flex4 amcharts 删除水印
package com.twobowl.component.manualTopo { import com.amcharts.AmPieChart; import com.amcharts.chart ...
- Android SVN开发实战的文件夹结构呈现
svn有一个非常标准的文件夹结构,这是. 例如,该项目是proj.svn地址svn://proj/,然后该标准svn布局是 svn://proj/ | +-trunk +-branches +-tag ...
- EHCache的使用
在开发高并发量,高性能的网站应用系统时,缓存Cache起到了非常重要的作用.本文主要介绍EHCache的使用,以及使用EHCache的实践经验.笔者使用过多种基于Java的开源Cache组件,其中包括 ...
- C++习题 复数类--重载运算符+
Description 定义一个复数类Complex,重载运算符"+",使之能用于复数的加法运算.将运算符函数重载为非成员.非友元的普通函数.编写程序,求两个复数之和. Input ...
- JQuery Easy Ui (Tree树)详解(转)
第一讲:JQuery Easy Ui到底是什么呢? 首先咱们知道JQuery是对Java Script的封装,是一个js库,主要提供的功能是选择器,属性修改和事件绑定等等.. JQuery ui是在j ...
- Quartz CronTrigger应用
CronTrigger配置格式: 格式: [第二] [支] [小时] [日本] [月] [周] [年] 序号 说明 是否必填 同意填写的值 同意的通配符 1 秒 是 0-59 , ...
- WPF3D学习,立方体的绘制
原文:WPF3D学习,立方体的绘制 以此为一个好的开始吧!一直都太懒,坚持写文章是个不错的开始!碰巧最近在研究WPF3D这块的知识,也为了练练自己的写作水平,整理这篇文章.新手上路,多多关照! 本文先 ...
- 设计Mysql索引的原则
1. 搜索的索引列,不一定是所要选择的列.换句话说,最适合索引的列是出如今WHERE 子句中的列,或连接子句中指定的列,而不是出如今SELECT keyword后的选择列表中的列. 2. 使用惟一索引 ...
- hdu 1171 Big Event in HDU(母函数)
链接:hdu 1171 题意:这题能够理解为n种物品,每种物品的价值和数量已知,现要将总物品分为A,B两部分, 使得A,B的价值尽可能相等,且A>=B,求A,B的价值分别为多少 分析:这题能够用 ...
- jdk阅读xml文件
前言 你需要阅读的时间来写一个通用组件xml文件,但考虑到组件分布更容易,这样一来在第三方小引用jar包.因此,直接jdk内建的xml分析方法.可能都没有第三发的组件强大. 导入的文件: import ...