Pich
Pich

Reputation: 65

Observer pattern using generics is too much generic

I implemented a simple observer pattern using generics in Java.

Anyway, now the example works only because the update method of the ConcreteObserver is implemented with the .toString() method that is common to all objects.

In a real world case, the ConcreteObserver only operates on a specific type of data. For example, if my ConcreteObserver is a player, it can plays .mp3 or .avi but not .doc.

Which is the correct way to "force" a specific kind of data to be passed as a contract between Observer and Observable? Generics is too generic...

In the case of method signatures, I can use Interfaces to force the implementation of a specific signature.

Which is the way I can force a specific data type passed?

For example: I want a kind of data composed of:

I want this kind of data interface the only allowed.


Here is the code where I use generics.

public interface Observable<T> {
    public void add(Observer<T> observer);
    public void remove(Observer<T> observer);
    public void sendNotify(T notification);
}

public class ConcreteObservable<T> implements Observable<T> {

    ArrayList<Observer<T>> observerList = new ArrayList<>();

    public void add(Observer<T> observer){
        observerList.add(observer);
    }

    public void remove(Observer<T> observer) {
        observerList.remove(observer);
    }

    public void sendNotify(T notification) {
        for (Observer observer : observerList) {
            observer.update(notification);
        }
    }
}


public interface Observer<T> {
    public void update(T value);
}

public class ConcreteObserver<T> implements Observer<T> {

    @Override
    public void update(T value) {
        System.out.println(value.toString()); 
    }
}

public static void main(String[] args) {

    ConcreteObservable observable = new ConcreteObservable();
    ConcreteObserver observer = new ConcreteObserver();
    observable.add(observer);

    int value = 5;
    observable.sendNotify(value);

    String string = "Test";
    observable.sendNotify(string);      

}

Upvotes: 0

Views: 369

Answers (1)

Andy Turner
Andy Turner

Reputation: 140318

ConcreteObservable observable = new ConcreteObservable();
ConcreteObserver observer = new ConcreteObserver();

The problem here is that you're using raw types: you've omitted the type parameters on observer and observable.

Add type parameters. For example, if you specify Integer, you won't be able to pass a String parameter:

ConcreteObservable<Integer> observable = new ConcreteObservable<>();
ConcreteObserver<Integer> observer = new ConcreteObserver<>();

observable.add(observer);       // Fine.

int value = 5;
observable.sendNotify(value);   // Fine.

String string = "Test";
observable.sendNotify(string);  // Error!

Your IDE or compile will give you warnings about the use of raw types: pay attention to them (there are other raw types in your code).


Also, note that you can use Observer<? super T> instead of Observer<T> in your Observable<T> interface and its implementations. This just makes the API a little bit more flexible, so you can use, say, an Observer<Object> (which will accept an Integer parameter to sendNotify) rather than just specifically an Observer<Integer>.

Upvotes: 2

Related Questions