﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-Java &amp;&amp; Flex-文章分类-Java</title><link>http://www.blogjava.net/sl2cj/category/10602.html</link><description>&lt;br&gt;&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp&amp;nbsp----&gt;乞怪猪blog搬家了--[www.ialway.com/blog]--希望继续关注!谢谢!^0^</description><language>zh-cn</language><lastBuildDate>Wed, 28 Feb 2007 05:53:20 GMT</lastBuildDate><pubDate>Wed, 28 Feb 2007 05:53:20 GMT</pubDate><ttl>60</ttl><item><title>利用MVC模式开发Java应用程序</title><link>http://www.blogjava.net/sl2cj/articles/46841.html</link><dc:creator>blog搬家了--[www.ialway.com/blog]</dc:creator><author>blog搬家了--[www.ialway.com/blog]</author><pubDate>Thu, 18 May 2006 08:12:00 GMT</pubDate><guid>http://www.blogjava.net/sl2cj/articles/46841.html</guid><wfw:comment>http://www.blogjava.net/sl2cj/comments/46841.html</wfw:comment><comments>http://www.blogjava.net/sl2cj/articles/46841.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sl2cj/comments/commentRss/46841.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sl2cj/services/trackbacks/46841.html</trackback:ping><description><![CDATA[<table style="WORD-BREAK: break-all" cellspacing="0" cellpadding="0" width="700" align="center" border="0">
    <tbody>
        <tr>
            <td align="center" height="25"><font face="黑体" size="4">利用MVC模式开发Java应用程序</font></td>
        </tr>
        <tr bgcolor="#f9f9f9">
            <td id="fontzoom" style="LINE-HEIGHT: 200%">
            <p align="center">作者：龚赤兵&nbsp;&nbsp;&nbsp;&nbsp;来自：开放系统世界</p>
            <p>&nbsp;</p>
            <p>　　Java是一种面向对象的语言，是实现面向对象编程的强大工具。我们在实际编程中，应该运用并发挥其最大效能。但是，要利用面向对象编程思想，自己独立开发出好的Java应用程序，特别是大、中型程序，并不是一件简单的事情。正是基于面向对象编程思想，人们将实际中的各种应用程序，进行了大量的分析、总结，从而归纳出许多标准的设计模式。将这些设计模式合理地运用到自己的实际项目中，可以最大限度地减少开发过程中出现的设计上的问题，确保项目高质量的如期完成。 </p>
            <p>　　<strong>MVC模式介绍 </strong></p>
            <p>　　模型－视图－控制器(Model-View-Controller,MVC)模式就是为那些需要为同样的数据提供多个视图的应用程序而设计的。它很好地实现了数据层与表示层的分离，特别适用于开发与用户图形界面有关的应用程序，其示意图见图1。模式中基本结构定义为： </p>
            <p>　　控制器 用来处理用户命令以及程序事件的； </p>
            <p>　　模型 维护数据并提供数据访问方法； </p>
            <p>　　视图 数据的显示。 </p>
            <p>　　MVC模式基本实现过程为： </p>
            <p>　　1. 控制器（如Java中的main程序入口）要新建模型； </p>
            <p>　　2. 控制器要新建一个或多个视图对象，并将它们与模型相关联； </p>
            <p>　　3. 控制器改变模型的状态； </p>
            <p>　　4. 当模型的状态改变时，模型将会自动刷新与之相关的视图。</p>
            <p><img src="http://school.ccidnet.com/pub/attachment/2003/6/201332.gif" align="center" border="0" alt="" /></p>
            <p>　　图1 MVC模式基本结构 </p>
            <p>　　本文要实现的Ｊａｖａ应用程序是当用户在图形化用户界面输入一个球体的半径时，程序将显示该球体的体积与表面积。我们首先利用基本MVC模式实现以上程序，然后利用不同数量的模型、视图、控制器结构来扩展该程序。 </p>
            <p>　　<strong>基本MVC模式 </strong></p>
            <p>　　该程序主要由三个类构成，分别为Sphere类、TextView类及SphereWindow类。其中Sphere类扮演Model的角色，TextView类为View角色，SphereWindow类为Controller角色。 </p>
            <p>　　Java通过专门的类Observable及Observer接口来实现MVC编程模式。其UML类图及MVC模式的实现方式见图2。</p>
            <p><img src="http://school.ccidnet.com/pub/attachment/2003/6/201333.gif" align="center" border="0" alt="" /></p>
            <p>　　图2 MVC模式的UML类图 </p>
            <p>　　从图2中可以看出，Model类必须继承Observable类，View类必须实现接口Observer。正是由于实现了上述结构，当模型发生改变时（当控制器改变模型的状态），模型就会自动刷新与之相关的视图。其UML序列图可以表示为图3。 </p>
            <p>　　Model类Sphere，必须扩展Observable类，因为在Observable类中，方法addObserver()将视图与模型相关联，当模型状态改变时，通过方法notifyObservers()通知视图。其中实现MVC模式的关键代码为：</p>
            <table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="550" align="center" bordercolorlight="#000000" border="1">
                <tbody>
                    <tr>
                        <td bgcolor="#e6e6e6">
                        <pre>import java.util.Observable;
                        class Sphere extends Observable
                        {
                        ....
                        public void setRadius(double r)
                        {
                        myRadius = r;
                        setChanged();         // Indicates that the model has changed
                        notifyObservers();
                        }
                        ....
                        }</pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <p><img src="http://school.ccidnet.com/pub/attachment/2003/6/201334.gif" align="center" border="0" alt="" /></p>
            <p>　　图3 MVC模式的UML序列图 </p>
            <p>　　View类的角色TextView类必须实现接口Observer，这意味着类TextView必须是implements Observe，另外还需实现其中的方法update()。有了这个方法，当模型Sphere类的状态发生改变时，与模型相关联的视图中的update()方法就会自动被调用，从而实现视图的自动刷新。View类的关键代码如下：</p>
            <table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="550" align="center" bordercolorlight="#000000" border="1">
                <tbody>
                    <tr>
                        <td bgcolor="#e6e6e6">
                        <pre>import java.util.Observer;
                        import java.util.Observable;
                        public class TextView extends JPanel implements Observer
                        {
                        ......
                        public void update(Observable o, Object arg)
                        {
                        Sphere balloon = (Sphere)o;
                        radiusIn.setText(&ldquo; &rdquo;+f3.format(balloon.getRadius()));
                        volumeOut.setText(&ldquo; &rdquo;+f3.format(balloon.volume()));
                        surfAreaOut.setText(&ldquo; &rdquo; + f3.format(balloon.surfaceArea()));
                        }
                        ......
                        }</pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <p>　　SphereWindow类作为Controller，它主要新建Model与View，将view与Model相关联，并处理事件，其中的关键代码为：</p>
            <table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="550" align="center" bordercolorlight="#000000" border="1">
                <tbody>
                    <tr>
                        <td bgcolor="#e6e6e6">
                        <pre>public SphereWindow()
                        {
                        super(&ldquo;Spheres: volume and surface area&rdquo;);
                        model = new Sphere(0, 0, 100);
                        TextView view = new TextView();
                        model.addObserver(view);
                        view.update(model, null);
                        view.addActionListener(this);
                        Container c = getContentPane();
                        c.add(view);
                        }
                        public void actionPerformed(ActionEvent e)
                        {
                        JTextField t = (JTextField)e.getSource();
                        double r = Double.parseDouble(t.getText());
                        model.setRadius(r);
                        }</pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <p>　　该程序是通过Java中的MVC模式编写的，具有极其良好的可扩展性。它可以轻松实现以下功能： </p>
            <p>　　1. 实现一个模型的多个视图； </p>
            <p>　　2. 采用多个控制器； </p>
            <p>　　3. 当模型改变时，所有视图将自动刷新； </p>
            <p>　　4. 所有的控制器将相互独立工作。 </p>
            <p>　　这就是Java编程模式的好处，只需在以前的程序上稍作修改或增加新的类，即可轻松增加许多程序功能。以前开发的许多类可以重用，而程序结构根本不再需要改变，各类之间相互独立，便于团体开发，提高开发效率。 </p>
            <p>　　<strong>一个模型、两个视图和一个控制器 </strong></p>
            <p>　　下面我们讨论如何实现一个模型、两个视图和一个控制器的程序。当用户在图形化用户界面输入一个球体的半径，程序除显示该球体的体积与表面积外，还将图形化显示该球体。该程序的4个类之间的示意图可见图4。</p>
            <p><img src="http://school.ccidnet.com/pub/attachment/2003/6/201335.gif" align="center" border="0" alt="" /></p>
            <p>　　图4一个模型、两个视图和一个控制器的基本结构 </p>
            <p>　　其中Model类及View1类根本不需要改变，与前面的完全一样，这就是面向对象编程的好处。对于Controller中的SphereWindows类，只需要增加另一个视图，并与Model发生关联即可。其关键实现代码为：</p>
            <table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="550" align="center" bordercolorlight="#000000" border="1">
                <tbody>
                    <tr>
                        <td bgcolor="#e6e6e6">
                        <pre>public SphereWindow()
                        {
                        super(&ldquo;Spheres: volume and surface area&rdquo;);
                        model = new Sphere(0, 0, 100);
                        TextView tView = new TextView();
                        model.addObserver(tView);
                        tView.addActionListener(this);
                        tView.update(model, null);
                        GraphicsView gView = new GraphicsView();
                        model.addObserver(gView);
                        gView.update(model, null);
                        Container c = getContentPane();
                        c.setLayout(new GridLayout(1, 2));
                        c.add(tView);
                        c.add(gView);
                        }</pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <p>　　其程序输出结果见图5。</p>
            <p><img src="http://school.ccidnet.com/pub/attachment/2003/6/201336.gif" align="center" border="0" alt="" /></p>
            <p>　　图5 输出结果 </p>
            <p>　　<strong>一个模型、两个视图和两个控制器 </strong></p>
            <p>　　在上面的程序中，我们只能通过键盘输入球体半径，现在我们修改以上程序，利用鼠标放大、缩小右边的球体图形及可改变球体的半径，从而获得球体半径的输入。 </p>
            <p>　　此时的MCV模式为一个模型、两个视图和两个控制器，其结构可以见图6，其UML类图可以表示为图7。 </p>
            <p>　　其中Sphere、TextView与GraphicsView类与前面完全一样。在主程序SphereWindows中，该类这时不是直接作为Controller，它控制Controller1与Controller2的新建。该程序的关键代码为：</p>
            <table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="550" align="center" bordercolorlight="#000000" border="1">
                <tbody>
                    <tr>
                        <td bgcolor="#e6e6e6">
                        <pre>public SphereWindow()
                        {
                        super(&ldquo;Spheres: volume and surface area&rdquo;);
                        Sphere model = new Sphere(0, 0, 100);
                        TextController tController = new TextController(model);
                        GraphicsController gController = new GraphicsController(model);
                        Container c = getContentPane();
                        c.setLayout(new GridLayout(1, 2));
                        c.add(tController.getView());
                        c.add(gController.getView());
                        }</pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <p><img src="http://school.ccidnet.com/pub/attachment/2003/6/201337.gif" align="center" border="0" alt="" /></p>
            <p>图6一个模型、两个视图和两个控制器的基本结构</p>
            <p><img src="http://school.ccidnet.com/pub/attachment/2003/6/201338.gif" align="center" border="0" alt="" /></p>
            <p>　　图7 一个模型、两个视图和两个控制器的UML类图 </p>
            <p>　　当程序SphereWindow运行时，将鼠标移动到球体的外圆处，点击拖动即可实现球体的放大与缩小，同时球体半径、表面积与球体积也同时变化。 </p>
            <p>　　<strong>小结 </strong></p>
            <p>　　从上面介绍可以看出，通过MVC模式实现与图形用户化界面相关的应用程序具有极其良好的可扩展性，是Java面向对象编程的未来方向。</p>
            </td>
        </tr>
    </tbody>
</table><img src ="http://www.blogjava.net/sl2cj/aggbug/46841.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sl2cj/" target="_blank">blog搬家了--[www.ialway.com/blog]</a> 2006-05-18 16:12 <a href="http://www.blogjava.net/sl2cj/articles/46841.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>在Java中应用设计模式--Singleton</title><link>http://www.blogjava.net/sl2cj/articles/46839.html</link><dc:creator>blog搬家了--[www.ialway.com/blog]</dc:creator><author>blog搬家了--[www.ialway.com/blog]</author><pubDate>Thu, 18 May 2006 08:08:00 GMT</pubDate><guid>http://www.blogjava.net/sl2cj/articles/46839.html</guid><wfw:comment>http://www.blogjava.net/sl2cj/comments/46839.html</wfw:comment><comments>http://www.blogjava.net/sl2cj/articles/46839.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sl2cj/comments/commentRss/46839.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sl2cj/services/trackbacks/46839.html</trackback:ping><description><![CDATA[<table style="WORD-BREAK: break-all" cellspacing="0" cellpadding="0" width="700" align="center" border="0">
    <tbody>
        <tr>
            <td align="center" height="25"><font face="黑体" size="4">在Java中应用设计模式--Singleton</font></td>
        </tr>
        <tr bgcolor="#f9f9f9">
            <td id="fontzoom" style="LINE-HEIGHT: 200%">
            <p align="center">作者：刘湛&nbsp;&nbsp;&nbsp;&nbsp;来自：IBM</p>
            <p>　　本文介绍了设计模式中 Singleton 的基本概念,对其功能和用途进行了简单的分析,列出了通常实现 Singleton 的几种方法,并给出了详细的Java 代码.</p>
            <p>　　<strong>基本概念</strong></p>
            <p>　　Singleton 是一种创建性模型,它用来确保只产生一个实例,并提供一个访问它的全局访问点.对一些类来说,保证只有一个实例是很重要的,比如有的时候,数据库连接或 Socket 连接要受到一定的限制,必须保持同一时间只能有一个连接的存在.再举个例子,集合中的 set 中不能包含重复的元素,添加到set里的对象必须是唯一的,如果重复的值添加到 set,它只接受一个实例.JDK中正式运用了Singleton模式来实现 set 的这一特性,大家可以查看java.util.Collections里的内部静态类SingletonSet的原代码.其实Singleton是最简单但也是应用最广泛的模式之一,在 JDK 中随处可见.</p>
            <p>　　<strong>简单分析</strong></p>
            <p>　　为了实现 Singleton 模式,我们需要的是一个静态的变量,能够在不创建对象的情况下记忆是否已经产生过实例了.静态变量或静态方法都可以在不产生具体实例的情况下直接调用,这样的变量或方法不会因为类的实例化而有所改变.在图1的结构中可以看到,uniqueInstance 就是这个独立的静态变量,它可以记忆对象是否已经实例化了,在静态方法 Instance 中对这个变量进行判断,若没有实例化过就产生一个新的对象,如果已经实例化了则不再产生新的对象,仍然返回以前产生的实例.</p>
            <p align="center"><img height="154" src="http://www.javafan.net/uploadfiles/20050331140706102.gif" width="518" alt="" /><br />图1: Singleton 模式结构</p>
            <p>　　<strong>具体实施</strong></p>
            <p>　　实现 Singleton 模式的办法通常有三种.</p>
            <p>　　一. 用静态方法实现 Singleton</p>
            <p>　　这种方法是使用静态方法来监视实例的创建.为了防止创建一个以上的实例,我们最好把构造器声明为 private.</p>
            <p>　　这样可以防止客户程序员通过除由我们提供的方法之外的任意方式来创建一个实例,如果不把构造器声明为private,编译器就会自作聪明的自动同步一个默认的friendly构造器.这种实现方法是最常见的,也就是图1中结构的标准实现.</p>
            <p>&nbsp; &nbsp; &nbsp; &nbsp; public class Singleton {<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; private static Singleton s; <br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; private Singleton(){};<br />&nbsp; &nbsp; &nbsp; &nbsp; /**<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; * Class method to access the singleton instance of the class.<br />&nbsp; &nbsp; &nbsp; &nbsp;&nbsp; */<br />&nbsp; &nbsp; &nbsp; &nbsp; public static Singleton getInstance() {<br />&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp; if (s == null)<br />&nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; s = new Singleton();<br />&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; return s;<br />&nbsp; &nbsp; &nbsp; &nbsp; }<br />}</p>
            <p>// 测试类<br />class singletonTest {<br />&nbsp; public static void main(String[] args) {<br />&nbsp;&nbsp;&nbsp; Singleton s1 = Singleton.getInstance();<br />&nbsp;&nbsp;&nbsp; Singleton s2 = Singleton.getInstance();<br />&nbsp;&nbsp;&nbsp; if (s1==s2)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(&quot;s1 is the same instance with s2&quot;);<br />&nbsp;&nbsp;&nbsp; else<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(&quot;s1 is not the same instance with s2&quot;);<br />&nbsp; }<br />}</p>
            <p>　　singletonTest运行结果是:<br />　　s1 is the same instance with s2<br />　　这证明我们只创建了一个实例.</p>
            <p>　　二. 以静态变量为标志实现 Singleton</p>
            <p>　　在类中嵌入一个静态变量做为标志,每次都在进入构造器的时候进行检查.</p>
            <p>　　问题在于构造器没有返回类型,如果确定创建一个实例成功与否.一个方法是调用一个函数来检查创建是否成功,然后简单的返回一个来自静态变量的值,但是这样做是不优雅的,而且容易发生错误.比较好的做法是创建一个当创建了一个以上的实例时可以抛出异常的类,这个类仅仅是调用父类方法,好处是用了自己命名的异常类型,错误信息更加清晰:</p>
            <p>class SingletonException extends RuntimeException {<br />&nbsp; public SingletonException(String s) {<br />&nbsp;&nbsp;&nbsp; super(s);<br />&nbsp; }<br />}</p>
            <p>class Singleton {<br />&nbsp; static boolean instance_flag = false; // true if 1 instance<br />&nbsp; public Singleton() {<br />&nbsp;&nbsp;&nbsp; if (instance_flag)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw new SingletonException(&quot;Only one instance allowed&quot;);<br />&nbsp;&nbsp;&nbsp; else<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; instance_flag = true; // set flag for 1 instance<br />&nbsp; }<br />}</p>
            <p>// 测试类</p>
            <p>public class singletonTest {<br />&nbsp; static public void main(String argv[]) {<br />&nbsp;&nbsp;&nbsp; Singleton s1, s2;<br />&nbsp;&nbsp;&nbsp; // create one incetance--this should always work<br />&nbsp;&nbsp;&nbsp; System.out.println(&quot;Creating one instance&quot;);<br />&nbsp;&nbsp;&nbsp; try {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; s1 = new Singleton();<br />&nbsp;&nbsp;&nbsp; } catch (SingletonException e) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(e.getMessage());<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; // try to create another spooler --should fail<br />&nbsp;&nbsp;&nbsp; System.out.println(&quot;Creating two instance&quot;);<br />&nbsp;&nbsp;&nbsp; try {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; s2 = new Singleton();<br />&nbsp;&nbsp;&nbsp; } catch (SingletonException e) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(e.getMessage());<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp; }<br />}</p>
            <p>　　singletonTest运行结果是:<br />　　Creating one instance<br />　　Creating two instance<br />　　Only one instance allowed<br />　　可以看出,第一个实例顺利创建,第二个实例创建实抛出了我们自定义的异常.</p>
            <p>　　三. 用注册器机制来创建 Singleton</p>
            <p>　　首先用集合中的Hashtable 和Enumeration来实现addItem(Object key, Object value),getItem(Object key), ,removeItem(Object key)等方法实现一个管理器,将key和value一一关联起来,客户程序员创建实例前首先用addItem方法进行注册,再用getItem方法获取实例.Hashtable中的key是唯一的,从而保证创建的实例是唯一的,具体实现限于篇幅不再细说,在Prototype模型的应用一文中我将会给出一个实现注册器的代码.用注册器机制来创建 Singleton模式的好处是易于管理,可以同时控制多个不同类型的Singleton 实例.</p>
            <p>　　<strong>小结</strong></p>
            <p>　　1. Singleton模式可以方便的进行扩充,产生指定数目的实例. </p>
            <p>　　2. 在The Design Patterns Java Companion 一书中曾提到过用静态类的方式来实现 Singleton模式,并指出java.lang.Math就是一个例子,这里我并不表示赞同,因为Math并不是一个真正的对象,我们只是直接调用Math类所包装的静态方法而已,根本就没有创建实例的过程,又从何说起只产生一个实例呢?这个问题我曾到Javaranch的论坛上发过帖子,所有回帖的人也都是对这一观点持否定态度. </p>
            <p>　　3. 在多线程的程序中,singleton可能会变的不可靠,可能会出现多个实例,解决的办法很简单,加个同步修饰符: public static synchronized Singleton getInstance(). 这样就保证了线程的安全性. </p>
            <p>　　4. 最后要说的是大家可能会看见一些其他实现Singleton模式的方法,因为模式在具体的应用时是灵活的,不是一成不变的,并没有一个固定的做法,但大都是上面几种方法的变形.</p>
            </td>
        </tr>
    </tbody>
</table><img src ="http://www.blogjava.net/sl2cj/aggbug/46839.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sl2cj/" target="_blank">blog搬家了--[www.ialway.com/blog]</a> 2006-05-18 16:08 <a href="http://www.blogjava.net/sl2cj/articles/46839.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>在Java中应用设计模式--Factory Method</title><link>http://www.blogjava.net/sl2cj/articles/46838.html</link><dc:creator>blog搬家了--[www.ialway.com/blog]</dc:creator><author>blog搬家了--[www.ialway.com/blog]</author><pubDate>Thu, 18 May 2006 08:06:00 GMT</pubDate><guid>http://www.blogjava.net/sl2cj/articles/46838.html</guid><wfw:comment>http://www.blogjava.net/sl2cj/comments/46838.html</wfw:comment><comments>http://www.blogjava.net/sl2cj/articles/46838.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sl2cj/comments/commentRss/46838.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sl2cj/services/trackbacks/46838.html</trackback:ping><description><![CDATA[<table style="WORD-BREAK: break-all" cellspacing="0" cellpadding="0" width="700" align="center" border="0">
    <tbody>
        <tr>
            <td align="center" height="25"><font face="黑体" size="4">在Java中应用设计模式--Factory Method</font></td>
        </tr>
        <tr bgcolor="#f9f9f9">
            <td id="fontzoom" style="LINE-HEIGHT: 200%">
            <p align="center">作者：刘湛&nbsp;&nbsp;&nbsp;&nbsp;来自：ibm</p>
            <p>　　在设计模式中,Factory Method也是比较简单的一个,但应用非常广泛,EJB,RMI,COM,CORBA,Swing中都可以看到此模式的影子,它是最重要的模式之一.在很多地方我们都会看到xxxFactory这样命名的类,那么,什么是Factory Method,为什么要用这个模式,如何用Java语言来实现该模式,这就是本文想要带给大家的内容.</p>
            <p>　　<strong>基本概念</strong></p>
            <p>　　Factory Method是一种创建性模式,它定义了一个创建对象的接口,但是却让子类来决定具体实例化哪一个类.当一个类无法预料要创建哪种类的对象或是一个类需要由子类来指定创建的对象时我们就需要用到Factory Method 模式了.简单说来,Factory Method可以根据不同的条件产生不同的实例,当然这些不同的实例通常是属于相同的类型,具有共同的父类.Factory Method把创建这些实例的具体过程封装起来了,简化了客户端的应用,也改善了程序的扩展性,使得将来可以做最小的改动就可以加入新的待创建的类. 通常我们将Factory Method作为一种标准的创建对象的方法,当发现需要更多的灵活性的时候,就开始考虑向其它创建型模式转化</p>
            <p>　　<strong>简单分析</strong></p>
            <p>　　图1是Factory Method 模式的结构图,这里提供了一些术语,让我们可以进行更方便的描述:</p>
            <p align="center"><img height="178" src="http://www.javafan.net/uploadfiles/20050330153429001.gif" width="530" alt="" /><br />图1: Factory Method 模式结构</p>
            <p>　　1．Product: 需要创建的产品的抽象类. <br />　　2．ConcreteProduct: Product的子类,一系列具体的产品. <br />　　3．Creator: 抽象创建器接口,声明返回Product类型对象的Factory Method. <br />　　4．ConcreteCreator: 具体的创建器,重写Creator中的Factory Method,返回ConcreteProduct类型的实例. </p>
            <p>　　由此可以清楚的看出这样的平行对应关系: Product &lt;====&gt; Creator ; ConreteProduct &lt;====&gt; ConreteCreator</p>
            <p>　　抽象产品对应抽象创建器,具体产品对应具体创建器.这样做的好处是什么呢?为什么我们不直接用具体的产品和具体的创建器完成需求呢?实际上我们也可以这样做.但通过Factory Method模式来完成,客户(client)只需引用抽象的Product和Creater,对具体的ConcreteProduct和ConcreteCreator可以毫不关心,这样做我们可以获得额外的好处:</p>
            <p>　　首先客户端可以统一从抽象创建器获取产生的实例,Creator的作用将client和产品创建过程分离开来,客户不用操心返回的是那一个具体的产品,也不用关心这些产品是如何创建的.同时,ConcreteProduct也被隐藏在Product后面,ConreteProduct继承了Product的所有属性,并实现了Product中定义的抽象方法,按照Java中的对象造型(cast)原则,通过ConcreteCreator产生的ConcreteProduct可以自动的上溯造型成Product.这样一来,实质内容不同的ConcreteProduct就可以在形式上统一为Product,通过Creator提供给client来访问. </p>
            <p>　　其次,当我们添加一个新的ConcreteCreator时,由于Creator所提供的接口不变,客户端程序不会有丝毫的改动,不会带来动一发而牵全身的灾难, 这就是良好封装性的体现.但如果直接用ConcreteProduct和ConcreteCreator两个类是无论如何也做不到这点的. 优良的面向对象设计鼓励使用封装(encapsulation)和委托(delegation),而Factory Method模式就是使用了封装和委托的典型例子,这里封装是通过抽象创建器Creator来体现的,而委托则是通过抽象创建器把创建对象的责任完全交给具体创建器ConcreteCreator来体现的. </p>
            <p>　　现在,请再回头看看基本概念中的那段话,开始也许觉得生涩难懂,现在是不是已经明朗化了很多.</p>
            <p>　　下面让我们看看在 Java 中如何实现Factory Method模式,进一步加深对它的认识.</p>
            <p>　　<strong>具体实施</strong></p>
            <p>　　先说明一点,用Factory Method模式创建对象并不一定会让我们的代码更短,实事上往往更长,我们也使用了更多的类,真正的目的在于这样可以灵活的,有弹性的创建不确定的对象.而且,代码的可重用性提高了,客户端的应用简化了,客户程序的代码会大大减少,变的更具可读性.</p>
            <p>　　标准实现: 这里我采用Bruce Eckel 用来描述OO思想的经典例子 Shape.这样大家会比较熟悉一些.我完全按照图1中所定义的结构写了下面的一段演示代码.这段代码的作用是创建不同的Shape实例,每个实例完成两个操作:draw和erase.具体的创建过程委托給ShapeFactory来完成. </p>
            <p>　　1.a 首先定义一个抽象类Shape,定义两个抽象的方法.</p>
            <p>abstract class Shape {<br />&nbsp; // 勾画shape<br />&nbsp; public abstract void draw();<br />&nbsp; // 擦去 shape<br />&nbsp; public abstract void erase();</p>
            <p>&nbsp; public String name;<br />&nbsp; public Shape(String aName){<br />&nbsp;&nbsp;&nbsp; name = aName;<br />&nbsp; }<br />}</p>
            <p>　　1.b 定义 Shape的两个子类: Circle, Square,实现Shape中定义的抽象方法</p>
            <p>// 圆形子类<br />class Circle extends Shape {<br />&nbsp; public void draw() {<br />&nbsp;&nbsp;&nbsp; System.out.println(&quot;It will draw a circle.&quot;);<br />&nbsp; }<br />&nbsp; public void erase() {<br />&nbsp;&nbsp;&nbsp; System.out.println(&quot;It will erase a circle.&quot;); <br />&nbsp; }<br />&nbsp; // 构造函数<br />&nbsp; public Circle(String aName){<br />&nbsp;&nbsp;&nbsp; super(aName);<br />&nbsp; }<br />}</p>
            <p>// 方形子类<br />class Square extends Shape {<br />&nbsp; public void draw() { <br />&nbsp;&nbsp;&nbsp; System.out.println(&quot;It will draw a square.&quot;); <br />&nbsp; }<br />&nbsp; public void erase() { <br />&nbsp;&nbsp;&nbsp; System.out.println(&quot;It will erase a square.&quot;); <br />&nbsp; }<br />&nbsp; // 构造函数<br />&nbsp; public Square(String aName){<br />&nbsp;&nbsp;&nbsp; super(aName);<br />&nbsp; }<br />}</p>
            <p>　　1.c 定义抽象的创建器,anOperation调用factoryMethod创建一个对象,并对该对象进行一系列操作.</p>
            <p>abstract class ShapeFactory {&nbsp; <br />&nbsp; protected abstract Shape factoryMethod(String aName);<br />&nbsp; // 在anOperation中定义Shape的一系列行为<br />public void anOperation(String aName){<br />&nbsp;&nbsp;&nbsp; Shape s = factoryMethod(aName);<br />&nbsp;&nbsp;&nbsp; System.out.println(&quot;The current shape is: &quot; + s.name);<br />&nbsp;&nbsp;&nbsp; s.draw();<br />&nbsp;&nbsp;&nbsp; s.erase();<br />&nbsp; }<br />}</p>
            <p>　　1.d 定义与circle和square相对应的两个具体创建器CircleFactory,SquareFactory,实现父类的methodFactory方法</p>
            <p>// 定义返回 circle 实例的 CircleFactory<br />class CircleFactory extends ShapeFactory {<br />&nbsp; // 重载factoryMethod方法,返回Circle对象<br />&nbsp; protected Shape factoryMethod(String aName) {<br />&nbsp;&nbsp;&nbsp; return new Circle(aName + &quot; (created by CircleFactory)&quot;);<br />&nbsp; }<br />}<br />&nbsp; <br />// 定义返回 Square 实例的 SquareFactory<br />class SquareFactory extends ShapeFactory {<br />&nbsp; // 重载factoryMethod方法,返回Square对象<br />protected Shape factoryMethod(String aName) {<br />&nbsp;&nbsp;&nbsp; return new Square(aName + &quot; (created by SquareFactory)&quot;);<br />&nbsp; }<br />}</p>
            <p>　　1.e 测试类:请注意这个客户端程序多么简洁,既没有罗嗦的条件判断语句,也无需关心ConcreteProduct和ConcreteCreator的细节(因为这里我用anOperation封装了Product里的两个方法,所以连Product的影子也没看见,当然把Product里方法的具体调用放到客户程序中也是不错的).</p>
            <p>class Main {<br />&nbsp; public static void main(String[] args){<br />&nbsp;&nbsp;&nbsp; ShapeFactory sf1 = new SquareFactory(); <br />&nbsp;&nbsp;&nbsp; ShapeFactory sf2 = new CircleFactory();<br />&nbsp;&nbsp;&nbsp; sf1.anOperation(&quot;Shape one&quot;);<br />&nbsp;&nbsp;&nbsp; sf2.anOperation(&quot;Shape two&quot;);<br />&nbsp; }<br />}&nbsp; </p>
            <p>　　运行结果如下:<br />　　The current shape is: Shape one (created by SquareFactory)<br />　　It will draw a square.<br />　　It will erase a square.<br />　　The current shape is: Shape two (created by CircleFactory)<br />　　It will draw a circle.<br />　　It will erase a circle.</p>
            <p>　　参数化的Factory Method: 这种方式依靠指定的参数作为标志来创建对应的实例,这是很常见的一种办法.比如JFC中的BorderFactory就是个很不错的例子. 以下的这个例子是用字符串作为标记来进行判断的,如果参数的类型也不一样,那就可以用到过载函数来解决这个问题,定义一系列参数和方法体不同的同名函数,这里java.util.Calendar.getInstance()又是个极好的例子.参数化的创建方式克服了Factory Method模式一个最显著的缺陷,就是当具体产品比较多时,我们不得不也建立一系列与之对应的具体构造器. 但是在客户端我们必须指定参数来决定要创建哪一个类. </p>
            <p>　　2.a 我们在第一种方法的基础上进行修改,首先自定义一个的异常,这样当传入不正确的参数时可以得到更明显的报错信息.</p>
            <p>class NoThisShape extends Exception {<br />&nbsp; public NoThisShape(String aName) {<br />&nbsp;&nbsp;&nbsp; super(aName);<br />&nbsp; }<br />}</p>
            <p>　　2.b 去掉了ShapeFactory的两个子类,改为由ShapeFactory直接负责实例的创建. ShapeFactory自己变成一个具体的创建器,直接用参数化的方法实现factoryMethod返回多种对象.</p>
            <p>abstract class ShapeFactory {&nbsp; <br />&nbsp; private static Shape s;<br />&nbsp; private ShapeFactory() {}<br />&nbsp;&nbsp;&nbsp; <br />&nbsp; static Shape factoryMethod(String aName, String aType) throws NoThisShape{<br />&nbsp;&nbsp;&nbsp; if (aType.compareTo(&quot;square&quot;)==0)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new Square(aName);<br />&nbsp;&nbsp;&nbsp; else if (aType.compareTo(&quot;circle&quot;)==0)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new Circle(aName);<br />&nbsp;&nbsp;&nbsp; else throw new NoThisShape(aType);&nbsp; <br />&nbsp; }<br />&nbsp; <br />&nbsp; // 在anOperation中定义Shape的一系列行为<br />&nbsp; static void anOperation(String aName, String aType) throws NoThisShape{<br />&nbsp;&nbsp;&nbsp; s = factoryMethod(aName, aType);<br />&nbsp;&nbsp;&nbsp; System.out.println(&quot;The current shape is: &quot; + s.name);<br />&nbsp;&nbsp;&nbsp; s.draw();<br />&nbsp;&nbsp;&nbsp; s.erase();<br />&nbsp; }<br />}</p>
            <p>　　2.c 测试类:这里客户端必须指定参数来决定具体创建哪个类.这个例子里的anOperation是静态函数,可以直接引用.</p>
            <p>class Main {<br />&nbsp; public static void main(String[] args) throws NoThisShape{<br />&nbsp;&nbsp;&nbsp; ShapeFactory.anOperation(&quot;Shape one&quot;,&quot;circle&quot;);<br />&nbsp;&nbsp;&nbsp; ShapeFactory.anOperation(&quot;Shape two&quot;,&quot;square&quot;);<br />&nbsp;&nbsp;&nbsp; ShapeFactory.anOperation(&quot;Shape three&quot;, &quot;delta&quot;);<br />&nbsp; }<br />}</p>
            <p>　　运行结果如下:<br />　　The current shape is: Shape one<br />　　It will draw a circle.<br />　　It will erase a circle.<br />　　The current shape is: Shape two<br />　　It will draw a square.<br />　　It will erase a square.<br />　　Exception in thread &quot;main&quot; NoThisShape: delta<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; at ShapeFactory.factoryMethod(ShapeFactory.java:10)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; at ShapeFactory.anOperation(ShapeFactory.java:15)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; at Main.main(Main.java:5)</p>
            <p>　　<strong>动态装载机制:</strong> </p>
            <p>　　有的时候我们会把ConcreteProduct的实例传给创建器作为参数,这种情况下,如果在创建器里完成创建过程,就必须判断参数的具体类型(用instanceof),然后才能产生相应的实例,那么比较好的做法是利用Java的动态装载机制来完成这件事.比如:</p>
            <p>　　我们得到一个Shape的子类s,但不知道具体是那个子类,就可以利用Class类自带的方法newInstance()得到实例</p>
            <p>　　return (Shape)s.getClass().newInstance();</p>
            <p>　　这种方法有兴趣得读者可以自己尝试,限于篇幅,不写具体代码出来了.</p>
            <p>　　<strong>后话:</strong></p>
            <p>　　看完这篇文章后,相信读者对Factory Method模式有一个比较清楚的了解了.我想说的是,我们不仅应该关心一个具体的模式有什么作用,如何去实现这个模式,更应该透过现象看本质,不但知其然,还要知其所以然.要通过对模式的学习加深对面向对象思想的理解,让自己的认识得到升华.Factory Method模式看似简单,实则深刻.抽象,封装,继承,委托,多态,针对接口编程等面向对象中的概念都在这里得到了一一的体现.只有抓住了它的本质,我们才能够不拘于形式的灵活运用,而不是为了使用模式而使用模式.</p>
            </td>
        </tr>
    </tbody>
</table><img src ="http://www.blogjava.net/sl2cj/aggbug/46838.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sl2cj/" target="_blank">blog搬家了--[www.ialway.com/blog]</a> 2006-05-18 16:06 <a href="http://www.blogjava.net/sl2cj/articles/46838.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Struts，MVC的一种开放源码实现</title><link>http://www.blogjava.net/sl2cj/articles/46806.html</link><dc:creator>blog搬家了--[www.ialway.com/blog]</dc:creator><author>blog搬家了--[www.ialway.com/blog]</author><pubDate>Thu, 18 May 2006 06:16:00 GMT</pubDate><guid>http://www.blogjava.net/sl2cj/articles/46806.html</guid><wfw:comment>http://www.blogjava.net/sl2cj/comments/46806.html</wfw:comment><comments>http://www.blogjava.net/sl2cj/articles/46806.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sl2cj/comments/commentRss/46806.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sl2cj/services/trackbacks/46806.html</trackback:ping><description><![CDATA[<table style="WORD-BREAK: break-all" cellspacing="0" cellpadding="0" width="700" align="center" border="0">
    <tbody>
        <tr>
            <td align="center" height="25"><font face="黑体" size="4">Struts，MVC的一种开放源码实现</font></td>
        </tr>
        <tr bgcolor="#f9f9f9">
            <td id="fontzoom" style="LINE-HEIGHT: 200%">
            <p align="center">作者：Malcolm G. Davis &nbsp;&nbsp;&nbsp;&nbsp;来自：IBM</p>
            <p>　　本文介绍 <a href="http://www.javafan.net/special/struts/index.jsp" target="_blank">Struts</a>，它是使用 servlet 和 JavaServer Pages 技术的一种 Model-View-Controller 实现。Struts 可帮助您控制 Web 项目中的变化并提高专业化水平。尽管您可能永远不会用 Struts 实现一个系统，但您可以将其中的一些思想用于您以后的 servlet 和 JSP 网页的实现中。</p>
            <p>　　<strong>简介</strong></p>
            <p>　　小学生也可以在因特网上发布 HTML 网页。但是，小学生的网页和专业开发的网站有质的区别。网页设计人员（或者 HTML 开发人员）必须理解颜色、用户、生产流程、网页布局、浏览器兼容性、图像创建和 JavaScript 等等。设计漂亮的网站需要做大量的工作，大多数 Java 开发人员更注重创建优美的对象接口，而不是用户界面。JavaServer Pages (JSP) 技术为网页设计人员和 Java 开发人员提供了一种联系钮带。</p>
            <p>　　如果您开发过大型 Web 应用程序，您就理解变化这个词的含义。&ldquo;模型-视图-控制器&rdquo;(MVC) 就是用来帮助您控制变化的一种设计模式。MVC 减弱了业务逻辑接口和数据接口之间的耦合。Struts 是一种 MVC 实现，它将 Servlet 2.2 和 JSP 1.1 标记（属于 J2EE 规范）用作实现的一部分。尽管您可能永远不会用 Struts 实现一个系统，但了解一下 Struts 或许使您能将其中的一些思想用于您以后的 Servlet 的 JSP 实现中。</p>
            <p>　　在本文中，我将以一个 JSP 文件为起点讨论该网页的优缺点，该文件中使用的元素可能是您所熟悉的。随后我将讨论 Struts，并说明它是如何控制您的 Web 项目中的变化并提高专业化水平的。最后，我将重新开发这个简单的 JSP 文件，在开发过程中我已顾及到网页设计人员和变化。</p>
            <p>　　<strong>一个 JSP 文件就是一个 Java servlet</strong></p>
            <p>　　JavaServer Page (JSP) 文件只是审视 servlet 的另一种方式。JSP 文件的概念使我们能够将 Java servlet 看作一个 HTML 网页。JSP 消除了 Java 代码中经常出现的讨厌的 print() 语句。JSP 文件首先被预处理为 .java 文件，然后再编译为 .class 文件。如果您使用的是 Tomcat，则可以在 work 目录下查看预处理后的 .java 文件。别的容器可能将 .java 和 .class 文件存储在其他位置；这个位置与容器有关。图 1 说明了从 JSP 文件到 servlet 的流程。</p>
            <p align="center"><img height="65" src="http://www.javafan.net/uploadfiles/20050125201732001.gif" width="410" alt="" /><br />图 1. 从 JSP 文件到 servlet 的流程</p>
            <p>（这与 Microsoft 的 Active Server Page (ASP) 明显不同。ASP 被编译到内存中，而不是编译到一个单独的文件中。）</p>
            <p>　　<strong>简单的独立 JSP 文件</strong></p>
            <p>　　在小型 JSP 应用程序中，经常会看到数据、业务逻辑和用户界面被组合在一个代码模块中。此外，应用程序通常还包含用来控制应用程序流程的逻辑。清单 1 和图 2 展示了允许用户加入一个邮件列表的一个简单 JSP 文件。</p>
            <p>　　<strong>清单 1. join.jsp -- 一个简单的请求和响应 JSP 文件</strong></p>
            <p style="BACKGROUND: #eeeeee">&lt;%@ page language=&quot;java&quot; %&gt;<br />&lt;%@ page import=&quot;business.util.Validation&quot; %&gt;<br />&lt;%@ page import=&quot;business.db.MailingList&quot; %&gt;<br />&lt;%<br />String error = &quot;&quot;;<br />String email = request.getParameter(&quot;email&quot;);<br /><br />// 是否有电子邮件地址<br />if( email!=null ) {<br /><br />&nbsp;&nbsp;&nbsp; // 验证输入...<br />&nbsp;&nbsp;&nbsp; if( business.util.Validation.isValidEmail(email) ) {<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 存储输入...<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; business.db.MailingList.AddEmail(email);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } catch (Exception e) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; error = &quot;Error adding email address to system.&nbsp; &quot; + e;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if( error.length()==0 ) {<br />%&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 重定向到欢迎页...<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;jsp:forward page=&quot;welcome.html&quot;/&gt;<br />&lt;%<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; } else {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 设置错误消息并重新显示网页<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; error = email + &quot; is not a valid email address, please try again.&quot;;<br />&nbsp;&nbsp;&nbsp; }<br /><br />} else {<br />&nbsp;&nbsp;&nbsp; email = &quot;&quot;;<br />}<br />%&gt;<br />&lt;html&gt;<br />&lt;head&gt;<br />&lt;title&gt;Join Mailing List&lt;/title&gt;<br />&lt;/head&gt;<br />&lt;body&gt;<br />&lt;font color=red&gt;&lt;%=error%&gt;&lt;/font&gt;&lt;br&gt;<br /><br />&lt;h3&gt;Enter your email to join the group&lt;/h3&gt;<br />&lt;form action=&quot;join.jsp&quot; name=&quot;joinForm&quot;&gt;<br />&nbsp;&nbsp;&nbsp; &lt;input name=&quot;email&quot; id=&quot;email&quot; value=&lt;%=email%&gt;&gt;&lt;/input&gt;<br />&nbsp;&nbsp;&nbsp; &lt;input type=submit value=&quot;submit&quot;&gt;<br />&lt;/form&gt;<br />&lt;/body&gt;<br />&lt;/html&gt;</p>
            <p align="center"><img height="70" src="http://www.javafan.net/uploadfiles/20050125201732002.gif" width="430" alt="" /><br />图 2. 在简单的请求和响应中，JSP 文件设置数据、控制到下一个网页的流程并创建 HTML</p>
            <p>　　这个邮件列表 JSP 文件是一个独立的、自主完成所有任务的模块。未包含在这个 JSP 文件中的仅有代码是包含在 isValidEmail() 中的实际验证代码和将电子邮件地址存入数据库的代码。（将 isValidEmail() 方法分离到可重用的代码中似乎是当然的选择，但我曾见过直接嵌入网页中的 isValidEmail() 代码。单页方法的优点是易于理解，并且最初也易于构建。此外，对于各种图形化开发工具，入门也很容易。</p>
            <p>　　<strong>join.jsp 的活动</strong></p>
            <p>　　1. 显示打开的输入网页。 <br />　　2. 从表单参数中读取 email 的值。 <br />　　3. 验证 email 地址。 <br />　　4. 如果 email 地址有效： <br />　　　☆ 将该地址添加到数据库中。 <br />　　　☆ 重定向到下一个网页。 <br />　　5. 如果 email 地址无效： <br />　　　☆ 设置错误消息。 <br />　　　☆ 重新显示含有错误消息的 join.jsp。</p>
            <p>　　<strong>单页方法的后果</strong></p>
            <p>　　<strong>☆ HTML 和 Java 强耦合在一起</strong><br />　　JSP 文件的编写者必须既是网页设计者，又是 Java 开发者。其结果通常要么是很糟的 Java 代码，要么是难看的网页，有时甚至 Java 代码和网页都很糟。</p>
            <p>　　<strong>☆ Java 和 JavaScript 的不足</strong><br />　　随着网页逐渐变大，很容易想到实现一些 JavaScript。当网页中出现 JavaScript 时，这种脚本就可能与 Java 代码产生混淆。可能产生混淆的一个例子是使用客户端的 JavaScript 来验证 email 域。</p>
            <p>　　<strong>☆ 内嵌的流程逻辑</strong><br />　　要理解应用程序的整个流程，您必须浏览所有网页。试想一下拥有 100 个网页的网站的错综复杂的逻辑。</p>
            <p>　　<strong>☆ 调试困难</strong><br />　　除了很糟的外观之外，HTML 标记、Java 代码和 JavaScript 代码都集中在一个网页中还使调试变得相当困难。</p>
            <p>　　<strong>☆ 强耦合</strong><br />　　更改业务逻辑或数据可能牵涉相关的每个网页。</p>
            <p>　　<strong>☆ 美学</strong><br />　　在很大的网页中，这编码样式看起来杂乱无章。我过去进行 Microsoft ASP 开发时，我经常看到有 1000 行的网页。即使有彩色语法显示，阅读和理解这些代码仍然比较困难。 </p>
            <p>　　<strong>请别在我的 HTML 中加入太多的 Java 代码</strong></p>
            <p>　　在清单 1 中，不是 Java 代码中有大量的 HTML，而是在 HTML 文件中有大量的 Java 代码。从这个观点来看，除了允许网页设计人员编写 Java 代码之外，我实际上没做什么。但是，我们并不是一无所有；在 JSP 1.1 中，我们获得一种称为&ldquo;标记&rdquo;的新特性。</p>
            <p>　　JSP 标记只是将代码从 JSP 文件中抽取出来的一种方式。有人将 JSP 标记看作是 JSP 文件的宏，其中用于这个标记的代码包含在 servlet 中。（宏的观点在很大程度上是正确的。）出于同样的原因，我不希望在 Java 代码中看到 HTML 标记，我也不希望在 JSP 文件中看到 Java 代码。JSP 技术的整个出发点就是允许网页设计人员创建 servlet，而不必纠缠于 Java 代码。标记允许 Java 程序员将 Java 代码伪装成 HTML 来扩展 JSP 文件。图 3 显示了从 JSP 网页中抽取代码并将它们放入 JSP 标记中的一般概念。</p>
            <p align="center"><img height="70" src="http://www.javafan.net/uploadfiles/20050125201732003.gif" width="220" alt="" /><br />图 3. JSP 标记</p>
            <p>　　清单 2 是用来说明 Struts 标记的功能的一个例子。在清单 2 中，正常的 HTML &lt;form&gt; 标记被用 Struts &lt;form:form&gt; 标记替换。清单 3 显示了浏览器接收到的结果 HTML。浏览器获得 HTML &lt;form&gt; 标记，但带有附加代码，如 JavaScript。附加的 JavaScript 激活 email 地址域。服务器端的 &lt;form:form&gt; 标记代码创建适当的 HTML，并使网页设计人员不再接触 JavaScript。</p>
            <p>　　<strong>清单 2. Struts 的 form 标记</strong></p>
            <p style="BACKGROUND: #eeeeee">&lt;form:form action=&quot;join.do&quot; focus=&quot;email&quot; &gt;<br />&nbsp;&nbsp;&nbsp; &lt;form:text&nbsp;&nbsp; property=&quot;email&quot; size=&quot;30&quot; maxlength=&quot;30&quot;/&gt;<br />&nbsp;&nbsp;&nbsp; &lt;form:submit property=&quot;submit&quot; value=&quot;Submit&quot;/&gt;<br />&lt;/form:form&gt;</p>
            <p>　　<strong>清单 3. 发送给浏览器的结果 HTML</strong></p>
            <p style="BACKGROUND: #eeeeee">&lt;form name=&quot;joinForm&quot; method=&quot;POST&quot; action=&quot;join.do;jsessionid=ndj71hjo01&quot;&gt;<br />&nbsp;&nbsp;&nbsp; &lt;input type=&quot;text&quot; name=&quot;email&quot; maxlength=&quot;30&quot; size=&quot;30&quot; value=&quot;&quot;&gt;<br />&nbsp;&nbsp;&nbsp; &lt;input type=&quot;submit&quot; name=&quot;submit&quot; value=&quot;Submit&quot;&gt;<br />&lt;/form&gt;<br />&lt;script language=&quot;JavaScript&quot;&gt;<br />&lt;!--<br />&nbsp;&nbsp;&nbsp; document.joinForm.email.focus()<br />// --&gt;<br />&lt;/script&gt;</p>
            <p>　　<strong>有关 JSP 标记的注意事项：</strong></p>
            <p>　　☆ JSP 标记需要一个运行 JSP 1.1 或更高版本的容器。<br />　　☆ JSP 标记在服务器上运行，而不像 HTML 标记那样由客户机解释。<br />　　☆ JSP 标记提供了适当的代码重用机制。<br />　　☆ 可以使用一种称为 include 的 JSP 机制将 HTML 和 JavaScript 添加到网页中。但是，开发人员常常会创建巨大的 JavaScript 库文件，这些库文件被包含在 JSP 文件中。结果返回给客户机的 HTML 网页要比必需的 HMTL 网页大得多。include 的正确用法是仅将它用于生成诸如页眉和页脚这类内容的 HTML 代码段。<br />　　☆ 通过抽取出 Java 代码，JSP 标记使开发角色更加专业化。</p>
            <p>　　<strong>模型-视图-控制器 (MVC)</strong></p>
            <p>　　JSP 标记只解决了部分问题。我们还得处理验证、流程控制和更新应用程序的状态等问题。这正是 MVC 发挥作用的地方。MVC 通过将问题分为三个类别来帮助解决单一模块方法所遇到的某些问题：</p>
            <p>　　<strong>☆ Model（模型）</strong><br />　　模型包含应用程序的核心功能。模型封装了应用程序的状态。有时它包含的唯一功能就是状态。它对视图或控制器一无所知。</p>
            <p>　　<strong>☆ View（视图）</strong><br />　　视图提供模型的表示。它是应用程序的外观。视图可以访问模型的读方法，但不能访问写方法。此外，它对控制器一无所知。当更改模型时，视图应得到通知。</p>
            <p>　　<strong>☆ Controller（控制器）</strong><br />　　控制器对用户的输入作出反应。它创建并设置模型。 </p>
            <p>　　<strong>MVC Model 2</strong></p>
            <p>　　Web 向软件开发人员提出了一些特有的挑战，最明显的就是客户机和服务器的无状态连接。这种无状态行为使得模型很难将更改通知视图。在 Web 上，为了发现对应用程序状态的修改，浏览器必须重新查询服务器。</p>
            <p>　　另一个重大变化是实现视图所用的技术与实现模型或控制器的技术不同。当然，我们可以使用 Java（或者 PERL、C/C++ 或别的语言）代码生成 HTML。这种方法有几个缺点：</p>
            <p>　　☆ Java 程序员应该开发服务，而不是 HTML。 <br />　　☆ 更改布局时需要更改代码。 <br />　　☆ 服务的用户应该能够创建网页来满足它们的特定需要。 <br />　　☆ 网页设计人员不能直接参与网页开发。 <br />　　☆ 嵌在代码中的 HTML 很难看。 </p>
            <p>　　对于 Web，需要修改标准的 MVC 形式。图 4 显示了 MVC 的 Web 改写版，通常也称为 MVC Model 2 或 MVC 2。</p>
            <p align="center"><img height="170" src="http://www.javafan.net/uploadfiles/20050125201732004.gif" width="410" alt="" /><br />图 4. MVC Model 2</p>
            <p>　　<strong>Struts，MVC 2 的一种实现</strong></p>
            <p>　　Struts 是一组相互协作的类、servlet 和 JSP 标记，它们组成一个可重用的 MVC 2 设计。这个定义表示 Struts 是一个框架，而不是一个库，但 Struts 也包含了丰富的标记库和独立于该框架工作的实用程序类。图 5 显示了 Struts 的一个概览。</p>
            <p align="center"><img height="170" src="http://www.javafan.net/uploadfiles/20050125201732005.gif" width="410" alt="" /><br />图 5. Struts 概览</p>
            <p>　　<strong>Struts 概览</strong></p>
            <p>　　<strong>☆ Client browser（客户浏览器）</strong><br />　　来自客户浏览器的每个 HTTP 请求创建一个事件。Web 容器将用一个 HTTP 响应作出响应。</p>
            <p>　　<strong>☆ Controller（控制器）</strong><br />　　控制器接收来自浏览器的请求，并决定将这个请求发往何处。就 Struts 而言，控制器是以 servlet 实现的一个命令设计模式。struts-config.xml 文件配置控制器。</p>
            <p>　　<strong>☆ 业务逻辑<br /></strong>　　业务逻辑更新模型的状态，并帮助控制应用程序的流程。就 Struts 而言，这是通过作为实际业务逻辑&ldquo;瘦&rdquo;包装的 Action 类完成的。</p>
            <p>　　<strong>☆ Model（模型）的状态<br /></strong>　　模型表示应用程序的状态。业务对象更新应用程序的状态。ActionForm bean 在会话级或请求级表示模型的状态，而不是在持久级。JSP 文件使用 JSP 标记读取来自 ActionForm bean 的信息。</p>
            <p>　　<strong>☆ View（视图）</strong><br />　　视图就是一个 JSP 文件。其中没有流程逻辑，没有业务逻辑，也没有模型信息 -- 只有标记。标记是使 Struts 有别于其他框架（如 Velocity）的因素之一。 </p>
            <p>　　<strong>详细分析 Struts</strong></p>
            <p>　　图 6 显示的是 org.apache.struts.action 包的一个最简 UML 图。图 6 显示了 ActionServlet (Controller)、ActionForm (Form State) 和 Action (Model Wrapper) 之间的最简关系。</p>
            <p align="center"><img height="228" src="http://www.javafan.net/uploadfiles/20050125201732006.jpg" width="600" alt="" /><br />图 6. Command (ActionServlet) 与 Model (Action &amp; ActionForm) 之间的关系的 UML 图</p>
            <p>　　<strong>ActionServlet 类</strong></p>
            <p>　　您还记得函数映射的日子吗？在那时，您会将某些输入事件映射到一个函数指针上。如果您对此比较熟悉，您会将配置信息放入一个文件，并在运行时加载这个文件。函数指针数组曾经是用 C 语言进行结构化编程的很好方法。</p>
            <p>　　现在好多了，我们有了 Java 技术、XML、J2EE，等等。Struts 的控制器是将事件（事件通常是 HTTP post）映射到类的一个 servlet。正如您所料 -- 控制器使用配置文件以使您不必对这些值进行硬编码。时代变了，但方法依旧。</p>
            <p>　　ActionServlet 是该 MVC 实现的 Command 部分，它是这一框架的核心。ActionServlet (Command) 创建并使用 Action、ActionForm 和 ActionForward。如前所述，struts-config.xml 文件配置该 Command。在创建 Web 项目时，您将扩展 Action 和 ActionForm 来解决特定的问题。文件 struts-config.xml 指示 ActionServlet 如何使用这些扩展的类。这种方法有几个优点：</p>
            <p>　　☆ 应用程序的整个逻辑流程都存储在一个分层的文本文件中。这使得人们更容易查看和理解它，尤其是对于大型应用程序而言。<br />　　☆ 网页设计人员不必费力地阅读 Java 代码来理解应用程序的流程。<br />　　☆ Java 开发人员也不必在更改流程以后重新编译代码。</p>
            <p>　　可以通过扩展 ActionServlet 来添加 Command 功能。</p>
            <p>　　<strong>ActionForm 类</strong></p>
            <p>　　ActionForm 维护 Web 应用程序的会话状态。ActionForm 是一个抽象类，必须为每个输入表单模型创建该类的子类。当我说输入表单模型时,是指 ActionForm 表示的是由 HTML 表单设置或更新的一般意义上的数据。例如，您可能有一个由 HTML 表单设置的 UserActionForm。Struts 框架将执行以下操作：</p>
            <p>　　☆ 检查 UserActionForm 是否存在；如果不存在，它将创建该类的一个实例。<br />　　☆ Struts 将使用 HttpServletRequest 中相应的域设置 UserActionForm 的状态。没有太多讨厌的 request.getParameter() 调用。例如，Struts 框架将从请求流中提取 fname，并调用 UserActionForm.setFname()。<br />　　☆ Struts 框架在将 UserActionForm 传递给业务包装 UserAction 之前将更新它的状态。<br />　　☆ 在将它传递给 Action 类之前，Struts 还会对 UserActionForm 调用 validation() 方法进行表单状态验证。注：这并不总是明智之举。别的网页或业务可能使用 UserActionForm，在这些地方，验证可能有所不同。在 UserAction 类中进行状态验证可能更好。<br />　　☆ 可在会话级维护 UserActionForm。 </p>
            <p>　　注：<br />　　☆ struts-config.xml 文件控制 HTML 表单请求与 ActionForm 之间的映射关系。 <br />　　☆ 可将多个请求映射到 UserActionForm。 <br />　　☆ UserActionForm 可跨多页进行映射，以执行诸如向导之类的操作。 </p>
            <p>　　<strong>Action 类</strong></p>
            <p>　　Action 类是业务逻辑的一个包装。Action 类的用途是将 HttpServletRequest 转换为业务逻辑。要使用 Action，请创建它的子类并覆盖 process() 方法。</p>
            <p>　　ActionServlet (Command) 使用 perform() 方法将参数化的类传递给 ActionForm。仍然没有太多讨厌的 request.getParameter() 调用。当事件进展到这一步时，输入表单数据（或 HTML 表单数据）已被从请求流中提取出来并转移到 ActionForm 类中。</p>
            <p>　　注：扩展 Action 类时请注意简洁。Action 类应该控制应用程序的流程，而不应该控制应用程序的逻辑。通过将业务逻辑放在单独的包或 EJB 中，我们就可以提供更大的灵活性和可重用性。</p>
            <p>　　考虑 Action 类的另一种方式是 Adapter 设计模式。Action 的用途是&ldquo;将类的接口转换为客户机所需的另一个接口。Adapter 使类能够协同工作，如果没有 Adapter，则这些类会因为不兼容的接口而无法协同工作。&rdquo;（摘自 Gof 所著的 Design Patterns - Elements of Reusable OO Software）。本例中的客户机是 ActionServlet，它对我们的具体业务类接口一无所知。因此，Struts 提供了它能够理解的一个业务接口，即 Action。通过扩展 Action，我们使得我们的业务接口与 Struts 业务接口保持兼容。（一个有趣的发现是， Action 是类而不是接口）。Action 开始为一个接口，后来却变成了一个类。真是金无足赤。）</p>
            <p>　　<strong>Error 类</strong></p>
            <p>　　UML 图（图 6）还包括 ActionError 和 ActionErrors。ActionError 封装了单个错误消息。ActionErrors 是 ActionError 类的容器，View 可以使用标记访问这些类。ActionError 是 Struts 保持错误列表的方式。</p>
            <p align="center"><img height="261" src="http://www.javafan.net/uploadfiles/20050125201732007.jpg" width="525" alt="" /><br />图 7. Command (ActionServlet) 与 Model (Action) 之间的关系的 UML 图</p>
            <p>　　<strong>ActionMapping 类</strong></p>
            <p>　　输入事件通常是在 HTTP 请求表单中发生的，servlet 容器将 HTTP 请求转换为 HttpServletRequest。控制器查看输入事件并将请求分派给某个 Action 类。struts-config.xml 确定 Controller 调用哪个 Action 类。struts-config.xml 配置信息被转换为一组 ActionMapping，而后者又被放入 ActionMappings 容器中。（您可能尚未注意到这一点，以 s 结尾的类就是容器）</p>
            <p>　　ActionMapping 包含有关特定事件如何映射到特定 Action 的信息。ActionServlet (Command) 通过 perform() 方法将 ActionMapping 传递给 Action 类。这样就使 Action 可访问用于控制流程的信息。</p>
            <p>　　<strong>ActionMappings</strong></p>
            <p>　　ActionMappings 是 ActionMapping 对象的一个集合。</p>
            <p>　　<strong>再访邮件列表样例</strong></p>
            <p>　　下面我们看一下 Struts 是如何解决困扰 join.jsp 的这些问题的。改写后的方案由两个项目组成。第一个项目包含应用程序的逻辑部分，这个应用程序是独立于 Web 应用程序的。这个独立层可能是用 EJB 技术实现的公共服务层。为了便于说明，我使用 Ant 构建进程创建了一个称为 business 的包。有几个原因促使我们使用独立的业务层：</p>
            <p>　　<strong>☆ 划分责任</strong><br />　　单独的包使管理人员能够在开发小组内委派责任。这也有助于提高开发人员的责任心。</p>
            <p>　　<strong>☆ 通用件</strong><br />　　我们设想开发人员将这个包看作一个商业软件。将它放在另外的包中使它更像通用件。这个包可能是通用件，也可能是由组织内部的另一个小组开发的。</p>
            <p>　　<strong>☆ 避免不必要的构建和单元测试。</strong><br />　　分开的构建进程有助于避免不必要的构建和单元测试。</p>
            <p>　　<strong>☆ 使用接口开发</strong><br />　　在进行开发和避免不必要的耦合时，它有助于从接口的观点来思考问题。这是极重要的一个方面。当开发您自己的业务包时，这些业务类不应该关心到底是 Web 应用程序执行调用，还是独立应用程序执行调用。因此，应该避免在业务逻辑层使用对 servlet API 或 Struts API 调用的任何引用。</p>
            <p>　　<strong>☆ 稳定性</strong><br />　　并不是每个组织都每天、每周甚至每月进行检修。因此，在进行开发时，稳定的接口点是重要的。不能因为业务包处于变迁阶段就认为 Web 项目也应该处于变迁阶段。 </p>
            <p>　　<strong>业务构建注释</strong></p>
            <p>　　我用 Ant 构建项目，并用 JUnit 运行单元测试。business.zip 包含构建业务项目所需的一切，当然 Ant 和 JUnit 除外。这个包脚本将构建类，运行单元测试，创建 Java 文档和 jar 文件，最后将所有这些内容压缩到一个 zip 文件中发送给客户。只要对 build.xml 作一些修改，您就可以将它部署到其他平台上。Business.jar 位于 Web 的下载部分，因此，您并非必须下载并构建这个业务包。</p>
            <p>　　<strong>Web 项目</strong></p>
            <p>　　第二个项目是用 Struts 开发的一个 Web 应用程序。您将需要一个符合 JSP 1.1 和 Servlet 2.2 规范的容器。最快的入门方法是下载并安装 Tomcat 3.2。直到有 Struts 的 1.0 发行版之前，我建议您从 Jakarta 项目获得最新的版本。这对我来说是个大问题，我不能确保我的 Web 项目样例能与您下载的 Struts 一起工作。Struts 仍在不断变化，所以我不得不经常更新我的项目。在本项目中，我使用的是 jakarta-struts-20010105.zip。图 8 显示了此 Web 项目的结构。如果您已安装了 Ant，则运行这个版本将创建一个称为 joinStruts.war 的 war 文件，您随时可以部署这个文件。</p>
            <p align="center"><img height="170" src="http://www.javafan.net/uploadfiles/20050125201732008.gif" width="345" alt="" /><br />图 8. Web 项目的结构</p>
            <p>　　清单 4 显示了转换后的 JSP 文件，称为 joinMVC.jsp。这个文件从最初的 50 行变为 19 行，并且现在不含任何 Java 代码。从网页设计人员的角度来看，这是个巨大的改进。</p>
            <p>　　<strong>清单 4. joinMVC.jsp -- 再访简单的 JSP</strong></p>
            <p style="BACKGROUND: #eeeeee">&lt;%@ page language=&quot;java&quot; %&gt;<br />&lt;%@ taglib uri=&quot;/WEB-INF/struts.tld&quot; prefix=&quot;struts&quot; %&gt;<br />&lt;%@ taglib uri=&quot;/WEB-INF/struts-form.tld&quot; prefix=&quot;form&quot; %&gt;<br />&lt;html&gt;<br />&lt;head&gt;<br />&lt;title&gt;&lt;struts:message key=&quot;join.title&quot;/&gt;&lt;/title&gt;<br />&lt;/head&gt;<br />&lt;body bgcolor=&quot;white&quot;&gt;<br /><br />&lt;form:errors/&gt;<br />&lt;h3&gt;Enter your email to join the group&lt;/h3&gt;<br /><br />&lt;form:form action=&quot;join.do&quot; focus=&quot;email&quot; &gt;<br />&nbsp;&nbsp;&nbsp; &lt;form:text&nbsp;&nbsp; property=&quot;email&quot; size=&quot;30&quot; maxlength=&quot;30&quot;/&gt;<br />&nbsp;&nbsp;&nbsp; &lt;form:submit property=&quot;submit&quot; value=&quot;Submit&quot;/&gt;<br />&lt;/form:form&gt;<br /><br />&lt;/body&gt;<br />&lt;/html&gt;</p>
            <p>　　<strong>网页的变化</strong></p>
            <p>　　下面是使用 Struts 标记库之后所发生变化的列表：</p>
            <p>　　☆ Import<br />　　&lt;%@ taglib uri=&quot;/WEB-INF/struts.tld&quot; prefix=&quot;struts&quot; %&gt;<br />　　用于 Java 代码的 &lt;%@page import? 已被替换为用于 Struts 标记库的 &lt;%@ taglib uri?。</p>
            <p>　　☆ 文本<br />　　&lt;struts:message key=&quot;join.title&quot;/&gt;<br />　　资源属性文件包含 join.title 的文本。在本例中，ApplicationResources 属性文件包含这个名值对。这使字符串更易于查看和国际化。</p>
            <p>　　☆ 错误<br />　　&lt;form:errors/&gt;<br />　　ActionServlet 或 ActionForm 构建要显示的错误消息。这些错误消息也可以包含在属性文件中。ApplicationResources 也提供了一种格式化错误的方法，即设置 error.header 和 error.footer。</p>
            <p>　　☆ HTML 表单<br />　　&lt;form:form action=&quot;join.do&quot; focus=&quot;email&quot; &gt;<br />　　　□ JSP &lt;form&gt; 标记和属性替代了 HTML &lt;form&gt; 标记和属性。 &lt;form action=&quot;join.jsp&quot; name=&quot;join&quot;&gt; 已更改为 &lt;form:form action=&quot;join.do&quot; focus=&quot;email&quot; &gt;。 <br />　　　□ HTML &lt;input&gt; 标记已替换为 &lt;form:text/&gt;。 <br />　　　□ HTML &lt;submit&gt; 标记已替换为 &lt;form:submit/&gt;。</p>
            <p>　　<strong>模型 -- 会话状态</strong></p>
            <p>　　JoinForm 扩展了 ActionForm 并包含表单数据。本例中的表单数据只有电子邮件地址。我已为电子邮件地址添加了一个写方法和读方法，以供框架访问。为了便于说明，我重写了 validate() 方法，并使用了 Struts 的跟踪功能。Struts 将创建 JoinForm 并设置状态信息。</p>
            <p>　　<strong>模型 -- 业务逻辑</strong></p>
            <p>　　如前所述，Action 是控制器和实际业务对象之间的接口。JoinAction 包装了对 business.jar 的调用，这些调用最初在 join.jsp 文件中。JoinAction 的 perform() 方法在清单 5 中列表。</p>
            <p>　　<strong>清单 5. - JoinAction.perform()</strong></p>
            <p style="BACKGROUND: #eeeeee">public ActionForward perform(ActionMapping mapping,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ActionForm form,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HttpServletRequest request,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HttpServletResponse response)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throws IOException, ServletException {<br />&nbsp;&nbsp;&nbsp; // 抽取我们将会用到的属性和参数<br />&nbsp;&nbsp;&nbsp; JoinForm joinForm = (JoinForm) form;<br />&nbsp;&nbsp;&nbsp; String email = joinForm.getEmail();<br /><br />&nbsp;&nbsp;&nbsp; ActionErrors errors = new ActionErrors();<br /><br />&nbsp;&nbsp;&nbsp; // 存储输入....<br />&nbsp;&nbsp;&nbsp; try {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; business.db.MailingList.AddEmail(email);<br />&nbsp;&nbsp;&nbsp; } catch (Exception e) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 记录日志，打印栈<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 将错误回显给用户<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; errors.add(&quot;email&quot;,new ActionError(&quot;error.mailing.db.add&quot;));<br />&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp; // 如需任何消息，请将指定的错误消息键保存到<br />&nbsp;&nbsp;&nbsp; //&nbsp; HTTP 请求中，以供 &lt;struts:errors&gt; 标记使用。<br />&nbsp;&nbsp;&nbsp; if (!errors.empty()) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; saveErrors(request, errors);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 返回到初始表单<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return (new ActionForward(mapping.getInput()));<br />&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp; // 将控制权转交给 Action.xml 中指定的 'success' URI<br />&nbsp;&nbsp;&nbsp; return (mapping.findForward(&quot;success&quot;));<br />}</p>
            <p>　　注：perform() 返回一个称为 ActionForward 的类，该类通知控制器下一步该执行什么操作。在本例中，我使用从控制器传入的映射来决定下一步的操作。</p>
            <p>　　<strong>控制器</strong></p>
            <p>　　我已修改了 JSP 文件，并创建了两个新类：一个类用来包含表单数据，一个类用来调用业务包。最后，我通过修改配置文件 struts-config.xml 将它们整合起来。清单 6 显示了我添加的 action 元素，这个元素用来控制 joinMVC.jsp 的流程。</p>
            <p>　　<strong>清单 6. Action 配置</strong></p>
            <p style="BACKGROUND: #eeeeee">&lt;action&nbsp; path=&quot;/join&quot;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; name=&quot;joinForm&quot;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; type=&quot;web.mailinglist.JoinAction&quot;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; scope=&quot;request&quot;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; input=&quot;/joinMVC.jsp&quot;<br />&nbsp;&nbsp;&nbsp;&nbsp; validate=&quot;true&quot;&gt;<br />&nbsp;&nbsp;&nbsp; &lt;forward&nbsp; name=&quot;success&quot;&nbsp; path=&quot;/welcome.html&quot;/&gt;<br />&lt;/action&gt;</p>
            <p>　　action 元素描述了从请求路径到相应的 Action 类的映射，应该用这些类来处理来自这个路径的请求。每个请求类型都应该有相应的 action 元素，用来描述如何处理该请求。对于 join 请求：</p>
            <p>　　1. joinForm 用来容纳表单数据。 <br />　　2. 因为 validate 被标记为 true，所以 joinForm 将试图进行自我验证。 <br />　　3. web.mailinglist.JoinAction 是用来处理对这个映射的请求的 action 类。 <br />　　4. 如果一切顺利，该请求将转到 welcome.jsp。 <br />　　5. 如果出现业务逻辑故障，流程将返回到 joinMVC.jsp，这是最初发出请求的网页。为什么会这样呢？在清单 6 的 action 元素中，有一个称为 input 的属性，其值为 &quot;/joinMVC.jsp&quot;。在我的 JoinAction.perform()（如清单 5 所示）中，如果业务逻辑失败，perform() 就返回一个 ActionForward，并以 mapping.getInput() 作为参数。本例中的 getInput() 是 &quot;/joinMVC.jsp&quot;。如果业务逻辑失败，它将返回到 joinMVC.jsp，这是最初发出请求的网页。 </p>
            <p>　　<strong>使用 Struts 前后的比较</strong></p>
            <p>　　正如我们在图 9 中所看到的那样，复杂性和层都有显著增加。不再存在从 JSP 文件到 Service 层的直接调用。</p>
            <p align="center"><img height="185" src="http://www.javafan.net/uploadfiles/20050125201732009.gif" width="385" alt="" /><br />图 9. 使用 Struts 前后的比较</p>
            <p>　　<strong>Struts 的优点</strong></p>
            <p>　　<strong>☆ JSP 标记机制的使用</strong><br />　　标记特性从 JSP 文件获得可重用代码和抽象 Java 代码。这个特性能很好地集成到基于 JSP 的开发工具中，这些工具允许用标记编写代码。</p>
            <p>　　<strong>☆ 标记库</strong><br />　　为什么要另发明一种轮子，或标记库呢？如果您在库中找不到您所要的标记，那就自己定义吧。此外，如果您正在学习 JSP 标记技术，则 Struts 为您提供了一个起点。</p>
            <p>　　<strong>☆ 开放源码<br /></strong>　　您可以获得开放源码的全部优点，比如可以查看代码并让使用库的每个人检查代码。许多人都可以进行很好的代码检查。</p>
            <p>　　<strong>☆ MVC 实现样例</strong><br />　　如果您希望创建您自己的 MVC 实现，则 Struts 可增加您的见识。</p>
            <p>　　<strong>☆ 管理问题空间</strong><br />　　分治是解决问题并使问题可管理的极好方法。当然，这是一把双刃剑。问题越来越复杂，并且需要越来越多的管理。 </p>
            <p>　　<strong>Struts 的缺点</strong></p>
            <p>　　<strong>☆ 仍处于发展初期<br /></strong>　　Struts 开发仍处于初级阶段。他们正在向着发行版本 1.0 而努力，但与任何 1.0 版本一样，它不可能尽善尽美。</p>
            <p>　　<strong>☆ 仍在变化中</strong><br />　　这个框架仍在快速变化。Struts 1.0 与 Struts 0.5 相比变化极大。为了避免使用不赞成使用的方法，您可能隔一天就需要下载最新的 Struts。在过去的 6 个月中，我目睹 Struts 库从 90K 增大到 270K 以上。由于 Struts 中的变化，我不得不数次修改我的示例，但我不保证我的示例能与您下载的 Struts 协同工作。</p>
            <p>　　<strong>☆ 正确的抽象级别</strong><br />　　Struts 是否提供了正确的抽象级别？对于网页设计人员而言，什么是正确的抽象级别呢？这是一个用 $64K 的文字才能解释清楚的问题。在开发网页的过程中，我们是否应该让网页设计人员访问 Java 代码？某些框架（如 Velocity）说不应该，但它提供了另一种 Web 开发语言让我们学习。在 UI 开发中限制访问 Java 有一定的合理性。最重要的是，如果让网页设计人员使用一点 Java，他将使用大量的 Java。在 Microsoft ASP 的开发中，我总是看到这样的情况。在 ASP 开发中，您应该创建 COM 对象，然后编写少量的 ASP 脚本将这些 COM 对象联系起来。但是，ASP 开发人员会疯狂地使用 ASP 脚本。我会听到这样的话，&ldquo;既然我可以用 VBScript 直接编写 COM 对象，为什么还要等 COM 开发人员来创建它呢？&rdquo;通过使用标记库，Struts 有助于限制 JSP 文件中所需的 Java 代码的数量。Logic Tag 就是这样的一种库，它对有条件地生成输出进行管理，但这并不能阻止 UI 开发人员对 Java 代码的狂热。无论您决定使用哪种类型的框架，您都应该了解您要在其中部署和维护该框架的环境。当然，这项任务真是说起来容易做起来难。</p>
            <p>　　<strong>☆ 有限的适用范围</strong><br />　　Struts 是一种基于 Web 的 MVC 解决方案，所以必须用 HTML、JSP 文件和 servlet 来实现它。</p>
            <p>　　<strong>☆ J2EE 应用程序支持</strong><br />　　Struts 需要支持 JSP 1.1 和 Servlet 2.2 规范的 servlet 容器。仅凭这一点远不能解决您的全部安装问题，除非使用 Tomcat 3.2。我用 Netscape iPlanet 6.0 安装这个库时遇到一大堆问题，按理说它是第一种符合 J2EE 的应用程序服务器。我建议您在遇到问题时访问 Struts 用户邮件列表的归档资料（请参阅参考资源）。</p>
            <p>　　<strong>☆ 复杂性</strong><br />　　在将问题分为几个部分的同时也引入了复杂性。毫无疑问，要理解 Struts 必须接受一定的培训。随着变化的不断加入，这有时会令人很沮丧。欢迎访问本网站。</p>
            <p>　　<strong>☆ 在何处...<br /></strong>　　我还能指出其他问题，例如，控制器的客户端验证、可适用工作流程和动态策略模式在什么地方？但是，目前这太容易成为吹毛求疵的问题，有些问题是无关紧要的，或者说应该对 1.0 发行版提这些问题。随着 Struts 小组的不断努力，到您阅读本文时 Struts 说不定已经有了这些功能，或者它很快就会具有这些功能。 </p>
            <p>　　<strong>Struts 的前景</strong></p>
            <p>　　在这个软件开发的新时代，一切都变得很快。在不到 5 年的时间内，我已经目睹了从 cgi/perl 到 ISAPI/NSAPI、再到使用 VB 的 ASP、一直到现在的 Java 和 J2EE 的变迁。Sun 正在尽力将新的变化反映到 JSP/servlet 体系结构中，正如他们对 Java 语言和 API 所作的更改一样。您可以从 Sun 的网站获得新的 JSP 1.2 和 Servlet 2.3 规范的草案。此外，一个标准 JSP 标记库即将出现；有关这些规范和标记库的链接，请参阅参考资源。</p>
            <p>　　<strong>最后的注释</strong></p>
            <p>　　Struts 使用标记和 MVC 解决了某些重大问题。这个方法有助于提高代码的可重用性和灵活性。通过将问题划分为更小的组件，当技术空间或问题空间中出现变化时，您就有更多的机会重用代码。此外，Struts 使网页设计人员和 Java 开发人员能将精力集中于自己最擅长的方面。但是，在强健性增强的同时，也意味着复杂性的增加。Struts 比简单的单个 JSP 网页要复杂得多，但对于更大的系统而言，Struts 实际上有助于管理复杂性。另外，我并不想编写自己的 MVC 实现，而只想了解一个这样的实现。不管您是否会使用 Struts，回顾这个 Struts 框架（对不起，应该是库）都会使您对 JSP 文件和 servlet 的特性、以及如何将它们组合起来用于您的下一个 Web 项目有更好的了解。正像翼间支柱是机翼结构中不可缺少的一部分一样，Strut 也可能成为您下一个 Web 项目的不可缺少的一部分。</p>
            </td>
        </tr>
    </tbody>
</table><img src ="http://www.blogjava.net/sl2cj/aggbug/46806.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sl2cj/" target="_blank">blog搬家了--[www.ialway.com/blog]</a> 2006-05-18 14:16 <a href="http://www.blogjava.net/sl2cj/articles/46806.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JSP+javabeans+servlet开发环境配置指南</title><link>http://www.blogjava.net/sl2cj/articles/44141.html</link><dc:creator>blog搬家了--[www.ialway.com/blog]</dc:creator><author>blog搬家了--[www.ialway.com/blog]</author><pubDate>Sun, 30 Apr 2006 02:26:00 GMT</pubDate><guid>http://www.blogjava.net/sl2cj/articles/44141.html</guid><wfw:comment>http://www.blogjava.net/sl2cj/comments/44141.html</wfw:comment><comments>http://www.blogjava.net/sl2cj/articles/44141.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sl2cj/comments/commentRss/44141.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sl2cj/services/trackbacks/44141.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: JSP+javabeans+servlet开发环境配置指南&nbsp;j2sdk1.4.2下载地址：http://java.sun.comtomcat5.5下载地址：http://cvs.apache.org/builds/jakarta-tomcat-5/nightly/j2sdk1.4.2安装目录：C:\j2sdk1.4.2tomcat5.5安装目录：C:\tomcat一.配置j2...&nbsp;&nbsp;<a href='http://www.blogjava.net/sl2cj/articles/44141.html'>阅读全文</a><img src ="http://www.blogjava.net/sl2cj/aggbug/44141.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sl2cj/" target="_blank">blog搬家了--[www.ialway.com/blog]</a> 2006-04-30 10:26 <a href="http://www.blogjava.net/sl2cj/articles/44141.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>