解决冗余代码的三种方法,让你的代码更上一层楼

前言冗余代码向来是代码的一种坏味道,也是我们程序员要极力避免的 。今天我通过一个示例和大家分享下解决冗余代码的3个手段,看看哪个最好 。
问题描述为了描述这个问题,我将使用 FtpClient 作为示例 。要从 ftp 服务器获取一些文件,你需要先建立连接,下一步是登录,然后执行查看ftp文件列表、删除ftp文件,最后注销并断开连接,代码如下:
public class FtpProvider{private final FTPClient ftpClient;public FTPFile[] listDirectories(String parentDirectory) {try {ftpClient.connect("host", 22);ftpClient.login("username", "password");return ftpClient.listDirectories(parentDirectory);} catch (IOException ex) {log.error("Something went wrong", ex);throw new RuntimeException(ex);} finally {try {ftpClient.logout();ftpClient.disconnect();} catch (IOException ex) {log.error("Something went wrong while finally", ex);}}}public boolean deleteFile(String filePath) {try {ftpClient.connect("host", 22);ftpClient.login("username", "password");return ftpClient.deleteFile(filePath);} catch (IOException ex) {log.error("Something went wrong", ex);throw new RuntimeException(ex);} finally {try {ftpClient.logout();ftpClient.disconnect();} catch (IOException ex) {log.error("Something went wrong while finally", ex);}}}}正如上面代码所示,listDirectories和downloadFtpFile?中都包含了ftp连接、登录以及最后的注销操作,存在大量冗余的代码,那有什么更好的办法清理冗余代码呢?下面推荐3个做法,所有三个提出的解决方案都将实现以下 FtpProvider 接口,我将比较这些实现并选择更好的一个 。
public interface FtpProvider {FTPFile[] listDirectories(String directory) throws IOException;boolean deleteFile(String filePath) throws IOException;}1. 使用@Aspect 代理

  • 首先创建一个注解, 用来注解需要代理的方法
@Target({ElementType.TYPE, ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)public @interface FtpOperation {}
  • 创建一个类实现 FtpProvider接口, 将注解添加到方法 listDirectories 和 deleteFile 中
@Slf4j@Serviceclass FtpProviderImpl implements FtpProvider {private final FTPClient ftpClient;@Overridepublic FTPFile[] listDirectories(String directory) throws IOException {return ftpClient.listDirectories(directory);}@Overridepublic boolean deleteFile(String filePath) throws IOException {return ftpClient.deleteFile(filePath);}}
  • 实现注解的代理切面逻辑
@Slf4j@Aspect@Component@RequiredArgsConstructorpublic class FtpOperationProxy {private final FTPClient ftpClient;@Around("@annotation(daniel.zielinski.redundancy.proxyaop.infrastructure.FtpOperation)")public Object handle(ProceedingJoinPoint joinPoint) throws Throwable {try {ftpClient.connect("host", 22);ftpClient.login("username", "password");return joinPoint.proceed();} catch (IOException ex) {log.error("Something went wrong", ex);throw new RuntimeException(ex);} finally {try {ftpClient.logout();ftpClient.disconnect();} catch (IOException ex) {log.error("Something went wrong while finally", ex);}}}}所有用@FtpOperation? 注解的方法都会在这个地方执行joinPoint.proceed() 。2. 函数式接口
  • 创建一个函数式接口
@FunctionalInterfaceinterface FtpOperation<T, R> {R Apply(T t) throws IOException;}
  • 定义ftp执行模板
@RequiredArgsConstructor@Slf4j@Servicepublic class FtpOperationTemplate {private final FTPClient ftpClient;public <K> K execute(FtpOperation<FTPClient, K> ftpOperation) {try {ftpClient.connect("host", 22);ftpClient.login("username", "password");return ftpOperation.apply(ftpClient);} catch (IOException ex) {log.error("Something went wrong", ex);throw new RuntimeException(ex);} finally {try {ftpClient.logout();ftpClient.disconnect();} catch (IOException ex) {log.error("Something went wrong while finally", ex);}}}}
  • 定义实现类
@RequiredArgsConstructor@Slf4j@Serviceclass FtpProviderFunctionalInterfaceImpl implements FtpProvider {private final FtpOperationTemplate ftpOperationTemplate;public FTPFile[] listDirectories(String parentDirectory) {return ftpOperationTemplate.execute(ftpClient -> ftpClient.listDirectories(parentDirectory));}public boolean deleteFile(String filePath) {return ftpOperationTemplate.execute(ftpClient -> ftpClient.deleteFile(filePath));}}


推荐阅读