您的位置:首页 > 其它

基于Lookup的IOC事件总线框架

2015-09-23 16:08 471 查看
Lookup在Netbeans IDE中可以说是整个软件架构系统的灵魂。从本质上讲就是以apdatable模式实现的一个Map,其主要功能体现以下几个方面:

提供一个Context 可以想象Lookup是一个magic bag可以在运行时(runtime)方便的在bag中添加、删除对象 而这些对象恰好可以组织成系统当前的context get it?
由于Adaptable的实现方式(核心是泛型),准确的用Class作为参数查找Map中的实例避免了类型转换的问题,把问题留在了编译时。
Map内部的监听机制 可以实现热插拔 动态更新的功能
基于Lookup这样令人兴奋的功能,我实在忍不住想用它来实现一个消息系统。于是我突然面临两个选择 用publisher/subscriber? 还是IOC的? 两者的代码量都不大,但是我更青睐IOC 虽然有悖“开 闭”原则但对于一个小的系统还是足够了 毕竟300行代码 我相信它的成长空间还是巨大的。多说无益直接上代码

/**
*
* @author Vijay
* of course  you can say Callback itself is one of subscribers
*/
public interface Subscriber {

/**
* the smaller priority will get done earlier
*
* @return
*/
Integer priority(Message msg);

}

/**
* All callbacks have to extends this ifc thanks for your cooperation
*
* @author Vijay
*/
public interface Callback extends Subscriber {

final static Integer DEFAULT_PRIORITY = 0;
final static Integer HIGH_PRIORITY = 1;
final static Integer MEDIUM_PRIORITY = 10;
final static Integer LOW_PRIORITY = 100;

/**
* The <code>call</code> method is called when required, and is given a
* single argument of type P, with a requirement that an object of type R is
* returned.
*
* @param param The single argument upon which the returned value should be
* determined.
* @return An object of type R that may be determined based on the provided
* parameter value.
*/
public void call(Message msg);

/*
we only broadcast abstract message
*/
public abstract class Message {

final private Param param;

protected Message(Param param) {
this.param = param;
}

public Param getParam() {
return param;
}

public abstract Class<? extends Callback> getRelatedCallback();

public static Message createLocate(Param param) {
Message message = new Locate(param);
return message;
}

/**
* concrete built-in message here and always come with Operation(Callback)
*/
//locate
private static class Locate extends Message {

private Locate(Param param) {
super(param);
}

@Override
public Class getRelatedCallback() {
return Moveable.class;
}

}

public interface Moveable extends Callback {
}

//add
private static class Add extends Message {

private Add(Param param) {
super(param);
}

@Override
public Class getRelatedCallback() {
return Addable.class;
}
}

public interface Addable extends Callback {

}

//delete ....   continue
}

}

/**
*
* @author Vijay
*/
public interface Context {
Lookup getBusContext();
}

/**
*
* @author Vijay
*/
public class DefaultContext implements Context{

@Override
public Lookup getBusContext() {
return  Utilities.actionsGlobalContext();
}

}
import java.util.Map;

/**
* Sometimes we needs to define data structure to get parameter
*
*
*/
public final class Param {

final Map<String, Object> attachments;
final Integer priority;

private Param(Integer priority, Map<String, Object> attachments) {
this.priority = priority;
this.attachments = attachments;
}

public static Param empty() {
return new Param(Callback.DEFAULT_PRIORITY, null);
}

public final Param priority(Integer priority) {
return new Param(priority, this.attachments);
}

public final Param attachments(Map<String, Object> attachments) {
return new Param(this.priority, attachments);
}

public Integer getPriority() {
return priority;
}

public Map<String, Object> getAttachments() {
return attachments;
}
}

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.openide.util.Lookup;

/**
* Treat Context as a System Bus
*
* @author Vijay
*/
public final class BusEngine {

private static final BusEngine instance = new BusEngine();
private final ExecutorService executor;

static {
}

private BusEngine() {
executor = Executors.newSingleThreadExecutor();
}

public void broadcast(final Callback.Message msg) {
executor.submit(new Runnable() {
@Override
public void run() {
doSyncCallback(msg,new DefaultContext().getBusContext());
}
});
}

void broadcast(final Callback.Message msg,final Lookup lkp) {
executor.submit(new Runnable() {
@Override
public void run() {
doSyncCallback(msg,lkp);
}
});
}

public static BusEngine getDefault() {
return instance;
}

@Deprecated
static void refreshAllInContext(Callback.Message msg,Context cxt) {
doSyncCallback(msg,cxt.getBusContext());
}

static void doSyncCallback(final Callback.Message msg,Lookup lkp) {
Lookup.Template template = new Lookup.Template(msg.getRelatedCallback());
Lookup.Result result = lkp.lookup(template);
Collection c = result.allInstances();
List<Callback> callbacks = new ArrayList();
callbacks.addAll(c);
Collections.sort(callbacks, new Comparator<Callback>() {
@Override
public int compare(Callback o1, Callback o2) {
return o1.priority(msg)-o2.priority(msg);
}
});
for (Callback cb : callbacks) {
processMsg(cb, msg);
}
}

static void processMsg(Callback cb, final Callback.Message msg) {
cb.call(msg);
}

}


以上代码需要依赖org.openide.util.jar 和org.openide.util.lookup.jar  由于测试需要有些方法开放为包可见

为了说明其用例 请参考以下测试代码

import java.util.ArrayList;
import java.util.List;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

/**
*
* @author Vijay
*/
public class BusEngineNGTest {

/**
* use list to test priority
*/
static List<Callback> GLOBAL_TEST_LIST;

public BusEngineNGTest() {
}

@BeforeClass
public static void setUpClass() throws Exception {
}

@AfterClass
public static void tearDownClass() throws Exception {
}

@BeforeMethod
public void setUpMethod() throws Exception {
GLOBAL_TEST_LIST = new ArrayList();
}

@AfterMethod
publi
9c84
c void tearDownMethod() throws Exception {
GLOBAL_TEST_LIST = null;
}

@Test
public void testBasicFunctionMsg1() throws InterruptedException {
BusEngine.getDefault().broadcast(new CBMsg1(Param.empty()), new TestContext().getBusContext());
Thread.sleep(1000);
Assert.assertEquals(GLOBAL_TEST_LIST.size(), 1);
Assert.assertEquals(GLOBAL_TEST_LIST.get(0).priority(null),new Integer(1));

}

@Test
public void testBasicFunctionMsg2() throws InterruptedException {
BusEngine.getDefault().broadcast(new CBMsg2(Param.empty()), new TestContext().getBusContext());
Thread.sleep(1000);
Assert.assertEquals(GLOBAL_TEST_LIST.size(), 2);
Assert.assertEquals(GLOBAL_TEST_LIST.get(0).priority(null),new Integer(1));
Assert.assertEquals(GLOBAL_TEST_LIST.get(1).priority(null),new Integer(10));
}
}

/**
*
* @author Vijay
*/
public interface CB1 extends Callback{

}
/**
*
* @author Vijay
*/
public interface CB2 extends Callback{

}
/**
*
* @author Vijay
*/
public class CBMsg1 extends Message{

public CBMsg1(Param param) {
super(param);
}

@Override
public  Class<? extends Callback> getRelatedCallback() {
return CB1.class;
}
}
/**
*
* @author Vijay
*/
public class CBMsg2 extends Message {

public CBMsg2(Param param) {
super(param);
}

@Override
public Class getRelatedCallback() {
return CB2.class;
}
}
/**
*
* @author Vijay
*/
public class Subscriber1 implements CB1, CB2 {

@Override
public void call(Message msg) {
final String CBName = msg.getRelatedCallback().getSimpleName();
if ("CB1".equals(CBName)) {
Assert.assertEquals(CBName, "CB1");
} else {
Assert.assertEquals(CBName, "CB2");
}
BusEngineNGTest.GLOBAL_TEST_LIST.add(this);
}

@Override
public Integer priority(Message msg) {
return 1;
}
}
/**
*
* @author Vijay
*/
public class Subscriber2 implements CB2{

@Override
public void call(Message msg) {
final String simpleName = msg.getRelatedCallback().getSimpleName();
Assert.assertEquals("CB2",simpleName);
BusEngineNGTest.GLOBAL_TEST_LIST.add(this);
}

@Override
public Integer priority(Message msg) {
return 10;
}

}
/**
*
* @author Vijay
*/
public class TestContext implements Context{

@Override
public Lookup getBusContext() {
return Lookups.fixed(new Subscriber1(),new Subscriber2());
}
}

我所说的有悖“开闭”原则 是指我们必须实现Callback的call方法 这样如果Subscriber实现多个Callback 不得不以Message来区分。目前我还没有想到解决的办法... 望各位看官不吝指教
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息