Reputation: 8276
I'm trying to come up with a simple way of organizing some objects, in terms of what classes to create. Let's say I'm trying to keep track of books. A book can fall under a number of different genres and subgenres. I want to be able to recognize a book as one book and yet have it fall under these different categories. I have a genre class which keeps track of all the subgenres, and a subgenre class which has all of the books in it. I want the book to know all of the genre and subgenres that it falls under. I also want to keep track of some statistics (reviews, comments, number of times read, etc.) based on genre and subgenre and then be able to aggregate them to get numbers for the entire book. In this way, a user could select a book and know, each genre/subgenre that the book belongs to, and soem statistics about that book for each category
What are some ideas for how I can design this?
My thought was to have each Book define a class called BookGroup, and the BookGroup would contain the Genre and Subgenre, along with any relevant information for that category (assuming that subgenres can only belong to one genre). Then in the Book class I would keep a set of bookgroups that the book belongs in. I can add up stats from all the different bookgroups. The only thing I don't like about this is that I feel like a BookGroup should contain Books, not the other way around.
Any other ideas?
Thanks!
Edit: All you guys gave really good tips. I think for simplicity reasons, I might do something like this for now:
class Book
{
Genre myGenre;
SubGenre mySubGenre;
String myTitle;
}
class Library
{
Map<String,Set<Book>> allBooks = new HashMap<String,Set<Book>>();
//where allBooks contains a mapping from book title, to all of the book objects which actually represent the same book but may contain different information related to their specific genre/subgenre
}
Upvotes: 3
Views: 681
Reputation: 179
I think you want collections pointing to each other. And when adding a book to a changre you would also add the changre to the book. Then just iterate as needed to obtain what you wanted. A changre and a sub changre should really be the same class, no need to have different classes here.
An alternative to this would be not to have references in a book to what changres it belongs to, instead if you need to know you would have to iterate through all changres and see if the book is in them. Depends on how many changres there are and how usual it is for a book to belong to a changre. Let's say if most changres have over half of all the books in them. The obvious third option is not to have books in changres, in that case you would have to iterate through the books to obtain the changres, the question is if most books belong to almost all changres, or if changres are unusual and only contain few books.
If you chose option number one, then a changre would be able to contain books and other changres, and a book would be able to contain changres but not other books. Sounds similar doesn't it? Well, it is, a changre and a book is the same thing, well, almost. The main difference is how you use them. Imagine a tree where the changres on top point down to subchangres and so forth, then they in turn point down to books who in turn point back up to the subchangres they're part of. Then in order to find all books in a changre for instance you would just have to traverse the tree from root up, except when you're at a book you stop. If a book can belong to several changres (yes, it can, right?), then you just need a loop variable in the book that's set when iterating and if the book is reached a second time you know because the variable has already been set.
For instance finding all the books in a changre: 1. Construct collection object that is to hold the result. 2. (in subclass changre) Iterate through all changres and books (they might be stored in the same collection object) 2. (same method as above, but in subclass book) Check if iteration field is set, if so just return, else add this to the result collection object. 3. Unset iteration field in all books of the result collection object to make it possible to redo from step one. (the alternative to having such an iteration field is of course use a collection that doesn't matter if you put in duplicates)
-Done, a book simply instead of iterating through the changres it has (like a changre does) knows that it has to add itself to the result.
Now that I think about it I think there's a tool that automatically generates code where you can specify things like a changre can have books and so on, and then to find all book reviews in a changre you can specify to traverse from the changre, pass at most one book on your path through the graph, and end in a review, and then agregate the results, and it generates code that does that. I don't remember the name or what language it was, but I think code like this can be generated from only a few lines, but of course writing it yourself shouldn't hurt either.
Upvotes: 1
Reputation: 1387
There have been good suggestions from the other posters, but your original idea might work as well. The biggest problem for you, if I understand you correctly, appears to be one of naming: your 'BookGroup' is not really a grouping as such, but a descriptor of which group (genre/subgenre) it belongs to plus associated statistics. If you renamed it to e.g. 'BookGenreStatistics', the question of who contains what would go away.
Upvotes: 1
Reputation: 1209
I would just make two enums, one BookGenre = {scifi, novel, ...} and similar for subgenres. When creating a new Book object, add a reference to the Book object to some list which keeps track of all scifi book, etc ( i.e. make an EnumMap> which maps each genre to a list of books ); in this way you can easily access all the books of a genre.
Upvotes: 1
Reputation: 22721
So, the problem is to do with inverse relationships, really. It's quite difficult to avoid this and maintain efficiency. A relational database sidesteps this issue by optimising in the background, using efficient query operations, and never storing the inverse relationship in the first place.
If you use a relational database in the background, you can create methods that get the book groups using a relational query without ever storing the information in Java.
Upvotes: 1
Reputation: 24316
I'd imagine you would want your classes to look something like this:
public class Book
{
String name;
List<Review> reviews;
Set<Genre> genres;
public Book(String name, Set<Genre> genres){}
}
public class Genre
{
String name;
Set<Book> books;
public Genre(String name, Set<Book> books){}
}
I am making an assumption here that you will be utilizing a database, in turn you would have a DAO to query on all known books that match a criteria and subsequently perform CRUD operations across the datasets. I feel a bit off by suggesting that the Genre constructor takes a Set of Book objects, but at the moment I can't think of another way to do this right now.
Upvotes: 1