質問

    private void fillInvoiceListForName(String name, ArrayList<Invoice> mInvoices) {
    for (SupplierAccount account : listcontracts) {
        if (account.getSupplier() != null)
            if (account.getSupplier().equals(name)) {
                ArrayList<Contract> mContracts = account.getContracts();
                for (Contract contract : mContracts) {
                    mInvoices.addAll(contract.getInvoices());
                }
            }
    }
}

private void fillIncomeListForName(String name, ArrayList<Income> mIncomes) {
    for (SupplierAccount account : listcontracts) {
        if (account.getSupplier() != null)
            if (account.getSupplier().equals(name)) {
                ArrayList<Contract> mContracts = account.getContracts();
                for (Contract contract : mContracts) {
                    mIncomes.addAll(contract.getIncomes());
                }
            }
    }
}


private void fillDocumentListForName(String name, ArrayList<Document> mDocuments) {
    for (SupplierAccount account : listcontracts) {
        if (account.getSupplier() != null)
            if (account.getSupplier().equals(name)) {
                ArrayList<Contract> mContracts = account.getContracts();
                for (Contract contract : mContracts) {
                    mDocuments.addAll(contract.getDocuments());
                }
            }
    }
}
役に立ちましたか?

解決

All of your methods have the iteration in common. What you want to do is abstract the iteration method while allowing the caller to specify an action to perform on the objects being iterated over. Essentially you want to combine an internal iterator (to perform the iteration) with a strategy (to perform the action).

With the Strategy pattern you can define different strategies which all have something in common, and then easily substitute one for the other. In this case, all of your methods collect information from a list of contracts and adds it to a list, though which information they collect varies.

Refactored method

private <E> void fillListForName(String name, List<? super E> listToFill, FillStrategy<E> fillStrategy) {
    if (name == null) {
        throw new IllegalArgumentException("name cannot be null");
    }
    for (SupplierAccount account : listContracts) {
        if (name.equals(account.getSupplier())) {
            List<Contract> contracts = account.getContracts();
            for (Contract contract : contracts) {
                fillStrategy.execute(listToFill, contract);
            }
        }
    }
}

The FillStrategy interface and an example implementation

interface FillStrategy<T> {
    public void execute(List<? super T> listToFill, Contract contract);
}

class InvoiceFillStrategy implements FillStrategy<Invoice> {
    @Override
    public void execute(List<? super Invoice> listToFill, Contract contract) {
        listToFill.addAll(contract.getInvoices());
    }   
}

Calling the refactored method

List<Invoice> invoices = new ArrayList<Invoice>();
InvoiceFillStrategy invoiceStrategy = new InvoiceFillStrategy();

System.out.println("Invoices for myCorp:");
fillListForName("myCorp", invoices, invoiceStrategy);
for (Invoice i : invoices) {
    System.out.println(i);
}

System.out.println("\nInvoices for otherCorp:");
invoices.clear();
fillListForName("otherCorp", invoices, invoiceStrategy);
for (Invoice i : invoices) {
    System.out.println(i);
}

Why?

The benefit of this approach is that you can create additional "strategies" without necessarily modifying any of the other classes involved. For example, you could create one that collects all invoices for sums over a given threshold:

class ExpensiveInvoiceFillStrategy implements FillStrategy<Invoice> {

    private int minimumAmount;

    public ExpensiveInvoiceFillStrategy(int minimumAmount) {
        this.minimumAmount = minimumAmount;
    }

    @Override
    public void execute(List<? super Invoice> listToFill, Contract contract) {
        for (Invoice invoice : contract.getInvoices()) {
            if (invoice.getAmount() >= minimumAmount) {
                listToFill.add(invoice);
            }
        }
    }
}

Just implementing this class and then calling fillListForName with an instance of it is enough - no change to fillListForName or Contract is necessary!

There are other ways to implement the iterator method and the strategies - some would even be considered "more pure" or "better" that what I've done here. I chose this approach because it kept the code similar to what you had, and because we're trying to solve a specific problem, not implement general support for internal iterators in Java (smarter guys are already working on that). Just be aware that it's not "perfect" :)

他のヒント

Consider using Guava, it has powerful utilities for manipulating collections. For example, you could use FluentIterable in conjunction with Predicates and Functions, to extract common logic, and use them to transform and filter your collections.

Below I have extracted common elements into the filterAndTransform method which allows you to pass in a Function so you can collect what you like from the Contract object:

private <T> List<T> filterAndTransform(String name, Function<Contract, Iterable<T>> function) {
    return FluentIterable.from(listcontracts)
        .filter(new HasSupplierPredicate())
        .filter(new SupplierNameMatchesPredicate(name))
        .transformAndConcat(new Function<SupplierAccount, Iterable<Contract>>() {
            @Override public Iterable<Contract> apply(final SupplierAccount account) {
                return account.getContracts();
            }
        })
        .transformAndConcat(function)
        .toList();
}

private void fillInvoiceListForName(String name, ArrayList<Invoice> mInvoices) {
    final Iterable<Invoice> invoices = filter(name, 
        new Function<Contract, Iterable<Invoice>>() {
            @Override public Iterable<Invoice> apply(final Contract contract) {
                return contract.getInvoices();
            }
    });
    mInvoices.addAll(invoices);
}

private void fillIncomeListForName(String name, ArrayList<Income> mIncomes) {
    final Iterable<Income> incomes = filter(name, 
        new Function<Contract, Iterable<Income>>() {
            @Override public Iterable<Income> apply(final Contract contract) {
                return contract.getIncomes();
            }
    });
    mIncomes.addAll(incomes);
}

// etc...

Define an enum, and pass it as a parameter to a method:

public enum ContractValue {
    INVOICE, INCOME, DOCUMENT;
}

private void fillIncomeListForName(String name, ArrayList<Income> mIncomes, ContractValue contractValue) {
for (SupplierAccount account : listcontracts) {
    if (account.getSupplier() != null)
        if (account.getSupplier().equals(name)) {
            ArrayList<Contract> mContracts = account.getContracts();
            for (Contract contract : mContracts) {
                if (contractValue == ContractValue.INCOME) {
                     mIncomes.addAll(contract.getIncomes()); 
                } else if (contractValue == ContractValue.INVOICE) {
                     mInvoices.addAll(contract.getInvoices());
                } else if (contractValue == ContractValue.DOCUMENT) {
                     mDocuments.addAll(contract.getDocuments());
                }
            }
        }
    }
}
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top