Search code examples
javageneralization

Generalized method to get similar object attributes


I have an object which has a few arrays as fields. It's class roughly looks like this:

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

All of the invoice types have a mutual marker interface Invoices.
I need to get all of the invoices to invoke another method on them.

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

The problem is that all invoices only have the marker interface in common. The method getCustomerID() is not defined by a mutual interface or class. This is a behaviour I cannot change due to a given specification.

The code repetition inside the for-each-loop is something that bugs me. I have to do the exact same thing on all invoice objects in the four different arrays. Hence four for-each-loops that unecessary bloat the code.

Is there a way that I can write a general (private) method? One idea was:

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

But this would require four instanceof checks because the class Invoice doesn't know the method getCusomterId(). Therefore I would gain nothing; the method would still contain repetitions.

I'm thankful for every possible solution to generalize this problem!


Solution

  • Possible solutions to generalize the problem (ordered from best to worst):

    Using wrapper class

    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 If I understood correctly, you need to do something with IDs in all arrays. To use InvoiceWrapper, you also need to implement iterator in Helper class, that will walk through arrays and return a wrapper for each entry. So, you will have code that works with 4 arrays anyway.

    Using instance of casts

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

    Calling methods by name via Reflection

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