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 に答える
すべてのメソッドには共通の反復があります。やりたいことは、呼び出し元が反復されるオブジェクトに対して実行するアクションを指定できるようにしながら、反復メソッドを抽象化することです。基本的に、内部反復子 (反復を実行するため) と戦略 (アクションを実行するため) を組み合わせたいと考えています。
戦略パターンを使用すると、共通点を持つさまざまな戦略を定義して、簡単に別の戦略に置き換えることができます。この場合、すべてのメソッドがコントラクトのリストから情報を収集し、それをリストに追加しますが、収集する情報はさまざまです。
リファクタリングされたメソッド
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
し、そのインスタンスを呼び出すだけで十分です。変更fillListForName
や変更Contract
は必要ありません。
イテレータ メソッドと戦略を実装する方法は他にもあります。ここで行った方法よりも「より純粋」または「優れている」と見なされるものもあります。私がこのアプローチを選んだのは、あなたが持っていたものと同様のコードを保持していたためであり、特定の問題を解決しようとしているためであり、Java で内部イテレーターの一般的なサポートを実装していません (賢い人はすでにそれに取り組んでいます)。「完璧」ではないことに注意してください:)
Guavaの使用を検討してください。コレクションを操作するための強力なユーティリティがあります。たとえば、および とFluentIterable
組み合わせて使用して、共通のロジックを抽出し、それらを使用してコレクションを変換およびフィルタリングすることができます。Predicate
Function
以下では、オブジェクトから好きなものを収集できるようfilterAndTransform
に、 a を渡すことができるメソッドに共通の要素を抽出しました。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...
列挙型を定義し、パラメーターとしてメソッドに渡します。
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());
}
}
}
}
}