HI guys, as I was playing with observers today I came across something interesting.
I have a program which keeps a list of users (Observers) and products (Observables) and the idea is that whenever the availability (that's a property of a product) changes the Observers are notified of this change. It all worked OK when there was 1 product with multiple users but now that I have multiple products things are falling apart and it seems I'm causing a memory leak somewhere - probably due to the fact thtat I'm not fully understanding the Observer pattern.
So when I try to print out the number of products per user (there should be only 3 product per user) the programs output the same product multiple times.
Let's have a look at the code and output:
Here is the product I'm observing:
//Product: this is the Observable
package observers;
import java.util.Observable;
public class Product extends Observable
{
private boolean isAvailable = false;
private String size = "";
private String colour = "";
private String name = "";
public Product(boolean isAvailable, String size, String colour, String name)
{
this.name = name;
this.isAvailable = isAvailable;
this.size = size;
this.colour = colour;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public boolean isAvailable()
{
return isAvailable;
}
public void setAvailable(boolean isAvailable)
{
this.isAvailable = isAvailable;
//if(isAvailable()){
setChanged();//set the flag to indicate that this observable has changed
notifyObservers();//notify everyone
//}
}
public String getSize()
{
return size;
}
public void setSize(String size)
{
this.size = size;
}
public String getColour()
{
return colour;
}
public void setColour(String colour)
{
this.colour = colour;
}
@Override
public String toString(){
return String.format("Product is: %s; Availability: %s; Size: %s; Colour: %s. ", this.getName(), this.isAvailable() ? "available" : "not available", this.getSize(), this.getColour());
}
}
Here are the observables, the users:
//User: these are the observers
package observers;
import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
public class User implements Observer
{
private String name = "";
private List<Product> products = new ArrayList<Product>();
public User(String name, List<Product> products)
{
this.name = name;
this.products = products;
}
public void setName(String name)
{
this.name = name;
}
public String getName()
{
return name;
}
@Override
public void update(Observable o, Object arg)
{
System.out.println("Username " + getName() + "'s update method called. " + o);
}
@Override
public String toString(){
return String.format("Username: %s", getName());
}
}
And this is the class that test the whole thing, creating 3 products and 4 users:
package observers;
import java.util.ArrayList;
import java.util.List;
import java.util.Observer;
public class ObserverTest
{
public static void main(String[] args)
{
List<Product> products = new ArrayList<>();
List<Observer> users = new ArrayList<>();
Product product = new Product(false, "small", "white", "jumper");
Product product1 = new Product(false, "medium", "yellow", "shorts");
Product product2 = new Product(false, "large", "red", "trousers");
products.add(product);
products.add(product1);
products.add(product2);
User user1 = new User("user1", products);
User user2 = new User("user2", products);
User user3 = new User("user3", products);
User user4 = new User("user4", products);
users.add(user1);
users.add(user2);
users.add(user3);
users.add(user4);
for(Observer user : users){
for(Product currentProduct : products){
currentProduct.addObserver(user);
currentProduct.setAvailable(true);
}
}
System.out.println("Num of observers: " + product.countObservers());
}
}
I'm think the sticky point is this one:
for(Observer user : users){
for(Product currentProduct : products){
currentProduct.addObserver(user);
currentProduct.setAvailable(true);
}
}
but I'm not 100% sure why it's tripping over this one because I add the user as the observer for each product and then when the setAvailable method is called all the observers are notified.
The output I get is this:
Username user1's update method called. Product is: jumper; Availability: available; Size: small; Colour: white.
Username user1's update method called. Product is: shorts; Availability: available; Size: medium; Colour: yellow.
Username user1's update method called. Product is: trousers; Availability: available; Size: large; Colour: red.
Username user2's update method called. Product is: jumper; Availability: available; Size: small; Colour: white.
Username user1's update method called. Product is: jumper; Availability: available; Size: small; Colour: white.
Username user2's update method called. Product is: shorts; Availability: available; Size: medium; Colour: yellow.
Username user1's update method called. Product is: shorts; Availability: available; Size: medium; Colour: yellow.
Username user2's update method called. Product is: trousers; Availability: available; Size: large; Colour: red.
Username user1's update method called. Product is: trousers; Availability: available; Size: large; Colour: red.
Username user3's update method called. Product is: jumper; Availability: available; Size: small; Colour: white.
Username user2's update method called. Product is: jumper; Availability: available; Size: small; Colour: white.
Username user1's update method called. Product is: jumper; Availability: available; Size: small; Colour: white.
Username user3's update method called. Product is: shorts; Availability: available; Size: medium; Colour: yellow.
Username user2's update method called. Product is: shorts; Availability: available; Size: medium; Colour: yellow.
Username user1's update method called. Product is: shorts; Availability: available; Size: medium; Colour: yellow.
Username user3's update method called. Product is: trousers; Availability: available; Size: large; Colour: red.
Username user2's update method called. Product is: trousers; Availability: available; Size: large; Colour: red.
Username user1's update method called. Product is: trousers; Availability: available; Size: large; Colour: red.
Username user4's update method called. Product is: jumper; Availability: available; Size: small; Colour: white.
Username user3's update method called. Product is: jumper; Availability: available; Size: small; Colour: white.
Username user2's update method called. Product is: jumper; Availability: available; Size: small; Colour: white.
Username user1's update method called. Product is: jumper; Availability: available; Size: small; Colour: white.
Username user4's update method called. Product is: shorts; Availability: available; Size: medium; Colour: yellow.
Username user3's update method called. Product is: shorts; Availability: available; Size: medium; Colour: yellow.
Username user2's update method called. Product is: shorts; Availability: available; Size: medium; Colour: yellow.
Username user1's update method called. Product is: shorts; Availability: available; Size: medium; Colour: yellow.
Username user4's update method called. Product is: trousers; Availability: available; Size: large; Colour: red.
Username user3's update method called. Product is: trousers; Availability: available; Size: large; Colour: red.
Username user2's update method called. Product is: trousers; Availability: available; Size: large; Colour: red.
Username user1's update method called. Product is: trousers; Availability: available; Size: large; Colour: red.
Num of observers: 4
so 3 times for user4 which is what I expected but 6x for user3, 9x for user 2 and 12x for user1?!