日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

前言

冗余代碼向來是代碼的一種壞味道,也是我們程序員要極力避免的。今天我通過一個示例和大家分享下解決冗余代碼的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
@Service
class FtpProviderImpl implements FtpProvider {

    private final FTPClient ftpClient;

    @Override
    public FTPFile[] listDirectories(String directory) throws IOException {
        return ftpClient.listDirectories(directory);
    }

    @Override
    public boolean deleteFile(String filePath) throws IOException {
       return ftpClient.deleteFile(filePath);
    }
}
  • 實現注解的代理切面邏輯
@Slf4j
@Aspect
@Component
@RequiredArgsConstructor
public 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. 函數式接口

  • 創建一個函數式接口
@FunctionalInterface
interface FtpOperation<T, R> {
      
     R Apply(T t) throws IOException;
}
  • 定義ftp執行模板
@RequiredArgsConstructor
@Slf4j
@Service
public 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
@Service
class 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));
    }
}

我們正在 FtpOperationTemplate? 上執行方法 execute? 并且我們正在傳遞 lambda? 表達式。我們將放入 lambda? 中的所有邏輯都將代替 ftpOperation.apply(ftpClient) 函數執行。

3. 模板方法

  • 創建一個抽象的模板類
@RequiredArgsConstructor
@Slf4j
@Service
abstract class FtpOperationTemplate<T, K> {

    protected abstract K command(FTPClient ftpClient, T input) throws IOException;

    public K execute(FTPClient ftpClient, T input) {
        try {
            ftpClient.connect("host", 22);
            ftpClient.login("username", "password");
            return command(ftpClient, input);
        } 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);
            }
        }
    }

}
  • 列出ftp目錄listDirectories方法的實現
@Slf4j
@Service
class FtpOperationListDirectories extends FtpOperationTemplate<String, FTPFile[]> {

    @Override
    protected FTPFile[] command(FTPClient ftpClient, String input) throws IOException {
        return ftpClient.listDirectories(input);
    }
}
  • 刪除文件deleteFile方法的實現
 
@Slf4j
@Service
class FtpOperationDeleteFile extends FtpOperationTemplate<String, Boolean> {

    @Override
    protected Boolean command(FTPClient ftpClient, String input) throws IOException {
        return ftpClient.deleteFile(input);
    }
}
  • 實現FtpProvider接口
 
@RequiredArgsConstructor
@Slf4j
@Service
public class FtpProviderTemplateImpl implements FtpProvider {

    private final FtpOperationTemplate<String, FTPFile[]> ftpOperationListDirectories;
    private final FtpOperationTemplate<String, Boolean> ftpOperationDeleteFile;
    private final FTPClient ftpClient;

    public FTPFile[] listDirectories(String parentDirectory) {
        return ftpOperationListDirectories.execute(ftpClient, parentDirectory);
    }

    public boolean deleteFile(String filePath) {
        return ftpOperationDeleteFile.execute(ftpClient, filePath);
    }
}

我們正在 FtpOperationTemplate? 上執行方法 execute? 并在那里傳遞我們的參數。因此執行方法的邏輯對于 FtpOperationTemplate 的每個實現都是不同的。

總結

我們現在來比較下上面種方式:

  • @Aspect切面方式實現

向 FtpProvider? 接口添加一個新方法,需要我們僅在一個地方進行更改。我們可以輕松地將我們的 FtpProvider? 注入到其他服務中。此解決方案的強項可能是 @FtpOperation? 注釋,它可以在 FtpProvider 上下文實現之外使用,但是將 Ftp 操作的邏輯劃分到單獨的類中并不是一個好方法。

  • 函數式接口實現

向接口 FtpProvider? 添加一個新方法,需要我們僅在一個地方進行更改。我們可以輕松地將我們的 FtpProvider 注入到其他服務中。我們將ftp操作的邏輯封裝在一個類中。相對于上面的方式,我們也沒有用到AOP的庫,所以我個人還是比較推薦的。

  • 模板方法實現

向接口 FtpProvider? 添加一個新方法,需要我們在兩個地方進行更改。我們需要添加一個新的類,會導致類爆炸,另外,我們還需要將實現注入到 FtpProvider。

如果是你,你會選擇哪種方式呢?還是有更好的方法?

分享到:
標簽:代碼
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定