Skip to content

Instantly share code, notes, and snippets.

@markmelville
Created May 18, 2017 19:29
Show Gist options
  • Save markmelville/4adacdb8ca1e5fa6ce4cb8fb6bec72e5 to your computer and use it in GitHub Desktop.
Save markmelville/4adacdb8ca1e5fa6ce4cb8fb6bec72e5 to your computer and use it in GitHub Desktop.
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
/**
* Examples of some best practices
*/
// singleton
public class SolidProcessor<T> // implements MultiProcessor<T>
{
private final Map<String, Processor<T>> processors = new HashMap<>(); // know the implementation of the map this class will use.
private final String defaultKey;
private final SuccessTracker tracker = new SuccessTracker();
// constructor injection
public SolidProcessor(final Iterable<Map.Entry<String, Processor<T>>> processorEntries) // dependency inversion, interface segregation, open/closed
{
// throw all exceptions in constructor
if (processorEntries == null)
{
throw new IllegalArgumentException("Must provide processors.");
}
String aKey = null;
for (Map.Entry<String, Processor<T>> processor : processorEntries)
{
String key = processor.getKey().toLowerCase();
Processor<T> instrumentedProcessor = instrument(key, processor.getValue()); // decorator pattern
Processor<T> existingValue = processors.put(key, instrumentedProcessor);
aKey = key;
if (existingValue != null)
{
throw new IllegalArgumentException("Found duplicate processor name: " + key);
}
}
defaultKey = aKey;
}
//@Override
public void sendDataToAllProcessors(T data)
{
for (Map.Entry<String, Processor<T>> entry : processors.entrySet()) // strategy pattern?, open/closed principle
{
entry.getValue().process(data);
}
}
//@Override
public boolean sendDataToBestProcessor(T data)
{
String best = tracker.getMostSuccessfulKey(defaultKey);
Processor<T> bestProcessor = processors.get(best);
return bestProcessor != null && bestProcessor.process(data);
}
private Processor<T> instrument(final String name, final Processor<T> processor)
{
return data -> tracker.mark(name, processor.process(data)); // lambdas, partial application of functions
}
// define your own interface, use generics
interface Processor<T>
{
boolean process(T data);
}
// internal class encapsulates implementation details
private class SuccessTracker
{
Map<String, List<Boolean>> results = new ConcurrentHashMap<>();
boolean mark(String name, boolean result)
{
List<Boolean> resultList = results.get(name);
if (resultList == null)
{
resultList = new ArrayList<>();
}
resultList.add(result);
return result;
}
String getMostSuccessfulKey(String defaultKey)
{
if (results.size() == 0) {
return null;
}
Map<String, Long> counts = results.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().stream().filter(b -> b).count()));
Map.Entry<String, Long> best = null;
for (Map.Entry<String, Long> e : counts.entrySet()) {
if (e == null || e.getValue() > best.getValue()) {
best = e;
}
}
return best != null ? best.getKey() : defaultKey;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment