基于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行代码 我相信它的成长空间还是巨大的。多说无益直接上代码
以上代码需要依赖org.openide.util.jar 和org.openide.util.lookup.jar 由于测试需要有些方法开放为包可见
为了说明其用例 请参考以下测试代码
我所说的有悖“开闭”原则 是指我们必须实现Callback的call方法 这样如果Subscriber实现多个Callback 不得不以Message来区分。目前我还没有想到解决的办法... 望各位看官不吝指教
提供一个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来区分。目前我还没有想到解决的办法... 望各位看官不吝指教
相关文章推荐
- 插件管理框架 for Delphi(一)
- 使用CSS框架布局的缺点和优点小结
- 列举PHP的Yii 2框架的开发优势
- Windows窗体的.Net框架绘图技术实现方法
- 浅谈JavaScript 框架分类
- 轻量级javascript 框架Backbone使用指南
- javascript实现框架高度随内容改变的方法
- JS刷新框架外页面七种实现代码
- 超赞的动手创建JavaScript框架的详细教程
- asp.net4.0框架下验证机制失效的原因及处理办法
- 插件管理框架 for Delphi(二)
- 零基础学习AJAX之AJAX框架
- Ajax 框架学习笔记
- Flex中最好的MVC框架Mate框架
- JavaScript 异步调用框架 (Part 4 - 链式调用)
- JavaScript 异步调用框架 (Part 2 - 用例设计)
- 为什么使用框架 使用框架的优缺点
- JavaScript 异步调用框架 (Part 3 - 代码实现)
- js刷新框架子页面的七种方法代码
- JavaScript框架编程第1/2页