代码重复:我应该使用单独的方法还是构建器模式?
Posted
技术标签:
【中文标题】代码重复:我应该使用单独的方法还是构建器模式?【英文标题】:Code Duplication: Should I use a separate method or Builder Pattern? 【发布时间】:2021-12-06 02:30:44 【问题描述】:我的 Java (Spring) 应用程序中有以下 2 种方法(为简洁起见省略了一些代码),我想减少这些方法中的重复块。至此,经过一番研究,有人提出了Builder Pattern,也有人提出了类似的Template Method。我也认为,我可以简单地创建 2 个单独的方法并将每个重复的代码块移动到这些方法中。
但是,我对这个问题没有经验,我想先请你遵循最正确的方法。那么,我应该如何减少以下2种方法中的代码重复?我也想使用继承,但我真的很困惑找到合适的方法。
不是:为了简洁起见,我省略了我的代码并使用了一个简单的代码:
@Override
public MultipartFile exportAaaaa() throws IOException
// repeated code block I
workbook = new XSSFWorkbook();
sheet = workbook.createSheet(TextBundleUtil.read(TITLE));
rowCount = new AtomicInteger(0);
//
// private block to this method
final Page<Aaaaa> page = aaaaaService.findAll());
// ...
// repeated code block II
outputFile = File.createTempFile(TextBundleUtil.read(TITLE), EXTENSION);
try (FileOutputStream outputStream = new FileOutputStream(outputFile))
workbook.write(outputStream);
catch (IOException e)
LoggingUtils.error("Writing is failed ", e);
final FileInputStream input = new FileInputStream(outputFile);
final String fileName = TextBundleUtil.read(TITLE).concat(EXTENSION);
return new MockMultipartFile(fileName,
fileName, CONTENT_TYPE, IOUtils.toByteArray(input));
//
@Override
public MultipartFile exportBbbbb() throws IOException
// repeated code block I
workbook = new XSSFWorkbook();
sheet = workbook.createSheet(TextBundleUtil.read(TITLE));
rowCount = new AtomicInteger(0);
//
// private block to this method
final Page<Bbbbb> page = bbbbbService.findBy Uuid(uuid));
// ...
// repeated code block II
outputFile = File.createTempFile(TextBundleUtil.read(TITLE), EXTENSION);
try (FileOutputStream outputStream = new FileOutputStream(outputFile))
workbook.write(outputStream);
catch (IOException e)
LoggingUtils.error("Writing is failed ", e);
final FileInputStream input = new FileInputStream(outputFile);
final String fileName = TextBundleUtil.read(TITLE).concat(EXTENSION);
return new MockMultipartFile(fileName,
fileName, CONTENT_TYPE, IOUtils.toByteArray(input));
//
【问题讨论】:
我建议您为code review 发布您的实际工作代码。 【参考方案1】:我在这里看到,重复的代码是日志记录(或控制台打印)。 但是在这个例子中并不清楚 aService 和 bService 是做什么的?可能他们内部有不同的逻辑?正如我所看到的,他们返回了不同的结果。 但是如果A.class和B.class是一个接口的实现,aService和bService也是一个接口的实现。这可能是一种方法。也许)
【讨论】:
非常感谢您的帮助。对不起,它被误解了,因此我发布了完整的代码。你能看看并更新你的答案吗? 奇科?有回复吗?【参考方案2】:如果aaaaaService
和bbbbbService
共用一个接口,例如
interface PageService<T>
Page<T> findAll();
然后我们可以使用带有类型参数的单个方法。
private <T> MultipartFile export(Service<T> service) throws IOException
// repeated code block I
final Page<T> page = service.findAll());
// repeated code block II
我们可以将此方法声明为public
,也可以保留以前的方法作为覆盖方法,这取决于您是否希望客户端处理/访问底层服务以及您是否可以更改@987654326指示的接口或超类@。
public MultipartFile exportAaaaa() throws IOException
// assuming that 'aaaaaService' is a class member
return export(aaaaaService);
如果没有通用接口,我会简单地将通用代码块提取到单独的方法中。
private MultipartFile exportAaaaa() throws IOException
prepareWorkbook(/* parameters */);
final Page<T> page = aaaaaService.findAll());
constructOutput(/* parameters */);
private /* return type */ prepareWorkbook(/* parameters */)
// repeated code block I
private /* return type */ constructOutput(/* parameters */)
// repeated code block II
【讨论】:
非常感谢您的宝贵解释。但我关注的重点是重复的代码部分而不是服务。所以,忽略服务(假设他们完全做不相关的事情)并通过建议一个正确的方法来更新你的答案。我应该将 2 个重复的部分提取到 2 个单独的方法中并在这些exportAaaaa
和 exportBbbbb
方法中调用它们吗?
@Jonathan 我在回答中提到了这两种选择。如果有一个通用接口,你可以使用它。但是,如果您不想依赖服务来履行特定合同,则可以选择我强调的第二种方法——将两个重复的部分提取到专用方法中。【参考方案3】:
看起来你的代码中的重复可以很容易地提取到单独的方法中:
private void doCodeBlockI()
// repeated code block I
workbook = new XSSFWorkbook();
sheet = workbook.createSheet(TextBundleUtil.read(TITLE));
rowCount = new AtomicInteger(0);
private MultipartFile doCodeBlockII()
// repeated code block II
outputFile = File.createTempFile(TextBundleUtil.read(TITLE),
EXTENSION);
try (FileOutputStream outputStream = new FileOutputStream(outputFile))
workbook.write(outputStream);
catch (IOException e)
LoggingUtils.error("Writing is failed ", e);
final FileInputStream input = new FileInputStream(outputFile);
final String fileName = TextBundleUtil.read(TITLE).concat(EXTENSION);
return new MockMultipartFile(fileName,
fileName, CONTENT_TYPE, IOUtils.toByteArray(input));
@Override
public MultipartFile exportAaaaa() throws IOException
doCodeBlockI();
// private block to this method
final Page<Aaaaa> page = aaaaaService.findAll();
// ...
return doCodeBlockII();
@Override
public MultipartFile exportBbbbb() throws IOException
doCodeBlockI();
// private block to this method
final Page<Bbbbb> page = bbbbbService.findByUuid(uuid);
// ...
return doCodeBlockII();
实现这一目标的另一种方法是使用抽象类:
public abstract class Exporter
public MultipartFile export() throws IOException
// repeated code block I
workbook = new XSSFWorkbook();
sheet = workbook.createSheet(TextBundleUtil.read(TITLE));
rowCount = new AtomicInteger(0);
//
// private block will be called inside the abstract method
doPrivateBlock();
// repeated code block II
outputFile = File.createTempFile(TextBundleUtil.read(TITLE), EXTENSION);
try (FileOutputStream outputStream = new FileOutputStream(outputFile))
workbook.write(outputStream);
catch (IOException e)
LoggingUtils.error("Writing is failed ", e);
final FileInputStream input = new FileInputStream(outputFile);
final String fileName = TextBundleUtil.read(TITLE).concat(EXTENSION);
return new MockMultipartFile(fileName,
fileName, CONTENT_TYPE, IOUtils.toByteArray(input));
//
protected abstract void doPrivateBlock();
然后将其扩展两次,实现doPrivateBlock()
方法:
public class AaaaaExporter extends Exporter
AaaaaService aaaaaService; // need to instantiate this
public AaaaaExporter()
@Override
private void doPrivateBlock()
// private block to this method
final Page<Aaaaa> page = aaaaaService.findAll());
// ...
public class BbbbbExporter extends Exporter
BbbbbService bbbbbService; // need to instantiate this
public BbbbbExporter()
@Override
private void doPrivateBlock()
// private block to this method
final Page<Bbbbb> page = bbbbbService.findByUuid(uuid));
// ...
我忽略了代码中使用的共享字段(例如workbook
),您需要将它们作为参数添加到 export(...) 方法或将它们作为字段添加到 @ 987654326@ 类并将它们与您的类一起实例化,这将取决于您的问题中未明确定义的范围。
【讨论】:
非常感谢,这似乎是一个适当且最简单的解决方案。另一方面,如果其他类也有这些doCodeBlockI
和doCodeBlockII
呢?在这种情况下,我应该如何移动这些方法(doCodeBlockI
和 doCodeBlockII
)并在此方法和其他导出类中使用它们?我应该将它们(doCodeBlockI
和doCodeBlockII
)设为静态方法并从此类调用吗?或者有没有必要使用继承?
这真的取决于你的具体需求,但是是的,一个帮助类可能会解决问题(你也可以根据需要传递服务作为参数,这些服务可以根据上下文有不同的行为(例如@ 987654333@ vs CloudPrinter
vs DummyPrinter
都实现了IPrinter
的print()
方法)。
这点让我很困惑,因为我没有足够的经验。那么,您能否将这两种方法添加为更新?我会非常感激并从这样的例子中学到很多东西。提前致谢。
请回复?以上是关于代码重复:我应该使用单独的方法还是构建器模式?的主要内容,如果未能解决你的问题,请参考以下文章