Skip to content

Instantly share code, notes, and snippets.

@mmichaelis
Created November 22, 2011 10:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mmichaelis/1385405 to your computer and use it in GitHub Desktop.
Save mmichaelis/1385405 to your computer and use it in GitHub Desktop.
BDD Design Pattern: Reference Tracker
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
/**
* <p>
* Tracks references between BDD steps. Typically you need some global variables or something alike to remember
* states between steps you perform. Extending classes will support you in doing so as you can
* {@link #track(Object, String, Object) track} objects in one step and {@link #get(Object, String, Class) get}
* them in the next step.
* </p>
* <p>
* Mind that your project should decide one common tracker to use as otherwise you might have problems
* asking the correct tracker for the object you want to retrieve.
* </p>
*
* @param <K> the type of the key used for tracking
* @param <V> type of the tracked values
*/
public abstract class AbstractReferenceTracker<K, V> implements ReferenceTracker<K,V> {
/**
* My logger.
*/
private static final Logger LOG = LoggerFactory.getLogger(AbstractReferenceTracker.class);
/**
* Initial size of the name-value map (the inner map).
*/
private static final int INITIAL_NAME_VALUE_MAP_CAPACITY = 4;
/**
* Map which contains references where the outer map maps keys (most likely types) to name-value pairs in
* the inner map.
*/
private final Map<K, Map<String, V>> references = new HashMap<K, Map<String, V>>(16);
/**
* {@inheritDoc}
*/
@Override
public void track(final K type, final String name, final V obj) {
final Map<String, V> refToObject;
if (!references.containsKey(type)) {
refToObject = new HashMap<String, V>(INITIAL_NAME_VALUE_MAP_CAPACITY);
references.put(type, refToObject);
} else {
refToObject = references.get(type);
}
refToObject.put(name, obj);
}
/**
* {@inheritDoc}
*/
@Override
public <T> T get(final K type, final String name, final Class<T> targetType) {
if (references.containsKey(type)) {
final V o = references.get(type).get(name);
return targetType.cast(o);
}
return null;
}
/**
* {@inheritDoc}
*/
@Override
public final void cleanup() {
if (LOG.isDebugEnabled()) {
final Collection<Map<String, V>> values = references.values();
int sum = 0;
for (final Map<String, V> value : values) {
sum += value.size();
}
LOG.debug("Clearing {} tracked references.", sum);
}
references.clear();
}
}
/**
* <p>
* Tracks references between BDD steps. Typically you need some global variables or something alike to remember
* states between steps you perform. Extending classes will support you in doing so as you can
* {@link #track(Object, String, Object) track} objects in one step and {@link #get(Object, String, Class) get}
* them in the next step.
* </p>
* <p>
* Mind that your project should decide one common tracker to use as otherwise you might have problems
* asking the correct tracker for the object you want to retrieve.
* </p>
*
* @param <K> the type of the key used for tracking
* @param <V> type of the tracked values
*/
public interface ReferenceTracker<K, V> {
/**
* Tracks an object of a given type and name. If you track an object with the same parameters again it will
* overwrite the existing tracked object without further notice.
* @param type type of the object to track. Think of it like a namespace.
* @param name the name to use for the reference
* @param obj the object to track
*/
void track(K type, String name, V obj);
/**
* Retrieve a tracked object and cast it to the given type.
* @param type the type/namespace of the object to retrieve
* @param name the reference name
* @param targetType the class of the object to retrieve
* @param <T> type of the tracked object
* @return <code>null</code>, if there is no such tracked element, otherwise the tracked element
*/
<T> T get(K type, String name, Class<T> targetType);
/**
* Cleanup tracked references. Should be called either on <code>tearDown</code> of JUnit tests or after each scenario
* for example with the <code>@AfterScenario</code>-annotation.
*/
void cleanup();
}
@Inject
private TypedReferenceTracker tracker;
...
Content document = ...
tracker.track(Content.class, "A", document);
...
Content content = tracker.get(Content.class, "A");
content.doSomething();
import javax.annotation.ManagedBean;
/**
* <p>
* Tracks objects grouped by their classes.
* </p>
* <p><strong>Example:</strong></p>
* <pre>
* @Inject
* private TypedReferenceTracker tracker;
* ...
* Content document = ...
* tracker.track(Content.class, "A", document);
* ...
* Content content = tracker.get(Content.class, "A");
* content.doSomething();
* </pre>
*/
@SuppressWarnings("JavaDoc")
@ManagedBean
public class TypedReferenceTracker extends AbstractReferenceTracker<Class<?>, Object> {
/**
* Retrieve tracked object identified by its class and its reference name.
* @param type the type of the object to retrieve
* @param name the name of the reference
* @param <T> type of the object
* @return null if there is no such tracked object, otherwise the tracked object
*/
public <T> T get(final Class<T> type, final String name) {
return get(type, name, type);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment