有谁知道这个代码是否可以重构?如果是这样怎么样?

时间:2013-07-02 10:33:35

标签: java refactoring code-duplication

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

3 个答案:

答案 0 :(得分:4)

您的所有方法都有共同的迭代。您要做的是抽象迭代方法,同时允许调用者指定要对正在迭代的对象执行的操作。基本上,您希望将内部迭代器(执行迭代)与策略(执行操作)结合起来。

使用策略模式,您可以定义所有具有共同点的不同策略,然后轻松地将其替换为另一个策略。在这种情况下,您的所有方法都会从合同列表中收集信息并将其添加到列表中,但它们收集的信息会有所不同。

重构方法

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

FillStrategy接口和示例实现

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

调用重构方法

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

为什么?

这种方法的好处是您可以创建其他“策略”,而无需修改任何其他涉及的类。例如,您可以创建一个收集超过给定阈值的所有发票的发票:

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

只是实现此类,然后使用它的实例调用fillListForName就足够了 - 不需要更改fillListForNameContract

还有其他方法可以实现迭代器方法和策略 - 有些甚至可以被认为是“更纯粹”或“更好”,就像我在这里所做的那样。我之所以选择这种方法,是因为它保持代码与您的代码类似,并且因为我们正在尝试解决特定问题,而不是在Java中实现对内部迭代器的一般支持(更聪明的人已经在研究它)。请注意,它并非“完美”:)

答案 1 :(得分:1)

考虑使用Guava,它具有强大的实用程序来操作集合。例如,您可以将FluentIterablePredicateFunction结合使用,以提取常用逻辑,并使用它们来转换和过滤您的集合。

下面我已经将常见元素提取到filterAndTransform方法中,该方法允许您传入Function,以便您可以从Contract对象中收集您喜欢的内容:

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...

答案 2 :(得分:0)

定义枚举,并将其作为参数传递给方法:

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