RCP中的view基本上是由JFace的组件提供的.

Eclipse JFace 用ContentProvider和LabelProvider实现 MVC 架构.

RCP通过实例化viewer来实现MVC 的视图部分.而ContentProvider就是模型部分的.

看ContentProvider是要继承IStructuredContentProvider,其源码:

public Object[] getElements(Object inputElement);

可以看到输入的是object,所以其模型是和view无关的领域模型.

那要将这个模型展示在view上还需要通过LabelProvider来转换成适当的形式. 如下:

class ViewLabelProvider extends LabelProvider implements ITableLabelProvider {
public String getColumnText(Object obj, int index) {
return getText(obj);
}

public Image getColumnImage(Object obj, int index) {
return getImage(obj);
}

public Image getImage(Object obj) {
return PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_ELEMENT);
}
}

就是在getColumnText中进行转换的,将Object中的需要显示的东西retrun出来.

在 public void createPartControl(Composite parent)中

viewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL); 1)
viewer.setContentProvider(new ViewContentProvider()); 2)
viewer.setLabelProvider(new ViewLabelProvider()); 3)
viewer.setInput(new String(){"One","Two","Three"}); 4)

如上代码 1)实例化了一个viewer 2)注入一个 ContentProvider 3)注入一个LabelProvider 4)将数据模型传入view

然后可以通过viewer.refresh();来刷新视图.

可以看出JFace的做法在尽量强制你必须构建独立于GUI的模型.

 IAdaptableEclipse里是一个非常重要的接口。

乍看IAdaptable是不是想到了Adapter模式..~~,他们的作用的确是差不多的.. IAdaptable也算是对Adapter模式的一种扩展.不过比之普通的Adapter模式, IAdaptable显得是那么的强大

 

Adapter模式是能够将没有接口的实现,通过Adapter这个接口来体现出来.说白了这个实现还是存在的.IAdaptable做的不是添加接口来体现实现,他甚至可以将整个接口都改变.在数据保留的情况下将实现都改变,或者添加新的实现.所以IAdaptable做的最多的是类型的转换.而且最主要的是这一切都可以是动态的.

 

下面来看看IAdaptable是怎么运作的

1.         单单使用IAdaptable接口(静态的)

a)         普通的类型转换:

Object o = new ArrayList();

 List list = (List)o

         这种转换还是比较常见的.那我看来看看用IAdaptable是怎么转换的

b)         通过IAdaptable进行类型转换

  IAdaptable adaptable = new ArrayList();

    //注:这里的ArrayList不是指 java.util.ArrayList 而是实现了IAdaptable接口的一个ArrayList

List list = (List)adaptable.getAdapter(java.util.List.class);

这就是上面所说的动态类型转换,我们所做的事情是试图把 adaptable转换为一个 List实例。大家一定会想这不是对此一举么.但是如果我们想把一个HashMap通过IAdaptable转换成一个List.这个时候IAdaptable的威力就体现出来了.如下:

IAdaptable adaptable = new HashMap();

    //注:这里的HashMap不是指 java.util. HashMap而是实现了IAdaptable接口的一个HashMap

List list = (List)adaptable.getAdapter(java.util.List.class);

我们来看看这个getAdapter是怎么实现的:

   public class HashMap implements IAdaptable {

 public Object getAdapter(Class clazz) {

  if (clazz == java.util.List.class) {

     List list = new ArrayList(this.size());

     list.addAll(this.values());

     return list;

   }

   return null;

}

   …….

}

    大家可以看到当clazzList的时候getAdapter就会将HashMap转化为List如果还想将HashMap转为其他类型的话.只要再加一个if(){}就可以了,但是大家一定也觉得如果要增加一种转换类型就需要直接修改HashMap的源码并重新编译.这样做好像并不符合Eclipse的风格,所以Eclipse将他进一步抽象,提供了一个PlatformObject抽象类并通过AdapterManager来管理各种adapter.这样大家可以通过配置文件动态的添加adapter来动态的对一个类添加功能..大家往下看

2.         通过IAdapterManager管理Adapter(动态的)

要通过IAdapterManager来管理Adapter,那需要转换的对象就要继承PlatformObject, PlatformObject里已经实现了getAdapter:

public Object getAdapter(Class adapter) {

   return AdapterManager.getDefault().getAdapter(this, adapter);

}

可以看到它是调用了AdapterManagergetAdapter来实现转换

再看AdapterManagergetAdapter的源码:

public Object getAdapter(Object adaptable, Class adapterType) {

                   IAdapterFactory factory = (IAdapterFactory) getFactories(adaptable.getClass()).get(adapterType.getName());

                   Object result = null;

          

         if (factory != null)

                            result = factory.getAdapter(adaptable, adapterType);

                   if (result == null && adapterType.isInstance(adaptable))

                            return adaptable;

                   return result;

         }

可以看到它是将具体的转换实现封装在IAdapterFactory,通过IAdapterFactory. getAdapter来实现转换.那具体的IAdapterFactory就得大家自己来实现了.

记得还要将这个IAdapterFactory注册到platform上如下:

Platform.getAdapterManager().registerAdapters(

    new NodeListFactory(), List.class

  );

(NodeListFactory就是具体封装实现的那个IAdapterFactory)

,不然AdapterManager可不知道要去调那个IAdapterFactory.

一直觉得RCP很好玩.特别是Europa发布后,可以看到Eclipse.orgRCP的重视.RCP大有可为啊.可前段时候比较忙,没时间研究这个,现在空了点,照着RCP的教程做了我的第一个RCP程序..嘿嘿

 

1.         新建一个RCP工程(实际上就是一个plug-in工程啦).这个不用我说了吧.记得在Would you like to create a rich client application?上勾上勾.然后为了简单,可以选一个已存在的模板.来新建工程..我选了Hello world .

2.         工程起来了,如果你选了模板,那直接run as a eclipse application就可以跑起来了.虽然只有简单的一个试图而已,但至少起来了么..hoho

3.         接下来就是丰富这个RCP.我搞了个Designer 6.4.可视化编程就是爽.现在先介绍一下RCP最基本的构成.

a)         Application:这个就是整个RCP的启动程序了,里面有public Object start(IApplicationContext context)方法和public void stop() 方法.用来启动和关闭RCP应用的.

b)         Perspective:这个就是透视图了.透视图里可以包含视图和编辑器.每个RCP至少要有一个Perspective,不然没啥好显示了.

<

p style=”margin: 0cm 0cm 0pt 42pt;text-indent: -21pt” class=”MsoListParagraph”>c)         各类Advisor:最主要的有ApplicationActionBarAdvisor:用于定义菜单和工具条上的操作; ApplicationWorkbenchAdvisor:用户定

Java 序列化和反序列化之后的对象是会不一样的…这个时候还是和原来一样的操作这个对象就有可能出错.

jdk 1.5之后多了auto-boxing特性.它可以将int在使用是自动转化为Integer对象,或将Integer对象自动转化为int..所以在普通情况下两个Integer可以直接用==正确比较.但反序列化之后情况就不一样了,如果是两个Integer对象==这样的比较就不对了.必须用equal来比较.所以在重载equal方法的时候就要注意,如果是Integer比较的话,就要调用Integer的equal方法来比较,不然..嘿嘿…….

 同样的String也要注意了.

接触了一段时间的OSGi,现在也来谈一谈感想

OSGi全称Open Service Gateway Initiative ,它作为一个规范提供了开放和通用的架构,使得使用者能统一的开发,管理和部署服务.
以前OSGi主要是为嵌入式开发提供规范.但它优异的架构也使它在其他地方有所建树…最著名的就是Eclipse了,它的基础架构Equinox就是OSGi的规范工程.
现在Dynamic Module System For Java—JSR 291也通过了复审..可能过段时间JRE就直接集成OSGi了..

OSGi提供给人们一个模块设计的概念,将各种功能及服务做成Bundle即可实现热拔插.

OSGi还有一个主要的特性就是它的动态化,它可以动态的改变系统的行为..通过对不同Bundle的启动和关闭来达到不同的实现效果.

而OSGi采用的是微内核技术..以至于它能有一个稳定,健壮的系统.Bundle的崩溃不会影响到整个系统.而且它的动态加载机制,使其启动时间大幅缩短..BMW的汽车管理系统就是用的OSGi,而他启动只需要3.5秒.可见他的效率也是不差的.

综合以上的特点..可以看出OSGi提供了一个模块设计,开发的规范
这样的设计能带来更好的重用,更低的耦合
而他的动态化,也实现了热部署,热升级,无须重启任何东西,就可实现一切

但是我们也要看到OSGi不足的地方..
由于OSGi最初为嵌入式系统设计,他的Http Service很单薄,只提供了Servlet和静态资源的注册.还不能满足企业级的应用(一个OPENGOSS的开源项目扩充了OSGi的定义,已经能使它较为完整的运行web应用),以及OSGI对分布式部署和跨模块的事务支持都力不从心.而且由于设计理念的不同,将其他系统移植至OSGi下也会很困难.
现在基于Equinox的B/S应用是修改了jetty来作为http service,如其他的商业容器还不能和OSGi直接集成,不过现在IBM和BEA都在推广OSGi,相信不久的将来websphere和weblogic都将可以作为OSGi的http service.

 

 

因为OSGi是最早为嵌入式系统设计,所以OSGi标准中的HTTP服务只提供了有限的Servlet与静态资源的发布功能,没有一个完整的WEB容器概念,这种模式更适合通过WEB方式暴露(Export)服务,不太适合开发展现层的WEB应用。

现在有个开源项目opengoss通过对OSGi插件体系的扩展,基本实现了Servlet容器

具体实现:"插件"与我们通常所说软件"模块"的一个区别是:插件能自我描述,加载运行在插件容器中。那么,我们可以分层扩展一个插件的自我描述,用类似Decorator的模式为插件增加“特征”描述。 所以,我们在OpenCore中定义了三种特征的插件.

这三种插件类型间关系类似Decorator模式,从内到外增加"特征"描述:

OSGi标准插件,自描述文件"META-INF/MANIFEST.MF"
OpenCore插件,增加自描述文件"META-INF/opencore.xml",实现符合OSGi环境的依赖注册(IoC)与动态扩展点
OpenCore Web插件,增加自描述文件"WEB-INF/web.xml"(符合Servlet规范),WEB特性的插件,可以部署在Servlet容器内 .

部署图:

来看看具体怎么做

先下载opengoss的org.opengoss.osgi.core,org.opengoss.web.core,org.opengoss.web.jetty
http://opengoss.googlecode.com/svn

具体的bundle都要依赖于上面的bundle

然后就可以基于这个写具体的web层了

新建一个插件项目..这个时候的Activator和以前的就不一样了
它要继承自WebPluginActivator.如下
public class Activator extends WebPluginActivator {

 @Override
 protected ClassLoader getClassLoader() {
  return Activator.class.getClassLoader();
 }

}

然后在META-INF下添加OPENCORE.xml如下
<?xml version="1.0" encoding="utf-8"?>
 <plugin id="test" version="1.0" />
这里的id直接影响了这个应用的URI

在web.xml里添加
 <servlet>
  <servlet-name>default</servlet-name>
  <servlet-class>
   org.opengoss.web.internal.jetty.DefaultServlet
  </servlet-class>
  <init-param>
   <param-name>acceptRanges</param-name>
   <param-value>true</param-value>
  </init-param>
  <init-param>
   <param-name>dirAllowed</param-name>
   <param-value>true</param-value>
  </init-param>
  <init-param>
   <param-name>redirectWelcome</param-name>
   <param-value>false</param-value>
  </init-param>
  <init-param>
   <param-name>maxCacheSize</param-name>
   <param-value>2000000</param-value>
  </init-param>
  <init-param>
   <param-name>maxCachedFileSize</param-name>
   <param-value>254000</param-value>
  </init-param>
  <init-param>
   <param-name>maxCachedFiles</param-name>
   <param-value>1000</param-value>
  </init-param>
  <init-param>
   <param-name>useFileMappedBuffer</param-name>
   <param-value>true</param-value>
  </init-param>
  <load-on-startup>0</load-on-startup>
 </servlet>
 <servlet-mapping>
  <servlet-name>default</servlet-name>
  <url-pattern>/</url-pattern>
 </servlet-mapping>
 
OK,接下去就可以写正真的应用了….

写完之后连同opengoss的bunlde一起放到osgi环境下就可以跑起来了

目前opencore还实现了简单的IOC容器
就是在opencore.xml来配置依赖关系

OpenCore IoC容器有下面几个特性:

1.完全基于OSGi的插件体系结构。
2.简单,远没有Spring IoC容器复杂,支持属性注射(Property Injection)与构造函数注册(Constructor Injection)。
3.分级IoC容器,分为插件级、应用程序级、网络级
4.IoC容器管理的服务与OSGi框架管理的服务可以互访,这样保证了OpenCore IoC不会屏蔽OSGi本身的模型,使得基于OpenCore IoC的插件与大量第三方基于完全基于OSGi服务模型的插件可以协作。
5.支持通过动态扩展点实现1对多的依赖注册机制。

这次讲讲如何将http作为一个bundle,让其他bundle来访问其http.

从Eclipse.org上将以下的包下下来

org.eclipse.equinox.http
OR

org.eclipse.equinox.http.jetty
org.eclipse.equinox.http.servlet
org.mortbay.jetty (v5_1_11 - from Orbit Depot)
org.apache.commons.logging (v1_0_4 - from Orbit Depot)

javax.servlet (v2_4 - from Orbit Depot)
[optional] org.eclipse.equinox.http.registry

地址是
http://www.eclipse.org/equinox/server/downloads/equinoxhttp-anon.psf
http://www.eclipse.org/equinox/server/downloads/jettyhttp-anon.psf
上面两个文件是ecplise的小组项目导入文件.
可任选一个导入,下面的是将jetty作为http响应

这个时候就可以写web应用了.
可以先写一个在各种servlet容器能运行的程序,然后把它转化为bundle就可以了
具体做法是
先在plugin.xml中 加入资源的扩展
如下
<plugin>
  <extension point="org.eclipse.equinox.http.registry.resources">
    <resource
      alias="/files"
      base-name="/web_files"/>
  </extension>
</plugin>

这样http就能访问web_files下的文件了

在加入servlet扩展

<extension point="org.eclipse.equinox.http.registry.servlets">
    <servlet
      alias="/test"
      class="com.example.servlet.MyServlet"/>
  </extension>
这样.就能通过/test来访问com.example.servlet.MyServlet这个servlet了

现在Equinox已经支持servlet和jsp

ok,在OSGI框架下run一下这个bundle,然后在浏览器中输入http://localhost/files/index.html
成功

参考:http://www.eclipse.org/equinox/server/http_writing_application.php

Equinox作为一个OSGI的示范工程.那是相当的牛b啊.Eclipse就是建立在Equinox上的.所以说Equinox是个好东西啊.

既然Equinox是个好东西,那怎么将Equinox应用到Http Server上,让那些web应用作为bundle在Equinox如插件般插来插去,那是多么惬意的事情.

现在就来看看怎么实现这个目标吧.

现在Equinox实现与Http Server整合有两种方式
1.将Equinox建立在servlet容器上,由Equinox的servletbridge来转发request

2.将http服务作为一个bundle,让其他bundle来访问其http服务.

先来说说 1.
a)先装上tomcat
b)下载bridge.war (http://www.eclipse.org/equinox/server/downloads/bridge.war) 它主要的作用是将HTTP请求转发给OSGI HTTP Services
c)将bridge.war拷到webapps下,启动tomcat!OK,OSGI也会随着一起起来.

这个时候就可以在osgi下install ,start ,stop 各种bundle了
现在有两种方式将web应用发布成bunlde
i).用由org.eclipse.eqinox.servlet.bridge.http注册的OSGi HttpService来发布这个bundle
ii).作为org.eclipse.equinox.http.registry的扩展点来发布.

现在看i.)
  先写一个servlet:
     public class HelloWorldServlet extends HttpServlet {

 protected void doGet(HttpServletRequest req, HttpServletResponse resp)
   throws ServletException, IOException {
  resp.setContentType("text/html");
  resp.getWriter().write("<html><body>Hello World – sample servlet</body></html>");
 }

  }
 
  这个时候需要一个Activator来获得BundleContext 并注册上面的Servlet为Http Service
  public class Activator implements BundleActivator {

 private ServiceTracker httpServiceTracker;
 
 public void start(BundleContext context) throws Exception {
  httpServiceTracker = new HttpServiceTracker(context);
  httpServiceTracker.open();
 }

 public void stop(BundleContext context) throws Exception {
  httpServiceTracker.close();
  httpServiceTracker = null;
 }

 private class HttpServiceTracker extends ServiceTracker {

  public HttpServiceTracker(BundleContext context) {
   super(context, HttpService.class.getName(), null);
  }

  public Object addingService(ServiceReference reference) {
   HttpService httpService = (HttpService) context.getService(reference);
   try {   
    httpService.registerResources("/helloworld.html", "/helloworld.html", null); //$NON-NLS-1$ //$NON-NLS-2$
    httpService.registerServlet("/helloworld", new HelloWorldServlet(), null, null); //$NON-NLS-1$
   } catch (Exception e) {
    e.printStackTrace();
   }
   return httpService;
  }  
  
  public void removedService(ServiceReference reference, Object service) {
   HttpService httpService = (HttpService) service;
   httpService.unregister("/helloworld.html"); //$NON-NLS-1$
   httpService.unregister("/helloworld"); //$NON-NLS-1$
   super.removedService(reference, service);
  }
 }
  }

首先从BundleContext中得到HttpService的reference。然后从BundleContext中拿到HttpService的实例。向httpService注册你的Servlet。最后导出为一个Bundle。

现在导出成一个bundle,在刚才的OSGI中install 并start .然后在浏览器输入http://localhost/bridge/helloworld,就可以看到效果了.

在看ii)将web应用作为扩展点,这样Activator就可以省去了.用plugin.xml来代替Activator描述注册信息,如下

<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.0"?>
<plugin>
     <extension-point id="servlets" name="HttpService servlets" schema="schema/servlets.exsd"/>
     <extension-point id="resources" name="HttpService resources" schema="schema/resources.exsd"/>
     <extension-point id="httpcontexts" name="HttpService httpcontexts" schema="schema/httpcontexts.exsd"/>
     <extension
           id="helloServlet"
           point="org.eclipse.equinox.http.registry.servlets">
        <servlet
              alias="/ext/helloworld"
              class="sample.http.registry.HelloWorldServlet">
           <init-param
                 name="servlet-name"
                 value="Test Servlet">
           </init-param>
           <init-param
                 name="testParam"
                 value="test param value">
           </init-param>
        </servlet>
     </extension>
     <extension
           id="helloResource"
           point="org.eclipse.equinox.http.registry.resources">
        <resource
              alias="/ext/helloworld.html"
              base-name="/helloworld.html"
              />
     </extension>
</plugin>

OK,现在导出成一个bundle,在刚才的OSGI中install 并start .然后在浏览器输入http://localhost/bridge/helloworld,看看是不是和刚才的效果一样啊

好了下次在讲讲2.是怎么实现的

参考:http://www.eclipse.org/equinox

Test Case 作用

1. 检查你的产品代码是否按预期工作, 这由函数体来完成

2. 表达你的预期,让阅读代码的人知道你的产品能够干什么,如何使用, 甚至如何设计的;这除了函数体的assert语句外,Test case的名字更是重要的手段

3.保证重构不出错

4.展示API的用法, 最好的Quick Start

5.Domain知识的载体, 最好的文档

6……

 

Test Case的命名

1, 如果Domain比较简单,  比如上面的Calculator, 每个人都很清楚, 你就不需要再通过测试用例的名字罗里罗嗦了, 这时候可以选用表达实现的名字

2, 如果Domain比较复杂, 进入项目的新人不一定了解, 这时候你的测试用例的名字就是最好的领域知识

Test Case的装配
1. 如果一个对象很容易用一两句话装配, 一般可以在每个测试用例里就近创建它

2. 如果初始化一个对象很复杂, 要不少代码, 就写一个函数来做初始化, 然后在每个测试用例里就近调用它

3. setUp()里主要是一些对外部依赖环境的设置.

Mocks Vs. Stubs

1. Stubs 属于 State Based Test方法的实现方式 .他测试的是方法的结果..对其中的过程并不关心.

2. Mocks 属于 Interaction Based Test/ Behaviour verification Test的实现方式 .他还要测试方法的过程.

3. <<Mocks Aren't Stubs>>

4. Stubs 的注意事项:
     不要包含任何逻辑;
     强迫你重新考虑你的设计 (其实这几乎是任何高质量的测试用例都能带来的)
        1), 调用链
              如果调用链很长就需要写很多Stubs类.
        2), 针对接口编程

5.动态代理Stub(zz)

  这是介于静态手写Stub和完全的MockObject之间的一种方式, 基本上用它来简化Stub的编写, 所以本质上还是属于 Stub, 尽管Mock Object可能是用动态代理实现的

动态代理Stub相比静态手写Stub的主要好处就是不需要Stub实现所有API, 只要 在invoke之类的函数中针对测试用例用到的函数进行处理就可以了

还有一种用反射实现的代理, 可以复用现有的Stub, 不需要这些Stub实现什么接口, 只需要函数签名一致即可

6. Mock Object
   1)Mock则更多的关注于Mock 对象的某个方法是否被调用(行为是否发生), 以及调用时的参数和返回值.
  
   2)重构会破坏测试用例(包含Mock Object), 即使你的重构是正确的

   3)体现在那些紧凑的API上, 即单一API调用, 根据不同参数返回不同结果.不适合用Mock Object.最好使用真实数据.
 
   4)Stub 不到万不得以不用, Mock 能用则用.
  
Don’t Ask, Tell

   Ask 带来的坏处:

      1.   破坏对象的封装性, 你不得不暴露很多的属性供别人Ask.

      2.   容易使得一些对象过于复杂(会计部), 而一些对象(员工)又过于简单, 甚至成为了仅仅包含数据的”哑”对象.

   Tell 带来的好处:

      1.  更多的考虑责任分配的合理性, 方法涉及的数据在哪里方法就应该在哪里. 这样对象的内聚性就大大加强了.

      2.  增强了对象的封装性, 对象不必暴露更多的属性.

      3.  可以充分发挥面向对象的特性,比如多态, 减少”哑”对象从而获得更强的可维护性,扩展性以及面对变化的能力.