新秀翻译(两)——使用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


翻译的不好。欢迎拍砖。





本文转自mfrbuaa博客园博客,原文链接:http://www.cnblogs.com/mfrbuaa/p/4657272.html,如需转载请自行联系原作者


相关文章
|
8天前
|
Java 应用服务中间件 Windows
【应用服务 App Service】App Service 中部署Java项目,查看Tomcat配置及上传自定义版本
【应用服务 App Service】App Service 中部署Java项目,查看Tomcat配置及上传自定义版本
|
5天前
|
Java 应用服务中间件 HSF
Java应用结构规范问题之配置Logback以仅记录错误级别的日志到一个滚动文件中的问题如何解决
Java应用结构规范问题之配置Logback以仅记录错误级别的日志到一个滚动文件中的问题如何解决
|
1天前
|
设计模式 算法 Java
【揭秘】如何巧妙运用Java模板方法模式,让你的代码优雅升级?
【8月更文挑战第30天】模板方法模式是一种行为型设计模式,它定义了算法的骨架并将某些步骤延迟到子类中,使子类能在不改变算法结构的情况下重定义特定步骤。此模式适用于具有共同结构但细节不同的场景,如角色升级系统。通过定义一个抽象类 `Character` 包含模板方法 `levelUp` 和抽象步骤方法,子类如 `Warrior` 和 `Mage` 可以实现具体逻辑。这种方式提供了良好的扩展性,确保算法结构不变,同时保持系统的稳定性和一致性,在数据处理和业务流程管理中广泛应用。
11 2
|
5天前
|
Java 应用服务中间件 HSF
Java应用结构规范问题之配置Logback以在控制台输出日志的问题如何解决
Java应用结构规范问题之配置Logback以在控制台输出日志的问题如何解决
|
28天前
|
IDE Oracle Java
Java零基础(2) - Java环境配置
【8月更文挑战第2天】🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
34 5
Java零基础(2) - Java环境配置
|
6天前
|
Java
【Azure 应用服务】如何查看App Service Java堆栈JVM相关的参数默认配置值?
【Azure 应用服务】如何查看App Service Java堆栈JVM相关的参数默认配置值?
【Azure 应用服务】如何查看App Service Java堆栈JVM相关的参数默认配置值?
|
21天前
|
消息中间件 Java 大数据
"深入理解Kafka单线程Consumer:核心参数配置、Java实现与实战指南"
【8月更文挑战第10天】在大数据领域,Apache Kafka以高吞吐和可扩展性成为主流数据流处理平台。Kafka的单线程Consumer因其实现简单且易于管理而在多种场景中受到欢迎。本文解析单线程Consumer的工作机制,强调其在错误处理和状态管理方面的优势,并通过详细参数说明及示例代码展示如何有效地使用KafkaConsumer类。了解这些内容将帮助开发者优化实时数据处理系统的性能与可靠性。
52 7
|
18天前
|
安全 前端开发 Java
Web端系统开发解决跨域问题——以Java SpringBoot框架配置Cors为例
在Web安全上下文中,源(Origin)是指一个URL的协议、域名和端口号的组合。这三个部分共同定义了资源的来源,浏览器会根据这些信息来判断两个资源是否属于同一源。例如,https://www.example.com:443和http://www.example.com虽然域名相同,但由于协议和端口号不同,它们被视为不同的源。同源(Same-Origin)是指两个URL的协议、域名和端口号完全相同。只有当这些条件都满足时,浏览器才认为这两个资源来自同一源,从而允许它们之间的交互操作。
Web端系统开发解决跨域问题——以Java SpringBoot框架配置Cors为例
|
24天前
|
存储 Java 测试技术
Java零基础教学(05):如何Java环境配置??
【8月更文挑战第5天】Java零基础教学篇,手把手实践教学!
43 3
|
2天前
|
jenkins Java Shell
jenkins学习笔记之十三:配置SonarScanner扫描Java项目
jenkins学习笔记之十三:配置SonarScanner扫描Java项目
下一篇
云函数