Created
May 18, 2017 19:29
-
-
Save markmelville/4adacdb8ca1e5fa6ce4cb8fb6bec72e5 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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