package cn.com.chenyixiao.designpattern.observer.zoo;

import java.util.ArrayList;
import java.util.List;

/**
 * 动物园类, 相当于观察者模式中的Subject, 负责存储动物园中的所有动物, 并在新动物加入时通知所有已注册的监听器.
 * @author 尘一笑
 * 这个类比前面两个都复杂，其包含两个list，一个用来存储动物园中所有动物，另一个用来存储所有的监听器，
 * 鉴于animals和listener集合存储的对象都很简单，本文选择了ArrayList来存储。
 * 存储监听器的具体数据结构要视问题而定，比如对于这里的动物园问题，如果监听器有优先级，那就应该选择其他的数据结构，或者重写监听器的register算法。
 * 

	注册和移除的实现都是简单的委托方式：各个监听器作为参数从监听者的监听列表增加或者移除。
	notify函数的实现与观察者模式的标准格式稍微偏离，它包括输入参数：新增加的animal，这样一来notify函数就可以把新增加的animal引用传递给监听器了。
	用streams API的forEach函数遍历监听器，对每个监听器执行theonAnimalAdded函数。
	在addAnimal函数中，新增的animal对象和监听器各自添加到对应list。
	如果不考虑通知过程的复杂性，这一逻辑应包含在方便调用的方法中，只需要传入指向新增animal对象的引用即可，
	这就是通知监听器的逻辑实现封装在notifyAnimalAddedListeners函数中的原因，这一点在addAnimal的实现中也提到过。
	除了notify函数的逻辑问题，需要强调一下对notify函数可见性的争议问题。在经典的观察者模型中，
	如GoF在设计模式一书中第301页所说，notify函数是public型的，然而尽管在经典模式中用到，这并不意味着必须是public的。
	选择可见性应该基于应用，例如本文的动物园的例子，notify函数是protected类型，并不要求每个对象都可以发起一个注册观察者的通知，只需保证对象能从父类继承该功能即可。
	当然，也并非完全如此，需要弄清楚哪些类可以激活notify函数，然后再由此确定函数的可见性。
 */
public class Zoo {

	List<Animal> animals = new ArrayList<>();
	List<AnimalAddedListener> animalAddedListeners = new ArrayList<>();
	
	public void AddAnimal(Animal animal) {
		this.animals.add(animal);
		this.notifyAnimalAddedListeners(animal);
	}
	
	public void registerAnimalAddedListener(AnimalAddedListener listener) {
		this.animalAddedListeners.add(listener);
	}
	
	public void unregisterAnimalAddedListener(AnimalAddedListener listener) {
		this.animalAddedListeners.remove(listener);
	}
	
	protected void notifyAnimalAddedListeners(Animal animal) {
		this.animalAddedListeners.forEach(listener -> listener.onAnimalAdded(animal));
	}
}
