如何删除重复?

时间:2017-09-14 05:43:53

标签: java design-patterns

//Represents list books command for biblioteca

public class ListBooksCommand implements Command {

    private static final String BOOKS = "Books::";
    private static final String FORMAT = "%-35s %-35s %-35s";
    private static final String HEADER = String.format(FORMAT, "Name", "Author", "YearPublished");
    private static final String NO_BOOKS_AVAILABLE = "No Books Available";

    private final Biblioteca biblioteca;
    private final IO io;

    public ListBooksCommand(Biblioteca biblioteca, IO io) {
        this.biblioteca = biblioteca;
        this.io = io;
    }

    @Override
    public void execute() {
        if (this.biblioteca.isEmpty(Book.class)) {
            this.io.println(NO_BOOKS_AVAILABLE);
            return;
        }
        this.displayBooks();
    }

    private void displayBooks() {
        this.io.println(BOOKS);
        this.io.println(HEADER);
        this.io.println(this.biblioteca.representationOfAllLibraryItems(Book.class));
    }

}    

public class ListMoviesCommand implements Command {

    private static final String Movies = "Movies::";
    private static final String FORMAT = "%-35s %-35s %-35s";
    private static final String HEADER = String.format(FORMAT, "Name", "Director", "YearPublished");
    private static final String NO_BOOKS_AVAILABLE = "No Movies Available";

    private final Biblioteca biblioteca;
    private final IO io;

    public ListBooksCommand(Biblioteca biblioteca, IO io) {
        this.biblioteca = biblioteca;
        this.io = io;
    }

    @Override
    public void execute() {
        if (this.biblioteca.isEmpty(Movie.class)) {
            this.io.println(NO_MOVIES_AVAILABLE);
            return;
        }
        this.displayMovies();
    }

    private void displayMovies() {
        this.io.println(MOVIES);
        this.io.println(HEADER);
        this.io.println(this.biblioteca.representationOfAllLibraryItems(MOVIE.class));
    }

}

我这里有两个类,一个是listbooks命令,listmovies命令都在biblioteca上。 Book和Movie都是LibraryItem(接口)类型。 以下两个代码都相同。两者都会要求biblioteca获得自己类型的表示。两个命令都将显示表示。

这是biblioteca实施

//Represents a library

public class Biblioteca {

    private final List<LibraryItem> allLibraryItems;

        public String representationOfAllLibraryItems(Class<? extends LibraryItem> itemType) {
    return this.allLibraryItems
            .stream()
            .filter(libraryItem -> libraryItem.getClass().equals(itemType))
            .map(LibraryItem::representation)
            .collect(Collectors.joining(LINE_SEPARATOR));
}

public boolean isEmpty(Class<? extends LibraryItem> itemType) {
    return this.allLibraryItems.stream().noneMatch(libraryItem -> libraryItem.getClass().equals(itemType));
}

}

请建议我避免重复的模式。

3 个答案:

答案 0 :(得分:1)

注意:我不了解您的要求。我只是在这个答案中提出一些一般性的设计观察。

观察1: Biblioteca是一个库,拥有库项目。在您的情况下,库中的项目是Movie项和Book项。因此,库具有两种主要类型的项目(或者甚至可以包含更多项目。无关紧要)。因此Biblioteca的成员应该是:

private HashMap<Class<? extends LibraryItem>, List<LibraryItem>> libraryItems;

项目类型为KeyList<LibraryItem>为值的地图。 Biblioteca还应包含查询方法,该方法将返回给定项类型的表示形式以及所有项类型的表示形式。所以在我看来,Biblioteca类看起来应该是这样的:

public class Biblioteca {
    private HashMap<Class<? extends LibraryItem>, List<LibraryItem>> libraryItems;

    public Biblioteca(HashMap<Class<? extends LibraryItem>, List<LibraryItem>> libraryItems) {
        this.libraryItems = libraryItems;
    }

    /*
     * Representation of a given type
     */
    public String representationOfLibraryItemType(Class<? extends LibraryItem> itemType) {
        if(libraryItems.containsKey(itemType)) {
            return libraryItems.get(itemType).stream()
                                        .filter(libraryItem -> libraryItem.getClass().equals(itemType))
                                        .map(LibraryItem::representation)
                                        .collect(Collectors.joining(System.lineSeparator()));
        } else {
            throw new IllegalArgumentException("Missing type " + itemType.getSimpleName());
        }
    }

    /*
     * Representation of all types
     */
    public List<String> representationOfAllLibraryItems() {

        return libraryItems.values()
                           .stream()
                           .flatMap(list -> list.stream()
                                                .map(LibraryItem::representation))
                           .collect(Collectors.toList());
    }
}

方法representationOfLibraryItemType应该采用{{1>} 项类型进行过滤。如果在库中找到了项类型,则返回它的表示,否则抛出异常,说明它是未知的项类型。

另一方面,Class不应该接受任何输入参数。它应该返回库中的所有可用表示。

观察2:您的representationOfAllLibraryItems()应该是一个抽象类,库中的每个项都应该扩展此特定类。由于LibraryItem 是-a MovieLibraryItem 是-a Book。现在,您的每个商品都可以覆盖LibraryItem方法,这是representation()中的抽象方法。您的LibraryItem课程应该如下所示:

LibraryItem

观察3:您的public abstract class LibraryItem { abstract String representation(); } Book课程应独立于Movie,因为它们只是 in-a 中的项目图书馆。今天他们在一个名为Biblioteca的图书馆,明天他们可以在名为Biblioteca的图书馆。所以,你的项目类应该是这样的:

CentralHallLibrary

观察4:我没有找到您正在使用的/* * Book Item */ public class Book extends LibraryItem { private String title; private String author; private String publishedYear; public Book(String title, String author, String publishedYear) { this.title = title; this.author = author; this.publishedYear = publishedYear; } @Override public String representation() { /* * I'm just returning a call to toString * from this method. You can replace it * with your representation logic. */ return toString(); } @Override public String toString() { return "Book [title=" + title + ", author=" + author + ", publishedYear=" + publishedYear + "]"; } } /* * Movie Item */ public class Movie extends LibraryItem { private String title; private String director; private String releaseYear; public Movie(String title, String director, String releaseYear) { this.title = title; this.director = director; this.releaseYear = releaseYear; } @Override public String representation() { /* * I'm just returning a call to toString * from this method. You can replace it * with your representation logic. */ return toString(); } @Override public String toString() { return "Movie [title=" + title + ", director=" + director + ", releaseYear=" + releaseYear + "]"; } } 课程的任何用途。因为,正如我所见,您的Command类只有一个名为Command的方法用于显示表示。通常我会在客户端(UI)中放置这样的“显示”代码。如果execute()类除了仅打印内容之外没有其他功能,我认为没有必要。

测试设计:让我们创建几个Command项和少量Book项,然后将这些项添加到Movie

Biblioteca

现在,在查询库以获取所有表示时 -

        Book effJava = new Book("Effective Java", "Josh Bloch", "2008");
        Book cloudNativeJava = new Book("Cloud Native Java", "Josh Long", "2017");
        Book java9modularity = new Book("Java 9 Modularity", "Paul Bakker", "2017");

        Movie gotgV2 = new Movie("Guardians of the Galaxy Vol. 2", "James Gunn", "2017");
        Movie wonderWoman = new Movie("Wonder Woman", "Patty Jenkins", "2017");
        Movie spiderHomeCmg = new Movie("Spider-man Homecoming", "Jon Watts", "2017");

        List<LibraryItem> bookItems = new ArrayList<>();
        List<LibraryItem> movieItems = new ArrayList<>();

        bookItems.add(java9modularity);
        movieItems.add(spiderHomeCmg);
        bookItems.add(cloudNativeJava);
        movieItems.add(wonderWoman);
        bookItems.add(effJava);
        movieItems.add(gotgV2);

        HashMap<Class<? extends LibraryItem>, List<LibraryItem>> store = new HashMap<>();
        store.put(Movie.class, movieItems);
        store.put(Book.class, bookItems);

        //CREATE STORE
        Biblioteca bibloiteca = new Biblioteca(store);

将返回同时包含List<String> allLibraryItemsRep = bibloiteca.representationOfAllLibraryItems(); Movie表示的结果。

在库中查询特定项目类型 -

Book

将返回特定表示 -

String movieRep = bibloiteca.representationOfLibraryItemType(Movie.class);
String bookRep = bibloiteca.representationOfLibraryItemType(Book.class);

在库中查询库中不存在的类型 -

Movie [title=Spider-man Homecoming, director=Jon Watts, releaseYear=2017]
Movie [title=Wonder Woman, director=Patty Jenkins, releaseYear=2017]
Movie [title=Guardians of the Galaxy Vol. 2, director=James Gunn, releaseYear=2017]

Book [title=Java 9 Modularity, author=Paul Bakker, publishedYear=2017]
Book [title=Cloud Native Java, author=Josh Long, publishedYear=2017]
Book [title=Effective Java, author=Josh Bloch, publishedYear=2008]

会抛出异常 -

String carRep = bibloiteca.representationOfLibraryItemType(Car.class);

我知道这是一个非常冗长的答案,并希望这能使设计更清晰。

答案 1 :(得分:0)

您可以创建一个通用类ListItemsCommand,它将接受项目名称或类作为列出和检查空列表的参数。 然后使用ListItemsCommandMovie

等项目类型调用Book

答案 2 :(得分:0)

如果您想删除重复,我建议您使用groupingBy的收件夹。这允许您指定哪个是用于重复数据删除(或分组)的密钥,以及减少函数,如果重复,则选择要从重复集中选择的元素。

以下是groupingBy收集器的示例方法:

public String representationOfAllLibraryItems(Class<? extends LibraryItem> itemType) {
    return this.allLibraryItems
            .stream()
            .filter(libraryItem -> libraryItem.getClass().equals(itemType))
            .collect(Collectors.groupingBy(LibraryItem::getName, LinkedHashMap::new,
                    Collectors.reducing((o1, o2) -> o1.toString().compareTo(o2.toString()) < 0 ? o1 : o2)))
            .values()
            .stream()
            .map(Optional::get)
            .map(LibraryItem::representation)
            .collect(Collectors.joining(LINE_SEPARATOR));
}

这是一个小测试,我们根据电影的名称去重复,并选择数据中的最新条目:

public static void main(String[] args) {
    List<LibraryItem> items = Arrays.asList(new Movie("Valerian", "Luc Besson", "2017"),
            new Movie("Valerian", "Luc Besson", "2016"),
            new Movie("Spiderman", "Sam Raimi", "2002"),
            new Movie("Spiderman", "Sam Raimi", "2001"),
            new Movie("Spiderman", "Sam Raimi", "2003"));
    Biblioteca biblioteca = new Biblioteca(items);
    System.out.println(biblioteca.representationOfAllLibraryItems(Movie.class));
}

结果如下:

Luc Besson - Valerian - 2017
Sam Raimi - Spiderman - 2003

此处重复数据删除是通过电影名称进行的,并且选择了最近的电影。