Better way to write many if statements in Java? - java

So I have this homework where I need to build a vending machine, and I assigned a coordinate to every product (A1, A2, A3..) and when the user enters the coin value I have to calculate if he can buy the product that he chose and if yes calculate the change, since i'm still new to programming I now have ended up with many statements like this
if ("a1".equals(choice)) {
System.out.println("You chose SNICKERS!");
if (money < 50) {
System.out.println("This is not enough money to buy this product");
} else if (money >= 50) {
System.out.println(" Price = 50 Your change = " + (money - 50));
}
}
where the only things changing are the coordinates (a1, a2, a3, a4, b1, b2 and so on) and the prices. What would be a better way to do this?

You could use a more OO approach.
Make a class called Product:
class Product {
private String name;
private int price;
public String getName() { return name; }
public int getPrice() { return price; }
public Product(String name, int price) {
this.name = name;
this.price = price;
}
}
Then, create a HashMap<String, Product> and add all your products and their coordinates in:
HashMap<String, Product> productMap = new HashMap<>();
productMap.put("A1", new Product("Snickers", 50));
productMap.put("A2", new Product("Something else", 40));
// do this for every coordinate...
Now, you can just use this code:
Product chosenProduct = productMap.get(choice);
System.out.println("You chose " + chosenProduct.getName() + "!");
if (money < chosenProduct.getPrice()) {
System.out.println("This is not enough money to buy this product");
} else {
System.out.println(" Price = " + chosenProduct.getPrice() + " Your change = " + (money - chosenProduct.getPrice()));
}

This is a common moment in programming and you're right to think there's a better way to go about it.
In your particular case, where you have a similar structure of code in many places, you should try using a function. Try writing a private function in the same class that takes in some of the parameters that change. Depending on the level of complexity you end up with you might end up with a large function with a lot of parameters which isn't great either. Regardless, writing a function is probably the first place you should go when you encounter this situation.
Secondarily, consider what this segment of code is doing. Making a generic function to replace the whole code segment might not be ideal but could you easily write a function to do a small part of it? If so, is it now easy to make the rest into another function? Keep track of any variances you have across your many similar code segments, if any, and try to create functions that address those variations.
Finally, depending on what is in scope for your current level of programming, you might be able to create data structure to help with the identification of the choice. For example, maybe you could make a map where you could store each of the choices and an associated object that contains all of the data you need to respond to the user (i.e. cost, item name, etc.). With that kind of approach you can pre-populate all of those options in a straightforward manner and have your code simply look up the set of data associated with the choice and call of function to print the necessary output.
Ultimately, how you go about this and future situations like it is highly dependent upon what your level of experience with programming, how many choices there are, and how complex the data associated with each choice is.
Continue to trust your instincts though, they will serve you well.

Switch case is the desired functionality.
switch(choice) {
case 'a' : <some code>
break;
case 'b': <some other code>
break;
default: <if none of the choices matched>
}
The only caveat is that your choice value has to be a char or an int
EDIT:
This was a bad answer from my side. Clearly, you need a more cleaner way to write your vending machine.
A good way to structure it would be to first create a Product class which can store the name and the price of the product.
class Product {
private String name;
private String price;
// Implement a constructor and an equals() method to equate two
// instances of product
}
You could then create a function (which could be used in your main()) which takes the text input from your user and map it to an actual product.
public static Main(String[] args) {
// ...
private Product processUserInput(String productName) {
// Add logic to process the string and return a Product
}
// ...
}
Then you could add a function that would process the Product and the amount the user has and return the change remaining or maybe -1 if the product costs more than the available amount.
public static Main(String[] args) {
// ...
private int processUserProductChoice(Product product, int amountAvailable) {
// Add logic to process the product and amount
// Probably use switch case now.
}
// ...
}

Related

I have been tasked to create a foodstore that I can add and take food from, does my code achieve this?

I have been asked to model a foodstore that contains different types of food. I should be able to add a given quantity of a food type by using the addFood method and remove food using the takeFood method. The addFood must take the form addFood(String, int) and the takeFood must take the form takeFood(String), i.e. addFood("Steak", 5) would add 5 items of steak to the foodstore. I have attempted to make this class and wondered whether this meets what I have been tasked to do. For the sake of this example I will only use 2 food items but in reality there is much more.
public class Foodstore {
public void addFood(String food, int quantity) {
addFood("steak", quantity);
addFood("hay", quantity);
}
public void takeFood(String food) {
takeFood("Steak");
takeFood("hay");
}
}
Thanks in advance
Your food store is missing a warehouse
Map<String,Integer> warehouse = new HashMap<>();
and, as it is, when you add food you're ignoring the food you were told to add, not a good idea.
These are just some starting point, reorganize your code and explore your warehouse when you add data to make sure you're doing well.
I don't think so, you would need some kind of register where you actually save the information, like a Map:
Map<String,Integer> register = new HashMap<>();
You would have to rewrite your functions sth like this (have not been able to type it in an editor, but just so that you get the idea of it):
public void addFood(String food, int quantity) {
if (register.containsKey(food)) {
Integer newAmount = register.get(food) + quantity;
register.put(food,newAmount);
}
else {
register.put(food,quantity);
}
}
PS. You are (mis)using recursion, and I don't think it's what you want in your case. Your function addFood calls itself again and again without an end.

Overriding and Inheritance

i am having problems with my codes. this is our assignment:
"Write a superclass named clsWorker and subclasses clsHourlyWorker and clsSalariedWorker. Every worker has a name and a salary rate. Write a method computePay(int hours) that computes the weekly pay for every worker. An hourly worker gets paid the hourly wage for the actual number of hours worked if hours is at most 40. If the hourly worker worked for more than 40 hours, the excess is paid at time and a half. The salaried worker gets paid the hourly wage for 40 hours, no matter what the actual number of hours is. Write a test program for the inheritance. Input the name of the worker, the number of hours worked and the type of worker, H for hourly and S for salaried, the salary rate of the worker. Display the salary of every worker entered."
we need to do overriding and inheritance. i always encounter the NoSuchMethodError
My superclass:
public class clsWorker
{
int hours=0,excess=0;
double salary,newSalary=0.0;
String sType;
public clsWorker()
{
}
public clsWorker(double sal,String type)
{
//this.hours=hours;
salary=sal;
sType=type;
}
public double computePay(int hours)
{
sType=sType.toUpperCase();
if(sType.equals("H"))
newSalary=(salary*hours)*7;
else if(sType.equals("S"))
{ if(hours>=40)
newSalary=salary*40;
else if(hours>40)
{
excess=hours-40;
newSalary=(newSalary+((newSalary*excess)*1.5))*7;
}
}
return newSalary;
}
}
My subclasses
public class clsHourlyWorker extends clsWorker
{
double dSalary=0.0;
int iHours=0;
public clsHourlyWorker()
{
//super();
}
public clsHourlyWorker(int iHours, double salaryph,String sRate)
{
super(iHours,salaryph,sRate);
//clsWorker w=new clsWorker();
dSalary=newSalary;
}
public double getSalary()
{
return dSalary;
}
}
public class clsSalariedWorker extends clsWorker
{
double dSalary=0.0;
int iHours=0;
public clsSalariedWorker()
{
//super();
}
public clsSalariedWorker(int iHours,double salaryph,String sRate)
{
super(iHours,salaryph,sRate);
//super(salaryph,sRate);
//this.iHours=iHours;
//clsWorker w=new clsWorker();
dSalary=newSalary;
}
/*public void setSalary(int iHourjs)
{
}*/
public double getSalary()
{
return dSalary;
}
}
my test class
import java.util.*;
public class testSalary
{
public static void main(String[]args)
{
Scanner console=new Scanner(System.in);
System.out.println("Input Salary per hour: ");
double salaryph=console.nextDouble();
System.out.println("Input number of hours: ");
int iHours=console.nextInt();
console.nextLine();
System.out.println("Input Worker Name: ");
String sName=console.nextLine();
System.out.println("Input Type of Worker: ");
String sRate=console.next();
clsHourlyWorker hw=new clsHourlyWorker(iHours,salaryph,sRate);
clsSalariedWorker sw=new clsSalariedWorker(iHours,salaryph,sRate);
//System.out.println("Worker Name: "+sName+"\nSalary: "+hw.getSalary());
if(sRate.equals("H"))
System.out.println("Worker Name: "+sName+"\nSalary: "+hw.getSalary());
else if (sRate.equals("S"))
System.out.println("Worker Name: "+sName+"\nSalary: "+sw.getSalary());
}
}
can someone tell me how to fix my problem? thanks.
This doesn't make sense:
public clsHourlyWorker(int iHours, double salaryph,String sRate)
{
super(iHours,salaryph,sRate);
//clsWorker w=new clsWorker();
dSalary=newSalary;
}
Since the super class's constructor doesn't take int, double, and String but rather, it takes String, double, int representing name, dRate, and hours. Also is sRate really supposed to be a String? I strongly doubt it.
I suggest that your child class constructor accept in the parameter all the information needed for the super constructor (a Strinng for name, double for dRate and int for hours) plus additional parameters the additional information for the new child class fields. You may wish to re-think those fields though as some seem to be duplicates of the super's fields and thus unnecessary.
A couple of things could be improved:
Use inheritance
Simply put, the code you posted does not leverage the power of inheritance at all. You created two subclasses, but all they do is delegate the payment computation back to the superclass. Make clsWorker abstract, make computePay abstract and implement two different versions of the method in the subclasses:
public abstract class Worker {
// ...
public abstract double computePay(int hours);
}
public class HourlyWorker extends Worker {
// ...
#Override
public double computePay(int hours) {
// code from the 'sType.equals("H")' block
}
}
public class SalariedWorker extends Worker {
// ...
#Override
public double computePay(int hours) {
// code from the 'sType.equals("S")' block
}
}
Now you will also realize that the field sType is not serving any useful purpose. In fact, this approach would be required only if polymorphism was not supported by Java. You already know (when writing the code) whether you're dealing with an HourlyWorker or a SalariedWorker because you put the code either in HourlyWorker's computePay or in SalariedWorker's computePay.
sType does not provide any new information that we would not already know from looking at the inheritance hierarchy. This way, you're giving the exact same information twice (once by inheritance, once by sType), which is something we're generally trying to avoid in software development.
Improve "test" class
(I put "test" in quotes because most developers would think that by "test" class, you mean some sort of automated, unit-testing "test" - e.g. a JUnit test case.)
First thing that seems odd is: Why instantiate both a HourlyWorker and a SalariedWorker (i.e. new clsHourlyWorker, new clsSalariedWorker), if the user can choose to either create a HourlyWorker or a SalariedWorker before (but not both, this is 'exclusive or')?
What you really want is something along the lines of:
Worker w;
if(sRate.equals("H"))
w = new HourlyWorker(iHours,salaryph);
else if (sRate.equals("S"))
w = new SalariedWorker(iHours,salaryph);
else
throw new Exception("Don't recognize worker type: " + sRate);
System.out.println("Worker Name: "+sName+"\nSalary: "+w.getSalary());
As you can see, it is valid to use the abstract superclass Worker as the type for your worker instance variable, w, if you don't know at this point (during runtime) whether the user will choose H or S. You only need to access getSalary, which is declared in Worker, so everyhing is fine. (If you ever later needed to access a method which is implemented only by a subclass, you could still type-cast it.)
You will also realize that you need only one println. Again, we'd like to minimize duplication in favor of abstraction. Both lines do basically the same, they print name and computed salary of a worker, so we try to reduce two lines to one, more abstract line of code.
Second, you might also, instead of printing sName directly, want to have a getter (getName) in Worker. When you get to work on larger projects, you'll see that input and output can happen at a very different time and place (e.g. Worker might be stored in a database and the original variable sName will be long gone when a different user retrieves the Worker from the database). So be prepared to get all required data from your real objects, not the test input.
Third - but I might be mistaken here - "Display the salary of every worker entered" sounds to me like you're supposed to implement a while loop where the user can enter multiple workers. But then, there are more important things you can learn from this assignment.
Follow naming conventions
In Java, it is very common practice to have uppercase class names and lowercase method names. Normally you'd have Worker, HourlyWorker and SalariedWorker (see code sample above). This way, every Java developer in the world can already tell we're talking about classes.
However, for your assignment, I'd suggest you follow whatever arbitrary "conventions" there are - just keep in mind that this is not the standard Java way. Read up on naming conventions (general and particular) before you do something like this in real-world projects.

Making an add method for an ArrayList

So I am supposed to make an add method for an array list which adds a new movie object to the list if it doesnt exist, or if it finds a movie object with a similar title within the list, it just increases the quantity property of that object. Here is what I've got so far.
public void add(String title, double rating, int releaseYear){
if(this.myMovies.size() < 1)
{
Movie mymovie = new Movie(title, rating, releaseYear);
this.myMovies.add(mymovie);
}
else
{
for(int i = 0; i < this.myMovies.size(); i++)
{
Movie temp = this.myMovies.get(i);
if(temp.Title.equals(title)){
this.myMovies.get(i).quantity++;
break;
}
else
{
Movie mymovie = new Movie(title, rating, releaseYear);
this.myMovies.add(mymovie);
break;
}
}
}
}
My problem is that this ends up not taking account of similar names and doesn't increase the quantity but just adds another object to the list. I have a strong feeling that the problem lies within my For loop but I just can't identify it. Can anyone see anything that I may be doing wrong? Thank you!
You're testing only for equality, not similarity here:
if(temp.Title.equals(title)){
Instead, you should write a helper method to test for similarity based on whatever criteria are appropriate. For example:
if (isSimilar(temp.Title, title)){
and the isSimilar method might look something like this (assuming you don't need any input validation):
private void isSimilar(String title1, String title2) {
return title1.equalsIgnoreCase(title2)
|| title1.toLowerCase().contains(title2.toLowerCase())
|| title2.toLowerCase().contains(title1.toLowerCase());
}
or, perhaps more appropriately, like this (if you implement it in the Movie class):
private void isSimilar(otherMovie) {
return title.equalsIgnoreCase(otherMovie.title)
|| title.toLowerCase().contains(otherMovie.title.toLowerCase())
|| otherMovie.title.toLowerCase().contains(title.toLowerCase());
}
...in which case your if statement would also change slightly.
Keep in mind that I don't know what you consider 'similar'; only that the movies are considered similar if the names are similar.
A couple more comments:
Fields and method names generally start with a lowercase letter (so the field Movie.Title should instead be Movie.title).
It's usually preferable to loop over a Collection using an Iterator instead of using the raw index--partly because the Iterator should always know how to loop over the Collection efficiently.
Learn to use your IDE's debugger (it's probably very easy). Then you can step through each line of code to see exactly where your program is doing something unexpected.
I would do something like this:
public void add(String title, double rating, int releaseYear){
for(Movie m: myMovies.size())
{
if(m.Title.equals(title)){
m.quantity++;
return;
}
}
// movie with same title not found in the list -> insert
this.myMovies.add(new Movie(title, rating, releaseYear));
}
By the way: variable names should start with a lowercase character (Title -> title).
I'm addressing your "similarity" requirement. If you really want to do this properly it could be a lot of work. Essentially you have two strings and want to get a measure of the similarity. I am doing the same thing for figure captions and I plan to tackle it by:
splitting the title into words
lowercasing them
using them as features for classifier4J (http://classifier4j.sourceforge.net/)
That will go a long way based on simple word counts. But then you have the problem of stemming
(words that differ by endings - "Alien" and "Aliens"). If you go down this road you'll need to read up about Classification and Natural Language Processing

Array parameter passing

I'm in a beginner's java class. This Lab is for me to make a class "Wallet" that manipulates an array that represents a Wallet. Wallet contains the "contents[]" array to store integers represing paper currency. The variable "count" holds the number of banknotes in a wallet. After writing methods (that match provided method calls in a serpate Driver class) to initialize the Wallet and add currency/update "count", I need to transfer the array of one instantiated Wallet to another. I don't know how that would work because the one Wallet class has only been messing with a wallet called "myWallet" and now I need to take a new Wallet called "yourWallet" and fill it with "myWallet"'s array values.
//I should note that using the Java API library is not allowed in for this course
My Wallet class looks like this so far:
public class Wallet
{
// max possible # of banknotes in a wallet
private static final int MAX = 10;
private int contents[];
private int count; // count # of banknotes stored in contents[]
public Wallet()
{
contents = new int[MAX];
count = 0;
}
/** Adds a banknote to the end of a wallet. */
public void addBanknote(int banknoteType)
{
contents[count] = banknoteType;
count = count + 1;
}
/**
* Transfers the contents of one wallet to the end of another. Empties the donor wallet.
*/
public void transfer(Wallet donor)
{
//my code belongs here
}
...
The Driver code looks like this:
public class Driver
{
public static void main(String args[])
{
Wallet myWallet = new Wallet();
myWallet.addBanknote(5);
myWallet.addBanknote(50);
myWallet.addBanknote(10);
myWallet.addBanknote(5);
System.out.println("myWallet contains: " + myWallet.toString());
// transfer all the banknotes from myWallet to yourWallet
Wallet yourWallet = new Wallet();
yourWallet.addBanknote(1);
yourWallet.transfer(myWallet);
System.out.println("\nnow myWallet contains: "
+ myWallet.toString());
System.out.println("yourWallet contains: "
+ yourWallet.toString());
I want to use addBanknote() to help with this, but I don't know how to tell the transfer() method to transfer all of myWallet into yourWallet.
I had the idea to do somethign like this in transfer():
yourWallet.addBanknote(myWallet.contents[i]);
with a traversal to increase i for myWallet contents. It seems horribly wrong, but I'm at a complete loss as to write this method.
If my problem is so unclear that nobody can help, I would be more than happy to receive advice on how to ask a better question or on how to search with correct terms.
Thanks for any help you can provide.
I don't want to spoil your homework as you seem to be going the right way, but I do have some comments which you may either take or not :)
First, I would probably put the bank note types in some enumeration. But as that sounds a bit to advanced, consider
public class Wallet {
public static final int ONE_DOLLAR_BILL = 1;
public static final int FIVE_DOLLAR_BILL = 5;
...
// looks a bit more readable to me
myWallet.addBanknote(ONE_DOLLAR_BILL);
Transferring all the banknotes from the donor to yourself should not be so much of a problem
(a for loop would do) but I think you're in a world of hurt if you are trying to implement a
removeBanknote(int banknoteType);
as you are using count not only as a length but also as an index variable. By this I mean that you assume contents[0] ... contents[count-1] hold valid banknotes. And how do you remove one without too much work?
Warning: a bit more advanced
In your case I would probably opt to have a banknoteType of 0 indicating an empty banknote slot in your wallet, and implement _addBanknote(int banknoteType) as:
public void addBanknote(int banknoteType) {
for (int i=0; i < contents.length; i++) {
if (contents[i] == 0) {
contents[i] = banknoteType;
count++;
return; // OK inserted banknote at the first empty slot
}
}
throw new RuntimeException("Wallet is full");
}
This may be a bit overwhelming at this point. But it would allow you to implement:
public void removeBanknote(int banknoteType) {
for (int i=0; i < contents.length; i++) {
if (contents[i] == banknoteType) {
contents[i] = 0; // better: NO_BANKNOTE = 0
count--;
return;
}
}
throw new RuntimeException("This wallet does not contain a banknote of type " + banknoteType);
}
Please note that in both methods I return when I successfully removed or added the banknote. Only when I could not find a free slot, or the requested banknote, I finish the for loop and end up throwing an exception and thereby stopping the program.
I think the question is fine and I think you're on the right path. The way you're calling Wallet#addBanknote(int) is correct. What you have said is the right thing:
public void transfer(Wallet donor)
{
// Traverse the donor's wallet
// Add the bank note from the donor to this wallet
// What do you think also needs to happen to make sure
// the donor is actually giving their bank note?
}
Just another thing, what would happen in your Wallet#addBanknote(int) method if you have more contents than the MAX?
You can create either a constructor that takes another wallet, or a function (as already mentioned) and use System.arraycopy to copy the array in one fell swoop. System.arraycopy is fast, and its definitely overkill for something small like this, but its good tool to have in your toolkit.
The other alternative mentioned, copy the elements from one array to the other element by element in a loop will work fine too.
The myWallet inside the transfer method is named 'donor', and with that, it doesn't look horribly wrong:
addBanknote (donor.contents [i]);
You just need a loop around it, and to remove the yourWallet. which is the name of an instance of that class. That instance is inside the Class/method this, but needn't be specified, because there is no other addBanknote-Method in scope, which could be meant. (Thanks to mangoDrunk).

Java: For loop and If algorithm

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,
Is there a better way to write the above code rather than checking that it's the end of the line?
Is there a better way to utilize getName() method or to use other methods?
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.
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.
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!
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.
}
}
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.
chubbsondubs came closest to giving a correct answer to this question
What he missed is that his algorithm is incorrect because it contains two tests, when only one is needed. The correct code requires only 3 statements and is as follows:
public boolean zLibaryContains( String title ) {
books[bookCount] = title;
int xBook = 0;
while( true )
if( books[xBook].getTitle().equals( title ) )
return xBook != bookCount;
else xBook++;
}
Noticeably smaller and faster than all other solutions. Simplify, simplify, simplify.
Object-oriented code is a crutch to support poor designs that would otherwise be too complex to understand. The goal is write code that is so easy to understand and maintain that OO is unnecessary and would make the program worse. When your program can be improved by adding OO, it means you are doing something wrong to begin with.

Categories