I'm implementing observer pattern in the following way:
interface Layer{
void adjustString(Set<String> strings);
}
interface NotifiableLayer extends Layer{
void layerAdjusted(Layer layer);
}
abstract class ObservableLayer implements Layer{
Set<NotifiableLayer> observers = new HashSet<>();
void addObserver(NotifiableLayer layer){
observers.add(layer);
}
void removeObserver(NotifiableLayer layer){
observers.remove(layer);
}
void notifyObservers(){
observers.forEach(l -> l.layerAdjusted(this));
}
}
class MyLayer extends ObservableLayer{
@Override
public void adjustString(Set<String> strings) {
this.notifyObservers(); //can this be auto?
}
}
And this works of course, but whoever is implementing ObservableLayer
needs to remember to call this.notifyObservers()
in the adjustString
method. This is not that of a big deal, but I wanted to see if there is a way to completely hide this.
So far, I only have this idea (using template method):
abstract class ObservableLayer implements Layer{
//...methods removed for simplicity
@Override
public void adjustString(Set<String> strings) {
this.doAdjustString(strings);
this.notifyObservers(); //<---- here is auto
}
abstract void doAdjustString(Set<String> strings);
}
class MyLayer extends ObservableLayer{
@Override
public void doAdjustString(Set<String> strings) {
//now notification is in base adjustString
}
}
but here I don't like that method name changed to doAdjustString
, and it is not anymore uniform between other layer implementations (layers that directly implement Layer
interface).
Is there any easy way to have this functionallity, but to keep public void adjustString(Set<String> strings)
signature in MyLayer
class?
One way would be to use a Decorator instance that holds an ObservableLayer
instance and delegates to it.
final class LayerDecorator implements Layer {
final private ObservableLayer delegate;
public LayerDecorator(ObservableLayer delegate) {
this.delegate = delegate;
}
@Override
public void adjustString(Set<String> strings) {
delegate.adjustString(strings);
delegate.notifyObservers();
}
}
This assumes that calling code is working using references to Layer
instead of ObservableLayer
.
If calling code has to work using references to ObservableLayer
then maybe it is better to refactor ObservableLayer
to be an interface having the methods to register listeners, remove them and notify them. This interface also extends the Layer
interface.
interface IObservableLayer extends Layer {
void addObserver(NotifiableLayer layer);
void removeObserver(NotifiableLayer layer);
void notifyObservers();
}
The abstract class ObservableLayer
changes to implement IObservableLayer
instead of Layer
directly. This class remains public to support application classes to define variations of observable layers.
Next an internal decorator for observable layers can be defined as shown below.
final class ObservableLayerDecorator implements IObservableLayer {
final private ObservableLayer delegate;
public ObservableLayerDecorator(ObservableLayer delegate) {
this.delegate = delegate;
}
@Override
public void addObserver(NotifiableLayer layer) {
delegate.addObserver(layer);
}
@Override
public void removeObserver(NotifiableLayer layer) {
delegate.removeObserver(layer);
}
@Override
public void notifyObservers() {
delegate.notifyObservers();
}
@Override
public void adjustString(Set<String> strings) {
delegate.adjustString(strings);
this.notifyObservers();
}
}
Please note how the notification is done in this case.
Now instances of IObservableLayer
can be created as
IObservableLayer observableLayer = new ObservableLayerDecorator(new MyClass());
Factory methods will be helpful here as they can be defined to handle creation of various application-level observable layer classes so that the instances can be created consistently that return an IObservableLayer
which is decorated. That will free up developers from knowing how to use the decorator and allow the decorator to be an internal utility.