I have been working on some methods which perform parallel operations on an LDAP server. My problem is that the method code is basically duplicate except for one line in all methods and I have no idea on how to refactor the methods such that the code would remain relatively readable. There are multiple duplicate methods; I will show two as an example.
Example of the methods:
public static void addEntriesInParallel(LdapConnParams ldapConnParams, List<LDAPEntry> entries) {
ExecutorService executor = Executors.newCachedThreadPool();
CountDownLatch latch = new CountDownLatch(batchCount(entries.size()));
LdapConnParamsBuilder connParamsBuilder = new LdapConnParamsBuilder(ldapConnParams);
for (List<LDAPEntry> entryBatch : Lists.partition(entries, BATCH_SIZE)) {
LdapConnParams threadConnParams = connParamsBuilder.build();
executor.submit(() -> {
try {
LdapTransactions.addEntries(threadConnParams, entryBatch);
latch.countDown();
} catch (LDAPException ex) {
// Exception handling
}
});
}
try {
latch.await();
} catch (InterruptedException ex) {
// Exception handling
}
}
public static void deleteAttributeInParallel(LdapConnParams ldapConnParams, String attribute, List<LDAPEntry> entries) {
ExecutorService executor = Executors.newCachedThreadPool();
CountDownLatch latch = new CountDownLatch(batchCount(entries.size()));
LdapConnParamsBuilder connParamsBuilder = new LdapConnParamsBuilder(ldapConnParams);
for (List<LDAPEntry> entryBatch : Lists.partition(entries, BATCH_SIZE)) {
LdapConnParams threadConnParams = connParamsBuilder.build();
executor.submit(() -> {
try {
LdapTransactions.deleteAttribute(threadConnParams, attribute, entryBatch);
latch.countDown();
} catch (LDAPException ex) {
// Exception handling
}
});
}
try {
latch.await();
} catch (InterruptedException ex) {
// Exception handling
}
}
The only point of difference is the line (first method):
LdapTransactions.addEntries(threadConnParams, entryBatch);
Compared to (second method):
LdapTransactions.deleteAttribute(threadConnParams, attribute, entryBatch);
Now, the problem also is that the methods called within don't match in signatures. Is there a way to rewrite the code in such a manner that I don't have to keep the same method multiple times with a single line difference?
Thanks for any tips!
CodePudding user response:
You already did most of the work by figuring out what the methods have in common, and what differs.
Now, you only need to extract the common parts into a more general method for executing LDAP jobs, while trying to keep enough flexibility for differences to be supported.
Assuming all functions that you call inside the processing loop take a threadConnParams
and entryBatch
, and the rest is function-dependent, you could start with this interface:
public static void performInParallel(
LdapConnParams ldapConnParams,
List<LDAPEntry> entries,
LDapAction action)
LDapAction
is a functional interface that is meant to be implemented by a lambda (or a concrete class if that's useful) and looks like this:
@FunctionalInterface
interface LDapAction {
void apply(LdapConnParams threadConnParams,
List<LDAPEntry> entryBatch) throws LDAPException;
}
The implementation changes slightly from what you have now, with the main call becoming:
executor.submit(() -> {
try {
action.accept(threadConnParams, entryBatch);
latch.countDown();
} catch (LDAPException ex) {
// Exception handling
}
});
It could look something like this from the API consumer point-of-view:
addEntries:
performInParallel(ldapConnParams, entries, (threadConnParams, entryBatch) -> LdapTransactions.addEntries(threadConnParams, entryBatch));
deleteAttribute:
performInParallel(ldapConnParams, entries, (threadConnParams, entryBatch) -> LdapTransactions.deleteAttribute(threadConnParams, attribute, entryBatch));
If the performInParallel
function requires more input, you can add that to the action interface, but from the code you've provided so far that doesn't seem to be necessary.