The Observer pattern is confusing, isn't it? So I would like to explain this pattern with the example of two cats begging for rice.
** Tama of domestic cat ** Tama lives on the 3rd floor of an apartment. The owner lives alone. The windows are often closed and you don't go out and play.
** Stray cat tiger ** A tiger lives in a shopping district. I go to cat lovers' houses and catch garbage to secure food.
Cat.java
public class Cat{//Subject class
private Human human;
public void setHuman(Human human){// setObserver()
this.human = human;
}
public void call(){// notifyObservers()
System.out.println("Nya (I'm hungry)");
this.human.called();
}
}
Human.java
public class Human{//Observer class
public void called(){// notify() or update()
System.out.println("Give crunchy");
}
}
Main.java
public static void main(String[] args) {
Cat tama = new Cat();
Human master = new Human();
tama.setHuman(master);
tama.call();
}
Nya (I'm hungry)
Give crunchy
There are two main points of the Observer pattern. The first is that the Subject class has an Observer class as a member variable. In other words, Tama (Cat class) needs to know the owner (Human class). This is represented by the setHuman (= setObserver) function. The second is to call the Observer class when the Subject class changes. In other words, if you are hungry, you will call the owner. This is represented by the Cat.call and Human.called functions.
However, in the simplest Observer pattern, Tama can only remember one owner. When you try to remember another person, you forget about the owner. The owner will have teary eyes.
Main.java
public static void main(String[] args) {
Cat tama = new Cat();
Human master = new Human();
tama.setHuman(master);
//The owner's friend came to play
Human friend = new Human();
tama.setHuman(friend);
//After that, you will be asking your friend for food.
tama.call();
}
Tigers can do things that Tama couldn't. Tigers remember various homes and seem to go around them every time they are hungry.
Cat.java
public class Cat {//Subject class
private final ArrayList<Human> humans = new ArrayList<>();
public void addHuman(Human human){// addObserver()
this.humans.add(human);
}
public void deleteHuman(Human human){// deleteObserver()
this.humans.remove(human);
}
public void call(){// notifyObservers()
System.out.println("Nya (I'm hungry)");
for(Human human : this.humans){
human.called();
}
}
}
Human.java
public class Human {//Observer class
public void called(){// notify() or update()
int dice = new Random().nextInt(99);
if(dice < 50){
System.out.println("This is for you!");
} else {
System.out.println("There is nothing today ...");
}
}
}
Main.java
public static void main(String[] args) {
Cat tora = new Cat();
Human suzuki = new Human();
Human saito = new Human();
Human kobayashi = new Human();
tora.addHuman(suzuki);
tora.addHuman(saito);
tora.addHuman(kobayashi);
tora.call();
}
Nya (I'm hungry)
There is nothing today ...
This is for you!
There is nothing today ...
There are three particularly important differences between the Tama and Tiger codes. First, the member variable of the Subject class is now an ArrayList. This allows tigers to beg for food under the eaves of multiple homes. Second, the function of the Subject class has changed from setObserver to addObserver. The name has been changed because it is now possible to set multiple elements instead of a single element. Third, you can now not only register the Observer class, but also delete it. The deleteObserver function demonstrates its role. Non-owners have a 50% chance of feeding, but not the remaining 50%. It's hard to know, but I may not go to a house that doesn't feed much (not implemented).
Make the tiger code more versatile and versatile. Then it will be in the form of the Observer pattern you often see.
Subject.java
public class Subject {
private final ArrayList<Observer> observers = new ArrayList<>();
public void addObserver(Observer observer){
this.observers.add(observer);
}
public void deleteObserver(Observer observer){
this.observers.remove(observer);
}
public void notifyObservers(){
for(Observer observer : this.observers){
observer.update();
}
}
}
Cat.java
public class Cat extends Subject {
@Override
public void notifyObservers(){
System.out.println("Nya (I'm hungry)");
super.notifyObservers();
}
}
Observer.java
public interface Observer{
public void update(); // or notify()
}
Human.java
public class Human implements Observer{
@Override
public void update(){
int dice = new Random().nextInt(99);
if(dice < 50){
System.out.println("This is for you!");
} else {
System.out.println("There is nothing today ...");
}
}
}
Main.java
public static void main(String[] args) {
Cat tora = new Cat();
Human suzuki = new Human();
Human saito = new Human();
Human kobayashi = new Human();
tora.addObserver(suzuki);
tora.addObserver(saito);
tora.addObserver(kobayashi);
tora.notifyObservers();
}
Nya (I'm hungry)
This is for you!
This is for you!
There is nothing today ...
There are three generalizations and changes to the tiger code. First, the Human class now implements the Observer interface. The reason for sandwiching the Observer interface is explained with the third change. Second, I extracted the functions of the Cat class as the Subject class. This makes the Cat class, which is volatile, simpler. Third, we now include the Observer interface in the Subject class ArrayList instead of the Human class. By calling the Observer.update function, you can call the update function of various classes as well as the Human class, which makes it more versatile.
This commentary is for those who have just started studying design patterns but find the Observer pattern difficult. If you read this commentary and then read the class diagram and other commentary again, it will be easier to understand what this pattern is trying to do. Please also read Part 2 if you like. Also, I would appreciate your comments and likes.