JHotDraw应用程序框架

注:这是本人发表在javaeye的文章, 这里对这一系列文章做一个整理.
 
JHotDraw简介

JHotDraw是面向文档的应用程序框架, 它能通过桌面或者web的方式来发布. 早期的JHotDraw则是以二维图形编辑器的Java图形框架而闻名. JHotDraw起源于Erich Gamma的一个教学实例。更早期的JHotDraw可上溯到1992年由Johnson采用smalltalk开发的hotdraw, 而hotdraw则是参考1989年weinand用C++实现的ET++框架.

JHotDraw是基于LGPL协议的发布的, 即其代码是完全开发的,并可以作为第三方类库用于商业用途(修改或衍生代码则必须遵循LGPL). JHotDraw 采用了大量的设计模式来处理Java矢量图形开发中的各种问题. 由于JHotDraw具有良好的框架,很好的复用性和扩展性,很快成为一个Java二维矢量图开发的应用框架。而历经了几个版本的发展,JHotDraw已经成为一个成熟的面向文档应用程序框架。本文的目的在于研究JHotDraw的框架,探讨与分析其源码, 以学习软件架构及设计模式的具体运用等知识.

JHotDraw项目主页位于sourceforge,你可以在http://sourceforge.net/projects/jhotdraw/上下载最新的版本。当前最新的版本为7.5。JHotDraw有详尽的文档, 而针对较新的版本, 学习JHotDraw唯一可用的资源也是这些文档, 但这对于研究其架构这些文档已经足够.

JHotDraw最初采用的语言并非Java,而是SmallTalk,项目命名为HotDraw。人们一开始是因为ET++(一个C++应用程序框架)而关注JHotDraw。而Erich Gamma将JHotDraw应用于教学,主要是通过JavaDoc的方式对程序进行说明,并提供相应的教程来说明框架设计的较为重要的地方。

JHotDraw特性

作为一个教学实例, JHotDraw 闻名于它的 excellent framework(构架良好),well design(设计良好),robust  and reliable(可靠与健壮). 整个程序存在大量的设计模式与OO设计原则,  除了用于教学, 它还是一个极好的可视化程序的框架, 图形编辑器工具箱, 等等.

目前, 基于JHotDraw的程序有很多, 国外的见JHotDraw Applications , 国内的有千鸟的jphotoshop .此外,本人开发的开源地图编辑器Mepper中应用程序框架部分的设计思想也来源于JHotDraw

鉴于JHotDraw的特性, 这里将开始一段JHotDraw之旅, 探究其框架与设计, 分析重要类的源码. 其最终目的在于提升面向对象程序设计与软件架构的能力. 如果你已经精通软件架构或设计模式(excellent in design pattern), 或者你讨厌设计模式, 那么这篇文章可能不适合你.

1.下载JHotDraw

    最新的JHotDraw可以在http://sourceforge.net/projects/jhotdraw/files/上下载,目前最新的版本是7.4.1. 这里就以7.0.1版作为例子, 你可以在上面的网址找到这个版本。解压后的文件结构图

2.框架实例

打开Samples,里面依次是Draw,net,pert,svg和teddy等应用于JHotDraw框架的实例。

2.1 Draw

Draw是一个二维图形编辑器,可用于简单绘图和图形制作。Draw可以说是JHotDraw最原始最核心的应用,它从HotDraw版本就一直存在。直到JHotDraw7以后,JHotDraw框架趋向成熟,Teddy等其它实例才出现。

在draw中,数据采用xml格式存储在一个proprietary中,draw的用户界面支持一些基本的桌面应用,例如:载入、保存、打印、输出以及剪切复制和撤销重做等操作。

draw可以作为一个桌面应用程序独立运行,也可以作为一个applet程序来运行。独立运行的draw支持三种不同的文档界面:单文档界面(single document interface SDI),多文档界面(multi document interface MDI) 以及Mac系统文档界面(the Mac OS X document interface)。下面是JHotDraw在apple上的运行效果图。

Draw运行图

图1:Draw在苹果操作系统(Mac OSX)上的文档界面

图2:draw多文档界面图

2.2 NET

    NET是由两种图形(节点图NodeFigure和可连接图ConnectionFigure)组成的简单图形编辑器,NET同样是构建在JHotDraw框架之上的。

NET屏幕截图

在NET中,每个节点图(NodeFigure)有四个可与其它图形链接的连接点,节点图(NodeFigure)构建于连接图(ConnectioinFigure)之上。

2.3 PERT

PERT是一个基于JHotDraw框架的计划评审图制作工具。PERT即 program evaluation and review technique,是一个软件工程概念,常用于项目规划中。PERT由任务(tasks)和依赖(dependencies)两部分组成。任务(tasks)是PERT图的节点,依赖(dependencies)则是PERT图的边框部分。

PERT运行效果图

每个任务图三个字段:任务名称(name)、开始时间(strat)与持续时间(duration)。在PERT中,用户可以在任务图中设置任务名称和持续时间这两个属性,并由PERT计算开始时间属性。当任务的持续时间(duration)设置为0时表示该任务是一个旅程碑(软件工程概念,表示重要的时间点,比如“开始”)。PERT中,箭头图形表示依赖关系。

在PERT中,任务图(task)能够从“语义”上感知自己:连接两个任务即表示在这两个任务之间建立依赖关系。连接两个任务后,被连接(dependent task)将重新计算其持续时间(duration)字段。另外,PERT不允许建立有循环(回路)的评审图。

2.4 SVG

正如你所想, SVG是一个应用在JHotDraw框架下的svg (1.2版, svg即可伸缩向量图形 Scalable Vector Graphics)编辑器. SVG编辑器支持旋转,缩放以及放射性变换(affine distortion).

SVG运行效果

2.5 Teddy

Teddy 是一个应用在JHotDraw框架下的文本编辑器(记事本).

当Teddy在Mac上运行时, 程序使用Mac文档界面. Teddy实现了基于苹果人机交互界面指南(Apple Interface Guidelines )上的文本编辑器.

当Teddy 运行在其它平台时便使用单文档界面(SDI), 如Windows, Linux和unix等其它系统.

 

 

2.6源码分析的准备

1. 获取JHotDraw

http://sourceforge.net/projects/jhotdraw/ 下载最新版的JHotDraw, 我下载的是7.4.1版.

2.  工具

eclipse: 下载地址: http://www.eclipse.org . eclipse用户源码跟踪与编写(个人使用习惯,你可以选择你喜欢的IDE).

netbeans: 下载地址:  http://www.netbeans.org (同上).

3. uml 工具

我使用的是netbeans(你也可以使用rose,ea,trufun等其它uml工具), netbeans自带了非常好用的uml插件, 可以大部分的uml建模需求. 如果你的netbeans没有uml功能,你可以按照以下步骤来安装这个插件:

打开netbeans, 在菜单栏找到:  工具--插件. 见下图:

    选择"可用插件", 如果插件太多, 可以在右边搜索框里填入:uml, 选中uml插件并安装.

如果插件安装成功, 则可在"已安装"里找到uml插件. 如图:

4. 导入

    JHotDraw是一个netbeans工程, 所以可以用netbeans直接导入.  并由netbeans进行反向工程.

反向工程建立后, netbeans会建立一个uml项目,该项目以原项目的名字加上"-model"来命名. 这个uml项目主要由三部分组成: Model, diagrams,imported elements. 这时我们可以在Model里找到所有与JHotDraw相关的Java对象.

在Diagrams里新建一个类图(class diagram), 并从model ../org/JHotDraw/app/里面拖出相应对象,就可以创建一个app包的结构图.

 

3 JHotDraw的MVC架构

1. JHotDraw框架概述

开发应用程序时, 大多数情况下会反复处理一些相同的事情: 管理应用程序的生命周期, 事件处理, 线程管理, 本地化资源持久化处理. 为了节省时间与精力, 开发一个可复用的框架以节省开发成本显得非常有必要. 而应用程序框架正是提供多数应用程序都会用到的基础设施(infrastruture), 以节省重复开发的成本, 并提高程序的可维护性. 框架常以可复用类库形式导入到程序中.

框架常常会令人望而却步, 因为框架必须是设计良好的,而良好的设计总是以复杂和庞大为代价. 对于比较小的应用程序来说, 引入大型框架可能使原来的应用程序复杂化, 甚至比没有使用框架还费力. JHotDraw的框架并非是庞大和复杂的(不过随着版本的升级,它正在朝着正方面发展), 它致力于减轻使用框架的成本. 它提供了开发基于swing的应用程序的核心架构,包括程序生命周期控制, 资源管理, 事件处理, 线程管理和本地存储等功能.

2. 框架架构

整个JHotDraw应用框架的api都位于包: org.jhotdraw.app . app包定义了Application, ApplicationModel, view等接口. JHotDraw框架正是构建在MVC模式之上, 其中Application封装了控制应用程序生命周期的方法:

1.    init() 初始化程序;

2.    launch() 启动程序;

3.    start() 开始运行;

4.    stop() 停止运行;

5.    destroy() 退出程序;

构建一个基于JHotDraw应用程序框架的基本步骤为:

1.    定义一个View

2.    创建Application,并启动它.

下面是示例代码:

HelloView.Java

import java.io.IOException;  
import java.net.URI;  

import org.jhotdraw.app.AbstractView;  
import org.jhotdraw.gui.URIChooser;  

public class HelloView extends AbstractView{  
public void clear() {  
    }  

public void read(URI uri, URIChooser chooser) throws IOException {  
    }  

public void write(URI uri, URIChooser chooser) throws IOException {  
    }  

}

HelloApplication.java

import org.jhotdraw.app.Application;  
import org.jhotdraw.app.DefaultApplicationModel;  
import org.jhotdraw.app.SDIApplication;  

public class HelloApplication {  

public static void main(String[] args) {  
        Application app=new SDIApplication();  
        DefaultApplicationModel model=new DefaultApplicationModel();  
        model.setViewClass(HelloView.class);  
        app.setModel(model);  
        app.launch(args);  
    }  
}

示例程序的运行效果

图1  org.jhotdraw.app包结构图

图2. JHotDraw MVC架构

图3 org.jhotdraw.app.Applicatioin 接口契约

3.2 MVC架构

这里只介绍JHotDraw框架的MVC模式. 关于MVC的基本概念,如MVC是什么, 为什么要用MVC, MVC的优点和缺点有哪些, 什么是改进的MVC等等等等, 这些问题可以Google一下,  这里只简单概述.

MVC发明于80年代,最早是运用在smalltalk上的. MVC试图将视图,控制器和模型分离,使之成为三个独立的部件以提高代码可复用率及可维护性.

视图:即肉眼看到的界面(严格的说法是:用户看到并与之交互的界面便为视图),视图只负责显示数据或其它可与用户交互的元件(按钮,菜单和链接等).

模型:模型是对现实数据和业务规则的模拟,也可以说是对数据和规则建模的结果. 模型为众多视图提供了数据来源. 对模型进行分离的最大好处就是一个模型可以被多个视图重用, 既减少了代码的重复量, 也提高代码的可维护度.

控制器:控制器接收用户的输入(操作)并调用模型和视图去完成用户的需求. 如博客网站中, 当用户编写完博客点击(页面上的)提交按钮时, 控制器接收请求并将请求交给相应的模型去处理, 而控制器本身对请求不做任何处理. 控制器发送完请求后, 根据模型返回的信息调用相应的视图显示处理的结果.

MVC的好处在于重用了大量的代码, 并且极大地提高了代码的可维护性. 比如在一个交友网站中, 如果你的数据从mySql迁移到noSql上, 你只需要修改模型的内部逻辑, 而无需修每个页面都需改.

再来看JHotDraw的MVC,

HelloApplication.java

import org.jhotdraw.app.Application;   
import org.jhotdraw.app.DefaultApplicationModel;   
import org.jhotdraw.app.SDIApplication;   

public class HelloApplication {   

public static void main(String[] args) {   
        Application app=new SDIApplication();  
    DefaultApplicationModel model=new DefaultApplicationModel();   
    model.setViewClass(HelloView.class);   
    app.setModel(model); app.launch(args);   
    }  
}

首先是模型的建立(model)(即上面第8行代码). 这里的model不是业务逻辑的model, 而是应用程序的model. 它是在应用程序层次上对应用程序建模而来的. Model封装了程序名称, 程序版本号以及一系列的action(控制器)(model的详细构造将在下面的章节论述). model还存放了应用程序的视图(view)(第10行代码).  这样, 当程序运行时, application便调用model的getView()方法来获取视图并将其显示, 而用户通过点击控制器(按钮,菜单)激活控制器, 控制器将用户的操作传送给model处理, model将处理完之后, 显示相应的视图来告知用户的操作结果.

下面是应用JHotDraw框架的程序启动顺序图 (点击查看原图):

 

3.3 面向文档基本框架

通过前文, 我们大概了解了JHotDraw的MVC架构.简单的, 试想如果要实现一个日记程序(Daily), 应该有如下步骤:

1. 定义模型(model): DialyModel. 该模型封装了程序的名称(daily), 版本(1.0), 版权(你);

2. 定义视图(view):DailyView. 该视图可以是一个JPanel, 里面包括一个填写日记内容的JTextPane, 两个用于保存和打开的按钮(仅仅是按钮, 并没有按钮按下的动作);

3. 定义控制类(controller): 定义用于保存和打开日记的控制方法.

4. 将视图添加到模型: 通过model.setView/ViewClass方法将DailyView添加到DialyModel.

5. 将模型添加到应用程序:创建一个DailyApplication, 并通过setModel方法添加模型;

6. 启动程序: 调用DialyApplication.launch 方法.

下面补充JHotDraw框架(framework)主要接口的框架契约(contract of a framework).

设计模式:Framework(框架)

以下的接口和org.jhotdraw.app.action 包里所有类定义了面向文档应用程序的契约: ApplicationModel(org.jhotdraw.app), View(org.jhotdraw.app).

1. 接口 Application (org.jhotdraw.app) 应用程序

JavaDoc:

    Application 用于控制View类的生命周期并提供一个窗口来显示View类.

    一个application拥有一个ApplicationModel, ApplicationModel封装了关于application的信息(名称,版本,版权)以及创建View的模板方法.

    application实现了基于文档界面的风格(document interface style),  一个application可以同时处理一个或者多个文档.

    典型的文档界面有单文档界面(SDI, Single Document Interface), 多文档界面(MDI, Multiple Document Interface)和Mac系统界面(OSX, Mac OS X Application Interface). 针对这些文档风格, JHotDraw已经提供了每种文档的默认实现.

    一些应用程序需要对所有打开的窗口和对话窗进行特别的设置, 这就需要在打开JFrame, JDialog或JWindow时调用application的addWindow/Palette 或removeWindow/Palette方法.(仅7.4之后的版本)

该类的使用方法:

  public class MyMainClass {
     public static void main(String[] args) {
         Application app = new SDIApplication(); // or OSXApplication(), MDIApplication().
         DefaultApplicationModel model = new DefaultApplicationModel();
         model.setName("MyApplication");
         model.setVersion("1.0");
         model.setCopyright("Copyright 2006 (c) Werner Randelshofer. All Rights Reserved.");
         model.setViewClassName("org.jhotdraw.myapplication.MyView");
         app.setModel(model);
         app.launch(args);
     }

2. 接口 ApplicationModel (org.jhotdraw.app) 应用程序模型

ApplicationModel提供Application的元数据(名称,版本,版权), Application的控制器, 创建视图,工具栏及URIChooser的工厂方法.

3. 接口 View(org.jhotdraw.app) 视图

  View通过JComponent来显示应用程序的文档.

  文档以URI格式定位(一个文档的URI应为 file://home/readme.txt). 如果在多视图界面中打开同一个文档, 则应用程序将为每个文档设置"多重打开标志"来区分这些视图.

  view(视图)的生命周期由application控制, 一个view的生命周期为:

    1.创建(Creation): 应用程序通过调用view类的newInstance()方法将view实例化;

    2.初始化(Initialisation): 应用程序调用view类的以下方法: setActioinMap(), setApplicatiin(), init().然后通过工作者线程(worker thread)调用clear()或者read()方法;

    3. 开始(Start):应用程序将view添加到一个容器(如JFrame)中, 并调用view类的start方法.

    4. 激活(Activation): 当view在应用程序中被激活时, 应用程序调用其activate()方法.

    5. 钝化(Deactivation):当view不处在被激活状态(如其它view被激活), 在应用程序调用其deactivate(0方法. 钝化的view可以再次被激活;

    6. 停止(Stop):应用程序调用view的stop()方法来停止view, 并将其从应用程序容器中删除. 停止后的view可以通过开始(Start)再次显示.

    7. 销毁(Dispose):当一个view不再需要时, 应用程序调用其dispose()方法来销毁一个view. dispose()方法通过setApplication(null)并删除所有对它的引用而进入垃圾回收机制.

4. 包 org.jhotdraw.app.actions 控制器

  提供"面向文档的应用程序"的抽象动作(abstract actions)及其默认实现的动作. 按照框架契约, 应用程序层次的所有动作都必须继承自org.jhotdraw.app.actions.AbstractApplicationAction类.  该类定义了诸如此类的属性:当应用程序处在disable状态时, 所有继承自AbstractApplicationAction的动作(控制器)的状态也都为disable. 基于swing的特性, 所有继承自AbstractApplicationAction的动作(控制器)既可以作为一个菜单项(menu item),也可以作为工具栏按钮. 当然, 这些动作也可以仅添加在指定的视图上.

  org.jhotdraw.app.actions还定义了应用程序的一些基本动作(控制器)的默认实现类,如"关于","退出","复制","剪切"等.

相关链接:

    1. http://www.jhotdraw.org/ JHotDraw项目首页

    2. http://sourceforge.net/projects/jhotdraw/ JHotDraw下载

    3. http://dirkriehle.com/computer-science/research/dissertation/chapter-8.html

    4. http://article.yeeyan.org/compare/14599/4185 JHotDraw让你成为程序设计的毕加索

    5. http://softarch.cis.strath.ac.uk/PLJHD/Patterns/JHDDomainOverview.html JHotDraw Pattern Language

    6. http://www.randelshofer.ch/oop/jhotdraw/index.html  在线演示

    7. http://st-www.cs.illinois.edu/users/brant/HotDraw/HotDraw.html 5.1版首页

    8. http://www.c2.com/cgi/wiki?HotDraw 一些关于JHotDraw的资源汇集

    9. http://twiki.org/cgi-bin/view/Wikilearn/JHotDraw 5.1版wiki
    10. http://personal.cis.strath.ac.uk/~murray/efocswww/papers/EFoCS-38-2001.html Patterns for JHotDraw
    11. http://st-www.cs.illinois.edu/users/brant/HotDraw/HotDraw.html 早期版本的hotdraw主页与下载

 

 

其它

 

一网友问题

您好!请教一个问题:jhotdraw7.6中samples中draw实例中有个file菜单中有个“open”的功能,可以根据xml文件生成图形,请问这个功能的代码在哪儿?谢谢

 

你好,我现在用的是7.5的版本,我想应该是一样的。
实现机制:
draw的存储接口是DOMStorable,负责drawing的IO操作;所有继承或实现该接口的子类都必须实现了read和write方法,即实现具体类的读入和写出责任。AbstractAttributedFigure和Drawing都继承该接口。
读取文件的顺序为:

  1. openAction读入文件,调用DrawView的read方法读取该文件;
  2. DrawView中维护多个inputFormat,每个inputFormat负责读取特定的Figure;当调用read方法时,DrawView尝试让所有的inputFormat读取该文件,但只要有一个inputFormat读取成功就停止。

至于DrawView装载了多少InputFromat,可以参考org.jhotdraw.samples.draw.DrawView.createDrawing():

protected Drawing createDrawing() {  
       Drawing drawing = new QuadTreeDrawing();  
       DOMStorableInputOutputFormat ioFormat =  
new DOMStorableInputOutputFormat(new DrawFigureFactory());  

       drawing.addInputFormat(ioFormat);  
       ImageFigure prototype = new ImageFigure();  
       drawing.addInputFormat(new ImageInputFormat(prototype));  
       drawing.addInputFormat(new TextInputFormat(new TextFigure()));  
       TextAreaFigure taf = new TextAreaFigure();  
       taf.setBounds(new Point2D.Double(10,10), new Point2D.Double(60,40));  
       drawing.addInputFormat(new TextInputFormat(taf));  

       drawing.addOutputFormat(ioFormat);  
       drawing.addOutputFormat(new ImageOutputFormat());  
return drawing;  
   }

参考org.jhotdraw.draw.LineConnectionFigure对write()和read()的实现:

//org.jhotdraw.draw.LineConnectionFigure.read
public void read(DOMInput in) throws IOException {  
        readAttributes(in);  
        readLiner(in);  

// Note: Points must be read after Liner, because Liner influences
// the location of the points.
        readPoints(in);  
    }  

//org.jhotdraw.draw.AbstractAttributedFigure.readAttributes
protected void readAttributes(DOMInput in) throws IOException {  
if (in.getElementCount("a") > 0) {  
            in.openElement("a");  
for (int i=in.getElementCount() - 1; i >= 0; i-- ) {  
                in.openElement(i);  
                String name = in.getTagName();  
                Object value = in.readObject();  
                AttributeKey key = getAttributeKey(name);  
if (key != null && key.isAssignable(value)) {  
if (forbiddenAttributes == null
                            || ! forbiddenAttributes.contains(key)) {  
                        set(key, value);  
                    }  
                }  
                in.closeElement();  
            }  
            in.closeElement();  
        }  
    }  




protected void readLiner(DOMInput in) throws IOException {  
if (in.getElementCount("liner") > 0) {  
            in.openElement("liner");  
            liner = (Liner) in.readObject();  
            in.closeElement();  
        } else {  
            liner = null;  
        }  

    }  


//org.jhotdraw.draw.LineConnectionFigure.readPoints
protected void readPoints(DOMInput in) throws IOException {  
super.readPoints(in);  
        in.openElement("startConnector");  
        setStartConnector((Connector) in.readObject());  
        in.closeElement();  
        in.openElement("endConnector");  
        setEndConnector((Connector) in.readObject());  
        in.closeElement();  
    }

读入机制参见:
org.jhotdraw.app.action.file.OpenFileAction#openViewFromURI() line 105
org.jhotdraw.samples.draw.DrawView#read()
org.jhotdraw.xml.DOMFactory

posted on 2012-07-22 11:20  brook.tran  阅读(2593)  评论(0编辑  收藏  举报

导航