views:

116

answers:

5

Let's say I need to design a system for a collection of books. And let's assume I have millions of books and for simplicity we don't need to add or remove books from the system.

I can create a Book class and initiate an array in the size of the collection:

Book book = new Book[number of books];

In this case, Book would include fields such as:

long bookIsbn
String bookTitle 

Or, instead, initiate just one object:

Book book = new Book();

In this case, the Book class needs to include arrays/lists/maps etc that include info about the entire collection. For example:

Map<Integer, String> isbnToTitle = new Map<Integer, String>();

Which representation is more efficient? or maybe to rephrase the question, which is considered as a better OOP approach?

Thanks!

+2  A: 

Separate different concerns:

1) Create a book with its attributes (title, author, etc) and methods (read, shelf, lend, borrow).

2) Create a data structure for storing your books that supports the operations you want (search by author, seach by title, browse, etc)

flybywire
"lend"/"borrow" don't belong on a `Book` class. They belong on the `Library` object, and take `Book` as parameters. (FWIW, I downvoted you for this, but only because you'd been upvoted twice [presumably for including the fancy phrase 'Separation of Concerns').
Noon Silk
@silky: I think one can argue about that. These are detailed design questions where there is no right and wrong.
Felix Kling
@Felix Of course one can argue about it, and one would be wrong (IMHO), hence my disagreement and downvote. I think it's unnatural for a book to provide the facility to 'Lend' itself (needless to say, it would need to access properties of the Library to actually perform this function, and it would be wrong, IMHO, to include that sort of logic inside the Book itself. For example, imagine the book is not in the Library anymore. You can still call that function, which is nonsense).
Noon Silk
+10  A: 

Considering OOP: Think about what a book is and which properties it has. A book definitely does not know about other books, so the second approach is not the way to go.

Arrays... well yes if it is sufficient.
The best thing (from a model perspective) would be to create a class Book and a class e.g. Library that manages all the books. Then the last class contains a list of all books and provides operations on all books.

Felix Kling
So if I understand correctly, Library would include a list of Books, which means that for every book, I create a Book object and add it to the list.
@user247866: You'd typically add a *reference* to the book (i.e., it's code in the Libraries system). It wouldn't really be appropriate to hold all the Books inside the library directly. (Consider what happens if a book is removed from the library, it's still "there", just out on loan. You can reflect this in a seperate collection. Arguably that could contain 'Books', but it'd get a but unmanageably (and inappropriate) to do it this way, IMHO).
Noon Silk
But the things is, if I need for example a 'search by isbn', 'search by author', and 'search by title' methods. I need to run over the Book array/list which is slow. If instead I add to Library 3 maps - isbn, title, and author, then I can do search in constant time and. This might be a waste of memory and poorly design, but very fast. And that's my problem, many time the ugly solutions have important advantages.
@user247866 You can have, in the Library, 3 Maps for the various keys, and have Books stored in them. A Book is nothing more than a reference to the object, so you don't get 3 times as many Book objects (instances).
extraneon
@user247866: You're making it slightly harder to conceptualise properly by making everything an object (i.e. setup at runtime). Consider the DB design; it's appropriate to have a book *indexed* in a few ways, for efficient searching. Of course, this results in more data (in the database) but it's significantly better. If you model this in your head with objects that are instantiated always in a given "Library" class it seems "wrong" (it kind of is), but the DB model is sensible. So go with it.
Noon Silk
@user247866: just implement `findByISBN()`, `findByAuthor()` and similar methods in your `Library`. As a first implementation, simply iterate over all Books. If at some point this turns out to become a bottleneck, then you can easily change the implementation of `Library` to keep some kind of index (probably a `Map`) and use that in the `find*` methods.
Joachim Sauer
Got it. Thanks everyone!
+1  A: 

It's about what is logical really.

"Book" isn't a logical name for a container that stores a lot of books. You can just model it the way the world does.

And, as you will hear often as a newbie, "efficiency" isn't to be considered (generally) when designing your structures.

You design for correctness, logic, and "meaning". (as well as, obviously, general operation thereof). Efficiency can (typically) come as part of the implementation. It may be such that when you review your design and attempt to implement it, you discover a component won't be efficient. In this case, you attempt to solve that within the context of the overall design, or you adjust the design significantly, based on your findings (this is rare, though).

Noon Silk
This kind of efficiency consideration should be ignored until later profiling shows it's a problem; but efficiency in general should not be ignored. You shouldn't use bubblesort until profiling shows quicksort would be faster; you should use a fast sort, preferably from a library, from the start.
Philip Potter
A: 

Neither are correct.

You may want to consider having a Book object (with Title, ISBN code) and a Library (or Bookstore, or Shelf) object with a collection of Book objects.

You don't add "Title and ISBN" to a Book, in real life. You may add a book to a library.

OOP is not necessarily about mimicking real life entities, but your way of doing things is a bit strange and would puzzle anyone reading your code.

p.marino
+1  A: 

I don't think the question is informative enough to answer in a good way. OO is, first and foremost, about where to draw the line. If you model a book, and it should have and ISBN, that's fine, but an OO fanatic would also have you equip the Book with an array of Leaf-s, each Leaf having exactly two Page-s, each Page having a PageNumber, and an array of Line-s, each Line being an array of chars. Or maybe a String. But then, a String is not really good OO in this case.

You must take a decision about where to stop modelling, and it is important to realize that you are not modelling the real word, but rather a metaphor negotiationg the mind of the (second) programmer and the machine. What you are doing is at the same time explaining something to the machine and to a reader. OO can be a good tool in this, but it can also be used to obstruct. Be careful! Keep it simple!

If the only thing you need is to lookup a title based on an ISBN, then, keep it simple:

Map<String, String> indexedBookList;

If you suspect that the functionality may grow, you should maybe create a

class Book {
  String title;
  String isbn;
  String author;
}

and keep all your books in a

List<Book> bookList;

Then again, it might be that your project grows from this static little thing into something that has to communicate with an SQL database, that updates occationally and out of your control. Then it might be better, for readability, to keep it simple, and model the database, rather than trying to model the real world.

Programming is about telling a machine what to do, and at the same time telling other people, what we are trying to tell the machine to do. The most important feature in your code, is that it is readable to the programmer that needs to understand it. In a year, that could be you.

Bex
Good answer. Thanks.(I cannot vote up answers yet...)