【问题标题】:Best Practice in an OSGi UI applicationOSGi UI 应用程序的最佳实践
【发布时间】:2013-12-11 16:59:00
【问题描述】:

我对 OSGi 世界有些陌生。我仍然无法理解一些概念。

我正在尝试使用 Swing、Equinox 和声明式服务创建图形 OSGi 应用程序。目标是简化为应用程序创建插件和扩展的过程。

我偶然发现了一个设计问题,由于我是从头开始做这件事的,所以我想尽我所能使用所有最佳实践。

我确实有一个包含 API 的包,并且只公开要作为服务实现的接口。

public class SomeClass {
}

public interface Manager<T> {
     void add(T obj);
     void update(T obj);
     void remove(T obj);
}

public interface SomeClassManager extends Manager<SomeClass> {
}

public interface Listener<T> {
    void added(T obj);
    void updated(T obj);
    void removed(T obj);
}

public interface SomeClassListener extends Listener<SomeClass> {
}

假设我有一个包 (Core),它提供的服务是某些类型对象的管理器(它基本上包含一个内部列表并添加、删除和更新它)。

public class SomeClassCoreManager implements SomeClassManager {

      private ArrayList<SomeClass> list = new ArrayList<SomeClass>();
      private ArrayList<SomeListener> listeners = new ArrayList<SomeListener>();

      protected void bindListener(SomeListener listener) {
            listeners.add(listener); 
      }

      protected void undindListener(SomeListener listener) {
            listeners.remove(listener);
      }

      public void add(SomeClass obj) {
          // Adds the object to the list
          // Fires all the listeners with "added(obj)"
      }


      public void update(SomeClass obj) {
          // Updates the object in the list.
          // Fires all the listeners with "updated(obj)"
      }

      public void remove(SomeClass obj) {
          // Removes the object from the list.
          // Fires all the listeners with "removed(obj)"
      }

}

我还有第二个包 (UI) 负责处理主 UI。它不应该“关心”管理自身的对象,而是应该在添加、删除或更改对象以更新 JTree 时通知它。为此,我使用了白板模式:UI 包实现了一个服务,核心包使用该服务来触发对象更改事件。

public class MainWindow extends JFrame {

     private JTree tree = new JTree();
     private SomeClassManager manager;

     protected void activate() {
          // Adds the tree and sets its model and creates the rest of the UI.
     }

     protected void bindManager(SomeClassManager manager) {
          this.manager = manager;
     }

     protected unbindManager(SomeClassManager manager) {
          this.manager = null;
     }
}

public class SomeClassUIListener implements SomeClassListener {
     public void added(SomeClass obj) {
          // Should add the object to the JTree.
     }

     public void updated(SomeClass obj) {
          // Should update the existing object in the JTree.
     }

     public void removed(SomeClass obj) {
          // Should remove the existing object from the JTree.
     }

}

我的问题如下:

MainWindow 是一个 DS 组件。我正在使用它的激活器来启动整个 UI。实例创建由 OSGi 处理。

为了从经理那里获得更新,我将 SomeClassUIListener 公开为声明式服务。它的实例也由 OSGi 处理。

我应该如何从 SomeClassUIListener 访问 JTree 模型的实例?

我想出了几个选项,但我不确定要使用哪个:

选项 1: 为 UI 包使用某种内部 DI 系统(如 Guice 或 Pico),并将其放入具有静态方法的类中,以获取它并在整个包中使用它。

这种方法似乎有些人不赞成。

选项 2: 通过 OSGi 在 SomeClassUIListener 中注入对 MainWindow 的引用(通过将其转换为服务)并从那里开始。这是可能的还是可取的?在我看来,这是更简单的解决方案。但是,另一方面,随着 UI 变得越来越复杂,组件配置文件不会使包变得混乱吗?

选项 3: 仅为侦听器创建单独的包并使用 OSGi 更新 MainWindow。这在我看来有点极端,因为随着 UI 复杂性的增加,我将不得不创建大量的捆绑包。

选项 4: 使用 MainWindow 类来实现监听器。但是,主 UI 包中的服务越多,MainWindow 类就越大。我认为这不是一个好的选择。

我想不出更多的选择。以上任何一条路都可以吗?还是有其他选择?

提前谢谢你。

编辑:

只是澄清一下,因为 Peter Kriens 对这个问题有一些疑问。

我的目标是将用户界面与管理器分离。 Manager 我的意思是一种存储库,我在其中存储某种类型的对象(例如,如果您考虑http://docs.oracle.com/javase/tutorial/uiswing/components/tree.html 上的 Oracle 的 JTree 教程,管理器将包含 Books 的实例)。

Manager 可以被任何其他捆绑包使用,但根据我目前的计划,它会通知在其中注册的任何侦听器。侦听器可以是主 UI 包,但也可以是选择侦听更新的任何其他包。

【问题讨论】:

  • Richard Hall 的 OSGi in Action 一书中有一个基于此模型的 GUI 程序作为示例。您可能还想查看felix.apache.org/site/…
  • 我已经检查了你提到的例子。这本书的源代码确实可以回答我的问题!在第 11 章的paint-example-ds 中,windowlistener 提示了一个解决方案:我可以在将 SomeClassCoreManager 绑定到 MainWindow 时注册监听器。这样我就可以在监听器中注入我需要的任何组件。我提到的来源可能在link

标签: java swing osgi equinox declarative-services


【解决方案1】:

这里的一些选项是将树模型实例作为参数传递给侦听器方法。

public void added(JTree tree, SomeClass obj)

这样监听器管理器将只负责监听器逻辑,而不是树状态。

另一个不错的选择是创建一个单独的TreeProviderService,负责为应用程序保存和服务单例JTree 实例。在这种情况下,您将直接从听众那里消费TreeProviderService

【讨论】:

  • 感谢您的意见,亨利克。关于你的第一个选择。 JTree 会是 UI 提供的服务,而另一个捆绑软件会在触发事件更改时使用它吗?关于你的第二个选择:它需要一个单独的包来保存 JTree?还是会在 UI 包中提供和使用该服务?
【解决方案2】:

我不确定我是否完全掌握了您的建议,感觉就像您正在创建一个完整的基础架构负载。在 OSGi 中,这通常不是必需的,所以为什么不从小而简单的开始。

您的基本模型是经理和扩展。这是域模型,我会尝试在这里流动:

@Component(immediate)
public class ManagerImpl { // No API == immediate
   List<Extension>  extensions  = new CopyOnWriteArrayList<Extension>();
   JFrame frame = new JFrame();

   @Reference(cardinality=MULTIPLE) 
   void addExtension( Extension e ) {
       addComponent(frame, e.getName(), e.getComponent());
       extensions.add(e);
   }

   void removeExtension( Extension e) {
     if ( extensions.remove(e) ) {
        removeComponent(frame, e.getName());
   }
 }

 @Component 
 public class MyFirstExtension implements Extension {
    public String getName() { return "My First Extension";}
    public Component getComponent() { return new MyFirstExtensionComponent(this); }
 }

这不是你要找的吗?小心不要创建各种类型的侦听器,通常您会发现事件已经在 OSGi 注册表中。

【讨论】:

  • 这似乎不是我想要的。但我可能错了!我怎么能在我给出的例子中适应这个,我有一个数据源(管理器)和一个显示(UI)?无论如何,我已经更新了帖子以提供更多信息。
【解决方案3】:

我建议也简单地使用 DS 来创建和连接 UI。如果您使用 Peter 提到的注解,您将不会使用 XML 格式的组件描述符使您的包变得混乱。

所以你的监听器是一个 @Component 并且你注入了它需要更新的 UI 元素。

顺便说一句。你打算做什么对我来说听起来有点像数据绑定,所以你还应该调查这些已经提供了什么。 见:Swing data binding frameworks

顺便说一句。您可能还想寻找比 swing 更高级的框架。例如前段时间我为 vaadin 做了一个小教程:https://github.com/cschneider/Karaf-Tutorial/tree/master/vaadin 它已经有一个用于 java bean 的数据绑定。所以这让我很容易编写 UI 代码。完整的 UI 就是这个小类:https://github.com/cschneider/Karaf-Tutorial/blob/master/vaadin/tasklist-ui-vaadin/src/main/java/net/lr/tutorial/karaf/vaadin/ExampleApplication.java

在旧版本中,我仍然需要一个桥来在 OSGi 中运行 vaadin,但版本 7 应该已经为 OSGi 做好了准备。

【讨论】:

  • 在您使用 DS 的建议中,我会将服务绑定在自己的捆绑包中,还是应该创建其他捆绑包?关于数据绑定,它似乎也很有趣。但这是否意味着我应该放弃整个 OSGi 架构?此外,使用 Swing 以外的更高级的框架也是可能的。保持良好的分离可能让我有机会在未来更改 UI 框架。
  • 我不确定数据绑定与 OSGi 的工作情况如何。它可能取决于数据绑定框架。在 vaadin 中,它运行良好。顺便提一句。我本人目前正在研究在 OSGi 上使用 CDI。它的优点是它对 JavaEE 人来说很熟悉,并且它很好地分离了包内和包间的布线。我没有尝试用它来连接摆动 UI,但应该很有可能:github.com/cschneider/Karaf-Tutorial/tree/master/tasklist-cdi
猜你喜欢
  • 2011-12-16
  • 1970-01-01
  • 2011-06-24
  • 2011-02-18
  • 1970-01-01
  • 2015-06-11
  • 2012-01-14
  • 1970-01-01
  • 2012-06-06
相关资源
最近更新 更多