如何在保持松散耦合的情况下通过 Eclipse Selection Service 处理来自不同供应商的选择?

How to process selections from different providers via the Eclipse Selection Service while maintaining loose coupling?

TL;DR:有没有一种方法,最好是遵循设计模式最佳实践,在不破坏松散耦合的情况下处理 ISelectionListener 中来自多个 ISelectionProviderISelection,无需猜测(类型检查、转换)?

在我的 Eclipse RCP 3.7 应用程序中,我有两个 views 和一个 (GEF) editor。视图包含清单,允许用户通过 selection 控制编辑器的内容。假设编辑器显示大学教职工层次结构,一个视图将允许 select 应显示教职工的部门,另一个将允许 select 应显示教职工的当前学位级别。

即,如果用户在一个视图中select编辑了计算机科学英语文学,并且Post-DocFull Professor* 在另一个中,编辑器会显示人员(如,例如,节点)是 Post-Docs 或 CS 或 EngLit 系的全职教授。但是,该模型包含所有部门的所有员工。

我已经根据 Eclipse Workbench: Using the Selection Service 实现了视图和编辑器:编辑器有一个 org.eclipse.ui.ISelectionListener,视图都实现了 org.eclipse.jface.viewers.ISelectionProvider。这些视图维护一个监听器列表,只要 selection 发生变化,就会在循环中通知这些监听器:

((ISelectionChangedListener) listeners.getListeners()[i]).selectionChanged(
        新的 SelectionChangedEvent(DeptView.this,
        新结构化选择({selection})))

到目前为止,我已经尝试了以下实现,但我对其中任何一个都不满意。

  1. 对于 {selection},使用自定义 class(例如,DeptSelection),它封装了 selected 元素,或者更确切地说是模型他们指向的元素。我认为这打破了松耦合,因为DeptSelection将在部门插件[=66=中定义],它将与 Editor 插件 分开,同时我希望允许客户端添加扩展,其 selection 监听器也可以监听.

  2. 对于{selection},只需传递selected元素(在本例中通过org.eclipse.jface.viewers.CheckboxTableViewer#getCheckedElements()),并在ISelectionListener#selectionChanged()中进行处理编辑。这是笨拙,因为它涉及很多猜测:类型检查、转换等。另外,对于这个解决方案,我然后 "save" select 离子在一个abstract util class SelectionUtil,在编辑器(这里是根编辑部分的内容编辑部分)中读取,将相应的模型元素添加到 AbtractEditPart 的 [= 返回的列表中23=]。我 (a) 不确定抽象实用程序 class 的使用是否会破坏松耦合 ,以及 (b) 是否有 抽象实用程序 class 是一个很好的设计模式.

因此我的问题是:有没有一种不笨拙的方法,最好是遵循设计模式最佳实践,在不破坏松散耦合的情况下处理多个 ISelection

*我知道,这不是最准确的例子,因为 "Full Professor" 和 "Post-Doc" 不完全是度数。

查看 Adapter Pattern, the IAdaptable 界面,并用您的实际对象填充 StructuredSelection,而不是为传达选择而创建的其他容器(您已正确识别为有问题)。

编辑:此外,https://eclipse.org/articles/Article-WorkbenchSelections/article.html

您可以将您放入选择工具中的对象设为 IAdaptable,这样您就可以查询您真正想要的 class 对象:

MyObject obj = ((IAdaptable)selectionObject).getAdapter(MyObject.class);

您的选择对象将具有:

public Object getAdapter(Class adapter)
{
  if (adapter == MyObject.class)
    return .... my object instance

  return null;
}

您可以更进一步,使用 IAdapterFactory class,这样您就可以将完全独立的 class 用于适配代码。类似于:

class AdapterFactory implements IAdapterFactory
{
  @Override
  public Object getAdapter(Object adaptableObject, Class adapterType)
  {
    if (adaptableObject instanceof MyObject && adapterType == MyObject.class)
      {
        ... convert from 'adaptableObject' to MyObject
        return myobject;
      }

    return null;
  }

  @Override
  public Class<?> [] getAdapterList()
  {
    return new Class<?> [] {MyObject.class};
  }
}

您可以使用适配器管理器或使用 org.eclipse.core.runtime.adapters 扩展点以编程方式声明工厂。

这样您就可以使用适配器管理器从您的选择中找到对象:

MyObject obj = (MyObject)Platform.getAdapterManager().getAdapter(selectionObject, MyObject.class);

Eclipse 本身使用适配器工厂方法将用户界面对象适配为 IFile 之类的对象。