2010-11-21 1 views
1

Ich habe zwei Methoden in einer DB-Helper-Klasse, die im Grunde die gleiche Sache für zwei verschiedene Datenbank-Entitäten tun, und ich möchte sie umzuformen, um zu vermeiden, Code zu duplizieren.Wie Refactor dieses Objective-C-Code

Die erste Einheit:

- (void) insertOrUpdateEntityA:(NSDictionary*)data { 
    sqlite3_stmt *exists_stmt; 
    if(sqlite3_prepare_v2(database, RMSQLEntityAExists, -1, &exists_stmt, NULL) == SQLITE_OK) { 
     [RMStoreDB bindPrimaryKey:exists_stmt data:data from:1]; 
     if (sqlite3_step(exists_stmt) == SQLITE_ROW) { 
      int count = sqlite3_column_int(exists_stmt, 1); 
      sqlite3_stmt *update_stmt; 
      if (count) { // Update 
       if (sqlite3_prepare_v2(database, RMSQLEntityAUpdate, -1, &update_stmt, NULL) == SQLITE_OK) { 
        int index = [RMStoreDB bindEntityA:update_stmt data:data from:1]; 
        [RMStoreDB bindPrimaryKey:update_stmt data:data from:index]; 
       } 
      } else { // Insert 
       if (sqlite3_prepare_v2(database, RMSQLEntityAInsert, -1, &update_stmt, NULL) == SQLITE_OK) { 
        int index = [RMStoreDB bindPrimaryKey:update_stmt data:data from:1]; 
        [RMStoreDB bindEntityA:update_stmt data:data from:index]; 
       }   
      } 
      sqlite3_step(update_stmt); 
      sqlite3_finalize(update_stmt); 
     }   
    } 
    sqlite3_finalize(exists_stmt); 
} 

die zweite Einheit:

- (void) insertOrUpdateEntityB:(NSDictionary*)data { 
    sqlite3_stmt *exists_stmt; 
    if(sqlite3_prepare_v2(database, RMSQLEntityBExists, -1, &exists_stmt, NULL) == SQLITE_OK) { 
     [RMStoreDB bindPrimaryKey:exists_stmt data:data from:1]; 
     if (sqlite3_step(exists_stmt) == SQLITE_ROW) { 
      int count = sqlite3_column_int(exists_stmt, 1); 
      sqlite3_stmt *update_stmt; 
      if (count) { // Update 
       if (sqlite3_prepare_v2(database, RMSQLEntityBUpdate, -1, &update_stmt, NULL) == SQLITE_OK) { 
        int index = [RMStoreDB bindEntityB:update_stmt data:data from:1]; 
        [RMStoreDB bindPrimaryKey:update_stmt data:data from:index]; 
       } 
      } else { // Insert 
       if (sqlite3_prepare_v2(database, RMSQLEntityBInsert, -1, &update_stmt, NULL) == SQLITE_OK) { 
        int index = [RMStoreDB bindPrimaryKey:update_stmt data:data from:1]; 
        [RMStoreDB bindEntityB:update_stmt data:data from:index]; 
       }   
      } 
      sqlite3_step(update_stmt); 
      sqlite3_finalize(update_stmt); 
     }   
    } 
    sqlite3_finalize(exists_stmt); 
} 

Die Unterschiede sind die Konstanten für die SQL-Anweisungen verwendet (RMSQLEntityAExists, RMSQLEntityBExists, etc.) und die zur Bindung verwendete Methode die Daten zur SQLite-Anweisung (bindEntityA und bindEntityB). Letzteres ist das, was ich besonders verallgemeinern möchte.

Wie überarbeite ich diese beiden Methoden? Sollte ich?

+0

Verwenden Sie [FMDB] (http://github.com/ccgus/fmdb). –

+0

Aber dann würde ich nicht lernen, wie man diesen speziellen Code umgestaltet. ;) – hpique

Antwort

0

Unterklasse beide Entitäten von einem gemeinsamen Objekt mit dieser Methode im Stammobjekt.

Das Differential kann geändert werden mit ivars, die Sie in der Unterklasse init Methoden eingerichtet und einen Aufruf an ein mutiertes Methode

Wie so ...

@interface RootEntity : MyObject { 

    NSString *statementmech1; 
    NSString *statementmech2; 
    NSString *statementmech3; 
} 

@interface Entity1 : RootEntity { 

} 

@interface Entity2 : RootEntity { 

} 

und

@implementation RootEntity 

-(void)variantMethod 
{ 
    //varies in subclasses 
} 

    - (void) insertOrUpdateItem:(NSDictionary*)item { 
     sqlite3_stmt *exists_stmt; 
     if(sqlite3_prepare_v2(database, statementmech1 , -1, &exists_stmt, NULL) == SQLITE_OK) { 
      [RMStoreDB bindPrimaryKey:exists_stmt data:item from:1]; 
      if (sqlite3_step(exists_stmt) == SQLITE_ROW) { 
       int count = sqlite3_column_int(exists_stmt, 1); 
       sqlite3_stmt *update_stmt; 
       if (count) { // Update 
        if (sqlite3_prepare_v2(database, statementmech2, -1, &update_stmt, NULL) == SQLITE_OK) { 
         int index = [RMStoreDB bindItem:update_stmt data:item from:1]; 
         [RMStoreDB bindPrimaryKey:update_stmt data:item from:index]; 
        } 
       } else { // Insert 
        if (sqlite3_prepare_v2(database, statementmech3, -1, &update_stmt, NULL) == SQLITE_OK) { 
         [self variantMethod]; 
        }   
       } 
       sqlite3_step(update_stmt); 
       sqlite3_finalize(update_stmt); 
      }   
     } 
     sqlite3_finalize(exists_stmt); 
    } 
+0

Würde dies nicht eine zusätzliche Klasse erstellen, die nicht instanziiert werden kann? Da Objective-C keine abstrakten Klassen hat, finde ich das irreführend.Außerdem habe ich die Frage bearbeitet, um zu verdeutlichen, dass beide Methoden in einer db-Hilfsklasse sind. – hpique

+0

Wer sagt, Obj-C hat keine abstrakten Klassen? Es ist bestenfalls eine semantische Teilung. Wenn Sie nicht möchten, dass diese Instanz instanziiert eine Ausnahme in der Root-Klasse init-Methode. –

+0

Eigentlich entfernt Ihre Bearbeitung die Gemeinsamkeit in der Methode sig. Stören Sie nicht das Refactoring. –

0

Die erste Sache, die ich ändern würde, wäre, frühe Rückkehr hinzuzufügen; weniger gegliederter Code ist weniger einschüchternd:

- (void) insertOrUpdateItem:(NSDictionary*)item { 
    sqlite3_stmt *exists_stmt; 
    if (sqlite3_prepare_v2(database, RMSQLItemExists, -1, &exists_stmt, NULL) != SQLITE_OK) 
     return; 
    [RMStoreDB bindPrimaryKey:exists_stmt data:item from:1]; 
    if (sqlite3_step(exists_stmt) != SQLITE_ROW) 
     return; 
    int count = sqlite3_column_int(exists_stmt, 1); 
    sqlite3_stmt *update_stmt; 
    if (count) { // Update 
     if (sqlite3_prepare_v2(database, RMSQLItemUpdate, -1, &update_stmt, NULL) == SQLITE_OK) { 
      int index = [RMStoreDB bindItem:update_stmt data:item from:1]; 
      [RMStoreDB bindPrimaryKey:update_stmt data:item from:index]; 
     } 
    } else { // Insert 
     if (sqlite3_prepare_v2(database, RMSQLItemInsert, -1, &update_stmt, NULL) == SQLITE_OK) { 
      int index = [RMStoreDB bindPrimaryKey:update_stmt data:item from:1]; 
      [RMStoreDB bindItem:update_stmt data:item from:index]; 
     }   
    } 
    sqlite3_step(update_stmt); 
    sqlite3_finalize(update_stmt); 
    sqlite3_finalize(exists_stmt); 
} 

Darüber hinaus scheint es, dass der Unterschied ist, dass man für die Einzelteile ist, und der andere ist für Sammlungen. Wenn Sie das Element in eine Auflistung einfügen und dann insertOrUpdateCollection() aufrufen können, sind Sie fertig. Ich kenne das Ziel C nicht; in Java würden wir Collections.singleton() verwenden, die, wie die Dokumentation sagt:

Gibt einen unveränderlichen Satz nur das angegebene Objekt enthält.

+0

Danke für den Hinweis auf vorzeitige Rückgaben. In diesem Fall finalisieren Sie die Anweisungen jedoch nicht. In Bezug auf "Sammlungen" und "Elemente" waren die Namen zufällig, also habe ich die Frage bearbeitet, um sie allgemeiner zu machen. – hpique

2

Zunächst sollten Sie keine Vererbung dafür verwenden. Die Vererbung dient zum Freigeben der Schnittstelle, nicht zum Freigeben der Implementierung. Sie haben zwei Methoden mit sehr ähnlichen Implementierungen, aber unterschiedlichen Schnittstellen.

Zweitens, betrachten Sie eine der wichtigsten Prinzipien von der GoF deklariert: identifizieren, was ändert und kapseln es. Der einfachste Weg, dies in diesem Fall zu tun, wäre, einige sinnvolle Methoden herauszuziehen. Wenn nichts anderes Ihren Code einfacher lesbar macht. Sie sollten so etwas wie dieses schießen (I Pseudo-Code bin mit, da ich nicht wirklich wissen, was Ihr Code tut):

- (void)insertOrDeleteItem:(NSDictionary *)item { 
    if ([self databaseAppearsToBeWorking]]) { 
     row = [self findRowForItem:item]; 
     if (row) { 
      [self updateRow:row withItem:item]; 
     } else { 
      [self insertItem:item]; 
     } 
    } 
} 

Sobald Sie etwas haben, das mehr so ​​aussieht dann die Gemeinsamkeiten werden entweder Stellen Sie sich deutlicher vor, oder Sie werden entdecken, dass die Methoden tatsächlich getrennt bleiben sollten.