This question already has answers here:
Does log.debug decrease performance
(5 answers)
Closed 2 years ago.
I don't really know what is the best way to log information from an application. Most of what I've seen is programmed like this:
while(true) {
//code
if(debug == true) {
log(info);
}
end
And I've thought of using two separate loops like this:
if(debug == true) {
while(true) {
//code
log(info);
}
else {
while(true) {
//code
}
}
But that has the problem of being twice the work to change later on. The last solution, which I think is probably the best, is to use something like a lambda expression and pass an empty implementation to use without debug:
public void loop(Debug debug) {
while(true) {
//code
debug.log(info)
}
}
public static void main(String[] args) {
loop(d -> {
//print to file, command line, etc. OR do nothing
});
}
Any clarity on issues or unintended consequences would be greatly appreciated.
Thanks in advance!
Don't try to invent a bicycle. Use existing logging frameworks and follow their guidelines.
Whatever you will think of - you will need to get over same problems that are already solved there.
For example https://logging.apache.org/log4j/2.x
Or http://www.slf4j.org/
Related
I'm creating a springboot banking API and in order to create a transaction a bunch of "rules" have to be checked.
e.g:
Current logged in user can't withdraw money from another user's savings account
Amount can't be higher/lower than certain number
etc.
This causes my createTransaction method to contain a lot of if statements (12!). This is what my code looks like in pseudo:
public ResponseEntity<String> createTransaction(Transaction body) {
if (check rule 1) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("...");
}
if (check rule 2) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("...");
}
// etc...
// Transaction complies to set rules
return ResponseEntity.status(HttpStatus.CREATED).body("Transaction successful!");
}
I can post my actual code if necessary but I think this paints the picture without having anyone to read 100 lines of code.
Because I have around 12 if statements checking these rules, my function is quite lengthy and difficult to read/maintain.
Googling for a solution didn't bring up results I was looking for. I've tried implementing exceptions but this didn't remove the amount of if statements. Maybe a switch could improve a bit, but I'm wondering if there's a clean OOP solution.
My question is: How can I clean this code up (OOP style)?
Thanks in advance.
You should create a TransactionRule interface that allows you to implement specific transaction rules, and then use a stream to get the final result:
public interface TransactionRule {
public boolean isAllowed(Transaction someTransaction);
}
Example implementation 1:
public class SufficientBudgetTransactionRule implements TransactionRule {
public boolean isAllowed(Transaction someTransaction) {
// Custom logic e.g.
return someTransaction.wallet.value >= someTransaction.transaction.value;
}
}
Example implementation 2:
public class NotInFutureTransactionRule implements TransactionRule {
public boolean isAllowed(Transaction someTransaction) {
// Custom logic e.g.
return someTransaction.transaction.datetime.isBefore(OffsetDateTime.now());
}
}
Then, you can store all the TransactionRules in a List and check whether they all validate like so:
private final List<TransactionRule> transactionRules; // Fill these of course
public boolean allTransactionRulesMatch(Transaction someTransaction) {
return transactionRules.stream()
.map(transactionRule -> transactionRule.isAllowed(someTransaction))
.allMatch(result => result);
}
I am trying to make a hangman game as follows:
public void guessLetter(String letter) {
HashSet<String> guessedLettersA = new HashSet<>();
guessedLettersA.add(letter);
for (String guessedLetterA : guessedLettersA) {
this.guessedLetters += guessedLetterA;
}
if (!this.word.contains(letter)) {
this.numberOfFaults++;
}
}
public boolean letterCheck() {
if ( = false ) {
System.out.println("You have already guessed this letter!");
}
I am currently working in the letterCheck method and want to see if one of the inputs is a repeat and let the user know that their guess doesn't count. I assume it wont make up their failures or count as another guess because it is never added to the hashset. So where I am struggling with, is how do I do as I want, I was thinking of using the built-in way a hashset returns false to detect this, but I have no idea how to implement this since it needs to refer to another method and I don't know how to make a string hashset return booleans. I would greatly appreciate any help at all, thanks.
The add API would return false for an existing value(that it cannot add to the Set), so your condition can be dealt with
boolean letterCheck = guessedLettersA.add(letter);
if(!letterCheck) {
System.out.println("You have already guessed this letter!");
}
Note: The invocation of this block is solely dependent on the design of your application.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
Some background: I am new to Java and am taking a basic java class. I am currently on the final project for the class and completed everything except for this last bit of code. For some reason, I am having the toughest time deleting an element from an array list. Here is the code I am working on:
public static void delete(String bookID) {
for (book eachElement : catalog) {
if (eachElement.getBookID().equals(bookID)) {
catalog.remove(eachElement);
return;
}
}
}
code executes, no run time errors but it won't delete anything.
also, I know everything works prior to the remove statement because I have another method that computes calculations using the same exact for and if statement with a select bookID string.
You should not and cannot remove an Element from a Collection while being in a forEach loop.
Please read the Documentation for ArrayList in Java.
https://docs.oracle.com/javase/7/docs/api/java/util/ArrayList.html
There you actually can see, that ArrayList.remove(Object o), removes o if it is in the list, so your method is not needed.
So the answer is, find the book Object with your ID and then remove it. Or better use a Map to store your data.
In your case it would be
Book b = null;
for(Book book : books) {
if(book.getBookId().equals(bookId)) {
b = book.getBookId();
break;
}
}
books.remove(b);
Or if you are into Java8 which you really should be :D
books.stream().filter(b -> b.getBookId().equals(bookId)).getFirst().ifPresent(books::remove);
You need to use iterator, otherwise you will get java.util.ConcurrentModificationException
public static void delete(String bookID) {
for (Iterator<Book> it = catalog.listIterator(); it.hasNext(); ) {
Book book = it.next();
if (book.getBookID().equalsIgnoreCase(bookID)) {
it.remove(book);
return;
}
}
}
Note: equalsIgnoreCase is used to discard case differences.
java.util.ConcurrentModificationException is thrown, because you are doing 2 operations on the list: iteration and removal. So, actually, there is another approach - copy the list on each step of iteration.
public static void delete(String bookID) {
for (Book book : new ArrayList<>(catalog)) {
if (book.getBookID().equalsIgnoreCase(bookID)) {
catalog.remove(book);
return;
}
}
}
Note: Because of performance considerations (quadratic memory usage and linear removal on each step), I don't recommend the last approach. I give this example only to stress out the underlying reason why java.util.ConcurrentModificationException is thrown.
Removal of elements, while an iterator is being used, is undefined.The better approach would be to use removeIf.
catalog.removeIf(eachElement -> eachElement.getBookID().equals(bookId));
You need to use iterator in order to delete item while using loop .
also double check if the id exist (make some System.out.println("test") and check if it is entering the scope).
This question already has answers here:
Pass a class variable to another class
(3 answers)
Closed 4 years ago.
I'm stuck on a simple problem that I just can't solve.
I have two classes (Fruits.java with main and FruitDetails.java).
Fruits.java is a small program with tons of stuff, really. It has a ComboBox and I need to transfer its currently selected option to FruitDetails.
The problem is... my understanding of setters and getters seems to be very flawed. I've researched it online for the last 2 hours and this is the closest I could get to something. I'm really tight on time and I can't help but ask you now...
Inside class Fruits.java
public void selectedFruit() {
currentFruit = (String) fruitList.getSelectedItem();
}
public String getSelectedFruit() {
return currentFruit;
}
Inside class FruitDetails.java
public void fruitChoice() {
Fruits fruitChoice = new Fruits();
String chosenFruit = fruitChoice.getSelectedFruit();
System.out.println(chosenFruit);
// Rest of the code
}
Not only this opens another copy of my program(which I really don't want), system prints out "null" for the result.
I really need to get this working and hopefully it'll help fix my understanding of encapsulation a bit. There's a ton of online resources I've found, but using them seems to be too hard for the thick head of mine.
Thanks in advance for any help.
public void fruitChoice() {
Fruits fruitChoice = new Fruits();
String chosenFruit = fruitChoice.getSelectedFruit();
System.out.println(chosenFruit);
// Rest of the code
}
In second line you are creating new object that's why you are getting null when you try to get the value of currentFruit.
it looks like you method selectedFruit() sets currentFruit but your not actually calling selectedFruit()?
Unless your missing some code above that calls selectedFruit() elsewhere?
Try calling selectedFruit() after instantiating your Fruit object.
This is because you have not actually linked your currentFruit to your combo box. You need to do two things - call selectedFruit when you first populate the combo box, then attach a listener that calls selectedFruit everytime the combo box selection changes.
If you are using JComboBox, insert this code after you have created the JComboBox.
combo.addActionListener (new ActionListener () {
public void actionPerformed(ActionEvent e) {
selectedFruit();
}
})
selectedFruit();
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Long list of if statements in Java
I was tasked to work with some code, and there is a giant if-else-if chain (100+ else-ifs) that checks Strings.
What are some good techniques to update this code as to where the if-else-if chain can be shrunken down to something much more manageable.
The chain looks something like this:
if(name.equals("abc")){
do something
} else if(name.equals("xyz")){
do something different
} else if(name.equals("mno")){
do something different
} ......
.....
else{
error
}
You can extract the code in each branch to a separate method, then turn the methods into implementations of a common base interface (let's call it Handler). After that, you can fill a Map<String, Handler> and just look up and execute the right handler for given string.
Unfortunately the implementation of 100+ subclasses for the interface requires quite a lot of boilerplate code, but currently there is no simpler way in Java to achieve this. Implementing the cases as elements of an Enum may help somewhat - here is an example. The ideal solution would be using closures / lambdas, but alas we have to wait till Java 8 for that...
Some options / ideas:
Leave it as it is - it's not fundamentally broken, and is reasonably clear and simple to maintain
Use a switch statement (if you are using Java 7) - not sure if this gains you much though
Create a HashMap of String to FunctionObjects where the function objects implement the required behaviour as a method. Then your calling code is just: hashMap.get(name).doSomething();
Break it into a heirarchy of function calls by sub-grouping the strings. You could do this by taking each letter in turn, so one branch handles all the names starting with 'a' etc.
Refactor so that you don't pass the name as a String but instead pass a named object. Then you can just do namedObject.doSomething()
With Enums, you can have a method per instance.
public enum ActionEnum {
ABC {
#Override
void doSomething() {
System.out.println("Doing something for ABC");
}
},
XYZ {
#Override
void doSomething() {
System.out.println("Doing something for XYZ");
}
};
abstract void doSomething();
}
public class MyActionClass {
public void myMethod(String name) {
ActionEnum.valueOf("ABC").doSomething();
}
}
It is still kinda messy (big enum with 100+ entries, even it all it does is dispatching), but may avoid the HashMap initialization code (100+ puts is also messy in my opinion).
And yet another option (for documentation purposes) would be reflection:
public interface Action {
void doSomething();
}
public class ABCAction implements Action {
#Override
public void doSomething() {
System.out.println("Doing something for ABC");
}
}
public class MyActionClass {
void doSomethingWithReflection(String name) {
try {
Class<? extends Action> actionClass = Class.
forName("actpck."+ name + "Action").asSubclass(Action.class);
Action a = actionClass.newInstance();
a.doSomething();
} catch (Exception e) {
// TODO Catch exceptions individually and do something useful.
e.printStackTrace();
}
}
}
Each approach has it's trade offs:
HashMap = Fast + Kinda messy ("set-up" code with hundred of puts)
Enum = Fast + Kinda messy 2 (huge file).
Reflection = Slower + runtime error prone, but provides clean separation without resorting to clunky big HashMap.
Like Matt Ball said in his comment, you can use a command pattern. Define a collection of Runnable classes:
Runnable task1 = new Runnable() {
public void run() { /* do something */ }
};
Runnable task2 = // etc.
Then you can use a map from your keys to runnables:
Map<String,Runnable> taskMap = new HashMap<String,Runnable>();
taskMap.put("abc", task1);
taskMap.put("xyz", task2);
// etc.
Finally, replace the if-else chain with:
Runnable task = taskMap.get(name);
if (task != null) {
task.run();
} else {
// default else action from your original chain
}
you can use the switch statement , but Switch statements with String cases have been implemented in Java SE 7
the best solution is to use the command pattern
This is a popular Arrow Anti-Pattern and Jeff discusses some approaches to handle this very nicely in his post here.