For example:
getBooks(author, title)
If allowing author to be null, would return all books with specific title
If allowing title to be null, would return all books for the specific author
If allowing both to be null, would return all books regardless of title or author
To eliminate this, have the following functions:
getBooks(author)
getBooks(title)
getBooks(author, title)
getBooks()
In the new functions, there might be redundant codes or if we group those redundant codes into a function, we will still get into a function having null parameters. What's a better way to handle this - no redundant code and no null parameters?
Don't overload so much:
getBooksByAuthor(author)
getBooksByTitle(title)
getBooksByAuthorAndTitle(author, title)
getBooks()
Note that this will not reduce code reuse: These methods could reuse/share whatever code they needed to in their implementations
You could use a constant to denote what type of search to do, and check to see if a param was passed (very untested and error checked):
public static final int R_AUTH = 1;
public static final int R_BOOK = 2;
public static final int R_ALL = 3;
public bookSearch( int searchType, String... search )
{
switch( searchType )
{
case R_AUTH:
// search based off of (String)search[0].stringValue();
break;
case R_ALL:
// load all
break;
}
}
bookSearch(R_ALL);
bookSearch(R_AUTH, "Poe");
Assuming that author and title are Strings you can do the following:
public List getBooks(String params ...) {
if (params.length == 0) { //search
//do search all books regardless of title or author
} else if (params.length == 2 && "cte_author".equals(params[1])) {
//do search by author
} else if (params.length == 2 && "cte_title".equals(params[1])) {
//do search by title
} else if (params.length == 2){
//do search by title and book
} else {
//throw exception
}
}
So you can use this method as following:
getBooks();
getBooks("Gabriel Garcia Marquez", "cte_author");
getBooks("Cien anios de soledad", "cte_title");
getBooks("Gabriel Garcia Marquez","Cien anios de soledad");
Try this approach:
getAllBooks() {return getBooks("", "");}
getBooksByAuthor(author) {return getBooks(author, "");}
getBooksByTitle(title) {return getBooks("", title);}
getBooksByAuthorAndTitle(author, title)
{
if(author.equals("") && title.equals(""))
{
return //your book list
}
else if(author.equals(""))
{
return //a list of books with the given title
}
else if(title.equals(""))
{
return //a list of books with given author
}
else
{
return //a list of books with the given title and author
}
}
Edit:
Updated to circumvent ambiguity.
Related
The Inventory consists of two arrays, one an array of objects[10] and one an array of ints[10]. The array of objects is to identify an item, and the array of ints is supposed to keep track of how many you have. For some reason the code is producing all kinds of errors. Not really sure what to do!
public void additem(Object newItem) {
if (itemsInInventory == 0) {
invent[0] = newItem;
inventItemAmount[0]++;
itemsInInventory++;
}else if (itemsInInventory > 0) {
for (int i = 0; i < itemsInInventory; i++) {
if (invent[i].getItemNum() == newItem.getItemNum()) {
inventItemAmount[i]++;
} else {
invent[itemsInInventory] = newItem;
inventItemAmount[itemsInInventory]++;
itemsInInventory++;
}
}
}
}
Complete code can be found here: https://github.com/YungSheep/HitsujiStories
I see in your GitHub code that your inventory is limited to 10 ; and your if-else condition doesn't hold any case for itemsInInventory > 10, that will first give you an idea of where your NPE comes from. It might be better for you to change your current else if condition to something like "a > 0 && a <= max" then add a case when it's higher than your max capacity.
EDIT : Also, I'm pretty sure I know why you get messed up amounts for each type of item : if you imagine the if-else statement inside a loop, the item slot [0] would only be accessible once, when the player has an empty inventory. That means I can't add up any further in the slot [0] if I picked an item and set itemsInInventory to another number than 0 ! You might have to rebuild your if-else contents.
SECOND EDIT : In case you find it messy to code, I suggest you to make an InventorySlot class :
public class InventorySlot {
private Object object;
private int amount;
// CONSTRUCTOR (assuming you don't instanciate filled slots)
public InventorySlot() {
this.setObject(null);
this.setAmount(0);
}
// GETTERS AND SETTERS
public Object getObject() {
return this.object;
}
public int getAmount() {
return this.amount;
}
public void setObject(final Object object) {
this.object = object;
}
public void setAmount(final int amount) {
this.amount = amount;
}
// METHOD THAT ADDS NEW ITEM IF MATCHES. RETURNS BOOLEAN TO TELL IF SUCCEEDED
public bool addIfMatches(final Object object) {
if (this.getObject.getItemNum() == object.getItemNum()) {
this.setAmount(this.getAmount++);
return true;
} else {
return false;
}
}
// AND OTHER USEFUL METHODS...
}
Hope this helps you, happy coding !
I'm trying to get my lending system to function correctly, the problem seems to be focused in the part of the code below.
I'm trying to make it so every time I enter an item ID and borrower ID it finds the inputted ID's (if correct), pairs and retrieves them from the accessors to initiate a transaction. Every time I input an item and ID I always get the "No user input found" error I made, I suspect its because i'm doing something wrong with the LoanTransaction part of my code as I know the item ID and borrower ID I input to test is correct.
I'm still quite new to using maps and lists together so any advice and help would be useful stuff for me. Many thanks!
Suspect code
LoanTransaction mrkr = null
for (LoanTransaction t: parent.getLoans()) {
if (t.getBorrower() == bwr && t.getItem() == itm) {
mrkr = t;
break;
}
}
Full Method
public void loanTransaction() {
Integer itmID = new Integer(txtItemID.getText());
Item itm = parent.getItems().get(itmID);
Integer borrowerID = new Integer(txtBorrowerID.getText());
Borrower bwr = parent.getBorrowers().get(borrowerID);
if (itm == null) {
JOptionPane.showMessageDialog(this, "Item not found",
"Error", JOptionPane.ERROR_MESSAGE);
return;
}
Borrower bby = itm.getBorrowedBy();
if (bby != null) {
JOptionPane.showMessageDialog(this, "Already on loan",
"Error", JOptionPane.ERROR_MESSAGE);
return;
}
LoanTransaction mrkr = null;
for (LoanTransaction t: parent.getLoans()) {
if (t.getBorrower() == bwr && t.getItem() == itm) {
mrkr = t;
break;
}
}
if (mrkr == null) {
JOptionPane.showMessageDialog(this, "No user input found",
"Error", JOptionPane.ERROR_MESSAGE);
return;
}
parent.getLoans().add(mrkr);
itm.clearBorrowedBy();
itm.setBorrowedBy(bwr);
System.out.printf("Item loaned: [%s]\n\n", itm);
}
Accessors
public Map<Integer, Borrower> getBorrowers() { return borrowers; }
public Map<Integer, Item> getItems() { return items; }
public List<LoanTransaction> getLoans() { return loans; }
When you do a == comparison in Java between two objects, it’s an identity comparison, not an equals comparison. I don’t know how you judge if two borrowers are the same, but this code would only work if the two borrower objects were literally the same instance. What you want to do is use the equals method:
t.getBorrower().equals(bwr)
And similarly for the Item class. Also, you want to make sure you implement the equals (and hashCode) methods in your Borrower and Item classes such that each object knows how to judge itself equal to another object of the same type (such as comparing borrower IDs). If you don’t, it’ll still be an identity comparison.
I'm a beginner and the lecturer told me to reduce the duplicate code in these two functions. This is a library. I guess I need one more method for checking through the array. All I need is some advice/principle I need to use. I'll write the method myself. Thanks!
getBooks() returns the ArrayList where Books are stored.
public List<Book> getAvailableBooks() {
List<Book> result = new ArrayList<Book>();
for (int i = 0; i < this.getBooks().size(); i++) {
if (this.getBooks().get(i).getPerson() == null) {
result.add(this.getBooks().get(i));
}
}
return result;
}
public List<Book> getUnavailableBooks() {
List<Book> result = new ArrayList<Book>();
for (int i = 0; i < this.getBooks().size(); i++) {
if (this.getBooks().get(i).getPerson() != null) {
result.add(this.getBooks().get(i));
}
}
return result;
}
You already have two methods. You can't reduce by adding one more.
But you can have one method instead of two. e.g.
public List<Book> getBooks(boolean available) {
Now you have one method, and you can tell it whether you want available or unavailable books.
Pass in a boolean parameter that should indicate what
(this.getBooks().get(i).getPerson() == null)
Should evaluate to, and add a condition to check that. I.e. should this expression return true or false.
Here're some advice:
Use enhanced for loop instead of indexed one. This will avoid using this.getBooks().get(i) twice in each method.
Unavailable books + Available books = Total books. Use this equation to avoid writing all the codes in both the methods. You might want to use a Set<Book> instead of List<Book> to make this easier to work with. [HINT: Set Difference].
Also, rather than doing the null check in those methods, I'll add a method isAvailable() inside the Book class only, which will return true if it is available, else false.
In the general case, if you see a very common pattern repeated in your code, there is often an opportunity to reduce duplication. The first step is to look at your code and identify the actual differences. You have:
public List<Book> getAvailableBooks() {
List<Book> result = new ArrayList<Book>();
for (int i = 0; i < this.getBooks().size(); i++) {
if (this.getBooks().get(i).getPerson() == null) {
result.add(this.getBooks().get(i));
}
}
return result;
}
public List<Book> getUnavailableBooks() {
List<Book> result = new ArrayList<Book>();
for (int i = 0; i < this.getBooks().size(); i++) {
if (this.getBooks().get(i).getPerson() != null) {
result.add(this.getBooks().get(i));
}
}
return result;
}
Ignoring the method names, let's do a line-by-line comparison. Doing this, we can spot the difference:
if (this.getBooks().get(i).getPerson() == null) {
// vs:
if (this.getBooks().get(i).getPerson() != null) {
The only difference there is == vs. !=; the condition is inverted. After identifying the differences, the next step is to see if you can parameterize the behavior, so that the logic itself is exactly the same and depends only on the value of a few outside variables. In this case, we can see a transformation like this:
a == x => (a == x) == true
a != x => (a == x) == false
both: => (a == x) == <variable>
So we can make those two lines equivalent:
boolean available = true;
if ((this.getBooks().get(i).getPerson() == null) == available) {
// vs:
boolean available = false;
if ((this.getBooks().get(i).getPerson() == null) == available) {
Now the logic is equivalent, and we can select between the two by simply changing available. Make that a method parameter and, voila!
public List<Book> getBooksByAvailability (bool available) {
List<Book> result = new ArrayList<Book>();
for (int i = 0; i < this.getBooks().size(); i++) {
if ((this.getBooks().get(i).getPerson() == null) == available) {
result.add(this.getBooks().get(i));
}
}
return result;
}
Note that another way to approach this problem is to make "availability" itself be a property of a book. For example, if you move the availability test into a new method Book.isAvailable(), the solution becomes a bit more obvious:
public List<Book> getBooksByAvailability (bool available) {
List<Book> result = new ArrayList<Book>();
for (int i = 0; i < this.getBooks().size(); i++) {
if (this.getBooks().get(i).isAvailable() == available) {
result.add(this.getBooks().get(i));
}
}
return result;
}
And that has the added bonus of letting you change the internal definition of "availabilty" without modifying code anywhere else (e.g. if, in the future, you decide that getPerson() == null is not a sufficient indication of "availability", you only need to change it in Book.isAvailable()).
As for clarity, you could, as Rohit Jain mentioned in this answer, switch to enhanced for loops to improve readability a bit, e.g.:
public List<Book> getBooksByAvailability (bool available) {
List<Book> result = new ArrayList<Book>();
for (Book book : this.getBooks()) {
if (book.isAvailable() == available) {
result.add(book);
}
}
return result;
}
To keep your two existing functions, if that's necessary, you can use something like the above as a private utility method, called by the two public ones.
If you don't want to change the method signature, I think you cannot do much better than you already have. You can just rewrite the loops to foreach and possibly do the substractions of the lists. Dirty solution, but the lecturer may take it.
public List<Book> getAvailableBooks() {
Set<Book> result = new HashSet<>(getBooks());
result.removeAll(getUnavailableBooks());
return new ArrayList<Book>(result);
}
public List<Book> getUnavailableBooks() {
List<Book> result = new ArrayList<Book>();
for (Book b: getBooks()) {
if (b.getPerson() != null) {
result.add(b);
}
}
return result;
}
Probably not the fastest solution, but quite short
For example
private ArrayList<Book> books;
private int indexOfSelected;
I'm not sure if setIndexOfSelected() is correct, but it's supposed to set the index into the list of selected books.
public void setIndexOfSelected(int indexOfSelected) {
books.set(indexOfSelected, getSelectedBook());
and I need this method to return null if no book was selected, I'm not sure how to continue?
public Book getSelectedBook() {
}
I tried using a if statement but it gave me a error of unreachable code
If you have indexOfSelected as a variable, you could set it to -1 to indicate no book was selected.
Then:
public Book getSelectedBook() {
if (indexOfSelected < 0 || indexOfSelected >= books.size()) return null;
return books.get(indexOfSelected);
}
I have a number of fields where data needs to be input. This is a Hotel Reservation system so if the fields arent filled it must display that they are empty and cannot proceed without filling them. What I want to do is get the text from the fields but if they are blank it must either set all the fields text to something like "*Please fill in all fields" or show up a message.
I have some code which is not working because it cant get the text if there's nothing in the fields. The code looks like this:
this.Firstname = NameF.getText();
this.Lastname = NameL.getText();
this.Country = Countr.getText();
this.IDtype = IDTy.getText();
this.PassportNo = PassNo.getText();
this.IDNo = IDNumber.getText();
this.Addr1 = Add1.getText();
this.Addr2 = Add2.getText();
this.AreaCode = Integer.parseInt(Area.getText());
this.TelNo = Tel.getText();
this.CellNo = Cell.getText();
this.Email = Em.getText();
}
if (this.Firstname.equals("") || this.Lastname.equals("") || this.Country.equals("") || this.IDtype.equals("") || this.IDNo.equals("") || this.Addr1.equals("") || this.Addr2.equals("") || this.AreaCode == 0 || this.TelNo.equals("") || this.CellNo.equals("") || this.Email.equals("")) {
JOptionPane.showMessageDialog(null, "Please fill in all fields");
}
Not sure if I should ask this in another question but is there an easier way to make the if without so many || operators? Just like if this.Firstname,this.Lastname,etc.equals("")
You could do something like this.
public void validateFields () {
for (String field : getNonBlankFields()) {
if (field.equals("")) {
JOptionPane.showMessageDialog(null, "Please fill in all fields");
return;
}
}
}
Collection<String> nonBlankFields;
public Collection<String> getNonBlankFields () {
if (this.nonBlankFields != null) {
return this.nonBlankFields;
}
this.nonBlankFields = new ArrayList<String> ();
this.nonBlankFields.add(this.lastName);
// add all of the other fields
this.nonBlankFields.add(this.email);
return this.nonBlankFields;
}
You could do this by creating a function to do the checks for you in a loop;
public boolean isAnyEmpty(String... strArr){
for(String s : strArr){
if(s.equals("")) return true;
}
return false;
}
Then call it with
if(isAnyEmpty(this.Firstname, this.lastName, this.Country, /* rest of your strings */)){
//your code
}
This method makes use of varargs to let you treat the parameters as an array, without having to add in the additional code to explicitly create one.
You can create a method that will validate your Strings in varargs flavor:
public boolean validateString(String ... stringsToValidate) {
for (String validString : stringsToValidate) {
if (...) { //logic to validate your String: not empty, not null, etc
return false;
}
}
return true;
}
Then just call it like this:
//add all the strings that must be validated with rules defined in validateString
if (!validateString(NameF.getText(), NameL.getText(), Countr.getText(), ...) {
JOptionPane.showMessageDialog(null, "Please fill in all fields");
}