patternjavaMinor
Generic property with AOP-functionality in Java
Viewed 0 times
genericwithjavapropertyaopfunctionality
Problem
After playing around with Java 8 and Generics for some time, I came across an idea while reading some posts about Apple's new programming language Swift.
Swift has AOP-like methods for properties (
Example usage:
```
public class Foo {
private Property bar;
public Foo() {
bar = new Property<>();
bar.beforeSet((oldValue, ne
Swift has AOP-like methods for properties (
willSet and didSet) and I thought of creating something similar in Java with the help of the new functional interfaces of Java 8./**
* Generic wrapper for a property with the possibility to apply functions before, after and instead of getting/setting the value
*
* @author Thomas Eizinger
* @param The type of the wrapped value
*/
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
public class Property {
private T value;
private BiConsumer beforeSet = (u, v) -> {};
private Function onSet = (v) -> (v);
private Consumer afterSet = (v) -> {};
private Consumer beforeGet = (v) -> {};
private Function onGet = (v) -> (v);
private Consumer afterGet = (v) -> {};
public void set(T value) {
this.beforeSet.accept(this.value, value);
this.value = this.onSet.apply(value);
this.afterSet.accept(this.value);
}
public T get() {
this.beforeGet.accept(this.value);
this.value = this.onGet.apply(this.value);
this.afterGet.accept(this.value);
return this.value;
}
public void beforeSet(BiConsumer beforeSet) {
this.beforeSet = beforeSet;
}
public void onSet(Function onSet) {
this.onSet = onSet;
}
public void afterSet(Consumer afterSet) {
this.afterSet = afterSet;
}
public void beforeGet(Consumer beforeGet) {
this.beforeGet = beforeGet;
}
public void onGet(Function onGet) {
this.onGet = onGet;
}
public void afterGet(Consumer afterGet) {
this.afterGet = afterGet;
}
}Example usage:
```
public class Foo {
private Property bar;
public Foo() {
bar = new Property<>();
bar.beforeSet((oldValue, ne
Solution
My first thought is: Interesting.
The
First, some feature-requests or changes you can consider:
-
It could potentially be useful to use the
-
What if a previous function has been set for
-
I'm not a big fan of the
-
Actually, thinking about it... I'm not sure about any of the get-features... I just don't see a specific real use case for them.
The system itself
I'm going to list a couple of good things and a couple of bad things with this approach:
Good things:
(One time for the
Bad things:
-
Primitive types needs to be wrapped into it's non-primitive type, like
-
When should I make my Java classes use this approach or not? Should I introduce them when I feel like I need them or should I play safe and use this approach on all my properties, even though when I will probably never need them? I can imagine there will be some headache going on no matter what option I would choose.
-
It's breaking the Java beans style, which isn't a bad thing by itself. It can cause a bit of a problem when using libraries that depends a lot on reflection, but there are normally ways to work around that. (I.e. telling the library to ignore the
A different approach that's capable of doing the same thing
Consider Google's EventBus code. With the right event classes, this can accomplish the same things. Example code following: (not tested, but something similar would work)
Of course, there's bad things to say about this approach as well. One example is that it always creates a new object whenever
Still another approach
This is the fastest approach, but unfortunately it is the least flexible approach as well.
The simple "do-it-in-the-setter approach"
Summary
You might have use for your
Does this put too much logic into properties?
Personally, I think it does yes. But that's just my two three cents.
The
Property class as it is right nowFirst, some feature-requests or changes you can consider:
- You could combine
beforeSetandonSetinto aBiFunction(which is the same asBinaryOperator).
-
It could potentially be useful to use the
afterSet as a BiConsumer, to also let it know about what the previous value was.-
What if a previous function has been set for
afterSet/beforeSet/etc.? I think you should do a null-check there and either throw an exception if it has already been set, or auto-add it (using the default methods that exists on the interfaces), such as this.afterSet = this.afterSet.andThen(afterSet);-
I'm not a big fan of the
onGet feature. In my experience, getters should do what they do best: Get the actual value. Having the possibility to modify the value that should be returned seems risky. Doing conversion like String.toLowerCase() for example should lie where it is being used, or the property should be set to lower case right from the start IMO.-
Actually, thinking about it... I'm not sure about any of the get-features... I just don't see a specific real use case for them.
The system itself
I'm going to list a couple of good things and a couple of bad things with this approach:
Good things:
- Allows you to manipulate the "behavior" of a property in quite some detail.
- Allows you to manipulate the "behavior" of a property in quite some detail.
- Allows you to manipulate the "behavior" of a property in quite some detail.
(One time for the
beforeSet feature, once for the onSet and once for afterSet. Three-in-one)Bad things:
-
Primitive types needs to be wrapped into it's non-primitive type, like
int -> Integer. This leads to slower code as there will be some auto-boxing, and it will allow the value null where you might not want that value, or you have to handle the null with a beforeSet or similar. Although you could potentially separate classes for IntProperty, BooleanProperty, etc. I don't think you'd want to do all those classes.-
When should I make my Java classes use this approach or not? Should I introduce them when I feel like I need them or should I play safe and use this approach on all my properties, even though when I will probably never need them? I can imagine there will be some headache going on no matter what option I would choose.
-
It's breaking the Java beans style, which isn't a bad thing by itself. It can cause a bit of a problem when using libraries that depends a lot on reflection, but there are normally ways to work around that. (I.e. telling the library to ignore the
private property and use the getters and setters provided).A different approach that's capable of doing the same thing
Consider Google's EventBus code. With the right event classes, this can accomplish the same things. Example code following: (not tested, but something similar would work)
public class Foo {
private int bar;
private EventBus events = new EventBus();
public Foo() {
bar = 42;
events.registerListener(this);
}
@Subscribe
private void onSetChanged(BarChangeEvent event) {
System.out.printf("bar will be set from %d to %d.%n", event.getOldValue(), event.getNewValue()));
}
public int getBar() {
return bar.get();
}
public void setBar(int bar) {
events.post(new BarChangeEvent(this.bar, bar));
this.bar = bar;
}
}Of course, there's bad things to say about this approach as well. One example is that it always creates a new object whenever
bar is set. For most projects of course, the @Subscribe method is probably not inside the Foo class. Instead the class will have to expose the EventBus somehow (or the EventBus can be injected into the Foo class).Still another approach
This is the fastest approach, but unfortunately it is the least flexible approach as well.
The simple "do-it-in-the-setter approach"
public class Foo {
private int bar;
public Foo() {
bar = 42;
}
public int getBar() {
return bar;
}
public void setBar(int bar) {
System.out.printf("bar will be set from %d to %d.%n", this.bar, bar);
this.bar = bar;
}
}Summary
You might have use for your
Property code, if so then great. I personally would probably not use it. (I tend to use the Event-driven approach when I need to, not for each and every property of course. Sometimes I might even use the "put-it-in-the-setter-approach")Does this put too much logic into properties?
Personally, I think it does yes. But that's just my two three cents.
Code Snippets
public class Foo {
private int bar;
private EventBus events = new EventBus();
public Foo() {
bar = 42;
events.registerListener(this);
}
@Subscribe
private void onSetChanged(BarChangeEvent event) {
System.out.printf("bar will be set from %d to %d.%n", event.getOldValue(), event.getNewValue()));
}
public int getBar() {
return bar.get();
}
public void setBar(int bar) {
events.post(new BarChangeEvent(this.bar, bar));
this.bar = bar;
}
}public class Foo {
private int bar;
public Foo() {
bar = 42;
}
public int getBar() {
return bar;
}
public void setBar(int bar) {
System.out.printf("bar will be set from %d to %d.%n", this.bar, bar);
this.bar = bar;
}
}Context
StackExchange Code Review Q#53826, answer score: 3
Revisions (0)
No revisions yet.