GUI模式问题:PropertyChangeListener与专用视图接口

时间:2009-04-11 14:32:51

标签: user-interface language-agnostic design-patterns

我想通过界面将模型与它的视图配对。我想控制视图更新的时间和频率。所以像PropertyChangeListener这样的东西不会很好(在设置每个属性后触发事件)。

我不是为特定的GUI框架开发的。这里的目标是能够交换不同的GUI前端(现在用于测试,但对于不同版本的应用程序可能稍后有用)。这些可能是Swing,或者它可能是一个Web浏览器(例如通过GWT)。

以下是我的方法。该视图实现了一个接口,以提供更新方法。当控制器确定更新模型时,控制器会触发此操作。这对我来说仍然没问题,因为Controller只是通过模型与视图交互,控制器不依赖于View的特定实现。

所以,我想我的问题是

  • 这样做效果好吗?
  • 这是标准做法吗?
  • 此模式是否有名称?

粗糙代码示例(Java):

// Controller, manages Items (the model)
class ItemList {

   void addItem(Item item) {
   }

   void doStuffWithItems() {

     // perform some set of operations, such as sorting or layout
     for (Item item : items) {
       // ....
     }

     // now with everything in it's final position:
     for (Item item : items) {
       item.updateView();
     }
   }
}

// Model
class Item {
  private int top;
  private int left;
  private int width;
  private int height;

  // Can remember it's previous position/size:
  public void savePostion() {
  }

  // And recall it for the Controller to use:
  public public Position getSavedPosition() {
  }

  // Plus some other useful functions:
  public boolean intersectsWith(Item other) {

  }

  public void updateView() {
    this.view.update();
  }

  void setView(ItemView view) {
    this.view = view;
  } 
}

// Interface used by View implementations
public interface ItemView {
  // Trigger the view to reflect the current state of the model
  void update();
}

// Example, as a Swing component
class ItemComponent extends JComponent implements ItemView {
  private Item item;

  public ItemComponent(Item item) {
    this.item = item;
    item.setView(this);
  }

  // ItemView#update
  public void update() {
    // update the component's size/position
    setBounds(new Rectangle(item.getLeft(), item.getTop(), item.getWidth(), item.getHeight()));
  }

  @Override
  public void paint(Graphics g) {
    // ...
  }
}

3 个答案:

答案 0 :(得分:2)

我会避免强制View仅为更改通知实现接口。而是在模型上创建一个单独的“立即更新”事件。

答案 1 :(得分:0)

模型不应直接控制或了解视图。视图应该向控制器注册回调,以便控制器可以告诉视图何时更新,这就是控制器的原因。您可以让模型允许modelChangedEvent的外部侦听器。然后视图可以在这方面注册模型,而模型不知​​道有视图。请参阅用于MVC的J2EE blueprint以及模型中状态更改的间接事件通知。

答案 2 :(得分:0)

对于在计算机桌面上运行的传统应用程序,我推荐Passive View的变体。负责创建和管理表单的类是一个将事件传递给UI对象的瘦shell。 UI_Object通过接口与表单交互。在术语中,UI对象实现UI_View接口,并使用位于对象层次结构中较低位置的视图控制器进行自身注册。

UI_Object然后执行实现修改模型的Command Pattern的对象。命令对象可以通过View Control公开的接口与各种视图进行交互。

这样做可以让你破掉表单类并用实现表单接口的存根类替换它们。存根类用于自动化测试,尤其是集成测试。

接口精确定义了Form,UI_Object,Commands和视图之间的交互。它们可以设计成与语言无关,因此可以更轻松地在平台之间移植。

您的示例中缺少的是命令对象。你需要这个结构

  • ItemViewForms
  • ItemViewImplementation
  • ItemViewFormInterface
  • ItemViewCommands
  • ItemViewInterface
  • 为MyModel

将ItemList合并到ItemViewImplementation

ItemComponent将使用ItemViewInterface注册ItemViewImplementation。

事件序列看起来像这样

  • 用户想要更新项目
  • 点击UI(假设是UI 涉及用鼠标点击)
  • 表格告诉了 ItemViewImplementation通过 X已经是ItemViewInterface 用Y参数完成。
  • 然后是ItemViewImplementation 用。创建一个命令对象 它需要的参数来自Y。
  • 命令对象取Y. 参数修改模型和 然后告诉
  • ItemViewImplementation通过 用于更新UI的ItemViewInterface。
  • ItemViewImplementation告诉你 ItemViewForms更新UI 通过ItemViewFormInterface。
  • ItemViewForms更新。

这种方法的优点是每层的交互是通过接口精确定义的。软件操作本地化为命令对象。 Form层专注于显示结果。 View层负责路由命令和表单之间的操作和响应。命令是修改模型的唯一内容。此外,您还可以利用Forms Implementation来替换您想要的任何UI,包括模拟对象进行单元测试。