1. Requirement
Whenever there is a change in temperature, the temperature should be displayed in Celsius and Fahrenheit.
2. Design
- com.rama41222.main
- ObserverMain.java - Concrete Class
- com.rama41222.observer
- Observable.java - Abstract class
- Subject.java - Concrete Class
- com.rama41222.observer.thermometer
- TemperatureListener.java - Abstract class
- Thermometer.java - Concrete Class
- com.rama41222.subscriber
- CelsiusSubscriber.java - Concrete class
- FahrenheitSubscriber.java - Concrete Class
3. How it works ?
- Thermometer prototype will generate random temperatures.
- Whenever there is a temperature change, the change will be sent to Subject through the Thermometer interface.
- The subject implements the Thermometer interface. The changed temperature is set to its local Temperature variable through the overridden method from Thermometer interface.
- Since the temperature is stored in the Subject, whenever a subscriber subscribe to the Subject through Observable interface, they'll receive the temperature only when the temperature is changed.
- Once the temperature is received. it will be converted and displayed.
Observer Design Pattern - The big picture[1] |
4. Let's get started
The Thermometer - Generate random temperatures in 3000ms intervals
package com.rama41222.observer.thermometer;
import java.util.Random;
public class Thermometer implements Runnable {
private TemperatureListner tm;
private static final Random RAND = new Random();
//Assignment of the Temperature listener
public Thermometer(TemperatureListner t) {
this.tm = t;
}
//Generate random temperatures between 105 and 90 Fahrenheit in
//3000 ms intervals
@Override
public void run() {
while (true) {
try {
Thread.sleep(3000);
int no = RAND.nextInt(105 - 90) + 90;
//Passing new TemperatureListner to the interface
tm.newTemperature(no);
} catch (InterruptedException ex) {
System.err.println(ex.getMessage());;
}
}
}
}
Temperature Interface - Communication link between the subject and the thermometer
package com.rama41222.observer.thermometer;
public interface TemperatureListner {
public void newTemperature(double temp);
}
Subject - Acts as a server class which facilitates the addition of observers, removal of observers, notifying observers etc. This implements the TemperatureListner interface above.
In this case, the subject is eagerly instantiated(Singleton)
package com.rama41222.observer;
import com.rama41222.observer.thermometer.TemperatureListner;
import com.rama41222.observer.thermometer.Thermometer;
import java.util.ArrayList;
import java.util.List;
public class Subject implements TemperatureListner {
private final static List<Observer> observers = new ArrayList<>();
private static final Subject s = new Subject();
private static final Thermometer tmo = new Thermometer(s);
double temp;
private Subject() {}
public static void startThermo() {
// Creating a new thread and starting the Thermometer If multithreading isn't use, it will interrupt the main thread which will cause this program to stop.
Thread task = new Thread() {
@Override
public void run() {
tmo.run();
}
};
task.start();
}
// Singleton getInstance method
public static Subject getInstance() {
return s;
}
// Adding the subscribers to the observers list
public void setObserver(Observer s) {
observers.add(s);
}
private double getTemp() {
return this.temp;
}
// Looping throw all the obverses in the list
public void notifyObservers() {
observers.stream().forEach((ob) -> {
//setting the data for a particular subscriber in the list through the observer interface
ob.valueChange(this.temp);
});
}
@Override
public void newTemperature(double temp) {
this.temp = temp;
// Notify all the subscribers in the static list
notifyObservers();
}
}
Observer Abstract Class - The main communication link between the subscribers and the subject. The subject sends the data through this interface to all the subscribers whenever there's a change in the temperature.
package com.rama41222.observer;
public abstract class Observer {
protected Subject subject;
public abstract void valueChange(double temp);
}
Subscriber Class - Shown below is the celsius class. You can have a Fahrenheit class ass well.
package com.rama41222.subscriber;
import com.rama41222.observer.Observer;
import com.rama41222.observer.Subject;
public class SubscriberCelsius extends Observer {
// subject is declared in the observer abstract class.
public SubscriberCelsius(Subject s) {
this.subject = s;
this.subject.setObserver(this);
}
// Implementing the Observer interface. Since the data is already available through the interface, we just have to format and print the temperature.
@Override
public void valueChange(double temp) {
double f = (temp - 32) * 5 / 9;
double rf = Math.round(f * 100) / 100;
System.out.println("Temp Celsius " + rf);
}
}
Now you have fully implemented the observer design pattern to develop a demo thermometer. The only step remaining is to run the program.
ObserverMain - Main method is defined here.( Nothing to do with the observer pattern)
package com.rama41222.main;
import com.rama41222.observer.Subject;
import com.rama41222.subscriber.SubscriberCelsius;
import com.rama41222.subscriber.SubscriberFarenheit;
public class ObserverMain {
public static void main(String[] args) {
// Get the instance of the subject
Subject subject = Subject.getInstance();
// Turn on the thermometer
Subject.startThermo();
// Add subscribers
new SubscriberCelsius(subject);
new SubscriberFarenheit(subject);
// Subscribe to getState method in the subject, to get temperature changes.
subject.getState();
subject.getState();
}
}
Now run the main class and check the results
GITHUB Repo
References
[1] https://www.tutorialspoint.com/design_pattern/observer_pattern.htm
vety usefull article brother .. explained clearly :)
ReplyDelete