Я бы предложил сначала подумать об этом как о проблеме проектирования, а затем попытаться выразить дизайн в коде.
Итак, мы должны решить, какие классы (сущности) мы имеем.Book
- это класс, потому что он занимает центральное место в проблеме, имеет различные экземпляры и, возможно, несколько атрибутов и операций.Tag
может быть как объектом-значением, так и классом.
Рассмотрим первый вариант.Это может быть объект значения, поскольку он не имеет внутренней структуры, каких-либо операций и его экземпляры могут быть не различимы.Таким образом, Tag
можно рассматривать как маркер String
.Таким образом, Book
имеет атрибут, скажем, tags
, который содержит коллекцию значений tag
.Значения могут быть добавлены и удалены без каких-либо ограничений.Книги можно искать по тегам, где теги поставляются по значению.Трудно получить полный список тегов или получить все книги по определенному тегу.
Теперь второй вариант.Tag
также может быть классом, потому что он связан с другим классом (Book
) и его экземпляры могут отличаться.Тогда у нас есть два класса: Book
и Tag
, и связь между многими из них - TaggedWith
.Как вы, возможно, знаете, ассоциация - это своего рода класс, помимо отношения.Экземпляры TaggedWith
ассоциации (ссылки) соединяют экземпляры Book
и Tag
.Затем мы должны решить, какой класс будет отвечать за управление корреспонденцией (создание, чтение, поиск, уничтожение, обновление ...) между Book
и Tag
.Наиболее естественным выбором здесь является присвоение этой ответственности ассоциации TaggedWith
.
. Давайте напишем некоторый код.
Вариант 1
public class Book {
private Collection<String> tags;
/* methods to work with tags, e.g. */
public void addTag(String tag) {...}
public String[] getAllTags() {...}
...
}
Может показаться сложным, но на самом деле похожий код может быть сгенерирован из описания конструкции всего несколькими щелчками мыши.С другой стороны, если вы используете БД, большой объем кода здесь становится запросами SQL.
Опция 2
public class Tag {
/* we may wish to define a readable unique id for Tag instances */
@Id
private String name;
/* if you need navigation from tags to books */
private Collection<Book> taggedBooks;
public Collection<Book> getTaggedBooks() {...}
public void addBook(Book book) {...} // calls TaggedWith.create(this, book)
public void _addBook(Book book) {...} // adds book to taggedBooks
....
/* I think you get the idea */
/* methods to work with tags */
public String getName() {...}
...
/* Tags cannot be created without id (i.e. primary key...) */
public Tag(String name) {...}
/* if you'd like to know all tags in the system,
you have to implement 'lookup' methods.
For this simple case, they may be put here.
We implement Factory Method and Singleton patterns here.
Also, change constructor visibility to private / protected.
*/
protected static HashMap<String, Tag> tags = ...; // you may wish to use a DB table instead
public static Tag getInstance(String name) {...} // this would transform to DAO for DB
}
public class Book {
/* if we need an id */
@Id // made up
private String bookId;
/* constructors and lookup the same as for Tag
If you wish to use a database, consider introducing data access layer or use ORM
*/
/* if you need navigation from Book to Tag */
private Collection<Tag> tags;
public Collection<Tag> getTags() {...}
...
}
public TaggedWith {
/* constructor and lookup the same as for Tag and Book (!) */
/* manage ends of the association */
private Book book;
private Tag tag;
public Book getBook() {...}
public Tag getTag() {...}
protected TaggedWith(Book book, Tag tag) {
this.book = book;
this.tag = tag;
book._addTag(tag); // if you need navigation from books to tags
tag._addBook(book); // if you need navigation from tags to books
}
/* if you need to search tags by books and books by tags */
private static Collection<TaggedWith> tagsBooks = ...;
public static TaggedWith create(Tag tag, Book book) {
// create new TaggedWith and add it to tagsBooks
}
}