Refactoring methods that use the same code but different types

I have several methods that do the same when interacting with a MySQL database, saving or loading a different type of parameter. I currently have a different method for each type. How can I combine these methods to support different types?

The following is an example of two methods that are very similar but use different types:

public static void saveLongArray(Connection con, int playerID, String tableName, String fieldName, long[] array, long[] originalArray) { try { for (int i = 0; i < array.length; i++) { // Check for change before running query if (array[i] != originalArray[i]) { if (array[i] != 0 && array[i] != -1) { PreparedStatement updateQuery = con.prepareStatement("REPLACE INTO `" + tableName + "` (`player_id`, `index`, `" + fieldName + "`) VALUES(?, ?, ?)"); updateQuery.setInt(1, playerID); updateQuery.setInt(2, i); updateQuery.setLong(3, array[i]); updateQuery.execute(); } else { PreparedStatement deleteQuery = con.prepareStatement("DELETE FROM `" + tableName + "` WHERE `player_id` = ? AND `index` = ?"); deleteQuery.setInt(1, playerID); deleteQuery.setInt(2, i); deleteQuery.execute(); } originalArray[i] = array[i]; } } } catch (SQLException ex) { Logger.getLogger(PlayerSaveHandler.class.getName()).log(Level.SEVERE, "SQL Exception while saving a long array!", ex); } } public static void saveIntArray(Connection con, int playerID, String tableName, String fieldName, int[] array, int[] originalArray) { try { for (int i = 0; i < array.length; i++) { // Check for change before running query if (array[i] != originalArray[i]) { if (array[i] != 0 && array[i] != -1) { PreparedStatement updateQuery = con.prepareStatement("REPLACE INTO `" + tableName + "` (`player_id`, `index`, `" + fieldName + "`) VALUES(?, ?, ?)"); updateQuery.setInt(1, playerID); updateQuery.setInt(2, i); updateQuery.setInt(3, array[i]); updateQuery.execute(); } else { PreparedStatement deleteQuery = con.prepareStatement("DELETE FROM `" + tableName + "` WHERE `player_id` = ? AND `index` = ?"); deleteQuery.setInt(1, playerID); deleteQuery.setInt(2, i); deleteQuery.execute(); } originalArray[i] = array[i]; } } } catch (SQLException ex) { Logger.getLogger(PlayerSaveHandler.class.getName()).log(Level.SEVERE, "SQL Exception while saving an int array!", ex); } } 

Note that in this example, the types are numerical. In the case where the types are completely different (for example, int and String), what could I do to avoid methods that are close to duplication?

+9
source share
5 answers

You can apply the Strategy template here.

 interface TypeDependentBehavior<T> { void setFieldValue(PreparedStatement st, T value); } interface StringBehavior extends TypeDependentBehavior<String> { void setFieldValue(PreparedStatement st, String value) { st.setString(3, value); } } interface IntBehavior extends TypeDependentBehavior<Integer> { void setFieldValue(PreparedStatement st, Integer value) { st.setInt(3, value); } } 

...

 public static void saveArray<T>(Connection con, int playerID, String tableName, String fieldName, T[] array, T[] originalArray, TypeDependentBehavior<T> behavior) { try { for (int i = 0; i < array.length; i++) { // Check for change before running query if (array[i] != originalArray[i]) { if (array[i] != 0 && array[i] != -1) { PreparedStatement updateQuery = con.prepareStatement("REPLACE INTO '" + tableName + "' ('player_id', 'index', '" + fieldName + "') VALUES(?, ?, ?)"); updateQuery.setInt(1, playerID); updateQuery.setInt(2, i); behavior.setFieldValue(updateQuery, array[i]); updateQuery.execute(); } else { PreparedStatement deleteQuery = con.prepareStatement("DELETE FROM '" + tableName + "' WHERE 'player_id' = ? AND 'index' = ?"); deleteQuery.setInt(1, playerID); deleteQuery.setInt(2, i); deleteQuery.execute(); } originalArray[i] = array[i]; } } } catch (SQLException ex) { Logger.getLogger(PlayerSaveHandler.class.getName()).log(Level.SEVERE, "SQL Exception while saving an int array!", ex); } } 
+13
source

I would use long[] instead of int[] . The difference in memory is very small compared to the cost of using JDBC.

If you need to process a String, you can use an object type.

 public static void saveArray(Connection con, int playerID, String tableName, String fieldName, Object[] array, Object[] originalArray) { 

If you need one method for long[] and Object[] , you can use the Array.getLength() and Array.get() method to access all types of arrays in general. This can add more complexity than it saves.

+3
source

You can use generics for this, for example

 void doSomething(int[] array) { for (int i = 0; i < array.length; i++) System.out.println(array[i]); } void doSomething(long[] array) { for (int i = 0; i < array.length; i++) System.out.println(array[i]); } 

can be generalized to

 <T> void doSomething(T[] array) { for (int i = 0; i < array.length; i++) System.out.println(array[i]); } 

Now you can call

 int[] array1 = new int[] { 1, 2, 3 }; doSomething(array1); long[] array2 = new long[] { 1L, 2L, 3L }; doSomething(array2); String[] array3 = new String[] { "one", "two", "three" }; doSomething(array3); 

But you have to check your implementation of the method and make sure that it will still work with any type of array, especially with the SQL statement.

+2
source

What if you violated your comparative functionality and put your methods down to the granular level? For instance:

 public static void update(Connection con, int playerID, String tableName, String fieldName, String value) { // update query logic here } 

And the same goes for delete() . There is no reason to pass into this function both "new" and "original" values, as well as compare them inside. I suggest scrolling through arrays, comparing and calling either update() or delete() based on your needs. To deal with different types of data, I will always pass the String value of what you want in the database.

+1
source

With similar types you can create a shell - a method that takes int[] as an argument, generates long[] from the passed values ​​and calls a variant of the method that takes long[] as an argument to do the actual work. It has some overhead, but assuming your arrays are not millionth records long, this is negligible compared to the cost of linking to the database.

With completely different types, you can try using Object[] (or perhaps use generics somehow), but there would be some pitfalls. You will need to use a different delete token instead of 0 or -1 ( null seems like an obvious choice). The big problem is setting parameters in PreparedStatement , since you need to call different methods, but you can generate the entire query string manually using the methods of the provided toString() objects instead of setting parameters using setInt() , etc.

0
source

Source: https://habr.com/ru/post/910617/


All Articles