Skip to content

Instantly share code, notes, and snippets.

@grignaak
Created November 22, 2011 05:40
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save grignaak/1384984 to your computer and use it in GitHub Desktop.
Save grignaak/1384984 to your computer and use it in GitHub Desktop.
An basic base implementation of the RoleObjectPattern
package net.deardeuff.roleobject;
import com.google.common.base.Preconditions;
/**
* A Key based on a Class
*/
public final class ClassKey<T> implements View.Key<T> {
private final Class<T> delegate;
public static <T> ClassKey<T> of(Class<T> delegate) {
return new ClassKey<T>(delegate);
}
private ClassKey(Class<T> delegate) {
Preconditions.checkNotNull(delegate);
this.delegate = delegate;
}
public boolean equals(Object object) {
return (object instanceof ClassKey<?>) && equals((ClassKey<?>) object);
}
public String toString() {
return "ClassKey(" + delegate.getName() + ")";
}
public boolean equals(ClassKey<?> other) {
return delegate.equals(other.delegate);
}
public int hashCode() {
return delegate.hashCode();
}
}
package net.deardeuff.roleobject;
import com.google.common.base.Supplier;
/**
* Conversions from Class<T> to Key<T>
*/
public abstract class ClassKeyOperations implements View {
public final <T> boolean hasView(Class<T> key) {
return hasView(ClassKey.of(key));
}
public final <T> T getView(Class<T> key) {
return getView(ClassKey.of(key));
}
public final <T> T putView(Class<T> key, T value) {
return putView(ClassKey.of(key), value);
}
public final <T> T putView(Class<T> key, Supplier<? extends T> value) {
return putView(ClassKey.of(key), value);
}
public final <T> void putViewIfAbsent(Class<T> key, T value) {
putViewIfAbsent(ClassKey.of(key), value);
}
public final <T> void putViewIfAbsent(Class<T> key, Supplier<? extends T> value) {
putViewIfAbsent(ClassKey.of(key), value);
}
}
package net.deardeuff.roleobject;
import com.google.common.base.Supplier;
/**
* Supporting implementation for views that are not the core view.
*
* <p>Suggested use:<br/>
* Extend this class and implement your own view interface (see notes in {@link View}); it will be the abstract base class for your views.
* As such, you only need to implement the methods defined in your custom view class, forwarding them *all* to the coreView()
*
* <p>Example:
* <pre>
* public class AbstractUserView extends ForwardingView implements User {
* public AbstractUserView(User user) {
* super(user);
* }
* ....
* public User coreView() {
* return (User) coreView();
* }
*
* public final long id() {
* return coreView().id();
* }
*
* public final String name() {
* return coreView.name();
* }
*
* public final boolean equals(User other) {
* return coreView().equals(other);
* }
* }
* </pre>
*
*
*/
public class ForwardingView extends ClassKeyOperations implements View {
private final View core;
public ForwardingView(View core) {
this.core = core.coreView();
}
/**
* Default implementation: just a View
*/
public View coreView() {
return core;
}
public final <T> boolean hasView(Key<T> key) {
return core.hasView(key);
}
public final <T> T getView(Key<T> key) {
return core.getView(key);
}
public final <T> T putView(Key<T> key, T value) {
return core.putView(key, value);
}
public final <T> T putView(Key<T> key, Supplier<? extends T> value) {
return core.putView(key, value);
}
public final <T> void putViewIfAbsent(Key<T> key, T value) {
core.putViewIfAbsent(key, value);
}
public final <T> void putViewIfAbsent(Key<T> key, Supplier<? extends T> value) {
core.putViewIfAbsent(key, value);
}
public final boolean equals(Object obj) {
return core.equals(obj);
}
public final boolean equals(View other) {
return core.equals(other);
}
public final int hashCode() {
return core.hashCode();
}
}
package net.deardeuff.roleobject;
import com.google.common.base.Supplier;
/**
* A view of an object.
*
* <p>Most implementations use a class as a key, so methods accepting classes as keys are explicitly added to the interface.
*
* <p>Allows <em>suppliers</em> of views to support lazily-loaded views.
*
*
* <p>Suggested use:<br/>
* Extend this interface, adding common methods for your object; also overriding coreView() to return your interface:
*
* <p>Example:
* <pre>
* public interface User extends View {
* long id();
* String name();
* ...
* User coreView();
* boolean equals(User other);
* }
* </pre>
*
*
* <p>Implementation Note: to make things more flexible, this implemntation does _not_ require that
* only views are stored inside.
*/
public interface View {
interface Key<T> {}
<T> boolean hasView(Class<T> key);
<T> boolean hasView(Key<T> key);
<T> T getView(Class<T> key);
<T> T getView(Key<T> key);
<T> T putView(Class<T> key, T value);
<T> T putView(Key<T> key, T value);
<T> T putView(Class<T> key, Supplier<? extends T> value);
<T> T putView(Key<T> key, Supplier<? extends T> value);
<T> void putViewIfAbsent(Class<T> key, T value);
<T> void putViewIfAbsent(Key<T> key, T value);
<T> void putViewIfAbsent(Class<T> key, Supplier<? extends T> value);
<T> void putViewIfAbsent(Key<T> key, Supplier<? extends T> value);
View coreView();
boolean equals(View other);
}
package net.deardeuff.roleobject;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import com.google.common.base.Preconditions;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
/**
* <p>Supporting implementation for core Views.
*
* <p>Suggested use:<br/>
* Extend this class and implement your own view interface (see notes in {@link View}). Override equals to use logical equality
*
* <p>Example:
* <pre>
* public class UserCore extends ViewCore implements User {
* public UserCore(long id, String name) {
* this.id = id;
* this.name = name;
* }
* ....
* public boolean equals(View other) {
* return (other instanceof User) && equals((user) other);
* }
* public boolean equals(User other) {
* return id == other.id();
* }
* public long hashCode() {
* return id;
* }
* }
* </pre>
*
*/
public class ViewCore extends ClassKeyOperations implements View {
public class UnknownKeyException extends RuntimeException {
private static final long serialVersionUID = 1L;
public UnknownKeyException(String message) {
super(message);
}
}
private final ConcurrentMap<Key<?>, Supplier<?>> views = new ConcurrentHashMap<View.Key<?>, Supplier<?>>();
public final <T> boolean hasView(Key<T> key) {
return views.containsKey(key);
}
public final <T> T getView(Key<T> key) {
@SuppressWarnings("unchecked")
Supplier<T> view = (Supplier<T>) views.get(key);
if (view == null)
throw new UnknownKeyException("Unknown key: " + key);
synchronized (view) {
return view.get(); // may be a long loading operation, thus the lock
}
}
public final <T> T putView(Key<T> key, T value) {
return putView(key, Suppliers.ofInstance(value));
}
public final <T> T putView(Key<T> key, Supplier<? extends T> value) {
Preconditions.checkNotNull(key);
Preconditions.checkNotNull(value);
views.put(key, value);
return value.get();
}
public final <T> void putViewIfAbsent(Key<T> key, T value) {
views.putIfAbsent(key, Suppliers.ofInstance(value));
}
public final <T> void putViewIfAbsent(Key<T> key, Supplier<? extends T> value) {
Preconditions.checkNotNull(key);
Preconditions.checkNotNull(value);
views.putIfAbsent(key, value);
}
public View coreView() {
return this;
}
public final boolean equals(Object other) {
return (other instanceof View) && equals((View) other);
}
/**
* Default implementation: equality based on same coreView()
*/
public boolean equals(View other) {
return coreView() == other.coreView();
}
}
@grignaak
Copy link
Author

Start with the documentation of View, then ViewCore, and ForwardingView. Examples are included in the documentation

This particular implementation uses Google Guava's Supplier. This is not necessary, but supports lazy-loading views. This works especially well if you have your view factory inject the suppliers after creation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment