摘要:
我们需要一个工具将独立开发的组件整合成一个精心设计的最终产品。这个工具能够协调好体系结构中各个主要的子系统。由于提倡在非常模块化的环境下的快速开发,这个工具应该易于理解。seppia就是为这些目标设计的。
软件开发已经发生了变化。开发者不再是从头开始开发大型系统,而是通过装配已有的组件。许多产品无论是开源的还是非开源的,现在都已经足够稳定,能在这些组件上面开发应用程序。开发者已经疲于重新开发已有的代码,而是开始利用这些组件来开发程序。这是非常好的,这就像面向对象的软件开发所作的一个承诺:重用性。但是,装配独立的开发好的组件不是一个简单的任务,如果处理不得当,它可能导致复杂的整合问题,及紧耦合代码问题。
现在我们需要一个工具将独立开发的组件整合成一个精心设计的最终产品。这个工具能够协调好体系结构中各个主要的子系统。由于提倡在非常模块化的环境下的快速开发,这个工具应该易于理解。这个工具应该让你在添加尽量少额外编码的情况下,完成从已有组件整合为应用程序的工作。并且它应该能尽量简化为高定制应用程序开发的特定功能。
seppia就是为这些目标设计的。
版权声明:任何获得matrix授权的网站,转载时请务必保留以下作者信息和链接
作者:cicitgd;cicitgd;cicitgd at hotmail.com
原文:http://www.matrix.org.cn/resource/article/2006-11-06/seppia_778860da-6d27-11db-bdce-bdc029e475a1.html
关键字:seppia;模块化
seppia简介
seppia是一项简单的java技术,用于从组件创建java应用程序。构建seppia是围绕模块概念的。一个模块是系统中的一个自包含的(独立的)部分。模块以文件夹的形式存储。比方说一个文字处理器,它在seppia中包含以下这些模块:
|
+ modules
+-- org.hypotheticalwordproc.spellchecker
+-- org.hypotheticalwordproc.ui-swing
+-- org.hypotheticalwordproc.io
+-- org.hypotheticalwordproc.searchengine
+-- ...
在这个模型中,每个模块的职责是为系统只提供一个特定的功能。
每个模块的行为被分成java代码和javascript代码。java代码保存在.jar文件中,为与javascript代码协同工作提供api。javascript代码保存在.js文件中,定义了模块提供的服务。
spellchecker模块如下:
+-- org.hypotheticalwordproc.spellchecker
+-- jars
| +--- jazzy.jar // open source java api.
|
+-- javascripts
| +--- wordfinder.js
| +--- sentenceanalyzer.js
| +--- ...
|
+--- dictionaries
+--- english.dic
+--- france.dic
在这个例子中,wordfinder.js和sentenceanalyzer.js是这个模块的切入点。
本文中,我们将使用seppia技术创建一个具有丰富功能的应用程序。我们将使用一些开源产品,并向你展示如何将它们整合成为一个完美的产品。
但在我们开始前,我们知道更多关于seppia是怎么工作的。
快速开始seppia
seppia的二进制版本由下面的目录结构组成:
+--- jars // contains the jars to bootstrap seppia.
+--- modules // contains the modules. initially there
is only one module, "org.seppia.bootstrap"
+--- startup.class // the class to run seppia.
我们先假设已经安装了最新版本的seppia在c:/myfirstseppia文件夹中。(注意:这里假设你是一个windows用户。这样做主要是为了便于保持简洁的格式,也易于将其中的路径转换到其它系统中。)
使用下面的命令运行seppia:
c:/myfirstseppia/>java -cp . startup
输出为:
seppia successfully installed at url file:/c:/myfirstseppia/
信息"successfully installed"来自于模块org.seppia.bootstrap中的javascript文件startup.js。seppia是通过硬编码查找org.seppia.bootstrap和执行startup.js。这就是说startup.js是我们首先要了解的。它的源代码是:
function main()
{
java.lang.system.out.println(
"seppia successfully installed at url "+module.environment.url);
}
如果你想要编辑这个文件,你可以很容易地将它转变为一个"hello world" swing信息:
var jframe = packages.javax.swing.jframe;
function main()
{
var frame = new jframe("hello world");
frame.setdefaultcloseoperation(jframe.exit_on_close);
frame.setsize(360,80);
frame.setvisible(true);
}
当你重新启动seppia时,你将会看到像图 1这样的窗口:
图 1. seppia中的"hello world"
在你进行seppia安装前,详细了解seppia的规定是非常重要的,它是任何应用程序一致性设计的保证。仔细阅读,因为其中解释了你将碰到的大部分问题,你会因此而节省很多时间。
• javascripts存储在每个模型的javascripts文件夹中.
• 第一个运行的javascript是startup.js,在模块org.seppia.bootstrap中。
• 一个javascript使用“本地“函数run调用另一个javascript。
o run(scriptid)运行同一模块中的另一个javascript。
o run(moduleid, scriptid)运行其它模块中的一个javascript。
• javascript的执行包括两个步骤:
o 给javascript语句赋值。
o 执行main()函数。
• javascripts可以访问存储在它们模块的jars目录下的.jar文件中的类。
• javascript可利用下面的属性:
o id: javascript的名称
o code: javascript的代码
o module: javascript运行时所处的模块
创建一个示例应用程序
当你理解了结合seppia使用javascripts的基础,那么就应该使用它去创建更多的东西。我们要创建的应用程序是一个简单的gui,它提供了一个图表关于从任何一个html页得到outlinks的域扩展名的分配。应用程序允许用户输入任何url,返回web页面,查找在html标签中的链接,并提供一张他们的分配情况的图表。我们称这个应用程序为link-vision。图 2展示了它的实现。
图 2. link-vision查询o’reilly网站的结果
分解这个应用程序
第一步是考虑如何用分开的组件设计这个应用程序。即使不从面向对象思想方面分析,很明显的是,这个应用程序的图表制作模块不该具有查找html链的功能。因此,我们把应用程序分为两个模块:
• 一个解析html页面获得它的链接
• 另一个画一个图表组件
我们分别称这两个模块为org.link-vision.html-analyzer和org.link-vision.chart,并手动地添加它们到文件夹。
myfirstseppia
+--- jars
+--- modules
| +--- org.seppia.bootstrap
| | +--- javascripts
| | +--- startup.js
| |
| +--- org.link-vision.html-analyzer
| | +--- javascripts
| | +--- jars
| |
| +--- org.link-vision.chart
| +--- javascripts
| +--- jars
|
+--- startup.class
下一步是查找一些已有的产品,在我们的模块中重用它们。这里使用下面两个开源产品:
• html parser
• jfreechart
在这里要特别注意,虽然我喜欢这两个产品,但还可有很多其它的可用的选择。我的选择纯粹是由这个示例应用程序的限制驱使的。选择正确的工具嵌入到你的软件系统中仍然是一个重要的工作。只有重用合适的成熟的且测试过的产品,我们才能减少在创建新应用程序时所花的工夫。
一旦我们已经下载了html parser,我们可以从它的lib目录将htmlparser.jar拷贝org.link-vision.html-analyzer/jars。类似地,我们可以将jfreechart-0.9.21.jar 和jcommon-0.9.6.jar(jfreechart的代码依赖于jcommon)拷贝到org.link-vision.chart/jars。
构建html parser
现在我们终于可以通过编写合适的javascripts为我们的模块定义所要提供的服务了。对于org.link-vision.html-analyzer,我们编写linkfinder.js来提供从url中提取链接的方法。
var url = java.net.url
var linktag= packages.org.htmlparser.tags.linktag;
var parser= packages.org.htmlparser.parser;
var nodeclassfilter =
packages.org.htmlparser.filters.nodeclassfilter;
/*
this function returns a javascript object with
one method that given a url string gives you an
array of urls (outlinks from the given url)
*/
function main()
{
var obj = new object();
obj.getlinks = getlinks;
return obj;
}
function getlinks(url)
{
var f = new nodeclassfilter(linktag);
var parser = new parser (url);
var list = parser.extractallnodesthatmatch(f);
var array = [];
for (var i = 0; i < list.size (); i++)
{
var linktag = list.elementat(i);
// skip if no http link.
if (linktag.ishttplink()==false) continue;
var link = linktag.extractlink();
// skip any relative link.
if (link =="" ) continue;
array[array.length] = new url(link);
}
return array;
}
尽管这个javascript使用一些不熟悉的html parser api的类,但是它是非常容易读懂的。对于其它模块,org.link-vision.html-analyzer只是一个从url中获得链接服务的提供者。它的实现隐藏在javascript代码和htmlparser.jar之间,可以被很容易地改变或重新编写为使用其它的.jars。倘若javascript对象继续返回方法geturl(这个模块唯一暴露在外的地方),它的实现就不需要改变了。
这看上去不像我们想要的那样功能强大,但是这样的特性有效地减弱了你的体系结构中主要子系统间的耦合程度,这对于创建成功的大型系统是至关重要的。
构建图表
让我们转到其他模块。对于org.link-vision.chart,我们编写了piebuilder.js,它创建了一个拥有两个方法的javascript对象:
• createpair(name,value): 建立一对名称和数值对(如,createpair(“ibm”, 80);)
• createguicomponent(title): 建立一个swing组件(特指饼图)
下面是实现:
var jlabel = packages.javax.swing.jlabel;
var imageicon = packages.javax.swing.imageicon;
var chartfactory =
packages.org.jfree.chart.chartfactory;
var defaultpiedataset =
packages.org.jfree.data.general.defaultpiedataset;
var piedataset_ = new defaultpiedataset();
function main()
{
piedataset_ = new defaultpiedataset();
obj = new object();
obj.createpair = createpair;
obj.createguicomponent = createguicomponent;
return obj;
}
function createpair(name, value)
{
piedataset_.setvalue(name,value);
}
function createguicomponent(title)
{
var chart = chartfactory.createpiechart(
title,piedataset_,true,true,true);
var image = chart.createbufferedimage(500,400);
var lblchart = new jlabel();
lblchart.seticon(new imageicon(image));
return lblchart;
}
尽管这里又有一些你不熟悉的类,但是代码还是很容易读懂的。
将模块整合在一起
现在我们已经完成了两个模块,我们需要第三个模块将这两个模块整合在一起。我们命名这个模块为org.ling-vision.application,并编写了一个javascript:main.js。
main.js使用了前面两个模块的功能来创建swing应用程序。重要的一点是这个模块不需要其他.jars。
var hashmap = java.util.hashmap;
var integer = java.lang.integer;
var borderlayout = java.awt.borderlayout;
var actionlistener=java.awt.event.actionlistener;
var jlabel = packages.javax.swing.jlabel;
var jpanel = packages.javax.swing.jpanel;
var jframe = packages.javax.swing.jframe;
var jtextfield = packages.javax.swing.jtextfield;
function main()
{
// creates the linkfinder object.
var linkfinder = run(
"org.link-vision.html-analyzer",
"linkfinder");
var panel = new jpanel(new borderlayout());
var txturl = new jtextfield(
"http://www.java.sun.com");
txturl.addactionlistener(function f()
{
// actionlistener implementation.
var url = txturl.gettext();
var array = linkfinder.getlinks(url);
var map = new hashmap();
// loops thru all the urls updating
// the map: extensionname --> count.
for (var i=0;i<array.length;i++)
{
var host = array[i].gethost();
var extension =
host.substring(1+host.lastindexof('.'));
if (map.containskey(extension))
{
var t = new integer(1+
(map.get(extension)).intvalue());
map.put(extension,t);
}
else
{
map.put(extension,new integer(1));
}
}
// creates the piebuilder object.
var piebuilder = run("org.link-vision.chart",
"piebuilder");
var iterator = map.keyset().iterator();
while (iterator.hasnext())
{
var ext = iterator.next();
var hitscount = map.get(ext);
// creates the pairs.
piebuilder.createpair(ext,hitscount);
}
// creates the component for the main panel
var component=piebuilder.createguicomponent(
"domain extensions distribution");
panel.removeall();
panel.add(component,borderlayout.center);
panel.revalidate();
panel.repaint();
});
var f = new jframe("link-vision");
f.getcontentpane().add(txturl,borderlayout.north);
f.getcontentpane().add(panel,borderlayout.center);
f.setsize(510,470);
f.setdefaultcloseoperation(jframe.exit_on_close);
f.setvisible(true);
}
这个应用程序中还缺少一样东西:如果你重新调用,seppia通过执行org.seppiq.bootstrap中的startup.js开始运行。这个javascript需要重定向来执行org.link-vision.application中的main.js。
function main()
{
run("org.link-vision.application","main");
}
所有的都完成了。如果你已经设法按照上面的说明一步步做到了这里,你应该能够运行link-vision了。
下面是目录的结构:
myfirstseppia
+--- jars // the classes to bootstrap seppia
+--- modules
| +--- org.seppia.bootstrap
| | +--- javascripts
| | +--- startup.js
| |
| +--- org.link-vision.application
| | +--- javascripts
| | +--- main.js
| |
| +--- org.link-vision.html-analyzer
| | +--- javascripts
| | | +--- linkfinder.js
| | +--- jars
| | +--- htmlparser.jar
| |
| +--- org.link-vision.chart
| +--- javascripts
| | +--- piebuilder.js
| +-jars
| +--- jfreechart-0.9.21.jar
| +--- jcommon-0.9.6.jar
|
+--- startup.class
最后的事项
在完成应用程序后,你可能想知道这个过程是否值得。当然,你能将这个应用程序写成一个标准的java程序,但是如果你已经这么做了,你会失去一些seppia技术所带来的益处。这里列举了一些最重要的益处:
• seppia应用程序不需要为classpath多费心。运行任何seppia应用程序只需输入java ?ccp . startup到seppia的安装目录中。这样大大减少了对批处理或shell编码的需要。
• seppia应用程序是优雅的(elegant)和模块化的。seppia技术制定了存放.jars,javascripts和其他资源的精确的规则。这反映在任何seppia应用程序中非常整齐和定义明确的目录结构。开发者通过查看seppia应用程序的结构就能立即辨别出seppia应用程序和它的组成成分来。
• seppia提供了一种简单的原型方法。因为seppia使用javascript作为模块间的“粘合剂”,所以很容易测试新的行为。这个过程不能再简单了:改变javascript代码,并重新启动你的应用程序。不需要编译代码,补充(patch)类,替换.jars。还值得注意的是有时候修改一个编写好的javascript代码就像改变配置文件那么简单(当然更强大)。
• seppia应用程序随时随地可以执行(ship)。seppia是自包含的(独立的)。一个典型的seppia应用混合了它的模块中的所有的资源(.jars,javascripts,图片,其它文件)。你可以在它的根目录压缩这些应用程序文件,并且不需要任何额外的工作,在其它地方把它们解压缩出来使用。
• seppia应用程序是可扩展的。事实上,seppia是整个体系结构的核心,这个体系结构使可扩展的,能动态识别扩展模块。如果你从这方面出发,任何seppia应用程序只是已有seppia安装程序的一个扩展。seppia安装程序本身就是一个seppia应用程序;或许是最小的:最小的公共程序(最小公分母(the least common denominator))。
资源
• sample code .
• seppia:http://www.seppia.org/.
• rhino: http://www.mozilla.org/rhino.
• mozilla: javascript: [/url=http://www.mozilla.org/js]http://www.mozilla.org/js[/url].
• e4x: http://www.ecma-international.org/publications/standards/ecma-357.htm.
关于作者
leronzo puccetti是一个居住和工作在英国伦敦的软件开发专家/架构师。
闽公网安备 35060202000074号