//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命令都作用于书目。书本和电影都属于LibraryItem(interface)类型。
以下两个代码是相同的。两者都将要求书目获取其自身类型的表示。并且两个命令都将显示该表示形式。

这是书目实施

//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));
}

}


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

最佳答案

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

观察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应采用项类型的Class进行过滤。如果在库中找到了项目类型,则返回其表示形式,否则抛出异常,说明它是未知的项目类型。

另一方面,representationOfAllLibraryItems()不应采用任何输入参数。它应该返回库中所有可用的表示形式。

观察2:您的LibraryItem应该是一个抽象类,并且库中的每个项目都应扩展该特定类。因为Movie是-a LibraryItemBook是-a LibraryItem。现在,您的每个项目都可以覆盖representation()方法,这是LibraryItem中的抽象方法。您的LibraryItem类应如下所示:

public abstract class LibraryItem {
    abstract String representation();
}


观察3:您的BookMovie类应该独立于Biblioteca,因为它们只是库中的项。今天,它们在一个名为Biblioteca的库中,明天它们可以在一个名为CentralHallLibrary的库中。因此,您的项目类应该看起来像这样:

/*
 * 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 + "]";
    }

}


观察4:我没有发现您正在使用的Command类的任何用法。因为,正如我所看到的,您的Command类只有一个称为execute()的方法,用于显示表示形式。通常,我会将这样的“显示”代码放在我的客户端(UI)中。如果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();


将返回同时具有MovieBook表示形式的结果。

在查询图书馆中的特定商品类型时-

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);


会抛出异常-

java.lang.IllegalArgumentException: Missing type Car


我知道这是一个冗长的答案,希望这可以使设计更加清晰。

10-05 23:56