package com.xiaolu.eventdemo;

import java.util.EventListener;
import java.util.EventObject;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

/**
 * @class EventDemo
 * @author xiaolu
 * @date 2015年11月19日 上午10:40:51
 * @purpose 实现java中自定义事件
 */

/*
 * 最近重新翻看了c#的自定义事件,回头实现下java中的自定义事件。
 */

/*
 * java中自定义事件,需要知道三个角色:
 * 
 * 事件对象: 一般说来,事件对象需要从EventObject类进行继承,
 * 事件对象承载了事件发生时向监听者【对事件感兴趣的类】发送 的封装参数,
 *EventArgs继承的附加信息类】。
 * 
 * 事件源: 事件源是事件由谁发出的。比如一个Button按钮,
 * 在单击时会有"Click"事件产生,那么事件源就是Button按钮。
 * 通俗的说就是产生事件的"源头"。
 * 
 * 对事件进行监听【也就是哪些对事件感兴趣的客户(类),在期望的
 * 事件发生时,需要有一些"动作"】
 */

/*
 * 背景: 有新的邮件到来时,需要向传真机和寻呼机发送消息。
 * 【和C#事件那个背景一样,体会下两者的实现思想】
 */

// 按照一般习惯,先定义事件源

//我们需要自己实现对事件的注册,撤销注册等【c#直接有+=,-=】
// 同时,类似于c#中的事件委托,我们需要定义一个事件监听者接口
//【统一具体事件监听者的“回调”方法签名形式】

// 定义事件对象【叫做事件状态对象更好点,它只是传达事件相关附加信息】
class NewMailObject extends EventObject {

    private static final long serialVersionUID = 1L;
    private String from;
    private String to;
    private String subject;

    public String getFrom() {
        return from;
    }

    public String getTo() {
        return to;
    }

    public String getSubject() {
        return subject;
    }

    public NewMailObject(Object source, String from, String to, String subject) {
        super(source);
        this.from = from;
        this.to = to;
        this.subject = subject;
    }

}

interface NewMailListener extends EventListener {
    public void NewMailComing(NewMailObject nmo);
    public void register(MailManager mm);
    public void unregister(MailManager mm);
}

class MailManager {
    // 维护一个事件监听者集合【使用Set,不允许重复注册】
    private Set<NewMailListener> listeners = new HashSet<NewMailListener>();

    /**
     * @function register
     * @param num
     *            事件监听者
     * @purpose 具体事件监听者向事件源注册自己
     */
    public void register(NewMailListener num) {
        if (num != null) {
            listeners.add(num);
        }
    }

    /**
     * @function unregister
     * @param unm
     *            事件监听者
     * @purpose 具体事件监听者从事件源撤销自己【不再关注此事件】
     */
    public void unregister(NewMailListener unm) {
        if (unm != null) {
            if (listeners.contains(unm)) {
                listeners.remove(unm);
                unm = null;
            }
        }
    }

    /**
     * @function OnNewMailComming
     * @param nmo
     * @purpose 邮件到来时的响应
     */
    protected void OnNewMailComming(NewMailObject nmo) {
        // 一些自己的处理逻辑
        notifyListeners(nmo);// 发出通知
    }

    /**
     * @function notifyListeners
     * @param nmo
     *            邮件信息
     * @purpose 当邮件到来时,通知集合中的事件监听者
     */
    private void notifyListeners(NewMailObject nmo) {
        Iterator<NewMailListener> iter = listeners.iterator();
        while (iter.hasNext()) {
            iter.next().NewMailComing(nmo);
        }
    }

    // 为了实例完整运行,创建一个模拟邮件到来函数

    public void simulateNewMailComming() {
        OnNewMailComming(new NewMailObject(this, "xiaolu", ", "this is a demo!"));
    }
}

// 创建具体监听者类
class Posage implements NewMailListener {

    @Override
    public void NewMailComing(NewMailObject nmo) {
        System.out.println("Posage received : " + nmo.getSubject());
    }

    @Override
    public void register(MailManager mm) {
        mm.register(this);
    }

    @Override
    public void unregister(MailManager mm) {
        mm.unregister(this);
    }
}

class Fax implements NewMailListener {

    @Override
    public void NewMailComing(NewMailObject nmo) {
        System.out.println(
                "Fax receved : from : " + nmo.getFrom() +
+ nmo.getSubject());
    }

    @Override
    public void register(MailManager mm) {
        mm.register(this);
    }

    @Override
    public void unregister(MailManager mm) {
        mm.unregister(this);
    }

}

//驱动类
public class EventDemo {
    public static void main(String[] args) {
        MailManager mm = new MailManager();//创建事件源
        NewMailListener listener1 = new Fax();
        NewMailListener listener2 = new Posage();

        listener1.register(mm);
        listener2.register(mm);

        mm.simulateNewMailComming();
    }
}

/*
 * ps: 在设计这个NewMailListener的时候,纠结了好一会,
 * 网上大多数实现都是没有register/unregister这样的接口方法的
 * 那么在Main【驱动类中】,就需要写下
 * mm.register(listener1);
 * mm.register(listener2);
 * 这样的语句:不和符合语义: 注册应该是注册者自己的行为。而不应该
 * 是事件源去注册。
 * 有两段完全几乎重复的代码,可以考虑在接口下建立一个
 * 适配器【此处就不再写了】
 * 还是无法阻止外部去这样调用:
 * mm.register(listener1);....
 * 如果有好的想法和建议,欢迎讨论。
 */