TikeSwing框架入门

在Swing中使用高层的MVC和POJOs
TikeSwing框架入门

摘要:
TikeSwing 是一个开放源码的Swing框架,它提供了一个高度MVC(模型-视图-控制器)模式的体系结构并且使SWING组件的使用非常简单。它通过将视图组件和JavaBeans直接连接来支持POJO编程模式。在这篇文章中将阐述TikeSwing的特点,并且将示范怎样使用这个框架创建一个清晰的MVC的系结构。

By Tomi Tuomainen

最近,在Java社区里面,丰富的互联网应用程序(RIAs)的兴起成为一个热点话题。另外一些新的技术,就像,AJAX(异步的JavaScript和XML),MacroMedia Flex, 和Laszlo,以及使Java开始应用于网络的好的旧Swing,它们被提议作为RIA技术。
然而,Java社区里面的很多人对Java基础类库(JFC)和Swing提出了批评。在建立高度MVC模式的客户端体系方面不能提供太多的帮助。任何合理的服务器应用程序返回传递的对象,或者称为简单陈旧的Java对象,把它传递到客户端的技术证明了J2EE世界的挫败。从POJO范围映射到Swing组件需要太多的手动的代码,反之亦然。
同样的,使用Swing实现其他的功能,就像线程句柄和验证域,也是很费力的事情。而且有时候Swing组件很难使用:创建一个合适的表格或者树模型通常需要很多的编码,而且需要深入的研究Swing编程文档中的API。
TikeSwing 是一个开放源码的Swing框架,它提供了一个高度MVC(模型-视图-控制器)模式的体系结构并且实现了模型,组件和控制器通信的自动化。它简化了Swing组件的使用,并通过将视图组件和JavaBeans直接连接来支持POJO编程模式。
这篇文章将示范怎样使用TikeSwing创建一个清晰的MVC的体系结构。也将阐述建立TikeSwing组件的原则,并简单描述在这个框架中包含的最佳体验和机制。
体系结构
众所周知,MVC范例是推荐的图形用户界面发展的基本体系。它还有很多的可用的变种,就像MVC++, HMVC (Hierarchical MVC), MVC Model 2, MVC Push, and MVC Pull,它们每一个都有些不同之处。TikeSwing基于下面的MVC原则:

· 模型:
o 一些真实世界或者系统的提取
o 压缩在它上面操作的数据和函数
o 在数据改变时通知观察员
· 视图:
o 系统的用户界面
o 依附于模型将它的内容显示出来
o 在模型改变时自动刷新受到影响的部分
· 控制器:
o 控制应用程序的流程
o 接受用户的输入,o 并根据用户输入指o 导模型和视图完成任务
下面的图表表示了TikeSwing中MVC的类结构。
 


Figure 1. MVC class diagram of an application using TikeSwing 
类MyModel, MyView, 和MyController是应用程序通过使用框架来执行的。MyModel和MyController扩展了TikeSwing的YModel 和YController类。一个视图的类可以是任何的实现了YIComponent接口的java.awt.Component。
TikeSwing在装配类结构的时候不使用任何的配置文件。当YController,YModel和视图组件提供了要求的功能特性的时候,扩展适当的类已经足够了。下面讲述如何使用TikeSwing来实现模型、视图和控制器类。
模型

TikeSwing的模型是一个为实现视图而包含数据的JavaBeans组件。一个模型类可能包含嵌套的JavaBeans,数组,映射和集合。和标准JavaBeans中要求的一样,所有的模型域必须有适当的GET和SET方法。从这种意义上说,TikeSwing就像很多的网络应用程序框架那样工作,所以在不同的技术之间重用模型类是很容易的。
YModel是模型的基类。它提供了报告数据改变的方法。当触发了一个事件的时候,框架会更新与之相连的视图。在分布式环境中,一个模型类有从服务器应用程序中得到POJOs的方法(通常是从隐藏了业务服务的执行细节的业务代理)。模型自身存储了POJOs,且它有责任通知观察者。在有些MVC的体系结构中,一个控制器类和服务器通信,POJOs存储在控制器中。然而,TikeSwing分离出YModel类的方法有下面的优势:控制器专著于流程,另外的方法(操作模型数据的)可以被加在客户端。YModel遵循了传统的MVC模式,所以MVC中类的责任就清晰地分开了。
下面的代码演示了模型类如何通过给定的参数找到消费者变量。模型的域name和id是搜索标准,customers是包含搜索结果的Customer POJOs的集合。findCustomers()方法通过customerServiceDelegate从服务器应用程序中得到customers。当方法notifyObservers()激活时,框架会自动更新相连的视图。
public class FindCustomerModel extends YModel {
    
   private String name;
   private String id;
    
   private Collection customers;

   private CustomerServiceDelegate delegate = new CustomerServiceDelegate();
    
   public void findCustomers() {
        setCustomers(delegate.findCustomers(id, name));
        notifyObservers("customers");
    }

    public void setCustomers(Collection customers) {
        this.customers = customers;
    }

    public Collection getCustomers() {
        return customers;
    }
    
    public void setId(String id) {
        this.id = id;
    }

    public String getId() {
        return id;
    }

    public void setName(String name) {
        this.name = name;
    }
    
    public String getName() {
        return name;
    }
}
视图
TikeSwing视图是包含其他Swing组件的Swing组件。通常,一个视图类是一个面板,一个对话框,或者一个帧,它们建立了子组件并将之添加到自身(就像在通常的Swing开发环境中一样)。然而,TikeSwing应用程序中使用的所有组件都必须实现适当的接口以连接框架的MVC体系结构。幸运的是,框架包含一个很大的为了这种目的已经实现的组件的集合。
一个特殊的名字必须赋予一个视图组件,这样框架就能在组件和被命名的模型域之间复制数据。命名的惯例和其他的用于网络应用程序框架的和Apache BeanUtils库(它通常用于框架的执行)类似。下面是支持的命名格式:
· 简单的: 直接连接到模型域的组件;例如,· field1
· 嵌套的:连接到模型内部的JavaBeans域的组件;例如,· field1.field2
· 索引式的:连接到模型内的数组域的组件;例如myArray[1]
· 映射式的:连接到模型内的映射域组件;例如,· myHashMap(“foo”)
· 组合式的:通过结合符号连接到模型的内部域的组件;例如,· field.myArray[1].myHashMap["foo"]
除了模型类的GET和SET方法外,视图类必须为每一个视图组件建立一个GET方法。
下面的例子是为FindCustomerModel建立的视图类。它使用了扩展了基础Swing类的TikeSwing组件(从JLabel到YLabel,JTextField到YTextField,等)。例子的代码和标准的Swing视图很像,只有setMVCNames()方法包含了TikeSwing特有的代码。依照上面讲述的原则,它设定了模型组件的连接。resultTable列通过YColumn对象与customers集合中的POJO域相连。findButton不显示任何从模型得到的数据,但是MVC的名字是为TikeSwing的事件句柄设定的(以后再讲)。
public class FindCustomerView extends YPanel {
        
    private YLabel idLabel = new YLabel("Id");

    private YLabel nameLabel = new  YLabel ("Name");
    private YTextField idField = new YTextField();
    private YTextField nameField = new YTextField();
    private YPanel criteriaPanel = new YPanel();
    
    private YTable resultTable = new YTable();
    private YButton findButton = new YButton("Find");
        
    public FindCustomerView () {
        addComponents();
        setMVCNames();
    }
    
    private void setMVCNames() {
        idField.getYProperty().put(YIComponent.MVC_NAME,"id");
        nameField.getYProperty().put(YIComponent.MVC_NAME,"name");
        resultTable.getYProperty().put(YIComponent.MVC_NAME,"customers");
        findButton.getYProperty().put(YIComponent.MVC_NAME,"findButton");
        YColumn[] columns = {
                new YColumn("id"),
                new YColumn("name")};
        resultTable.setColumns(columns);
    }
    
    private void addComponents() {
        this.setLayout(new BorderLayout());
        this.add(criteriaPanel, BorderLayout.NORTH);
        idField.setPreferredSize(new Dimension(100, 19));
        nameField.setPreferredSize(new Dimension(100, 19));
        criteriaPanel.add(idLabel);
        criteriaPanel.add(idField);
        criteriaPanel.add(nameLabel);
        criteriaPanel.add(nameField);
        criteriaPanel.add(findButton);
        this.add(resultTable, BorderLayout.CENTER);
    }

    public YTextField getIdField() {
        return idField;
    }
    
    public YLabel getIdLabel() {
        return idLabel;
    }
    
    public YTextField getNameField() {
        return nameField;
    }
    
    public YLabel getNameLabel() {
        return nameLabel;
    }
    
    public YTable getResultTable() {
        return resultTable;
    }
    
    public YButton getFindButton() {
        return findButton;
    }
}
现在,无论任何时候用户修改idField 或者nameField,改变的地方都会自动更新到模型。而且,当notifyObservers()在 FindCustomerModel中调用的时候,框架会更新变化到resultTable。然而,为了通知结构,一个控制器必须被指定。
控制器
TikeSwing的控制器通过调用视图和模型的方法来处理应用程序的流程。一个控制器的类必须扩展YController,它提供了控制关系中的必要的方法。通常,控制器也创建视图和模型对象,但是要注意的是,几个视图和控制器可能共享相同的模型对象。
一个控制器类可能有好几种方法来获取用户事件。TikeSwing组件包括基于反射的事件句柄:一个事件可以通过实现带有合适签名的方法而在控制器类中得到处理。例如,当用户点击按钮的时候,一个MVC名字为myButton的按钮在控制器中会调用myButtonPressed()方法(如果实现了的话)。这与标准的Swing事件监听接口和适配器相比是很方便的。
另一方面,事件方法签名中的字符在编译器中是不显示的,但是Swing适配器类的情况是:编译器不说明public void actionperformed是一个新的或者重载的方法。因为监听接口经常需要许多空的方法的执行,基于反射的简单的事件处理一定会加快代码的进程。作为选择,你可以在视图类中使用标准的监听者,而手动调用控制器的方法。
下面的代码是FindCustomerModel和FindCustomerView的控制器的一个例子。控制器通知MVC的结构是通过调用setUpMVC()方法和使用findButton 来处理基于反射的事件。
public class FindCustomerController extends YController {
        
    private FindCustomerView view = new FindCustomerView();
    private FindCustomerModel model = new FindCustomerModel();

    public FindCustomerController() {
        super();
        setUpMVC(model, view);
    }
    
    public void findButtonPressed() {
        model.findCustomers();
    }  
}
YController是TikeSwing中功能的核心。除了上面讲述的特点之外,它还提供了很多有用的方法能用于:
· 获取特定域的改变
· 在控制器中发送和接收信息
· 跟踪用户的修改
· 取消用户的改变
· 获取模型抛出的异常
· 验证域的值的有效性
TikeSwing组件
TikeSwing是基于组件在模型是有责任处理连接的对象的思想的。这种思想以前在Sun的《Swing指南》中的WholeNumberField演示中有体现。组件必须知道怎样在屏幕上面显示模型的值和怎样转换用户给定的值到模型中。
框架现在提供了一个足够大部分的应用程序使用的组件的集合。框架组件的行为就像基础的Swing组件,当然了,你必须阅读Java文档以理解组件和MVC类的交互(组件可以处理什么类型的模型域和它提供了什么事件的方法)。TikeSwing组件也提供了其他的特点和简洁的开发。例如,一个POJOs的集合可以在不创建任何特殊的组件模型的情况下直接使用于YTable和YTree。
TikeSwing组件基本上可以是任何的java.awt.Component。然而,  一个组件必须实现适合的TikeSwing接口,那样它就能被集成到框架的MVC的体系结构中。它通常包含扩展了带有四个简单方法的标准Swing组件,因此这将是一个比较琐碎的任务。下面的代码是一个例子。和模型的集成是通过getModelValue() 和setModelValue()方法实现的。组件值的改变的通知是addViewListener()方法实现的。为了框架能在内部使用必须实现getYProperty()方法。
下面的代码表现了一个支持Integer对象的简单文本域:
public class YIntegerField extends JTextField implements YIModelComponent {

    /** Gets value of this field for the model. */
    public Object getModelValue() {
        try {
            return new Integer(getText());
        } catch (Exception ex) {
            return null;
        }
    }

    /** Sets the model value into this field. */
    public void setModelValue(Object obj) {
        if (obj == null) {
            setText("");
        } else {
            setText(obj.toString());
        }
    }

    /** Notifies the framework when the component value might have changed. */
    public void addViewListener(final YController controller) {
        this.addFocusListener(new FocusAdapter() {
            public void focusLost(FocusEvent ev) {
                controller.updateModelAndController(YIntegerField.this);
            }
        });
    }

    // The rest is for the framework internal use,
    // the implementation must be copied to each new component:
    private YProperty myProperty = new YProperty();

    public YProperty getYProperty() {
        return myProperty;
    }
}
其它的特点
除了MVC的体系结构,TikeSwing还有很多协助进行Swing开发的其它的特点。这些特点不是什么革命性的东西,它们可以在很多已经实现的Swing应用程序上面看到。但是,没有必要重新发明轮子,一些最好的Swing开发的体验包含在了这个框架中。
TikeSwing支持控制器多层结构的创建,就像在HMVC和MVC++中描述的那样。框架提供了使控制器之间实现父子关系的方法,这使类结构更协调和清晰。这种关系又助于和客户应用程序通信,而且可以用来和众所周知的设计模式集成。TikeSwing支持任务链模式,这种模式中,一个请求直到控制器对象才处理事件时才被传递。TikeSwing也支持Observer/Observable模式:一个控制器类可能传递能被所有已经注册了的控制器处理的事件。
TikeSwing也包含一种为小面积的脏数据修补的机制。在一个分布式的系统中,制表符一下子就从服务器得到数据可能需要很长的时间。为了优化性能,为每个标签取数据已经证明是必须的。框架提供了简化这种功能的机制,所以代码的复杂性,特别是在嵌套的制表格里面被很好的减少了。当用户触发可能导致刚修改的域的数据丢失的时间的时候,一些应用程序会检查没有保存的改变。这些事件可能是下面的例子,关闭窗口,改变制表键或者选择一个表格的列。TikeSwing 提供了进行检查特殊事件的工具。TikeSwing也会自动弹出“是否保存更新?”的对话框表示保存到一个控制器方法。另外,框架记得视图在特定时刻的状态,可以在稍晚的时候返回那种状态。这就意味着框架可以在不取得原始数据的情况下取消改变。
当两个或更多的组件执行相同的函数的时候,Swing的行为被证明是有用的。一个Action对象提供了集中的事件处理,但是如果行为用于单独的类的话,代码会因为增加的耦合而更加复杂。TikeSwing包含了一个集中处理产生事件的场所,因此一个动作可以用于不同的视图类而且不会直接耦合。
Swing组件只能由事件分派的线程进行创造,修改和查询,这使Swing应用程序中的线程处理更加复杂。《Swing指南》中说SwingWorker类对这个问题提供了帮助。TikeSwing封装了SwingWorker,并且是线程处理更加简单。例如,一些应用程序在进行远程调用或I/O操作的时候不会锁定。TikeSwing中,一个可管理的,可重画的对话框在这样的操作时可以弹出,而且实现只需要几行代码。
总结
TikeSwing使用高层的MVC和POJO的支持从而简化了Swing的开发。使用TikeSwing是合理的,特别是在分布式环境中,由服务器应用程序返回的POJOs可以直接用于模型类,这个类直接连接到视图类。这个框架也包含了一些解决复杂开发问题的最佳实践。因此,TikeSwing减少了为Swing客户所写的代码,加快了开发。
TikeSwing自身提供了丰富的平台无关的用户界面库。Swing开发已经成为这几年的重要的集成开发环境的一部分,所以可见即所得的设计,单元测试和调试已经被广泛的支持。早先的工作站上性能的问题现在已经不是问题了,Java的网络应用也简化了分布式的Java应用程序。与网络应用程序的框架相比,Swing提供了更加友好的用户界面,没有JavaScript支持的问题,通过工作站上面的客户逻辑简化了网路上的通信量。
尽管如此,对Swing复杂性的批判是正当的。但是,使用像TikeSwing的高层的MVC框架,复杂性就减少了,Swing就转换成了一个生产力很高的客户端技术。我希望Java社区可以为Swing开发和采用一个开源的MVC框架,这将使其成为RIA技术中的一员。可能像Spring似的富客户端技术更加的接近目标。在等待的时候,请检验一下TikeSwing,评价一下它是如何适应你的RIA工程的。
关于作者
Tomi Tuomainen是Entra e-Solutions的顾问和架构师,他从1999年开始使用J2EE应用系统和Java框架。他是计算机科学的理学硕士和SUN的认证企业架构师。他的兴趣(Java之外的)在于音乐,吉他和体操训练。你可以说他是芬兰最强的IT顾问之一。
Resources
· 最新版本的TikeSwing(包含必须的有类路径,· 源代码,· 用户指· 南和Javadoc API的JAR文件)可以在这里下载:
http://sourceforge.net/projects/tikeswing
· TikeSwing遵循的MVC范例的基本信息:
http://ootips.org/mvc-pattern.html
· 就像JavaBeans规范中说的那样,· TikeSwing的模型对象必须包含GET和SET方法:
http://java.sun.com/products/javabeans/docs/spec.html
· Swing指· 南:
http://java.sun.com/docs/books/tutorial/uiswing/index.html
· HMVC范例分解了客户端为父子MVC层,· 这也能用于TikeSwing。阅读更多的关于HMVC的信息在“HMVC:用于开发强壮客户端层的层次模式,· ” Jason Cai, Ranjit Kapila, and Gaurav Pal (JavaWorld, July 2000):
http://www.javaworld.com/javaworld/jw-07-2000/jw-0721-hmvc.html
· MVC++范例共享了HMVC的关于控制器层次的想法:
http://www.cs.uta.fi/~jyrki/ohto02/mvc.ppt
· Apache BeanUtils库,· 包含了能用于JavaBeans域(在TikeSwing中使用了)引用的格式的描述:
http://jakarta.apache.org/commons/beanutils/api/index.html
· 和TikeSwing有共通之处的Spring富客户端工程:
http://www.springframework.org/spring-rcp
· 关于Swing开发的更多文章,· 浏览JavaWorld的AWT/Swing部分的论题索引:
http://www.javaworld.com/channel_content/jw-awt-index.shtml
· 关于UI开发的更多文章,· 浏览JavaWorld的用户界面设计部分的论题索引:
http://www.javaworld.com/channel_content/jw-ui-index.shtml
· 最后,· 浏览JavaWorld的开发工具部分的论题索引:
http://www.javaworld.com/channel_content/jw-tools-index.shtml 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值