java里的回調(diào)與事件委托
1、java里的回調(diào)是什么?
這里講解的很詳細(xì),http://blog.csdn.net/veryitman/article/details/6937468
就是A類中調(diào)用B類中的某個(gè)方法c,然后B類中c反過來調(diào)用A類中的方法d,d這個(gè)方法就叫回調(diào)方法。
其實(shí)strategy**、template**、observer**、visitor**模式全部都是回調(diào)的不同應(yīng)用。簡(jiǎn)單來說就是本來可以寫死在一起的代碼給拆開來,讓其中一塊保持原有的流程,并在流程中挖出一些空,讓另一塊代碼作為參數(shù)傳進(jìn)來在流程中合適的地方被調(diào)用,也就是UML里所說的聚合。
2、委托到底是什么?
java中沒有委托,但是java中有個(gè)委托事件的模型。
參考 http://blog.csdn.net/yanshujun/article/details/6494447

1.1、觀察者模式與事件委托對(duì)比
可以參考博文:http://blog.csdn.net/gdutxiaoxu/article/details/51824769
1.2、事件委托
看個(gè)例子,我們先來看一下 我們的通知者GoodNotifier是怎樣實(shí)現(xiàn)的?
public class GoodNotifier extends Notifier {
@Override
public void addListener(Object object, String methodName, Object... args) {
System.out.println("有新的同學(xué)委托盡職盡責(zé)的放哨人!");
EventHandler handler = this.getEventHandler();
handler.addEvent(object, methodName, args);
}
@Override
public void notifyX() {
System.out.println("盡職盡責(zé)的放哨人告訴所有需要幫忙的同學(xué):老師來了");
try{
this.getEventHandler().notifyX();
}catch(Exception e){
e.printStackTrace();
}
}
}
接著我們?cè)趤?看它的父類Notifier是怎樣實(shí)現(xiàn)的
/**
* 通知者的 抽象類
*/
public abstract class Notifier {
private EventHandler eventHandler = new EventHandler();
public EventHandler getEventHandler() {
return eventHandler;
}
public void setEventHandler(EventHandler eventHandler) {
this.eventHandler = eventHandler;
}
/**
* 增加需要幫忙 放哨 的 學(xué)生
*
* @param object 要執(zhí)行方法的對(duì)象
* @param methodName 執(zhí)行方法 的方法名
* @param args 執(zhí)行方法的參數(shù)
*/
public abstract void addListener(Object object, String methodName, Object... args);
/**
* 告訴所有要幫忙放哨的學(xué)生:老師來了
*/
public abstract void notifyX();
}
看了上面notifier的代碼以后,你是不是有一種似曾相識(shí)的感覺,是不是感覺跟我們的觀察者模式中的通知者很相似
-
觀察者模式我們的通知者是這樣實(shí)現(xiàn)的
@Override public void notifyAllObserver(Object data) { for (Observer observer : mList) { observer.update(this,data); } } 在事件委托中我們是這樣實(shí)現(xiàn)的
@Override
public void notifyX() {
System.out.println("盡職盡責(zé)的放哨人告訴所有需要幫忙的同學(xué):老師來了");
try{
EventHandler handler = this.getEventHandler();
handler.notifyX();
}catch(Exception e){
e.printStackTrace();
}
}
-
兩者的區(qū)別其實(shí)即使我們將其交給EventHandler去處理
下面我們來看一下EventHandler我們是怎樣實(shí)現(xiàn)的
/**
* 事件的 處理者
*/
public class EventHandler {
//是用一個(gè)List
private List<Event> objects;
public EventHandler(){
objects=new ArrayList<Event>();
}
//添加某個(gè)對(duì)象要執(zhí)行的事件,及需要的參數(shù)
public void addEvent(Object object,String methodName,Object...args){
objects.add(new Event(object,methodName,args));
}
//通知所有的對(duì)象執(zhí)行指定的事件
public void notifyX() throws Exception{
for(Event e : objects){
e.invoke();
}
}
}
其實(shí)跟觀察者 模式一樣,我們把我們的事件對(duì)象存儲(chǔ)在
objects=new ArrayList<Event>();
當(dāng)我們添加 事件的時(shí)候,把事件封裝為Event對(duì)象,再添加到objects中
objects.add(new Event(object,methodName,args));
當(dāng)我們要通知事件的時(shí)候,再遍歷List,通知每一個(gè)事件對(duì)象
for(Event e : objects){
e.invoke();
}
看到invoke()這個(gè)方法,你是不是想到了什么,沒錯(cuò),就是java的反射機(jī)制,我們封裝在Event類 中,下面我們來看一下Event類是怎樣實(shí)現(xiàn)的
/**
* 事件對(duì)象的封裝類
*/
public class Event {
//要執(zhí)行方法的對(duì)象
private Object object;
//要執(zhí)行的方法名稱
private String methodName;
//要執(zhí)行方法的參數(shù)
private Object[] params;
//要執(zhí)行方法的參數(shù)類型
private Class[] paramTypes;
public Event(){}
public Event(Object object,String methodName,Object...args){
this.object=object;
this.methodName=methodName;
this.params=args;
contractParamTypes(this.params);
}
//根據(jù)參數(shù)數(shù)組生成參數(shù)類型數(shù)組
private void contractParamTypes(Object[] params){
this.paramTypes=new Class[params.length];
for(int i=0;i<params.length;i++){
this.paramTypes[i]=params[i].getClass();
}
}
public Object getObject() {
return object;
}
//這里省略了若干get和set方法
/**
* 根據(jù)該對(duì)象的方法名,方法參數(shù),利用反射機(jī)制,執(zhí)行該方法
* @throws Exception
*/
public void invoke() throws Exception{
Method method=object.getClass().getMethod(this.getMethodName(),
this.getParamTypes());
if(null==method){
return;
}
method.invoke(this.getObject(), this.getParams());
}
}
在Event類我們invoke()方法所做的工作就是根據(jù)我們的對(duì)象的方法名,方法參數(shù),利用發(fā)射執(zhí)行方法
運(yùn)行一下 測(cè)試代碼
//創(chuàng)建一個(gè)盡職盡責(zé)的放哨者
Notifier goodNotifier=new GoodNotifier();
//創(chuàng)建一個(gè)玩游戲的同學(xué),開始玩游戲
WatchCartoonListener playingGameListener=new WatchCartoonListener();
//創(chuàng)建一個(gè)看電視的同學(xué),開始看電視
WatchingNBAListener watchingTVListener=new WatchingNBAListener();
//玩游戲的同學(xué)告訴放哨的同學(xué),老師來了告訴一下
goodNotifier.addListener(playingGameListener, "stopPlayingGame",new Date());
//看電視的同學(xué)告訴放哨的同學(xué),老師來了告訴一下
goodNotifier.addListener(watchingTVListener, "stopWatchingTV",new Date());
try{
//一點(diǎn)時(shí)間后
Thread.sleep(1000);
}catch(Exception e){
e.printStackTrace();
}
//老師出現(xiàn),放哨的人通知所有要幫忙的同學(xué):老師來了
goodNotifier.notifyX();
結(jié)果 如下
WatchCartoonListener 我正在看漫畫,開始時(shí)間:Mon Jul 04 13:25:34 CST 2016
WatchingNBAListener我正在看NBA,開始時(shí)間是: Mon Jul 04 13:25:34 CST 2016
有新的同學(xué)委托盡職盡責(zé)的放哨人!
有新的同學(xué)委托盡職盡責(zé)的放哨人!
盡職盡責(zé)的放哨人告訴所有需要幫忙的同學(xué):老師來了
WatchCartoonListener 老師來了,不要看漫畫了,結(jié)束時(shí)間:Mon Jul 04 13:25:34 CST 2016
WatchingNBAListener老師來了,快關(guān)閉NBA直播 , 結(jié)束時(shí)間是:Mon Jul 04 13:25:34 CST 2016
1.3、委托能解決什么問題?(好處)
這里,通知者類完全不知道自己需要通知的是誰,做到了完全解耦(1),同時(shí)也去掉了抽象的觀察者類(2)。觀察者模式的缺點(diǎn)是抽象通知者依賴抽象觀察者,.NET中用委托技術(shù)處理這個(gè)問題,事件委托擴(kuò)展性好(3)
即:
1. 放哨者完全不知道做游戲者的存在,完全解耦。(當(dāng)然,功勞歸功于Event和EventHandler,且這兩個(gè)類具有通用性)
2. 老師來了后游戲者停止游戲回到座位,看NBA者停止看NBA,看漫畫這停止看漫畫,玩游戲這停止玩游戲。(一次通知,執(zhí)行了不同類的不同方法)
3. 擴(kuò)展性很高,再來一個(gè)打籃球的學(xué)生就先寫個(gè)打籃球?qū)W生類,并在測(cè)試代碼中告訴放哨者一下就好,放哨者完全沒有變。重用性好
3、代理模式(Proxy Pattern)
可以翻譯成代理模式,也可以翻譯成委托模式。
java中實(shí)現(xiàn)代理:
// 抽象角色:
abstract class Subject {
abstract public void request();
}
// 真實(shí)角色:實(shí)現(xiàn)了Subject的request()方法
class RealSubject extends Subject {
public RealSubject() {}
public void request() {
System.out.println(" From real subject. ");
}
}
// 代理角色:
class ProxySubject extends Subject {
// 以真實(shí)角色作為代理角色的屬性
private Subject realSubject;
public ProxySubject(Subject realSubject) {
this.realSubject = realSubject;
}
// 該方法封裝了真實(shí)對(duì)象的request方法
public void request() {
//preRequest();
System.out.println("代理準(zhǔn)備...");
// 此處執(zhí)行真實(shí)對(duì)象的request方法
realSubject.request();
//postRequest();
System.out.println("代理結(jié)束...");
}
}
public class Client {
public static void main(String[] args) {
RealSubject real = new RealSubject();
Subject sub = new ProxySubject(real);
sub.request();
}
}

以下是java中實(shí)現(xiàn)動(dòng)態(tài)代理的方式
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 抽象角色
interface Subject {
void request();
}
// 具體角色RealSubject:
class RealSubject implements Subject {
public RealSubject() {}
public void request() {
System.out.println(" From real subject. ");
}
}
//動(dòng)態(tài)代理對(duì)象
class DynamicSubject implements InvocationHandler {
private Object sub;
public DynamicSubject() {}
public DynamicSubject(Object obj) { sub = obj; }
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(" before calling " + method);
Object invoke = method.invoke(sub, args);
System.out.println(" after calling " + method);
return invoke;
}
}
public class Client {
static public void main(String[] args) throws Throwable {
RealSubject rs = new RealSubject(); // 在這里指定被代理類
InvocationHandler ds = new DynamicSubject(rs);
Class cls = rs.getClass();
Subject subject = (Subject) Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), ds);
subject.request();
}
}

java中委托事件的模型(Delegated Event Model)
六類uml關(guān)系
http://www.open-open.com/lib/view/open1328059700311.html
在idea自帶的diagram中組合和聚合都是一樣的實(shí)心箭頭,但是多了個(gè)create的虛線,
其實(shí)標(biāo)準(zhǔn)的來講組合應(yīng)該是實(shí)心箭頭,聚合應(yīng)該是虛心箭頭。
參考
[1]:http://hbohuan.blog.163.com/blog/static/208489820077132225530/
[2]:java語言實(shí)現(xiàn)事件委托模式 http://blog.csdn.net/yanshujun/article/details/6494447