【问题标题】:Refactoring methods that use the same code but different types使用相同代码但类型不同的重构方法
【发布时间】:2012-03-13 16:55:35
【问题描述】:

我有几种方法可以做同样的事情,当与 MySQL 数据库交互时,保存或加载不同类型的参数。目前,我对每种类型都有不同的方法。如何组合这些方法以使它们支持不同的类型?

以下是两个非常相似但使用不同类型的方法的示例:

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);
    }
}

请注意,在该示例中,类型都是数字的。在类型完全不同的情况下(例如 int 和 String),我该怎么做才能避免使用近乎重复的方法?

【问题讨论】:

    标签: java refactoring


    【解决方案1】:

    您可以在此处应用策略模式。

    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);
        }
    }
    

    【讨论】:

    • 我一直在研究这种设计模式,但从未想过在这种情况下使用它。谢谢!
    • 我在这里唯一要补充的是,由于数组现在将携带对象而不是基元,因此应该使用equals()(例如!array[i].equals(originalArray[i])compareTo())来比较它们。另外,它似乎假设数组仅包含数字类型,因此我们不需要 String 的行为,并且类型参数可能应该是 &lt;T extends Number&gt; 只是为了安全。
    • 在 Java 中将实现添加到接口中是个好主意吗?作为一个抽象类不是更好吗?
    【解决方案2】:

    我只会使用long[] 而不是int[]。与使用 JDBC 的成本相比,内存差异非常小。

    如果您需要处理字符串,您可以使用对象类型。

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

    如果您想要long[]Object[] 的一种方法,您可以使用Array.getLength()Array.get() 方法来通用访问所有数组类型。 这可能会增加比节省更多的复杂性。

    【讨论】:

    • 这并没有回答他的问题“如果intString?”
    【解决方案3】:

    例如,您可以为此使用泛型

    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]);
    }
    

    可以概括为

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

    现在你可以打电话了

    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);
    

    但是您应该检查您的方法实现,并确保它仍然适用于任何数组类型,尤其是 SQL 语句。

    【讨论】:

      【解决方案4】:

      如果您打破比较功能并将您的方法细化到最细粒度的水平会怎样?例如:

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

      delete() 也是如此。没有理由将“新”和“原始”值都传递给这个函数并在里面进行比较。我建议根据您的需要循环遍历数组、比较和调用update()delete()。为了处理不同的数据类型,我总是将String 值传入你想要的数据库中。

      【讨论】:

        【解决方案5】:

        对于类似的类型,您可以创建一个包装器 - 一个将 int[] 作为参数的方法,从传递的值生成 long[] 并调用以 long[] 作为参数的方法变体来执行实际工作。它有一些开销,但假设您的数组不是数百万个条目,与与数据库通信的成本相比,它可以忽略不计。

        对于完全不同的类型,您可以尝试使用Object[](或者以某种方式使用泛型),但会有一些陷阱。您需要使用不同的删除标记而不是 0 或 -1(null 似乎是显而易见的选择)。更大的问题是在PreparedStatement 中设置参数,因为需要调用不同的方法,但您可以手动生成整个查询字符串,使用提供的对象的toString() 方法而不是使用setInt() 等设置参数。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2022-06-10
          • 1970-01-01
          • 2019-04-13
          • 1970-01-01
          • 2020-12-15
          • 2010-10-30
          • 1970-01-01
          相关资源
          最近更新 更多