简体   繁体   English

使用相同代码但类型不同的重构方法

[英]Refactoring methods that use the same code but different types

I have several methods that do the same thing yet, when interfacing with the MySQL database, save or load a different type of parameter. 与MySQL数据库连接时,我有几种方法可以做同样的事情,保存或加载不同类型的参数。 Currently, I have a different method for each type. 目前,每种类型我都有不同的方法。 How can I combine these methods so that they support different types? 如何合并这些方法,以便它们支持不同的类型?

Below is an example of two methods that are very similar yet 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 in that example the types are both numerical. 请注意,在该示例中,类型均为数字。 In a case where types are completely different (eg int and String), what could I do to avoid have near-duplicate methods? 在类型完全不同的情况下(例如int和String),如何避免使用近乎重复的方法?

You can apply the Strategy pattern 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);
    }
}

I would just use long[] instead of int[] . 我只会使用long[]而不是int[] The memory difference is very small compared with the cost of using JDBC. 使用JDBC的成本相比,记忆差非常小。

If you need to handle String you can use an object type. 如果需要处理String,则可以使用对象类型。

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

If you want one method for long[] and Object[] you can use the Array.getLength() and Array.get() method to access all array types generically. 如果要为long[]Object[]使用一种方法,则可以使用Array.getLength()Array.get()方法来通用地访问所有数组类型。 This could add more complexity than it saves. 这可能会增加更多的复杂性。

You can use generics for that, 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 into 可以概括为

<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 should check your method implementation and make sure that it will still work with any array type, especially the SQL statement. 但是,您应该检查方法的实现,并确保它仍然可以与任何数组类型(尤其是SQL语句)一起使用。

What if you broke out your comparative functionality and had your methods down to the most granular level? 如果您突破了比较功能并将方法降至最细微的层次该怎么办? For example: 例如:

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

And the same for delete() . delete()相同。 There is no reason to pass both the "new" and "original" values into this function and do the compare inside. 没有理由将“新”和“原始”值都传递到此函数中并在内部进行比较。 I suggest looping through the arrays, comparing, and calling either update() or delete() based on your needs. 我建议根据需要循环遍历数组,比较并调用update()delete() To deal with different data types, I would always pass in the String value of what you want in the database. 为了处理不同的数据类型,我将始终在数据库中传递所需的String值。

With similar types, you could create a wrapper - a method which takes as an argument an int[] , generates a long[] from the values passed and calls the method variant which takes long[] as argument to perform the actual work. 对于类似的类型,您可以创建一个包装器-一种将int[]作为参数的方法,从传递的值生成long[]并调用将long[]作为参数的方法变体来执行实际工作。 It has some overhead, but assuming your arrays are not millions of entries long, it's negligible compared to the cost of communication with the database. 它有一些开销,但是假设您的数组没有几百万个条目长,那么与数据库通信的开销相比可以忽略不计。

With completely different types, you could try using Object[] (or maybe somehow use generics), but there would be some pitfalls. 对于完全不同的类型,您可以尝试使用Object[] (或也许以某种方式使用泛型),但是会有一些陷阱。 You would need to use a different deletion marker instead of 0 or -1 ( null seems the obvious choice). 您将需要使用其他删除标记而不是0或-1( null似乎是显而易见的选择)。 The bigger issue is setting parameters in PreparedStatement since different methods need to be called but you could generate the whole query string manually, using the provided objects' toString() methods instead of setting parameters with setInt() etc. 更大的问题是在PreparedStatement设置参数,因为需要调用不同的方法,但是您可以使用提供的对象的toString()方法手动生成整个查询字符串,而不是使用setInt()等设置参数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM