//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;
项类型为
Key
和List<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 LibraryItem
和Book
是-a LibraryItem
。现在,您的每个项目都可以覆盖representation()
方法,这是LibraryItem
中的抽象方法。您的LibraryItem
类应如下所示:public abstract class LibraryItem {
abstract String representation();
}
观察3:您的
Book
和Movie
类应该独立于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();
将返回同时具有
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);
会抛出异常-
java.lang.IllegalArgumentException: Missing type Car
我知道这是一个冗长的答案,希望这可以使设计更加清晰。