Java method design pattern - java

I'm developing a program that scrapes the web for certain data and feeds it back to the database. The problem is that I don't want duplicate entries of the same data as soon as the crawlers run for a second time. If some attributes changed, but the majority of the data is still the same, I'd like to update the DB entry rather than simply adding a new one. I know how to do this in code, but I was wondering if this could be done better.
The way the update works right now:
//This method calls several other methods to check if the event in question already exists. If it does, it updates it using the id it returns.
//If it doesn't exist, -1 is returned as an id.
public static void check_event(Event event)
{
int id = -1;
id = check_exact_event(event); //Check if an event exists with the same title, location and time.
if(id > 0)
{
update_event(event, id);
Logger.log("EventID #" + id + " found using exact comparison");
return;
}
id = check_similar_event_titles(event); //Check if event exists with a different (but similar) title
if(id > 0)
{
update_event(event, id);
Logger.log("EventID #" + id + " found using similar title comparison");
return;
}
id = check_exact_image(event); //Check if event exists with the exact same image
if(id > 0)
{
update_event(event, id);
Logger.log("EventID #" + id + " found using image comparison");
return;
}
//Otherwise insert new event
create_new_event(event);
}
This works, but it's not very pleasing to the eye. What's the best way to go about this?

Personally i can'tsee anything wron with your code, it is clean and effective.
If you really want to change it, you could do it in single if statement
public static void check_event(Event event) {
int id = -1;
if ((id = check_exact_event(event)) > 0
|| (id = check_similar_event_titles(event)) > 0
|| (id = check_exact_image(event)) > 0) {
update_event(event, id);
}
;
create_new_event(event);
}
But i cant see much gain in this way

Related

How can I change how an objects field is PRESENTED while still retaining the original value of said field?

I'm working on a project that involves creating a Spring Boot REST Application using JDBC Template to access a database of my own creation (MySQL). I'm using Postman to verify endpoints and entering data using JSON through postman. I'm currently tasked with creating a guessing game that generates a random 4 digit number, easy enough. My issue is this; " Returns a specific game based on ID. Be sure in-progress games do not display their answer" I've created a "starter" method that fulfills the requirement, but only at it's most basic level. I cannot operate on an object once it's field's value is changed to "Hidden" as it is no longer a 4 digit number. I'd like to hide the answer while still being able to operate in a functional manner on the randomly generate number. Here is my base method for this return:
#Override
public Game gameById(int id) {
Game game = gameDao.getGameById(id);
if (game.getFinished() == false) { // "Hides" the answer
game.setGameAnswer("Hidden");
}
return game;
}
Here is my method that uses the above code in my application:
#PostMapping("/guess")
public ResponseEntity<Game> play( int id, String guess) {
Game game = service.gameById(id); // Get method
if (game == null) {
return new ResponseEntity(null, HttpStatus.NOT_FOUND);
}
Round round = new Round();
round.setGuess(guess);
service.guess(round, game);
return ResponseEntity.ok(game);
}
Any ideas?
EDIT:
Game Object:
public class Game {
int gameId;
String gameAnswer;
Boolean finished;
List<Round> Rounds = new ArrayList<>();
I will also include the database game table which i am storing said fields into:
CREATE TABLE game(
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
gameAnswer CHAR(4) NOT NULL,
finished BOOLEAN DEFAULT false
);
To clarify my question, when running the game in postman, the game object and it's fields are all displayed as well as the games answer. My goal is to hide this answer as long as the games status reads "false". Once the number has been correctly guessed the status of the game will automatically be set to true.
Here is an example from the JSON output of postman:
{
"gameId": 1,
"gameAnswer": "2651",
"finished": false,
"rounds": [
{
"roundId": 1,
"guess": "1234",
"timeOfGuess": "2021-06-18 09:28:29",
"exactMatch": 0,
"partialMatch": 0,
"gameId": 1
}
]
}
While the "Finished" is set to false, I want the game answer to read "Hidden", but still have the numerical value be present to operate on.
Guess Method:
#Override
public void guess(Round round, Game game) {
// Takes the games generated answer and the user's guess and breaks them down into character array's
round.setGameId(game.getGameId());
char[] gameAnswer = characterBreakDown(game.getGameAnswer());
char[] roundGuess = characterBreakDown(round.getGuess());
// used for comparing the array elements one by one
int min = 0;
int max = 1;
int exact = 0;
boolean run = true;
while (run) {
for (int i = 0; i <= 3; i++) {
if (Arrays.equals(roundGuess, gameAnswer)) {
round.setExactMatch(4);
game.setFinished(true);
gameDao.updateGameById(game);
run = false;
} else if (Arrays.equals(roundGuess, min, max, gameAnswer, min, max)) {
exact++;
round.setExactMatch(exact);
min++;
max++;
if (i == 3 && round.getExactMatch() >= 2) {
round.setPartialMatch(round.getExactMatch());
run = false;
} else if (i == 3) {
run = false;
}
} else if (!Arrays.equals(roundGuess, min, max, gameAnswer, min, max)) {
min++;
max++;
if (i == 3 && round.getExactMatch() >= 2) {
round.setPartialMatch(round.getExactMatch());
run = false;
} else if (i == 3) {
run = false;
}
}
}
}
game.getRounds().add(round);
roundDao.createRound(round);
}
If I understand correctly, your issue is that in one use case (when you are responding to an API request), you want to retrieve a Game and respond to the API request with the Game's "gameAnswer" field hidden, but for other, purely background operations, you want to retrieve the Game with the "gameAnswer" field intact.
Assuming that's the case, I think that your only real issue is where you "blank-out" the "gameAnswer" field.
It looks like you are currently doing that in the "gameById" method of the repository. That results in the gameAnswer being hidden in all cases, right?
So, the solution would be to blank-out the gameAnswer in the controller method instead.
If I have this wrong / backwards I apologize, but the way that you posted your code, in pieces, makes it hard to follow. But anyway, I believe that the solution to your problem is to only blank-out the field where it actually needs to be blanked out. Or if you are having some other issue with it being blanked out / not blanked out because you are doing so in some shared method, perhaps create a new method for the use-case that needs it the other way.

Function not ending correctly after recursive loop

I am trying to build a copy cat of the game risk. I have a while loop which says while the attack isn't finished do something. Then I ask the user to type in either 'end turn' to end turn or 'continue' to recursively call the attack function again. The problem is after the user types in attack a few times and then 'end turn' the turn doesn't end rather it starts from the beginning or the function again. I would greatly appreciate an expert eye to look at my code and see what I am missing, thanks in advance.
public void attackOrSkip(Player player,Player[] playerArray, int playerId) {
boolean attackFinished = false;
int numUnitsAttackWith = 0;
int defenceArmiesNumber =0;
displayString(makeLongName(player) + ": Type 'attack' to attack or 'skip' to skip your turn...");
String command = commandPanel.getCommand();
displayString(PROMPT + command);
if(command.equals("skip") ||command.equals("skip ") ||command.equals("s")) {
return;
}else if (command.equals("attack") ||command.equals("attack ")){
displayString(PROMPT + command);
//while the attack isn't finished
while(attackFinished == false) {
//get the country the user is attacking
int countryAttackingFrom=countryFromCheck(playerId,player);
//get the country to attack
int countryToAttack = countryToCheck(player);
//get the player who we are attacking
int occupierPlayer =board.getOccupier(countryToAttack);
if ((board.getNumUnits(countryAttackingFrom)) < 2) {
displayString("You dont have enough units on this country to make an attack!");
attackOrSkip(player, playerArray, playerId);
break;
}
//if the country is adjacent to another one then you can attack else no
else if(isAdjacent(countryAttackingFrom,countryToAttack)) {
//check the number of unit to attack with
numUnitsAttackWith =numUnitsCheckerAttack(player,countryAttackingFrom);
//check the number of unit to defend with
defenceArmiesNumber = numUnitsCheckerDefence(player,countryToAttack);
//roll the dice
player.rollDice(numUnitsAttackWith);
playerArray[occupierPlayer].rollDice(defenceArmiesNumber);
//display the roll results
displayString(makeLongName(player) + "Rolled: "+printDie(player));
displayString(makeLongName(playerArray[occupierPlayer]) + "Rolled: "+printDie(playerArray[occupierPlayer]));
}
displayString(makeLongName(player) + ": 'end turn' or 'continue'");
command = commandPanel.getCommand();
displayString(PROMPT + command);
if(command.equals("end turn")||command.equals("end turn ") ||command.equals("endturn")||command.equals("endturn ") ||command.equals("end")) {
attackFinished = true;
return;
}else if(command.equals("attack") ||command.equals("attack ")){
// break;
}else if(command.equals("continue") ||command.equals("continue ") ||command.equals("con")){
attackOrSkip(player,playerArray,playerId);
}else {
return;
}
}else {
displayString(makeLongName(player) + ": ERROR, not adjacent countries");
}
}
}
}
Okay - as currently written - every time you call attackOrSkip within the method - you end up 1 level lower in the stack - with a new set of variables -
attackFinished, numUnitsAttackWith, defenceArmiesNumber
When you leave the recursion (i.e. via a return) you end up with simple variables as they were before you enter the recursive call - remember Java is Call By Value (even though you can pass in references to objects, you get a value (the current value of the variable reference when called ... and changing the reference to point at a different object doesn't change the callers reference).
SO, without looking to see whether you have done the correct algorithm - I would guess that if you made the method return type a boolean and returned the status instead of nothing, you could update attackFinished and the right thing might happen..
e.g. change all the
return;
to
return attackFinished;
AND change all the places where you call
attackOrSkip(....)
to set attack finished based on what the method return
attackFinished = attackOrSkip(....)
OR - you can pass in an extra parameter - attackFinished - in a Holder (an example of the concept) object - again the reference can't change, but you can go attackFinished.value = true (and it will then be the same the whole way out the stack as you drop out of the recursion).

Removing a number from a listed created by given numbers

so here's the details.
I'm coding using java - BlueJ, and currently have a main class Auction, and a few other classes: Lot, Person, Bid
The purpose of the program is to enter items into the auction, along with their prices, where the information will be stored according to whether it's price, person, or item name/description.
This is the code for my getLot method - user inputs lot number, and it shows info for the lot, which I assume should also be the starting point for the removeLot method since it should still check if the lot number given is valid first.
I'm trying to figure out how to add a removeLot method so that I can remove an item from the lot by typing in its lot number.
This is the code I have for that section.
public Lot removeLot(int number)
{
if((number >= 1) && (number < nextLotNumber)) {
// The number seems to be reasonable.
Lot selectedLot = lots.get(number - 1);
// Include a confidence check to be sure we have the
// right lot.
if(selectedLot.getNumber() != number) {
System.out.println("Internal error: Lot number " +
selectedLot.getNumber() +
" was returned instead of " +
number);
// Don't return an invalid lot.
selectedLot = null;
}
**else {
Lot.removeIf(selectedLot.getNumber() = number);**
}
return selectedLot;
The else block with the "**" is what I added, wanting to remove the given number. But it's clearly wrong, and I'm not sure what to do.
If the datatype of lots variable is List than you can try below code
public Lot removeLot(int number)
{
Lot selectedLot=null;
if((number >= 1) && (number < nextLotNumber)) {
// The number seems to be reasonable.
selectedLot = lots.get(number - 1);
// Include a confidence check to be sure we have the
// right lot.
if(selectedLot.getNumber() != number) {
System.out.println("Internal error: Lot number " +
selectedLot.getNumber() +
" was returned instead of " +
number);
// Don't return an invalid lot.
selectedLot=null;
}
else {
if(selectedLot.getNumber() = number)
{
lots.remove(number-1);
}
}
return selectedLot;
}
Lot.removeIf(selectedLot.getNumber() = number) is not correct
you can try:
} else if(selectedLot.getNumber() == number) {
Lot.removeIf(selectedLot.getNumber());
}

SHAppBarMessage and AutoHide

public void toggleAutoHide()
{
APPBARDATA data = new APPBARDATA.ByReference();
data.hWnd = hWndGlobal;
data.cbSize.setValue(data.size());
data.lParam.setValue(Shell32.INSTANCE.SHAppBarMessage(new DWORD(ShellAPI.ABM_GETSTATE), data).longValue());
data.lParam.setValue(data.lParam.intValue() ^ 0x0000001);
UINT_PTR result = Shell32.INSTANCE.SHAppBarMessage(new DWORD(ShellAPI.ABM_SETSTATE), data);
}
I have the code above that is supposed to autohide a created appbar, but somehow instead of doing this to the actual bar I'm creating, it's actually changing the status of the main Windows taskbar. Any clue what step I'm missing?
EDIT:
I've modified the code and changed the call but I'm getting the same values all the time, regardless of what I set the values to.
public void toggleAutoHide()
{
APPBARDATA data = new APPBARDATA.ByReference();
data.hWnd = hWndGlobal;
data.cbSize.setValue(data.size());
data.uEdge.setValue(ShellAPI.ABE_TOP);
System.out.println("LParam [byte, int]: " + data.lParam.byteValue() + " -- " + data.lParam.intValue());
//lParam always shows 0
if(data.lParam.intValue() == 1)
{
data.lParam.setValue(0);
}
else
{
data.lParam.setValue(1);
}
UINT_PTR result = Shell32.INSTANCE.SHAppBarMessage(new DWORD(ShellAPI.ABM_SETAUTOHIDEBAR), data);
System.out.println("Result = " + result.intValue()); //always returns 1
}
The ABM_SETSTATE call is using your data.hWnd variable to decide which window handle gets your changes. You assign that to the value a variable hWndGlobal but don't explain where that came from.
The fact that it's named "global" seems to imply somewhere earlier in the code you gave it the value for the Windows taskbar. Hunt down that assignment.
You probably want something like:
data.hWnd = User32.INSTANCE.FindWindowA(null, "Title of your new appbar");

Update parts of a row in oracle with java

I want to be able to update parts of a row in a table in an oracle database. The database has a number (which is the primary key) and 5 other columns.
The method takes an object and compares it with the object with the same primary key in the database. It should then compare the columns and change those which are changed. I've thought of a few different ways of doing this:
Perform a check for every single possible permutation of changes (long way of doing this).
For example:
public boolean updateOrder(Order o, Connection con) {
int rowUpdated = 0;
String SQLString = "";
Order origOrder = getOrder(o.getOno(), con);
if (origOrder.getCustomerNo() != o.getCustomerNo()
&& origOrder.getEmployeeNo() == o.getEmployeeNo()
&& origOrder.getReceived().compareTo(o.getReceived()) == 0
&& origOrder.getBeginDate().compareTo(o.getBeginDate()) == 0
&& origOrder.getEndDate().compareTo(o.getEndDate()) == 0
&& origOrder.getProjectLocation().compareTo(o.getProjectLocation()) == 0) {
SQLString = "UPDATE ORDERS SET "
+ "CNO = " + o.getCustomerNo()
+ "where ONO = " + o.getOno();
}
PreparedStatement statement = null;
try {
//== insert value----- Unit of work start
con.setAutoCommit(false);
statement = con.prepareStatement(SQLString);
rowUpdated = statement.executeUpdate();
etc...
Just change everything every time (pretty simple, I'm afraid it might go wrong though).
Does anyone have a clever way of doing this?
Why do you want to perform the check for something that has changed? Just perform the update.
If you really need to make the check, push the comparison logic into a method of the Order class.
if(origOrder.hasChanged(o)) {
// perform update
}
P.S. Variable names like o are not very meaningful or helpful.

Categories

Resources