The java.util.Observer and java.util.Observable are ugly. They require the sorts of casts that make type-safety fans uncomfortable, and you can't define a class to be an Observer of multiple things without ugly casts. In fact, in "How do I know the generic object that the Observer class sends in Java?", an answerer says that only one type of data should be used in each observer / observable.
I'm trying to make a generic version of the observer pattern in Java to get round both these problems. It's not unlike the one in the previously mentioned post, but that question was not obviously resolved (the last comment is an unanswered question from the OP).
Observer.java
package util;
public interface Observer<ObservedType> {
public void update(Observable<ObservedType> object, ObservedType data);
}
Observable.java
package util;
import java.util.LinkedList;
import java.util.List;
public class Observable<ObservedType> {
private List<Observer<ObservedType>> _observers =
new LinkedList<Observer<ObservedType>>();
public void addObserver(Observer<ObservedType> obs) {
if (obs == null) {
throw new IllegalArgumentException("Tried
to add a null observer");
}
if (_observers.contains(obs)) {
return;
}
_observers.add(obs);
}
public void notifyObservers(ObservedType data) {
for (Observer<ObservedType> obs : _observers) {
obs.update(this, data);
}
}
}
Hopefully this will be useful to someone.
I prefer using an annotation so a listener can listen to different types of events.
public class BrokerTestMain {
public static void main(String... args) {
Broker broker = new Broker();
broker.add(new Component());
broker.publish("Hello");
broker.publish(new Date());
broker.publish(3.1415);
}
}
class Component {
#Subscription
public void onString(String s) {
System.out.println("String - " + s);
}
#Subscription
public void onDate(Date d) {
System.out.println("Date - " + d);
}
#Subscription
public void onDouble(Double d) {
System.out.println("Double - " + d);
}
}
prints
String - Hello
Date - Tue Nov 13 15:01:09 GMT 2012
Double - 3.1415
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface Subscription {
}
public class Broker {
private final Map<Class, List<SubscriberInfo>> map = new LinkedHashMap<Class, List<SubscriberInfo>>();
public void add(Object o) {
for (Method method : o.getClass().getMethods()) {
Class<?>[] parameterTypes = method.getParameterTypes();
if (method.getAnnotation(Subscription.class) == null || parameterTypes.length != 1) continue;
Class subscribeTo = parameterTypes[0];
List<SubscriberInfo> subscriberInfos = map.get(subscribeTo);
if (subscriberInfos == null)
map.put(subscribeTo, subscriberInfos = new ArrayList<SubscriberInfo>());
subscriberInfos.add(new SubscriberInfo(method, o));
}
}
public void remove(Object o) {
for (List<SubscriberInfo> subscriberInfos : map.values()) {
for (int i = subscriberInfos.size() - 1; i >= 0; i--)
if (subscriberInfos.get(i).object == o)
subscriberInfos.remove(i);
}
}
public int publish(Object o) {
List<SubscriberInfo> subscriberInfos = map.get(o.getClass());
if (subscriberInfos == null) return 0;
int count = 0;
for (SubscriberInfo subscriberInfo : subscriberInfos) {
subscriberInfo.invoke(o);
count++;
}
return count;
}
static class SubscriberInfo {
final Method method;
final Object object;
SubscriberInfo(Method method, Object object) {
this.method = method;
this.object = object;
}
void invoke(Object o) {
try {
method.invoke(object, o);
} catch (Exception e) {
throw new AssertionError(e);
}
}
}
}
A modern update: ReactiveX is a very nice API for asynchronous programming based on the Observer pattern, and it's fully generic. If you're using Observer/Observable to "stream" data or events from one place in your code to another, you should definitely look into it.
It's based on functional programming, so it looks very sleek with Java 8's lambda syntax:
Observable.from(Arrays.asList(1, 2, 3, 4, 5))
.reduce((x, y) -> x + y)
.map((v) -> "DecoratedValue: " + v)
.subscribe(System.out::println);
I once wrote a generic implementation of the observer pattern for Java using dynamic proxies. Here's a sample of how it could be used:
Gru gru = new Gru();
Minion fred = new Minion();
fred.addObserver(gru);
fred.moo();
public interface IMinionListener
{
public void laughing(Minion minion);
}
public class Minion extends AbstractObservable<IMinionListener>
{
public void moo()
{
getEventDispatcher().laughing(this);
}
}
public class Gru implements IMinionListener
{
public void punch(Minion minion) { ... }
public void laughing(Minion minion)
{
punch(minion);
}
}
The full source code of AbstractObservable is available on pastebin. Way back I blogged about how it works in a bit more detail, also referring to related projects.
Jaana wrote an interesting summary of different approaches, also contrasting the dynamic proxy approach with others. Much thanks of course goes to Allain Lalonde from which I got the original idea. I still haven't checked out PerfectJPattern, but it might just contain a stable implementation of the observer pattern; at least it seems like a mature library.
Try to use class EventBus of Guava.
You can declare a Observer like this:
public class EventObserver {
#Subscribe
public void onMessage(Message message) {
...
}
}
New a EventBus like this:
EventBus eventBus = new EventBus();
And register Observer like this:
eventBus.register(new EventObserver());
Last notify Observer like:
eventBus.post(message);
I found a similar request but it was rather on codereview.
I think it's worth mentioning it here.
import java.util.ArrayList;
import java.util.Collection;
import java.util.function.Supplier;
/**
* like java.util.Observable, But uses generics to avoid need for a cast.
*
* For any un-documented variable, parameter or method, see java.util.Observable
*/
public class Observable<T> {
public interface Observer<U> {
public void update(Observable<? extends U> observer, U arg);
}
private boolean changed = false;
private final Collection<Observer<? super T>> observers;
public Observable() {
this(ArrayList::new);
}
public Observable(Supplier<Collection<Observer<? super T>>> supplier) {
observers = supplier.get();
}
public void addObserver(final Observer<? super T> observer) {
synchronized (observers) {
if (!observers.contains(observer)) {
observers.add(observer);
}
}
}
public void removeObserver(final Observer<? super T> observer) {
synchronized (observers) {
observers.remove(observer);
}
}
public void clearObservers() {
synchronized (observers) {
this.observers.clear();
}
}
public void setChanged() {
synchronized (observers) {
this.changed = true;
}
}
public void clearChanged() {
synchronized (observers) {
this.changed = false;
}
}
public boolean hasChanged() {
synchronized (observers) {
return this.changed;
}
}
public int countObservers() {
synchronized (observers) {
return observers.size();
}
}
public void notifyObservers() {
notifyObservers(null);
}
public void notifyObservers(final T value) {
ArrayList<Observer<? super T>> toNotify = null;
synchronized(observers) {
if (!changed) {
return;
}
toNotify = new ArrayList<>(observers);
changed = false;
}
for (Observer<? super T> observer : toNotify) {
observer.update(this, value);
}
}
}
Original answer from codereview stackexchange
Related
I have a problem with method override checks. I can detect simple override relations, but if the parent class has generics and the abstract method uses type parameters (return value/args), my code breaks down because the method description is not equal to the checked method.
Example:
public interface ISetting<T> {
public T method();
}
public class Setting implements ISetting<Integer> {
public Integer method() {
//Something
}
}
In ISetting, the method description is ()Ljava/lang/Object;
and in Setting, the method description is ()Ljava/lang/Integer;
How I can check this Override ?
On my head no thoughts come, how I can make this >~< All ideas which come to my head are bad (example: ignore check on desc, but overload method just break this idea)
Note that your issue does not only apply to generic supertype. You can also override a method with a more specific return type, with no Generics involved, e.g.
interface SomeInterface {
Object method();
}
class SomeImplementation implements SomeInterface {
#Override
public Integer method() {
return null;
}
}
You have to understand the concept of bridge methods.
A bridge method performs the task of overriding a method on the byte code level, having exactly the same parameter types and return type as the overridden method, and delegates to the actual implementation method.
Since the bridge method only consists of this invocation instruction, some type casts if required, and the return instruction, it is easy to parse such a method to find the actual method it belongs to, without dealing with the complex rules of the Generic type system.
Using, the following helper classes
record MethodSignature(String name, String desc) {}
record MethodInfo(int access, String owner, String name, String desc) {
MethodSignature signature() {
return new MethodSignature(name, desc);
}
}
final class MethodAndBridges {
MethodInfo actual;
final List<MethodInfo> bridges = new ArrayList<>();
MethodAndBridges(MethodSignature sig) {}
void set(MethodInfo mi) {
if(actual != null) throw new IllegalStateException();
actual = mi;
}
void addBridge(MethodInfo mi) {
bridges.add(mi);
}
}
We can gather the information in a form ready for checking override relations with the ASM library as follows:
class MethodCollector extends ClassVisitor {
static Map<MethodSignature, MethodAndBridges> getMethods(ClassReader cr) {
MethodCollector mc = new MethodCollector();
cr.accept(mc, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
return mc.found;
}
final Map<MethodSignature, MethodAndBridges> found = new HashMap<>();
String owner, superClass;
List<String> interfaces;
protected MethodCollector() {
super(Opcodes.ASM9);
}
#Override
public void visit(int version, int acc,
String name, String sig, String superName, String[] ifNames) {
owner = name;
superClass = superName;
this.interfaces = ifNames == null? List.of(): List.of(ifNames);
}
#Override
public MethodVisitor visitMethod(
int acc, String name, String desc, String sig, String[] exceptions) {
MethodInfo mi = new MethodInfo(acc, owner, name, desc);
if((acc & Opcodes.ACC_BRIDGE) == 0) {
found.computeIfAbsent(mi.signature(), MethodAndBridges::new).set(mi);
return null;
}
return new MethodVisitor(Opcodes.ASM9) {
#Override public void visitMethodInsn(
int op, String owner, String name, String tDesc, boolean i) {
found.computeIfAbsent(new MethodSignature(name, tDesc),
MethodAndBridges::new).addBridge(mi);
}
};
}
}
To demonstrate how this work, let’s enhance your example, to address more cases
interface SupplierOfSerializable {
Serializable get();
}
interface ISetting<T extends CharSequence> extends Supplier<T>, Consumer<T> {
T get();
#Override void accept(T t);
Number method(int i);
static void method(Object o) {}
private void method(Number n) {}
}
class Setting implements ISetting<String>, SupplierOfSerializable {
public String get() {
return "";
}
#Override
public void accept(String t) {}
public Integer method(int i) {
return i;
}
static void method(Object o) {}
void method(Number n) {}
}
and check the override relations (only considering the direct interfaces, without recursion)
public class CheckOverride {
public static void main(String[] args) throws IOException {
MethodCollector mc = new MethodCollector();
new ClassReader(Setting.class.getName())
.accept(mc, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
Map<MethodSignature, MethodAndBridges> implMethods = mc.found;
Map<MethodInfo, Set<MethodInfo>> overrides = new HashMap<>();
for(String ifType: mc.interfaces) {
Map<MethodSignature, MethodAndBridges> ifMethods
= MethodCollector.getMethods(new ClassReader(ifType));
System.out.println("interface " + ifType.replace('/', '.'));
printMethods(ifMethods);
System.out.println();
ifMethods.values().removeIf(CheckOverride::nonOverridable);
implMethods.forEach((sig, method) -> {
if(nonOverridable(method)) {
overrides.putIfAbsent(method.actual, Set.of());
return;
}
var overridden = ifMethods.get(sig);
if(overridden == null && method.bridges.isEmpty()) {
overrides.putIfAbsent(method.actual, Set.of());
return;
}
Set<MethodInfo> set = overrides.compute(method.actual,
(k, s) -> s == null || s.isEmpty()? new HashSet<>(): s);
if(overridden != null) set.add(overridden.actual);
for(var mi: method.bridges) {
overridden = ifMethods.get(mi.signature());
if(overridden != null) set.add(overridden.actual);
}
});
}
System.out.println("class " + mc.owner.replace('/', '.'));
printMethods(implMethods);
System.out.println();
System.out.println("Final result");
System.out.println("class " + mc.owner.replace('/', '.'));
overrides.forEach((m,overridden) -> {
System.out.println(" " + toDeclaration(m, false));
if(!overridden.isEmpty()) {
System.out.println(" overrides");
overridden.forEach(o ->
System.out.println(" " + toDeclaration(o, true)));
}
});
}
static boolean nonOverridable(MethodAndBridges m) {
return (m.actual.access() & (Opcodes.ACC_PRIVATE|Opcodes.ACC_STATIC)) != 0
|| m.actual.name().startsWith("<");
}
static void printMethods(Map<MethodSignature, MethodAndBridges> methods) {
methods.forEach((sig, methodAndBridges) -> {
System.out.println(" "+toDeclaration(methodAndBridges.actual,false));
if(!methodAndBridges.bridges.isEmpty()) {
System.out.println(" bridges");
for(MethodInfo mi: methodAndBridges.bridges) {
System.out.println(" " + toDeclaration(mi, false));
}
};
});
}
private static String toDeclaration(MethodInfo mi, boolean withType) {
StringBuilder sb = new StringBuilder();
sb.append(Modifier.toString(mi.access() & Modifier.methodModifiers()));
if(sb.length() > 0) sb.append(' ');
String clName = mi.owner();
var mt = MethodTypeDesc.ofDescriptor(mi.desc());
if(mi.name().equals("<init>"))
sb.append(clName, clName.lastIndexOf('/') + 1, clName.length());
else {
sb.append(mt.returnType().displayName()).append(' ');
if(withType) sb.append(clName.replace('/', '.')).append('.');
sb.append(mi.name());
}
if(mt.parameterCount() == 0) sb.append("()");
else {
String sep = "(";
for(ClassDesc cd: mt.parameterList()) {
sb.append(sep).append(cd.displayName());
sep = ", ";
}
sb.append(')');
}
return sb.toString();
}
}
interface ISetting
public static void method(Object)
public abstract void accept(CharSequence)
bridges
public void accept(Object)
public abstract Number method(int)
private void method(Number)
public abstract CharSequence get()
bridges
public Object get()
interface SupplierOfSerializable
public abstract Serializable get()
class Setting
Setting()
public Integer method(int)
bridges
public Number method(int)
public void accept(String)
bridges
public void accept(Object)
public void accept(CharSequence)
static void method(Object)
public String get()
bridges
public Object get()
public CharSequence get()
public Serializable get()
void method(Number)
Final result
class Setting
public String get()
overrides
public abstract Serializable SupplierOfSerializable.get()
public abstract CharSequence ISetting.get()
Setting()
public Integer method(int)
overrides
public abstract Number ISetting.method(int)
public void accept(String)
overrides
public abstract void ISetting.accept(CharSequence)
void method(Number)
static void method(Object)
The code uses newer Java features, like var, record, and the constant API, but I think, the result is straight-forward enough for converting it to older Java versions, if really required.
Given Java source code and a preprocessor (like C++), I would like to replace all mentions of null with a function that returns null. It finds a call to null and replaces it with the following function.
public static Object returnNull(){
return null;
}
This fails because there are varied classes and:
functionThatWantsCustomClass( returnNull() ); //Object cannot be converted to CustomClass
or
if( cc == returnNull() ) //Object cannot be converted to CustomClass
etc.
Easiest solution I can imagine is having to parametrize the preprocessor, although that would require going through every single null to add the parameter maually, eg: null/*CustomClass*/.
Another method is spending a lot of time writing a much better parser so it always knows the required class for a returnTypedNull() function.
Is there a way to get through this error with minimal modification/parsing?
Use generics:
public static <T> T returnNull() {
return (T) null;
}
Follow-up from comment
The following code is as close to comment as I can decipher, and it compiles fine:
public class Test {
public static void main(String[] args) {
CustomClass cc = new CustomClass();
if (cc != returnNull())
cc.errlog( returnNull() );
}
public static <T> T returnNull() {
return (T) null;
}
}
class CustomClass {
void errlog(Exception e) {
}
}
Now, if there are 2 errlog methods with only one non-primitive parameter:
class CustomClass {
void errlog(Exception e) {
}
void errlog(String s) {
}
}
Then it will fail with error The method errlog(Exception) is ambiguous for the type CustomClass, because the compiler doesn't know whether T should be Exception or String, i.e. which of the two to call.
You have to explicitly tell the compiler:
cc.errlog( Test.<Exception>returnNull() );
Use generics ant it will work.
Example:
public class ReturnNullExample {
public static void main(String[] args) {
ReturnNullExample example = new ReturnNullExample();
example.someMethod(ReturnNullClass.returnNull());
CustomClass cc = null;
if(cc == ReturnNullClass.returnNull()) {
System.out.println("cc is null");
}
cc = new CustomClass();
if(cc != ReturnNullClass.returnNull()) {
System.out.println("cc is not null");
}
}
public void someMethod(CustomClass customClass) {
System.out.println("This method does nothing");
}
}
class CustomClass {
private int number;
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
}
class ReturnNullClass {
public static <T> T returnNull() {
return null;
}
}
I have a generic class with a generic list in it. I want to ensure that the generic list only contains unique classes.
What I have done so far is to compare the class names with reflection (getClass()). But I think that's not a clean solution. Are there any better practices to check?
public class MyGenericClass<T extends MyGenericClass.MyInterface> {
private List<T> members = new ArrayList<>(0);
public void add(T t) {
final boolean[] classInMembers = {false};
members.forEach(member -> {
if (member.getClass().getName().equals(t.getClass().getName())) {
classInMembers[0] = true;
}
});
if (!classInMembers[0]) {
members.add(t);
}
}
public interface MyInterface {
void doSomething(String text);
}
}
public class Main {
public static void main(String[] args) {
MyGenericClass<MyGenericClass.MyInterface> myGenericClass = new MyGenericClass<>();
myGenericClass.add(new Performer1());
myGenericClass.add(new Performer2());
myGenericClass.add(new Performer3());
myGenericClass.add(new Performer3()); // should not be inserted!
}
private static class Performer1 implements MyGenericClass.MyInterface {
#Override
public void doSomething(String text) {
text = "Hi, I am performer 1!";
}
}
private static class Performer2 implements MyGenericClass.MyInterface {
#Override
public void doSomething(String text) {
text = "Hi, I am performer 2!";
}
}
private static class Performer3 implements MyGenericClass.MyInterface {
#Override
public void doSomething(String text) {
text = "Hi, I am performer 3!";
}
}
}
You could subclass a java.util.Set interface implementation. It will likely be easiest to subclass java.util.AbstractSet.
By default 'Set' will compare objects by their .equals() method - In your case, this is not sufficient. You will need to override the contains method to ensure that only instances of a unique class are added.
In your overrideen contains, it's probably the same / easier to compare class instances rather than their stringified package name
I.e. use a.getClass() == b.getClass(), rather than a.getClass().getName()
Don't use a List, use a java.util.Set instead.
A collection that contains no duplicate elements. More formally, sets contain no pair of elements e1 and e2 such that e1.equals(e2), and at most one null element.
If the iteration order is important or if you want to use a custom Comparator, the TreeSet implementation can be used:
A NavigableSet implementation based on a TreeMap. The elements are ordered using their natural ordering, or by a Comparator provided at set creation time, depending on which constructor is used.
Example of a Set using a Comparator:
class MyComparator implements Comparator<Object> {
#Override
public int compare(Object e1, Object e2) {
if (e1.getClass() == e2.getClass())
return 0;
//if you wish to have some extra sort order
return e1.getClass().getName().compareTo(e2.getClass().getName());
}
}
. . .
Set mySet = new TreeSet<Object>(new MyComparator());
mySet.add(new Object());
mySet.add(new Object());//same class already in set
mySet.add("wtf");
//mySet.size() is now 2 - the second "new Object()" was not inserted due to the comparator check
Why so complicated?
public class Main {
public static void main(String[] args) {
final Class<?> helloClass = "Hello".getClass();
final Class<?> worldClass = "World".getClass();
final Class<?> intClass = Integer.class;
System.out.println(helloClass.equals(worldClass)); // -> true
System.out.println(helloClass.equals(intClass)); // -> false
}
}
You could maintain a roster of members in a Set.
public static class MyGenericClass<T extends MyGenericClass.MyInterface> {
private List<T> members = new ArrayList<>(0);
// Add this.
private Set<Class<?>> roster = new HashSet<>();
public void add(T t) {
if (!roster.contains(t.getClass())) {
members.add(t);
roster.add(t.getClass());
}
}
private void soundOff() {
for (T t : members) {
t.doSomething();
}
}
public interface MyInterface {
void doSomething();
}
}
private static class Performer implements MyGenericClass.MyInterface {
final int n;
public Performer(int n) {
this.n = n;
}
#Override
public void doSomething() {
System.out.println("Hi, I am a " + this.getClass().getSimpleName() + "(" + n + ")");
}
}
private static class Performer1 extends Performer {
public Performer1(int n) {
super(n);
}
}
private static class Performer2 extends Performer {
public Performer2(int n) {
super(n);
}
}
private static class Performer3 extends Performer {
public Performer3(int n) {
super(n);
}
}
public void test() {
MyGenericClass<MyGenericClass.MyInterface> myGenericClass = new MyGenericClass<>();
myGenericClass.add(new Performer1(1));
myGenericClass.add(new Performer2(2));
myGenericClass.add(new Performer3(3));
myGenericClass.add(new Performer3(4)); // should not be inserted!
myGenericClass.soundOff();
}
You could implement a Wrapper which provides the necessary comparison and add the wrapped instance to the set. This way you don't have to override equals and hashcode in your concrete Performer classes and you don't have to subclass a concrete Set implementation (which you are coupled to. When you subclass a HashSet, you have to use that concrete class. But what if you want to use a LinkedHashSet at some point? You have to override LinkedHashSet as well) , which may be fragile since you have to make sure that the overridden method is consistent with the rest of the class.
class MyGenericClass<T extends MyInterface> {
private Set<ClassCompareWrapper<T>> members = new HashSet<>();
public void add(T t) {
members.add(new ClassCompareWrapper<T>(t));
}
}
class ClassCompareWrapper<T> {
T t;
public ClassCompareWrapper(T t) {
this.t = t;
}
#Override
public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof ClassCompareWrapper))
return false;
ClassCompareWrapper<?> that = (ClassCompareWrapper<?>) o;
return Objects.equals(t.getClass(), that.t.getClass());
}
#Override
public int hashCode() {
return Objects.hash(t.getClass());
}
#Override
public String toString() {
return "Wrapper{" +
"t=" + t +
'}';
}
}
Here are a few other ideas.
Using streams:
public void add(T t) {
if (!members.stream().anyMatch(m -> m.getClass() == t.getClass())) {
members.add(t);
}
}
Using AbstractSet and HashMap:
class ClassSet<E> extends AbstractSet<E> {
private final Map<Class<?>, E> map = new HashMap<>();
#Override
public boolean add(E e) {
// this can be
// return map.putIfAbsent(e.getClass(), e) != null;
// in Java 8
Class<?> clazz = e.getClass();
if (map.containsKey(clazz)) {
return false;
} else {
map.put(clazz, e);
return true;
}
}
#Override
public boolean remove(Object o) {
return map.remove(o.getClass()) != null;
}
#Override
public boolean contains(Object o) {
return map.containsKey(o.getClass());
}
#Override
public int size() {
return map.size();
}
#Override
public Iterator<E> iterator() {
return map.values().iterator();
}
}
A HashMap could also be used without wrapping it in a Set. The Set interface is defined around equals and hashCode, so any implementation which deviates from this is technically non-contractual. Additionally, you might want to use LinkedHashMap if the values are iterated often.
I am writing a servlet that will be conditionally modifying HTTP headers according to some user-definable rules. (Edit: these rules are defined in an XML file that is read at start-up.) For example, add "X-UA-Compatible: IE=edge,chrome=1" to a response header if it does not already exist and if the request specified a "User-Agent" header matching a known pattern. Not having any better ideas, I attempted to make my own POJOs representing these rules. It "works" but I feel like there must be a more standard or more flexible way to do this.
Are there general-purpose libraries or tools (whether built-in or 3rd-party) that would solve this problem? I have heard and read a little about "rules engines" but they seem like much more complex/heavy tools not meant for problems as simple as mine.
To illustrate what I'm trying to do, I've created a simplified program that applies "rules" to numbers based on "conditions" like "is an even number". Here it is, sorry it's a bit lengthy.
Main.java
package my.example;
import java.util.*;
import my.example.conditions.*;
import my.example.rules.*;
public class Main {
public static void main(String args[]) {
// Some sample objects to evaluate
Collection<Integer> numbers = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8);
print(numbers);
// Define rules
Collection<Rule<Integer>> rules = new ArrayList<Rule<Integer>>();
rules.add(createRuleToMultiplyEvenNumbersBy4());
rules.add(createRuleToAdd1ToEveryNumber());
// Process the rules for each sample object
Collection<Integer> newNumbers = new ArrayList<Integer>();
for (Integer number : numbers) {
Integer newNumber = number;
for (Rule<Integer> rule : rules)
newNumber = rule.apply(newNumber);
newNumbers.add(newNumber);
}
print(newNumbers);
}
private static Rule<Integer> createRuleToMultiplyEvenNumbersBy4() {
MultiplyNumberRule rule = new MultiplyNumberRule(true, 4);
rule.addCondition(new NumberIsEvenCondition());
return rule;
}
private static Rule<Integer> createRuleToAdd1ToEveryNumber() {
AddNumberRule rule = new AddNumberRule(true, 1);
rule.addCondition(new ConstantCondition<Integer>(true));
return rule;
}
private static void print(Collection<Integer> numbers) {
System.out.print("Numbers: ");
for (Integer number : numbers) {
System.out.print(number);
System.out.print(" ");
}
System.out.print("\r\n");
}
}
Condition.java
package my.example.conditions;
public interface Condition<T> {
boolean appliesTo(T obj);
}
ConstantCondition.java
package my.example.conditions;
public class ConstantCondition<T> implements Condition<T> {
private boolean constant;
public ConstantCondition(boolean alwaysReturnThisValue) {
constant = alwaysReturnThisValue;
}
#Override
public boolean appliesTo(T target) {
return constant;
}
}
NumberIsEvenCondition.java
package my.example.conditions;
public class NumberIsEvenCondition implements Condition<Integer> {
#Override
public boolean appliesTo(Integer i) {
return (i % 2 == 0);
}
}
Rule.java
package my.example.rules;
public interface Rule<T> {
T apply(T target);
}
AbstractRule.java
package my.example.rules;
import java.util.*;
import my.example.conditions.Condition;
public abstract class AbstractRule<T> implements Rule<T> {
private Collection<Condition<T>> conditions;
private boolean requireAllConditions;
public AbstractRule(boolean requireAllConditions) {
conditions = new ArrayList<Condition<T>>();
this.requireAllConditions = requireAllConditions;
}
public void addCondition(Condition<T> condition) {
conditions.add(condition);
}
#Override
public T apply(T target) {
boolean isApplicable;
if (requireAllConditions)
isApplicable = allConditionsSatisfied(target);
else
isApplicable = atLeastOneConditionSatisfied(target);
if (isApplicable)
target = process(target);
return target;
}
// Check if all conditions are met
protected boolean allConditionsSatisfied(T target) {
for (Condition<T> condition : conditions) {
if (!condition.appliesTo(target))
return false;
}
return true;
}
// Check if any conditions are met
protected boolean atLeastOneConditionSatisfied(T target) {
for (Condition<T> condition : conditions) {
if (condition.appliesTo(target))
return true;
}
return false;
}
abstract T process(T target);
}
AddNumberRule.java
package my.example.rules;
public class AddNumberRule extends AbstractRule<Integer> {
private Integer addend;
public AddNumberRule(boolean requireAllConditions) {
this(requireAllConditions, 0);
}
public AddNumberRule(boolean requireAllConditions, Integer addend) {
super(requireAllConditions);
this.addend = addend;
}
#Override
public Integer process(Integer i) {
return i + addend;
}
}
MultiplyNumberRule.java
package my.example.rules;
public class MultiplyNumberRule extends AbstractRule<Integer> {
private Integer factor;
public MultiplyNumberRule(boolean requireAllConditions) {
this(requireAllConditions, 1);
}
public MultiplyNumberRule(boolean requireAllConditions, Integer factor) {
super(requireAllConditions);
this.factor = factor;
}
#Override
public Integer process(Integer i) {
return i * factor;
}
}
Well, I'd use Commons Chain
A popular technique for organizing the execution of complex processing
flows is the "Chain of Responsibility" pattern, as described (among
many other places) in the classic "Gang of Four" design patterns book.
Although the fundamental API contracts required to implement this
design patten are extremely simple, it is useful to have a base API
that facilitates using the pattern, and (more importantly) encouraging
composition of command implementations from multiple diverse sources.
it's a common Design Pattern, guess that fits your problem
I have modified the original code attempting to use Commons Chain, but it doesn't seem much different. Luiz E., is this roughly what you are suggesting? It seems like commons-chain does not include any notion of "conditions" -- this is left to the user as part of the implementation of Command.
Main.java
package my.example;
import java.util.*;
import org.apache.commons.chain.*;
import org.apache.commons.chain.impl.*;
import my.example.commands.*;
import my.example.conditions.*;
public class Main {
private static final String NUMBERS = "numbers";
public static void main(String args[]) throws Exception {
// Some sample objects to evaluate
Context context = new ContextBase();
setNumbersInContext(context, Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8));
printNumbersFromContext(context);
// Define rules
Chain ruleChain = new ChainBase();
ruleChain.addCommand(new MultiplyNumberCommand(4, new NumberIsEvenCondition()));
ruleChain.addCommand(new AddNumberCommand(1));
// Process the rules
ruleChain.execute(context);
printNumbersFromContext(context);
}
private static void printNumbersFromContext(Context context) {
Collection<Integer> numbers = getNumbersFromContext(context);
System.out.print("Numbers: ");
for (Integer number : numbers) {
System.out.print(number);
System.out.print(" ");
}
System.out.print("\r\n");
}
#SuppressWarnings("unchecked")
public static Collection<Integer> getNumbersFromContext(Context context) {
Object obj = context.get(NUMBERS);
try {
return (Collection<Integer>) obj;
}
catch (ClassCastException e) {
throw new IllegalStateException("Context did not contain the required data. ClassCastException message is: " + e.getMessage());
}
}
#SuppressWarnings("unchecked")
public static void setNumbersInContext(Context context, Collection<Integer> numbers) {
context.put(NUMBERS, numbers);
}
}
AbstractNumberCommand.java
package my.example.commands;
import static my.example.Main.getNumbersFromContext;
import static my.example.Main.setNumbersInContext;
import java.util.ArrayList;
import java.util.Collection;
import my.example.conditions.Condition;
import org.apache.commons.chain.*;
public abstract class AbstractNumberCommand implements Command {
private boolean continueProcessing = true;
protected Condition<Integer> condition;
protected AbstractNumberCommand(Condition<Integer> condition) {
this.condition = condition;
}
public void continueProcessing(boolean continueProcessing) {
this.continueProcessing = continueProcessing;
}
#Override
public boolean execute(Context context) throws Exception {
Collection<Integer> numbers = getNumbersFromContext(context);
Collection<Integer> newNumbers = new ArrayList<Integer>();
for (int number : numbers)
if (condition.appliesTo(number))
newNumbers.add(modifyNumber(number));
else
newNumbers.add(number);
setNumbersInContext(context, newNumbers);
if (continueProcessing)
return CONTINUE_PROCESSING;
else
return PROCESSING_COMPLETE;
}
protected abstract int modifyNumber(int number);
}
AddNumberCommand.java
package my.example.commands;
import my.example.conditions.*;
public class AddNumberCommand extends AbstractNumberCommand {
private int addend;
public AddNumberCommand() {
this(0);
}
public AddNumberCommand(int addend) {
this(addend, new ConstantCondition<Integer>(true));
}
public AddNumberCommand(int addend, Condition<Integer> condition) {
super(condition);
this.addend = addend;
}
#Override
protected int modifyNumber(int number) {
return number + addend;
}
}
Condition.java
package my.example.conditions;
public interface Condition<T> {
boolean appliesTo(T obj);
}
ConstantCondition.java
package my.example.conditions;
public class ConstantCondition<T> implements Condition<T> {
private boolean constant;
public ConstantCondition(boolean alwaysReturnThisValue) {
constant = alwaysReturnThisValue;
}
#Override
public boolean appliesTo(T target) {
return constant;
}
}
NumberIsEvenCondition.java
package my.example.conditions;
public class NumberIsEvenCondition implements Condition<Integer> {
#Override
public boolean appliesTo(Integer i) {
return (i % 2 == 0);
}
}
package design.pattern.behavioral;
import design.pattern.behavioral.ChainOfResponsibility.*;
public class ChainOfResponsibility {
public static class Chain {
private Request[] requests = null;
private Handler[] handlers = null;
public Chain(Handler[] handlers, Request[] requests){
this.handlers = handlers;
this.requests = requests;
}
public void start() {
for(Request r : requests)
for (Handler h : handlers)
if(h.handle(r)) break;
}
}
public static class Request {
private int value;
public Request setValue(int value){
this.value = value;
return this;
}
public int getValue() {
return value;
}
}
public static class Handler<T> {
private Command<T> command = null;
public Handler(Command<T> command) {
this.command = command;
}
public boolean handle(T request) {
return command.execute(request);
}
}
public static abstract class Command<T>{
public abstract Boolean execute(T request);
}
}
class TestChainOfResponsibility {
public static void main(String[] args) {
new TestChainOfResponsibility().test();
}
private void test() {
new Chain(new Handler[]{ // chain of responsibility
new Handler<Request>(
new Command<Request>(){ // command
public Boolean execute(Request condition) {
boolean result = condition.getValue() >= 600;
if (result) System.out.println("You are rich: " + condition.getValue() + " (id: " + condition.hashCode() + ")");
return result;
}
}
),
new Handler<Request>(
new Command<Request>(){
public Boolean execute(Request condition) {
boolean result = condition.getValue() >= 100;
if(result) System.out.println("You are poor: " + condition.getValue() + " (id: " + condition.hashCode() + ")");
return result;
}
}
),
},
new Request[]{
new Request().setValue(600), // chaining method
new Request().setValue(100),
}
).start();
}
}
I don't think there is a meaningful answer to such a general question. Design patterns don't exist in isolation and don't have a "perfect form": they live in a context.
A pattern is a solution to a problem in a context.
So without knowing the context of your solution, there is not much we can say about it. What is the concrete problem you are trying to resolve with it? What forces are in play? What are your constraints? Do you have any problems / issues with the current solution? If you give more details about these, maybe we can give a better answer.
Lambda isn't very descriptive (to most developers). Is it something you are pulling in from functional language theory?
I'd probably just get rid of the 'controlling' class, and wire the individual handlers up to each other directly - use more of an IoC approach, basically.
Example (in C#, forgive me) per request...
public interface IIntMessage
{
void HandleMesasge(int i);
}
public class EvenPrinter : IIntMessage
{
private IIntMessage m_next;
public EvenPrinter(IIntMessage next)
{
m_next = next;
}
public void HandleMesasge(int i)
{
if(i % 2 == 0)
{
System.Console.WriteLine("Even!");
}
else
{
m_next.HandleMesasge(i);
}
}
}
public class OddPrinter : IIntMessage
{
private IIntMessage m_next;
public OddPrinter(IIntMessage next)
{
m_next = next;
}
public void HandleMesasge(int i)
{
if(i%2 == 1)
{
System.Console.WriteLine("Odd!");
}
else
{
m_next.HandleMesasge(i);
}
}
}
Note that we get rid of the "controlling" class altogether, and simply allow the request handlers to directly chain to each other, without having to go through an intermediary.
Also, I could probably extract out a 'base' chain-of-command request handler, removing some of the duplicate code.