views:

188

answers:

5

Hello,

I've this question from an assignment to create a Store which rent out books, using a Store.java and Book.java. I've finished this assignment, but I'm curious for better algorithm to a specific part.

--

Book.java

public class Book {

    private String name;

    Book(String name)
        this.name = name;

    public String getName()
        return name;

}

Store.java

Inside main();

 Book bookObj[] = new Book[3]; //Create 3 Array of Object.
 bookObj[0] = new Book("Game Over");
 bookObj[1] = new Book("Shrek"); 
 bookObj[2] = new Book("Ghost");
 Scanner console = new Scanner(System.in)
 input = console.nextLine();

Assuming, input = Devil.

Now, I need to do a simple search to check whether the specific book exist.

Example:

 for(int i = 0; i < bookObj.length; i++) {
     if(bookObj[i].getName().equals(input))
         System.out.println("Book Found!");
 }

Apparently, this is a for loop that cycles through the array of object and checks whether such Book exist. Now, the problem arise when I want to give an output that the Book was not found.

Example:

 for(int i = 0; i < bookObj.length; i++) {
     if(bookObj[i].getName().equals(input))
         System.out.println("Book Found!");
     else
         System.out.println("Book not Found!");
 }

The problem with the above code is that Book not Found would be printed thrice. My goal is to avoid such problem. I do have solutions to this, but I'm still in search for a better one to use that utilizes getName(), which in my opinion still has room to improve.

Usually, in structural programming, I would do the following,

for(int i = 0; i < bookObj.length; i++) {
     if(bookObj[i].getName().equals(input))
         System.out.println("Book Found!");
     else if(i == bookObj.length - 1)
         System.out.println("Book not Found!");
 }

This is useful to tell whether it's the end of the loop, and the search has ended, but there was no successful result from the search.

How should I think of it in Object Oriented way?

All in all, my question is,

  1. Is there a better way to write the above code rather than checking that it's the end of the line?
  2. Is there a better way to utilize getName() method or to use other methods?
+5  A: 

You should loop through the array and use an index / boolean flag to store whether or not the book is found. Then print the message in the end, based on the index / flag value.

int foundAtIndex = -1;
for(int i = 0; i < bookObj.length; i++) {
    if(bookObj[i].getName().equals(input)) {
        foundAtIndex = i;  // store the actual index for later use
        break;             // no need to search further
    }
}
if(foundAtIndex >= 0)
    System.out.println("Book Found!");
else
    System.out.println("Book not Found!");

Alternatively (unless your assignment specifically requires using an array) you should prefer a Set, which can do the search for you with a single call to contains().

How should I think of it in Object Oriented way?

When looking at a single method, there is not much difference between procedural and OO style. The differences start to appear at a higher level, when trying to organize a bunch of conceptually related data and methods that operate on these.

The OO paradigm is to tie the methods to the data they operate on, and encapsulate both into coherent objects and classes. These classes are preferably representations of important domain concepts. So for your book store, you may want to put all book related code into your Book class. However, the above search method (and the collection of books it operates on) is not related to any particular book instance, so you have different choices:

  • put both the collection of books and the search method into Store (probably as regular members), or
  • put them into Book as static members.

The first choice is more natural, so I normally would prefer that. However, under specific circumstances the second option might be preferable. In (OO) design, there are hardly ever clean "yes/no" answers - rather tradeoffs between different options, each having their own strengths and weaknesses.

Péter Török
This is correct. Use a collection (like a `Set`) instead of an array.
Dave Jarvis
+1, particularly for mentioning Set
Randolpho
It saddens me that arrays are taught as primary data structures. They have become special-case data structures now.
Tony Ennis
+2  A: 

You could introduce state and remember whether you have found the book or not.

If you're not using Java 1.4 or earlier, you could also use the foreach loop syntax:

boolean bookFound = false;
for(Book currentBook : bookObj) {
     if(currentBook.getName().equals(input))
     //TODO: see above
}

Also, I would suggest looking into the Collections library, and replace your array with a list or set:

Set<Book> books = new HashSet<Book>();
books.put(new Book("Game Over"));
books.put(new Book("Shrek")); 
books.put(new Book("Ghost"));

And, while were at it, you could also think about when two books are equal and override equals() and hashCode() accordingly. If equal() would be changed to check the title, you could simply use books.contains(new Book(input)); and have the libraries do the work for you.

Thomas Lötzer
note that you should break from the loop when you've found your item (either using break; or testing bookFound in the for loop). note too that the enhanced for looping in current java is a far better solution.
KevinDTimm
contains() does not appear in the original specification - equals() however does - downvote negated
KevinDTimm
@KevinDTimm I still edited my answer to include the contains() suggestion, since it may be useful.
Thomas Lötzer
IIRC, contains will find matches within the string. The OP 'requires' that 'Devil' be found - this implies exact match - so I would hesitate to use contain
KevinDTimm
@KevinDTimm I'm doing contains() not on a String but on the set. On the String you're right, contains() would not be a good idea there.
Thomas Lötzer
I haven't learn Generics so I can't use the Collection Framework until I do. Will learn it myself :)
Seeyabye
+1  A: 

To solve the problem in a better way you must understand that the power of Java comes not from the language itself but from the Java Framework.

You should learn the usage of the Java Collection classes (never work with arrays anymore). Then you will be able to solve the search with just one line of code:

ArrayList<Book> listOfBooks;
// init your list here
listOfBooks.contains(new Book(input));

To make this work, you must also learn how to correctly implement the equals() method of your Book class.

Happy learning!

Designpattern
A: 

Here is a working solution :

import java.util.Scanner;

public class Store {

   private static class Book {

      private String name;

      Book(String name) {
         this.name = name;
      }

      public String getName() {
         return name;
      }

   }

   public static void main(String[] args) {

      String input;

      Book[] bookObj = new Book[3];

      bookObj[0] = new Book("Game Over");
      bookObj[1] = new Book("Shrek"); 
      bookObj[2] = new Book("Ghost");

      Scanner console = new Scanner(System.in);
      input = console.nextLine();

      boolean found = false;
      int i = 0;
      while(!found && i < bookObj.length) {

         if(bookObj[i].getName().equals(input)) {

            System.out.println("Book Found at position : " + i);
            found = true;

         } else {
            i++;
         }
      }

      if(!found) {
         System.out.println("Book not Found!");
      }

      // Here i contains the indice of the element found in the array.

   }

}
Jérôme Radix
A: 

You've gotten some pretty good advice thus far. You asked if there was a more Object Oriented way of thinking about the problem so I thought I'd try and shed some light on it. As Peter already mentioned at this level of the design it's a single method implementation so the approach is going to be fairly similar as say a procedural approach. What's the advantage? In a word reuse. If you needed to find a book by name in lots of places then moving the code to it's own class will help.

So what you have is a single Book instance to encapsulate behavior around a single book, but you want to have behavior about multiple books, or a collection of books. You can keep the data (array of books), and the method that account on them separate as you outlined in your program. However, if we wanted to collect a place for doing behavior on a collection of books we can define a new class. Let's call it Library, and we might do something like the following:

public class Library {
   private Book[] books;
   private bookCount = 0;

   public Library( int numberOfTotalBooks ) {
      books = new Book[numberOfTotalBooks];
   }

   public boolean addBook( Book book ) {
      if( bookCount < book.length ) {
         books[bookCount++] = book;
         return true;
      }
      return false;
   }

   public Book findByTitle( String title ) {
       for( int i = 0; i < bookCount; i++ ) {
          if( books[i].getTitle().equals( title ) ) {
             return books[i];
          }
       }
       // didn't find one
       return null;
   }
}

So a couple of things to note about doing things this way. One is that when we work with a Library we don't know there is an Array back there. We could use an array, a Set, a List, or a database (most common). The point being the code that calls these functions just works with the interface of Library (not a literal Java interface, but the method signature of Library). Also this is a higher level interface. We don't worry about iterating over the books, doing for loops, if statements, etc. We just call a method saying "Hey find this book title in the Library". How that's done we don't care. This is the basic tenant of Object Orientation called encapsulation, and it's deceptively powerful. It's really about how we delegate responsibility in our program, and give the details of a job to individual class or classes. If Library had only public members (i.e. books and bookCount), or getter/setters then the client wouldn't be getting any advantages because the client would still have to do all the heavy lifting. The trick to OO is figuring out what can be delegated out of an object, without creating problems. This takes practice, and experience.

The second thing here is we've separated the presentation from the act of finding a book. The method you wrote assumed the next step which was to print "Hey we found it." However, Library object simply returns the Book to you when it finds it, or null if it didn't. That makes it possible to print to the console, display in a GUI, or serialize it to a JSON stream in a server. The act of finding a book is separate from the visualization. This is another important aspect of programming in general, but some what related to object orientation and encapsulation. This is typically called separation of concerns. The console application has concerns about supporting the UI, and printing the console. While the Library just manages cataloging and managing the book collection. How those details are performed neither cares.

In the end Library is a reusable class. We can use it in a console application, desktop, web, or middleware server. More importantly is we can also reuse the calls to findByTitle or addBooks from multiple locations within a single program. Also by putting the methods with the data we create a barrier to where that function can be used. You can't do it anywhere in your program. You have to have a reference to Library. If you don't have reference to a Library instance then you shouldn't be calling it. This can be troublesome to new developers because they lack the experience to properly organize their programs to not get into trouble with this (then they start doing value objects, creating statics, singletons, etc and things turn into a big ball of mud). It's a double edged sword.

One more thing I'd like to point out is say we wanted to model two Libraries. We have a Library uptown and downtown, and we want to allow people to check out books from either Library. With OO that's really easy to represent:

Library uptown = new Library( 50 );
Library downtown = new Library( 100 );

Now we can check out books from one or the other. And I didn't use statics (i.e. global variables) so reusing that logic is really easy. These are the basics of OO so they are really deep topics. Strange how I can write so much on very simple topics. Anyway I hope this helped you understand your program a little deeper, and see how you can use OO to help you.

chubbard
Thanks, this is a good tip for my future programming!
Seeyabye