2012-03-13 17 views
6

Mam kilka metod, które robią to samo jeszcze, po połączeniu się z bazą danych MySQL, zapisz lub załaduj inny typ parametru. Obecnie mam inną metodę dla każdego typu. Jak mogę połączyć te metody, aby obsługiwały różne typy?Metody refaktoryzacji, które używają tego samego kodu, ale różne typy

Poniżej znajduje się przykład z dwóch metod, które są bardzo podobne ale używają różnych typów:

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

Uwaga: W tym przykładzie są oba typy numeryczne. W przypadku, gdy typy są całkowicie różne (np. Int i String), co mogę zrobić, aby uniknąć prawie duplikatów metod?

Odpowiedz

12

Można zastosować Strategię wzór tutaj.

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

Szukałem tego wzoru, ale nigdy nie pomyślałbym o używaniu go w tym przypadku. Dziękuję Ci! – jSherz

+0

Dodam tylko, że ponieważ tablice będą teraz przenosić obiekty zamiast prymitywów, powinny być porównywane za pomocą 'equals()' (np. '! Tablica [i] .equals (originalArray [i])' lub 'compareTo()'. Wygląda też na to, że tablice zawierają tylko typy liczbowe, więc nie potrzebujemy zachowania dla String, a argumentem typu powinno być '' tylko po to, by być bezpiecznym – jpm

+0

Czy dodanie implementacji do interfejsów jest dobrym pomysłem w Javie? Czy nie byłoby lepiej jako klasa abstrakcyjna? – flurdy

3

Po prostu użyłbym long[] zamiast int[]. Różnica w pamięci jest niewielka w porównaniu z kosztem korzystania z JDBC.

Jeśli potrzebujesz obsługi String, możesz użyć typu obiektu.

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

Jeśli chcesz jedną metodę long[] i Object[] można użyć metody Array.getLength() i Array.get() dostęp do wszystkich rodzajów tablic rodzajowo. To może dodać więcej złożoności niż ją zapisuje.

+2

To nie jest odpowiedź na jego pytanie "co jeśli' int' i 'String'?" – ggrigery

2

Można użyć rodzajowych za to, na przykład

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

można uogólnić do

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

Teraz można zadzwonić

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

Ale należy sprawdzić implementację metody i upewnij się, że nadal będzie działać z dowolnym typem tablicy, szczególnie z instrukcją SQL.

1

Co się stanie, jeśli zepsujesz swoją porównywalną funkcjonalność i zminiesz swoje metody do najbardziej szczegółowego poziomu? Na przykład:

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

To samo dotyczy delete(). Nie ma powodu, aby przekazywać wartości "nowe" i "oryginalne" do tej funkcji i porównywać je w środku. Sugeruję przechodzenie przez tablice, porównywanie i wywoływanie update() lub delete() w zależności od Twoich potrzeb. Aby poradzić sobie z różnymi typami danych, zawsze przekazywałbym wartość String tego, co chcesz w bazie danych.

0

Z podobnego typu, można utworzyć opakowanie - metodę, która przyjmuje jako argument int[] generuje long[] od wartości przekazywanych i wywołuje wariant sposobu, który zaczyna long[] jako argument do przeprowadzenia rzeczywistej pracy. Ma trochę narzut, ale zakładając, że twoje tablice nie są długimi milionami wpisów, są one pomijalne w porównaniu do kosztów komunikacji z bazą danych.

Z zupełnie różnymi typami, możesz spróbować użyć Object[] (lub może jakoś używać generycznych), ale byłyby pewne pułapki. Będziesz musiał użyć innego znacznika usunięcia zamiast 0 lub -1 (null wydaje się oczywistym wyborem).Większym problemem jest ustawienie parametrów w PreparedStatement ponieważ różne metody trzeba nazwać, ale można wygenerować cały ciąg kwerendy ręcznie, przy użyciu toString() metod dostarczonego obiektu czy zamiast ustawienia parametrów z setInt() itp

Powiązane problemy