Java:如何修复多种方法中的代码重复?

Posted

技术标签:

【中文标题】Java:如何修复多种方法中的代码重复?【英文标题】:Java: How to fix code duplication in multiple methods? 【发布时间】:2021-12-05 11:21:09 【问题描述】:

我有一些与设计模式相关的理论知识,现在我有一些问题要让这些信息和其他信息付诸实践。

我的ServiceImpl 类中有以下两种方法:

@Override
public MultipartFile exportA() throws IOException 
    
    // repeated lines-I same as exportB method (code omitted for brevity)
    
    // other lines special to exportA method

    // repeated lines-II same as exportB method (code omitted for brevity)

@Override
public MultipartFile exportB() throws IOException 
    
    // repeated lines-I same as exportA method (code omitted for brevity)
    
    // other lines special to exportB method

    // repeated lines-II same as exportA method (code omitted for brevity)

如图所示,所有这些方法中都有重复的部分。那么,我是否应该为重复的行-I 和 II 创建 2 个方法,然后将这些代码块移动到这些新创建的 2 个方法中?或者,有没有更好的设计模式方法?

【问题讨论】:

嗯,这里想到的一种模式是"Template method"。但是,作为第一步,仅提取 createWorkbook()writeWorkbookToFile()-methods 可能会让您朝着正确的方向迈出第一步。 @Hulk 谢谢你的回复,我也是这么想的。但是特殊线路呢?请问有使用该代码的示例帖子吗? 【参考方案1】:

如果我很好地理解了你的说法,这听起来 Builder Pattern 您正在寻找。您的方法正在构建MultipartFile,而构建过程本身取决于“参数/参数”(这里我猜是工作表文件路径)和不同的代码(您称为“此方法专用的其他行”)。

为此,我将创建一个类MultipartFileBuilder 来处理员工,并且我会在每个方法中调用它;当然,通过每次设置适当的参数和“代码”的方式。该代码只是以下代码中使用的java.util.function.Consumer<T> 功能接口的实现(*2),其他参数也使用简单的设置器(此处(*1))。

请注意,我在这里调用了 Consumer<T> 作为 lambda 表达式(c->... 构造)。还要注意Consumer<T> 中的类型参数<T> 这里是我引入的一个新类MultipartFileBuildContext 允许将多个信息传递给您愿意在每个方法中编写的代码。我将“工作表”变量作为一个起点。如果需要,您可以添加其他信息。

总结一下,这就是代码的样子:

    
    @Override
    public MultipartFile exportMethodA() throws IOException 
        return new MultipartFileBuilder()
                .setSheetPath("sheetA/path") (*1)
                .setAction(c->
                    //(*2) do your staff here for method A
                    // the "other lines special to this method"
                    XSSFSheet sheet=c.getSheet()
                    ...
                ).build();
    

    @Override
    public MultipartFile exportMethodB() throws IOException 
        return new MultipartFileBuilder()
                .setSheetPath("sheetB/path") (*1)
                .setAction(c->
                    //(*2) do your staff here for method B
                    // the "other lines special to this method"
                    XSSFSheet sheet=c.getSheet()
                    ...
                ).build();
    

    class MultipartFileBuildContext 
        private XSSFSheet sheet;
        public MultipartFileBuildContext(XSSFSheet sheet)this.sheet=sheet;
        public String getSheetPath() 
            return sheetPath;
        
    
    
    class MultipartFileBuilder 
        String sheetPath; 
        Consumer<MultipartFileBuildContext> action; 

        public String getSheetPath() 
            return sheetPath;
        

        public MultipartFileBuilder setSheetPath(String sheetPath) 
            this.sheetPath = sheetPath;
            return this;
        

        public Consumer<MultipartFileBuildContext> getAction() 
            return action;
        

        public MultipartFileBuilder setAction(Consumer<MultipartFileBuildContext> action) 
            this.action = action;
            return this;
        
        
        public MockMultipartFile build() 
            // repeated lines
            workbook = new XSSFWorkbook();
            sheet = workbook.createSheet(sheetPath);

            //
            if(action!=null)
                MultipartFileBuildContext c=new MultipartFileBuildContext(sheet);
                action.accept(c);
            
        
        // repeated lines ======================================================
        outputFile = File.createTempFile(...);
        try (FileOutputStream outputStream = new FileOutputStream(outputFile)) 
                workbook.write(outputStream);
             catch (IOException e) 
                LoggingUtils.error("Writing failed ", e);
            
            final FileInputStream input = new FileInputStream(outputFile);

            final String fileName = TextBundleUtil.read(...);
            return new MockMultipartFile(fileName,
                    fileName, CONTENT_TYPE, IOUtils.toByteArray(input));
            //
        
    

最后,需要谨慎使用此模式,因为您需要尽可能地分解所有因素以使构建器真正有用,但不要过多使其成为“任何东西的船”。例如,您可以将工作表路径或其输入流作为输入,以使其更有用/更通用。

【讨论】:

模板方法呢? 据我了解,我们提取了独立的部分,并通过上面的方法使用这些独立的部分创建了一个单独的类。这是真的吗? 在模板方法中,我们或多或少会做同样的事情:我们将创建在本例中称为“builder”的模板方法,并在此处添加与 Consumer 接口等效的抽象方法),模板方法的缺点是您必须子类化该类,而在这里您要创建构建器的新实例。 最后一件事,在模板方法中您也将添加一个新类,但如前所述,您需要为每个方法子类化它以实现挂钩(抽象方法)。恕我直言,给出的例子更干净。并回答您的问题:确实,您会将重复的代码提取到一个新类中,并为数据和处理使用“properties:getter/setter”腾出空间。 感谢您的回复。我想知道如果我为所有重复的代码块创建一个方法然后从重复的块行中调用这些方法也很好吗?

以上是关于Java:如何修复多种方法中的代码重复?的主要内容,如果未能解决你的问题,请参考以下文章

如何在Java中修复/实现equals方法? [重复]

如何修复 Javascript 中的代码以避免 HTML 中的重复元素?

如何修复 FileWriter 抛出的 NullPointerException [重复]

Java - é 变成 é - 如何修复它 [重复]

如何修复无法启动活动 ComponentInfo.....:java.lang.NullPointerException [重复]

如何修复 Java 中的 OutOfMemory 错误?