Observer pattern is mainly used for notification, there are two kinds of object Subject and Observer. Whenever there is change on subject's state observer will receive notification.
The observer pattern is a software design pattern in which an object, called the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods. It is mainly used to implement distributed event handling systems.
The Observer pattern is also used in mvc, or gui event management.
The observer pattern defines a one-to-many dependency between objects so that when one object changes state, all of its dependents are notified and updated automatically.
The object which is being watched is called the subject. The objects which are watching the state changes are called observers or listeners.
The observer pattern can cause memory leaks, known as the lapsed listener problem, because in basic implementation it requires both explicit registration and explicit deregistration, as in the dispose pattern, because the subject holds strong references to the observers, keeping them alive. This can be prevented by the subject holding weak references to the observers.
It's also called publish-subscribe pattern. Model view controller (MVC) architecture’s core uses the observer design pattern.
Intent
- Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
- Encapsulate the core (or common or engine) components in a Subject abstraction, and the variable (or optional or user interface) components in an Observer hierarchy.
- The "View" part of Model-View-Controller.
http://www.oodesign.com/observer-pattern.html
Implementation
The participants classes in this pattern are:- Observable - interface or abstract class defining the operations for attaching and de-attaching observers to the client. In the GOF book this class/interface is known as Subject.
- ConcreteObservable - concrete Observable class. It maintain the state of the object and when a change in the state occurs it notifies the attached Observers.
- Observer - interface or abstract class defining the operations to be used to notify this object.
- ConcreteObserverA, ConcreteObserver2 - concrete Observer implementations.
- Differentiate between the core (or independent) functionality and the optional (or dependent) functionality.
- Model the independent functionality with a "subject" abstraction.
- Model the dependent functionality with an "observer" hierarchy.
- The Subject is coupled only to the Observer base class.
- The client configures the number and type of Observers.
- Observers register themselves with the Subject.
- The Subject broadcasts events to all registered Observers.
- The Subject may "push" information at the Observers, or, the Observers may "pull" the information they need from the Subject.
- Subject provides interface for observers to register and unregister themselves with the subject.
- Subject knows who its subscribers are.
- Multiple observers can subscribe for notifications.
- Subject publishes the notifications.
- Subject just sends the notification saying the state has changed. It does not pass any state information.
- Once the notification is received from subject, observers call the subject and get data that is changed.
http://www.vogella.com/tutorials/DesignPatternObserver/article.html
Using the observer pattern a subject can register an unlimited number of observers. If a new listener wants to register with the subject, no code change in the subject is necessary.
Using the listener pattern decouples the subject from its observers. Only the observers have direct knowledge about the subject.
Observer and Observable Java API in jdk
JDK provides Observer and Observable classes as part of util package.
Observer is an interface which needs to be implemented to observe the state change in a observable subject. Observable is a class which should be extended by a subject. Observable provides implementation for methods to register or unregister an Observer and to notify the Observer objects.
In this case, we may have to define and implement our own observable interface. We can reuse Observer interface.
http://javarevisited.blogspot.sg/2011/12/observer-design-pattern-java-example.html
How Observer Design Pattern is implemented in Java
Public Interface Observer:
Any class who implements this interface must be notified when subject or observable object change its status.
Update (Observable Ob, Object arg): This method is called when subject is changed.
Class Observable:
It’s a subject to whom observer wants to observe.
Some Important Method:
addObserver(Observer o):add Observers in the set of observers for this subject or observalbel object.
deleteObserver(Observer o): delete Observers in the set of observers .
hasChanged():check if object has changed.
clearChanged():this method will indicate that subject has no changes or all the observers has been notified when changes is made.
notifyObservers(): notify all the observers if object has changed .
Wathed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| public class Watched extends Observable { private String data = "" ; public String getData(){ return data; } public void setData(String data){ if (! this .data.equals(data)){ this .data = data; setChanged(); } notifyObservers(); } } |
Watcher
1
2
3
4
5
6
7
8
9
10
11
12
13
| public class Watcher implements Observer { public Watcher(Observable o) { o.addObserver( this ); } @Override public void update(Observable arg0, Object arg1) { System.out.println( "状态发生改变: " + ((Watched)arg0).getData()); } } |
Advantage of Observer Design Pattern in Java:
Main advantage is loose coupling between objects called observer and observable. The subject only know the list of observers it don’t care about how they have their implementation.
· The disadvantage is that the sometime if any problem comes, debugging becomes very difficult because flow of control is implicitly between observers and observable we can predict that now observer is going to fire and if there is chain between observers then debugging become more complex.
· Another issue is Memory management because subject will hold all the reference of all the observers if we not unregister the object it can create the memory issue.
Usage of Observer Design Pattern in Java API
HttpSessionBindingListener is an example where Observer design pattern in used in Java API.
-java.util.EventListener
-javax.servlet.http.HttpSessionBindingListener
-javax.servlet.http.HttpSessionAttributeListener
-javax.faces.event.PhaseListener
http://kktechkaizen.blogspot.com/2009/05/observer-pattern-push-vs-pull-model.html
http://prog3.com/sbdm/blog/hynial/article/details/47678757
Observer mode is also known as publish subscribe model. A publisher is corresponding to a plurality of subscribers, once the publisher changes state, the subscriber will receive a subscription event.
Class ConcreteSubject extends Subject{public
String subjectState private;
String getSubjectState public () {
SubjectState return;
}
Void setSubjectState public (subjectState String) {
This.subjectState = subjectState;
NotifyObservers ();
}
}
Void notifyObservers public () {
For (observer Observer: observers) {
Observer.update (this);
}
Observer interface {
Void update public (subject Subject);
}
Class ConcreteObserver implements Observer{
@Override
Void update public (subject Subject) {
System.out.println (name + "states:" + ((ConcreteSubject) subject).GetSubjectState ());
}
}
- Push mode
Subject sends a message to the Observer, regardless of whether the other side is required, the push of the information is usually the target object of all or part of the data, equivalent to the radio communication. - Pull model
Subject only transmits a small amount of information in the Observer, if the observer needs more specific information, then the Observer initiative to pull data. Such a model is implemented in the Subject itself through the introduction of update Observer.
The current implementation uses the pull model. Through
http://segmentfault.com/a/1190000000365279(ConcreteSubject) subject
Get specific objects and get the information.描述:多个对象对某一个主题感兴趣,当主题变化时,所有对这主题感兴趣的对象都能收到通知
场景:很多用户都订阅一篇新闻,当这篇新闻有变化时,订阅的用户都能收到通知
抽象一个观察者,它可以根据订阅的主题和状态值进行后续操作
抽象一个主题
abstract class Subject {
private List<Observer> observers = new ArrayList<Observer>();
void register(Observer observer) {
this.observers.add(observer);
}
void remove(Observer observer) {
this.observers.remove(observer);
}
void notify(int val) {
for(Observer o : observers) {
o.update(this, val);
}
};
}
http://segmentfault.com/a/1190000003784977观察者模式:又被称为订阅者模式,通过一个对象管理相依于它的多对象,同时当该对象的状态改变的时候会主动通知依赖于它的对象。常用在我们后台数据的变化对于前台view的更新上。
优点:让主题和依赖主题的观察者之间松耦合,实现逻辑层和表示层的分离
http://marxsoftware.blogspot.com/2017/05/observer-observable-deprecated.html
This class and the Observer interface have been deprecated. The event model supported by Observer and Observable is quite limited, the order of notifications delivered by Observable is unspecified, and state changes are not in one-for-one correspondence with notifications. For a richer event model, consider using the java.beans package. For reliable and ordered messaging among threads, consider using one of the concurrent data structures in the java.util.concurrent package. For reactive streams style programming, see the Flow API.http://massivetechinterview.blogspot.com/2015/09/reactive-pattern.html
https://dzone.com/articles/the-observer-pattern-using-modern-java
Thread-safety for the Observer Pattern focuses largely on the subject of the pattern, since thread contention can occur when altering the collection containing the registered listeners. For example, if one thread attempts to add a new listener while another thread is adding a new animal (which will trigger a notification of all registered listeners). Depending on the order, the first thread may or may not register the new listener prior to each of the registered listeners being notified of the new animal. This is a classic case of a race-condition and tips us off as developers that some mechanism is required to ensure thread-safety.
A naive implementation of thread-safety would make each of the methods that access or alter the list of registered listeners
synchronized
, as such:
While this implementation would successfully remove the race-condition, since only one thread at a time could alter or access the list of registered listeners, it is too restrictive
As such, so long as no listener is registered or unregistered, any number of concurrent notifications can be executed without introducing a race-condition on the list of registered listeners (there still exist other race-conditions that will be discussed in detail and rectified shortly). To accomplish this, a ReadWriteLock
is used to perform separate read and write locking.public class ThreadSafeZoo {
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
protected final Lock readLock = readWriteLock.readLock();
protected final Lock writeLock = readWriteLock.writeLock();
private List<Animal> animals = new ArrayList<>();
private List<AnimalAddedListener> listeners = new ArrayList<>();
public void addAnimal (Animal animal) {
// Add the animal to the list of animals
this.animals.add(animal);
// Notify the list of registered listeners
this.notifyAnimalAddedListeners(animal);
}
public AnimalAddedListener registerAnimalAddedListener (AnimalAddedListener listener) {
// Lock the list of listeners for writing
this.writeLock.lock();
try {
// Add the listener to the list of registered listeners
this.listeners.add(listener);
}
finally {
// Unlock the writer lock
this.writeLock.unlock();
}
return listener;
}
public void unregisterAnimalAddedListener (AnimalAddedListener listener) {
// Lock the list of listeners for writing
this.writeLock.lock();
try {
// Remove the listener from the list of the registered listeners
this.listeners.remove(listener);
}
finally {
// Unlock the writer lock
this.writeLock.unlock();
}
}
public void notifyAnimalAddedListeners (Animal animal) {
// Lock the list of listeners for reading
this.readLock.lock();
try {
// Notify each of the listeners in the list of registered listeners
this.listeners.forEach(listener -> listener.updateAnimalAdded(animal));
}
finally {
// Unlock the reader lock
this.readLock.unlock();
}
}
}
Even so, there are still two noticeable race-conditions:
- The concurrent access of each listener. Since multiple threads may notify a the list of registered listeners of a new animal, each listener may be concurrently called by multiple threads.
- The concurrent access of the list of animals. Multiple threads may add to the list of animals, requiring some concurrency mechanism to guard against a race-condition. This also leads to an issue with the ordering of notifications. For example, a race-condition can occur where the list of registered listeners is notified of the addition of animal 1 after being notified of the addition of animal 2, even if animal 1 is added before animal 2 if the addition of animal 1 is performed in a separate thread from the addition of animal 2 (i.e., thread 1 adds animal 1 and blocks before notifying the listeners; thread 2 adds animal 2 and notifies the listeners, completing execution; thread 1 resumes and notifies the listeners that animal 1 was added). While this is not always an issue (if the ordering of notifications does not matter), in the general case, this presents an issue (in the case where ordering does not matter, we simple ignore the race-condition, but it nonetheless exists).
public class ThreadSafeCountingAnimalAddedListener implements AnimalAddedListener {
private static AtomicLong animalsAddedCount = new AtomicLong(0);
public void updateAnimalAdded (Animal animal) {
// Increment the number of animals
animalsAddedCount.incrementAndGet();
// Print the number of animals
System.out.println("Total animals added: " + animalsAddedCount);
}
}
It is important to note that is the responsibility of each listener to ensure thread-safety. First, it is not simple for the subject to ensure that access to and alteration of a listener is performed in a thread-safe manner: The subject must understand the underlying logic used by the listener. Thus, the listener itself has the knowledge needed to acheive thread-safety, not the subject. Moreover, if another subject were to use the same listener, the thread-safety logic would need to reimplemented in the second subject (and all other subjects using the listener). Thus, it is desirable that a listener take on the responsibility of ensuring its own thread-safety.
Ordered Notification of Listeners
When ordered execution of listeners is required, the read-write lock implementation alone is insufficient for acheiving total ordering of listener invocation. Instead, some mechanism must be used to ensure that invocations of the notify method are executed in the order in which the animals are added to the zoo. Although it is tempting to use method synchronization to acheive this ordering, according to the Oracle documentation for method synchronization, method synchronization does not provide ordering of execution. Rather, it ensures that methods are executed atomically (i.e., execution will not be interrupted, but it does not ensure a first-come-first-out (FIFO) ordering of executing threads). To accomplish this ordering, a
ReentrantReadWriteLock
is used, with fair-ordering enabled private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(true);
With fair-ordering enabled, all threads accessing the register, unregister, and notify methods will approximate a FIFO ordering of read and write lock acquisition.
public abstract class ObservableSubjectMixin<ListenerType> {
private List<ListenerType> listeners = new ArrayList<>();
public ListenerType registerListener (ListenerType listener) {
// Add the listener to the list of registered listeners
this.listeners.add(listener);
return listener;
}
public void unregisterAnimalAddedListener (ListenerType listener) {
// Remove the listener from the list of the registered listeners
this.listeners.remove(listener);
}
public void notifyListeners (Consumer<? super ListenerType> algorithm) {
// Execute some function on each of the listeners
this.listeners.forEach(algorithm);
}
}
Multi-Method Listeners & Adapters
Complex & Blocking Listeners
- Dispatch a new thread within the listener. Instead of executing the logic of the listener sequentially, have the listener dispatch a new thread, off-loading the execution of the listener logic to the new thread. After the thread has been dispatched, return from the listener method, allowing other listeners to begin execution, while concurrently allowing the dispatched thread to execute the logic of the listener.
- Dispatch a new thread within the subject. Instead of iterating through the list of registered listeners sequentially, have the notification method of a subject dispatch a new thread and iterate through the list of registered listeners. This allows the notification method to return immediately, while allowing the concurrent execution of each listener. Note that some thread-safety mechanism is needed to ensure that concurrent modification of the list of registered listeners does not occur.
- Queue the listener function invocations and have a set of threads execute the listener functions. Instead of simply iterating through each listener in the list of registered listeners, have the execution of the listener methods encapsulated in some functor and queue these functors. Once these functors have been queued, a thread or set of threads (possibly from a thread pool) can pop each functor from the queue and execute the listener logic. This amounts to a Producer-Consumer problem, where the notification process produces a set of executable functors that are queued, while the threads consume these functors from the queue and execute each. The functor must store the parameters to be provided to the listener method at the time of creation, not at the time of execution (which may be some indeterminate time after creation). For example, the functor is created to store the state of the listener method execution at the time the functor is created, allowing the consumer threads to execute the functor at a later time as if it were being executed at the time of creation.