java - 适应DRY原则的方法

标签 java dry

我有一个网络抓取应用程序,可以简单地从两家大书店抓取书籍。这个想法是用户在 URL 中放置类别类型,例如 /romances/biographies

Controller :

package bookstore.scraper.controller;

imports...

import java.util.List;
import java.util.Map;

@RestController
public class BookController {

    private final MostPreciseBookService mostPreciseBookService;
    private final CategorizedBookService categorizedBookService;
    private final BestSellersService bestSellersService;

    @Autowired
    public BookController(MostPreciseBookService bookOperationsService, CategorizedBookService categorizedBookService, BestSellersService bestSellersService) {
        this.mostPreciseBookService = bookOperationsService;
        this.categorizedBookService = categorizedBookService;
        this.bestSellersService = bestSellersService;
    }
    
.....


    @GetMapping("/romances")
    public Map<Bookstore, List<Book>> get15RomanticBooks() {
        return categorizedBookService.get15BooksFromRomanceCategory();
    }

    @GetMapping("/biographies")
    public Map<Bookstore, List<Book>> get15BiographiesBooks() {
        return categorizedBookService.get15BooksFromBiographiesCategory();
    }

    @GetMapping("/guides")
    public Map<Bookstore, List<Book>> get15GuidesBooks() {
        return categorizedBookService.get15BooksFromGuidesCategory();
    }

    @GetMapping("/fantasy")
    public Map<Bookstore, List<Book>> get15FantasyBooks() {
        return categorizedBookService.get15BooksFromFantasyCategory();
    }

    @GetMapping("/crime")
    public Map<Bookstore, List<Book>> get15CrimeBooks() {
        return categorizedBookService.get15BooksFromCrimeCategory();
    }
}

以及服务:

package bookstore.scraper.book.scrapingtypes;

import bookstore.scraper.Bookstore;
import bookstore.scraper.book.Book;
import bookstore.scraper.empik.EmpikFetchingBookService;
import bookstore.scraper.merlin.MerlinFetchingBookService;
import lombok.extern.slf4j.Slf4j;
import org.jsoup.nodes.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static bookstore.scraper.JSoupConnector.connect;

@Service
@Slf4j
public class CategorizedBookService {

    private final EmpikFetchingBookService empikBookService;
    private final MerlinFetchingBookService merlinFetchingBookService;

    @Value("${external.library.url.empik.romances}")
    private String romancesCategoryEmpikURL;

    @Value("${external.library.url.empik.biographies}")
    private String biographiesCategoryEmpikURL;

    @Value("${external.library.url.empik.crime}")
    private String crimeCategoryEmpikURL;

    @Value("${external.library.url.empik.guides}")
    private String guidesCategoryEmpikURL;

    @Value("${external.library.url.empik.fantasy}")
    private String fantasyCategoryEmpikURL;

    @Value("${external.library.url.merlin.romances}")
    private String romancesCategoryMerlinURL;

    @Value("${external.library.url.merlin.biographies}")
    private String biographiesCategoryMerlinURL;

    @Value("${external.library.url.merlin.crime}")
    private String crimeCategoryMerlinURL;

    @Value("${external.library.url.merlin.guides}")
    private String guidesCategoryMerlinURL;

    @Value("${external.library.url.merlin.fantasy}")
    private String fantasyCategoryMerlinURL;

    @Autowired
    public CategorizedBookService(EmpikFetchingBookService empikBookService, MerlinFetchingBookService merlinFetchingBookService) {
        this.empikBookService = empikBookService;
        this.merlinFetchingBookService = merlinFetchingBookService;
    }


    public Map<Bookstore, List<Book>> get15BooksFromRomanceCategory() {
        Map<Bookstore, List<Book>> bookstoreWith15CategorizedBooks = new HashMap<>();

        bookstoreWith15CategorizedBooks.put(Bookstore.EMPIK, empikBookService
                .get15BooksFromCategory(connect(romancesCategoryEmpikURL)));
        bookstoreWith15CategorizedBooks.put(Bookstore.MERLIN, merlinFetchingBookService
                .get15BooksFromCategory(connect(romancesCategoryMerlinURL)));

        return bookstoreWith15CategorizedBooks;
    }

    public Map<Bookstore, List<Book>> get15BooksFromFantasyCategory() {
        Map<Bookstore, List<Book>> bookstoreWith15CategorizedBooks = new HashMap<>();

        bookstoreWith15CategorizedBooks.put(Bookstore.EMPIK, empikBookService
                .get15BooksFromCategory(connect(fantasyCategoryEmpikURL)));
        bookstoreWith15CategorizedBooks.put(Bookstore.MERLIN, merlinFetchingBookService
                .get15BooksFromCategory(connect(fantasyCategoryMerlinURL)));

        return bookstoreWith15CategorizedBooks;
    }

    public Map<Bookstore, List<Book>> get15BooksFromCrimeCategory() {
        Map<Bookstore, List<Book>> bookstoreWith15CategorizedBooks = new HashMap<>();

        bookstoreWith15CategorizedBooks.put(Bookstore.EMPIK, empikBookService
                .get15BooksFromCategory(connect(crimeCategoryEmpikURL)));
        bookstoreWith15CategorizedBooks.put(Bookstore.MERLIN, merlinFetchingBookService
                .get15BooksFromCategory(connect(crimeCategoryMerlinURL)));

        return bookstoreWith15CategorizedBooks;
    }

    public Map<Bookstore, List<Book>> get15BooksFromGuidesCategory() {
        Map<Bookstore, List<Book>> bookstoreWith15CategorizedBooks = new HashMap<>();

        bookstoreWith15CategorizedBooks.put(Bookstore.EMPIK, empikBookService
                .get15BooksFromCategory(connect(guidesCategoryEmpikURL)));
        bookstoreWith15CategorizedBooks.put(Bookstore.MERLIN, merlinFetchingBookService
                .get15BooksFromCategory(connect(guidesCategoryMerlinURL)));

        return bookstoreWith15CategorizedBooks;
    }

    public Map<Bookstore, List<Book>> get15BooksFromBiographiesCategory() {
        Map<Bookstore, List<Book>> bookstoreWith15CategorizedBooks = new HashMap<>();

        bookstoreWith15CategorizedBooks.put(Bookstore.EMPIK, empikBookService
                .get15BooksFromCategory(connect(biographiesCategoryEmpikURL)));
        bookstoreWith15CategorizedBooks.put(Bookstore.MERLIN, merlinFetchingBookService
                .get15BooksFromCategory(connect(biographiesCategoryMerlinURL)));

        return bookstoreWith15CategorizedBooks;
    }
}

正如你所看到的,有很多相同的代码。我做了这样的东西,但不知道做得好不好:

public Map<Bookstore, List<Book>> get15BooksFromCategory(String merlinCategoryURL, String empikCategoryURL) {
 

   Map<Bookstore, List<Book>> bookstoreWith15CategorizedBooks = new HashMap<>();

bookstoreWith15CategorizedBooks.put(Bookstore.EMPIK, empikBookService
        .get15BooksFromCategory(connect(empikCategoryURL)));
bookstoreWith15CategorizedBooks.put(Bookstore.MERLIN, merlinFetchingBookService
        .get15BooksFromCategory(connect(merlinCategoryURL)));

return bookstoreWith15CategorizedBooks;
}

我还在考虑在 Controller 中创建一种方法,例如:

@GetMapping("/{category}")
    public Map<Bookstore, List<Book>> get15CategorizedBooks(@PathVariable String category) {
        ...
    }

但我很难在 Service 中创建通用方法。

问题在最后。在 Service 中使用这么多 @Value 注释是个好方法吗?/(使用 yml 文件来存储 URL)。

最佳答案

The question in the end. Is it good way to have that many @Value annotations in Service?/ (using yml files to store URL's).

这并不是因为您注入(inject)了太细粒度的属性。这些应该被封装到一个特定的对象中:更具可读性、可维护性和可测试性。

Spring 提供了 @ConfigurationProperties 来做到这一点。
它必须注释包含属性的类。
你可以这样做:

@Component
@ConfigurationProperties("external.library.url")
public class BookStoreUrlProperties {

    private Empik empik = new Empik();
    private Merlin merlin = new Merlin();    
    // getters/setters    

    public BookStoreUrlProperties() {
    }

    public static class Empik {

        private String romances;
        private String biographies;
        private String crime;
        private String guides;
        private String fantasy;        
        // getters/setters    
    }

    public static class Merlin {

        private String romances;
        private String biographies;
        private String crime;
        private String guides;
        private String fantasy;    
        // getters/setters    
    }

}

然后像任何其他bean一样注入(inject)这个bean:

@Autowired
BookStoreUrlProperties bookStoreUrlProperties;

并使用 getter 来检索 url,例如:

String RomanceUrl = bookStoreUrlProperties.getMerlin().getRomances();

我建议的另一种选择是将属性(merlin 和 empik)拆分为两个属性类。

关于服务类中的重复,您可以通过提取参数重构轻松地排除它们,因为唯一的区别是 URL 值。

例如:

public Map<Bookstore, List<Book>> get15BooksFromGuidesCategory() {
   return get15BooksFrom(guidesCategoryEmpikURL, guidesCategoryMerlinURL)
}

public Map<Bookstore, List<Book>> get15BooksFromBiographiesCategory() {
   return get15BooksFrom(biographiesCategoryEmpikURL, biographiesCategoryMerlinURL)
}

public Map<Bookstore, List<Book>> get15BooksFrom(String bookStoreEmpikURL, String bookStoreMerlinURL) {

    bookstoreWith15CategorizedBooks.put(Bookstore.EMPIK, empikBookService
            .get15BooksFromCategory(connect(bookStoreEmpikURL)));
    bookstoreWith15CategorizedBooks.put(Bookstore.MERLIN, merlinFetchingBookService
            .get15BooksFromCategory(connect(bookStoreMerlinURL)));

    return bookstoreWith15CategorizedBooks;
}

关于java - 适应DRY原则的方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57175950/

相关文章:

javascript - 保持干燥的最佳实践

java - 如何在准备好的语句中包含 Oracle SQL 所需的单引号

java - 如何重新部署在tomcat上运行的一类webapp?

java - 如何更新数据库,同时在我们的服务器端进行更改

java - 打开默认照片库应用程序

javascript - 如何减少过度冗余的 for 循环

java - 将对象 w.r.t 列表排序为特定成员/状态

unit-testing - 在单元测试中重复的代码是否更容易被容忍?

css - 使用 SASS 干燥多个 3 列列表

ruby - 我怎样才能在我的井字游戏中干掉这段 Ruby 代码? (使用 1.times block !)