2015-06-18 11 views
7

Ich habe ein Objekt, das ein paar Arrays als Felder hat. Es ist Klasse sieht ungefähr wie folgt aus:Generalisierte Methode, um ähnliche Objektattribute zu erhalten

public class Helper { 
    InsuranceInvoices[] insuranceInvoices; 
    InsuranceCollectiveInvoices[] insuranceCollectiveInvoices 
    BankInvoices[] bankInvoices; 
    BankCollectiveInvoices[] bankCollectiveInvoices; 
} 

Alle der Rechnungsarten haben einen gemeinsamen Marker Schnittstelle Rechnungen.
Ich muss alle Rechnungen abrufen, um eine andere Methode für sie aufzurufen.

Helper helperObject = new Helper(); 
// ... 

for (InsuranceInvoices invoice : helperObject.getInsuranceInvoices()) { 
    Integer customerId = invoice.getCustomerId(); 
    // ... 
} 
for (BankInvoices invoice : helperObject.getBankInvoices()) { 
    Integer customerId = invoice.getCustomerId(); 
    // ... 
} 

// repeat with all array fields 

Das Problem ist, dass alle Rechnungen nur die Marker-Schnittstelle gemeinsam haben. Die Methode getCustomerID() ist nicht durch eine gemeinsame Schnittstelle oder Klasse definiert. Dies ist ein Verhalten, das ich aufgrund einer vorgegebenen Spezifikation nicht ändern kann.

Die Code-Wiederholung innerhalb der for-each-Schleife ist etwas, das mich nervt. Ich muss genau dasselbe auf allen Rechnungsobjekten in den vier verschiedenen Arrays tun. Daher bohren vier for-each-loops, die unnötig sind, den Code auf.

Gibt es eine Möglichkeit, dass ich eine allgemeine (private) Methode schreiben kann? Eine Idee war:

private void generalMethod(Invoice[] invoiceArray){ 
    // ... 
} 

Dies würde aber vier Instanceof Kontrollen erfordern, da die Klasse Rechnung nicht die Methode kennt getCusomterId(). Deshalb würde ich nichts gewinnen; Die Methode würde immer noch Wiederholungen enthalten.

Ich bin dankbar für jede mögliche Lösung, um dieses Problem zu verallgemeinern!

+1

so alle Klassen implementieren 'Invoice' und alle implementieren' getCustomerId', aber ' getCustomerId' ist nicht in der 'Rechnung'-Schnittstelle? Sie müssen Reflection verwenden, um über Namen auf die Methode zuzugreifen und sie aufzurufen. An dieser Stelle ist die Schnittstelle "Rechnung" ziemlich nutzlos. – njzk2

+0

Haben 'getInsuranceInvoices', 'getBankInvoices' ... alle vier die gleiche Anzahl an Rechnungen? – Rajesh

+3

Ich habe einen Vorschlag ... wenden Sie ein Cluebat auf die Person an, die die "Rechnung" -Schnittstelle zu einer Markierschnittstelle gemacht hat, anstatt einer, die angibt, welche Methoden Rechnungen gemeinsam haben. – Powerlord

Antwort

7

Mögliche Lösungen das Problem zu verallgemeinern (Reihenfolge vom besten zum schlechtesten):

Mit Wrapper-Klasse

public class InvoiceWrapper { 
    private String customerID; 
    public String getCustomerID() { 
     return customerID; 
    } 
    public InvoiceWrapper(BankInvoices invoice) { 
     this.customerID = invoice.getCustomerID(); 
    } 
    public InvoiceWrapper(InsuranceInvoices invoice) { 
     this.customerID = invoice.getCustomerID(); 
    } 
    // other constructors 
} 

Upd Wenn ich das richtig verstanden, Sie müssen etwas tun mit IDs in allen Arrays.Um InvoiceWrapper zu verwenden, müssen Sie auch einen Iterator in der Helper-Klasse implementieren, der durch Arrays läuft und einen Wrapper für jeden Eintrag zurückgibt. Sie haben also Code, der trotzdem mit 4 Arrays funktioniert.

Mit Instanz von Abgüssen

public class CustomerIdHelper { 
    public static String getID(Invoice invoice) { 
     if (invoice instanceof InsuranceInvoices) { 
      return ((InsuranceInvoices) invoices).getCustomerID(); 
     } else if ... 
    } 
} 

Aufruf von Methoden namentlich über Reflection

public class CustomerIdHelper { 
    public static String getID(Invoice invoice) { 
     Method method = invoice.getClass().getDeclaredMethod("getCustomerId"); 
     return (String) method.invoke(invoice); 
    } 
} 
+0

Vielen Dank für Ihre Vorschläge! Der erste Ansatz wurde von mir nicht berücksichtigt. Aber könnten Sie mir erklären, warum Sie den Reflection-Ansatz als den schlechtesten der drei gewählt haben? – DeMo

+1

@DeMo Das liegt daran, wenn jemand die Methode entfernt, werden Sie es nicht bemerken. In den ersten beiden Varianten sehen Sie einen Kompilierungsfehler. – AdamSkywalker

2

Es ist nicht schön, aber man könnte Reflektion verwenden die getCustomerIdMethod nachzuschlagen und invoke() es dann, vgl Class.getDeclaredMethod().

private void generalMethod(Invoice[] invoiceArray){ 
    try { 
    for (Invoice invoice : invoiceArray) { 
     Method getCustomerId = invoice.getClass().getDeclaredMethod("getCustomerId"); 
     getCustomerId.invoke(invoice); 
    } 
    } catch (Exception e) { 
    // ... 
    } 
} 

Sie beachten Sie, dass diese nicht getestet ist.

2

Wenn Sie die zu behandelnden Klassen nicht ändern können, fügen Sie ihnen eine benutzerdefinierte Schnittstelle hinzu. Das Beste, was Sie tun können, ist sie mit einer benutzerdefinierten Klasse zu umhüllen, die die gewünschten Eigenschaften hat.

Auf diese Weise haben Sie eine Klasse mit "nicht so nettem" Code, der die Klassen, die Sie nicht berühren können, in nette Klassen umwandelt, die einem richtigen und nützlichen Design entsprechen.

Zum Beispiel könnten Sie eine Klasse haben WrappedInsuranceInvoice, die WrappedInsurace erweitert und enthält ein Mitglied Feld InsuranceInvoice. Wenn Sie die ursprüngliche Klasse nicht beibehalten müssen, würden Sie durch das Kopieren der Daten noch besser dran sein. Auf diese Weise könnten Sie beispielsweise die Arrays verlieren und stattdessen Listen verwenden.