由于GoF(Gamma, Helms, Johnson, and Vlissides)的设计模式的广发普及,软件工程专家们找到了一个新的强有力的设计方法。什么是设计模式?他们这样定义:“设计模式就是描述一个反复出现的问题,以及解决这个问题的方案的核心。你可以重复使用这个解决方案而无须再做重复劳动。”一般来说,一个设计模式包括四个基本要素:模式名称,需要解决的问题,解决方案和使用带来模式的效果(或后果)。
在GoF中描述的设计模式,诸如Factory Method, Observer, Façade等,在现在的应用设计领域已是人人皆知,家喻户晓。正像编者在先前说的那样,设计模式的命名与描述和一样起着相当重要的作用。在设计会议中人们使用设计模式的命名比使用他们枯燥乏味的描述节省工作量是可以用人小时估算出来的。
这本书出版七年以后,我们发现设计模式大致和从前是相对一致的。尽管后来也有人增加了一些设计模式,但远远不及起初GoF的提出设计模式集合价值高。经试验证实,没有一个设计模式能达到最初的那23种设计模式的普及和声望。借用经济学的一个术语来说就是,新的模式的边际效用几乎趋近于零。因此设计模式科学是无非是一个一次性思想的静态集合。
正相反,从一个Web领域应用程序设计者的经验来讲,我们发现一些历史经验(假如你愿意也可以叫模式)事实上是无法实施在应用程序级或子系统级上实现一套指定的方法集这样的需求,同样的方式,最初的设计模式就能完成这种低级别功能标准化的需求。
接下来我们讨论的内容其实也不是什么新东西。它们是一些许多有经验的开发人员所知道的并经常使用的简单,标准,通用的方法。仿照最初设计模式集的方式,我们对这些显而易见的方法分类和命名,希望可以使用这些命名(通过生动地捕捉这些方法的基本功能获得的)来证实它们是非常有价值的。我们把它们命名为“Web和企业架构设计模式(Web and Enterprise Architecture Design Patterns)”,简称“WEA设计模式”一个应用程序级的设计模式的宏观命名。
作为一个J2EE的热衷者,我们仅仅关注与Java相关的问题和解决方案上,尽管这样,许多概念对其他技术也还是相当有帮助的。(难道设计模式不是独立于语言的吗?这是一个相当吸引人的想法,但是确实存在争论,如GoF的模式Abstract Factory and Prototype对C++和SmallTalk是非常有用的,而对Java来说是多余的。因为Java的Class类和类的反射机制非常好的解决了这些问题。)
WEA设计模式的分类和注重事项
回想GoF,它把设计模式分为三类,通俗易懂,便于记忆:创建(Creational),结构(StrUCtural), 和 行为(Behavioral)。
秉承这一优良传统,我们把我们的模式分为划分(Partitioning),作用域(Scope),安全(Security),导航(Navigation)和数据量控制(Data Volume Control)五类。这并不是一个详尽列表,而仅仅是最初的感觉比较适当的模式类型集。
模式描述
我们粗略地使用GoF建立的描述结构来描述我们的模式。为了简便,我们将压缩处理为三个大标题。在“意图(Intent)”标题下,我们用一行陈述使用这个模式的意图。我们在“问题和方案(Problem and Solution)”标题下阐述要解决的问题及其解决方案。最后,我们在“效果(Consequences)”标题下详尽论述使用此模式带来的积极和消极的因素。
为了简洁,我们也省去UML类图描述。我们的模式是相当简单,并不需过多的描述。我们也省去了Java代码样例。我们相信大多数J2EE开发人员都能明白这些模式。
划分模式(Partitioning Patterns)
在应用程序中,客户端(浏览器)和服务器都能执行逻辑,客户端通过javascript完成,服务器通过服务器端脚本语言如Java或PHP执行。但由于各种原因应该把这两个层中应用程序逻辑分开。
1. 哑客户端(Dumb Client)
意图(Intent)
不用客户端(浏览器)执行应用程序逻辑。
问题和方案(Problem and Solution)
许多应用程序采取一种避免使用javascript的策略。一个非常普遍的原因就是当用户
当关掉浏览器的支持javascript功能时,应用程序就不能正常的运转了。还有就是开发人员开发时还需要考虑对浏览器不同版本的支持。(尽管现在许多浏览器都改善了对w3c的兼容,但是这种考虑并没有消除)
假如有这些的原因其中一种,我就必须把所有的处理放到服务器上完成。即使是非常简单的属于前台的,而且使用javascript非常轻易实现的操作也要放到服务器上执行。例如,许多电子商务应用程序在服务器端计算总数,尽管计算所需的信息都在客户端。
效果(Consequences)
使用哑客户端模式给应用程序带来的好处就是可以在任何版本浏览器上运行,包括老以
前的版本。可以相当好的支持安全要求,不答应用户执行任何客户端脚本。
使用这个模式的短处就是增加了网络传输和服务器的负载。这可能对应用程序的性能造成不良的影响,尽管这种影响我们可以在用例级精确地评测出来。除此之外,许多操作不支持本地执行之后,操作会变得粗糙不流畅,使应用程序交互性变差,不友好。
2. 独立客户端(Independent Client)
意图(Intent)
使用交互接口提高客户端和服务器的执行能力
问题和方案(Problem and Solution)
现在大多数Web应用程序都是客户端和服务器都需处理逻辑的智能混合体。客户端使用javascript执行本地的表单验证来提高交互能力,服务器执行一些需要和后台交互的请求的处理。客户端和服务器代码单独编写,这样可能造成在两个层中一定程度的代码冗余。
效果(Consequences)
使用独立客户端模式的优点就是使应用程序更自然,更舒适。许多操作可以不和服务器交互在很短的响应时间内就完成。网络传输降到最小,服务器负载降到最小。
使用此模式的缺点就是使两个层之间逻辑代码造成冗余。例如客户端的验证要比服务器端的验证更友好,因为它的更易给用户快速反馈。但它并不能代替服务器端验证,因为后台并不能依靠前台验证,后台必须执行自己的验证来保证不被错误数据侵害。这种冗余可能会造成异常和错误,尤其是在维护阶段逻辑发生变化的时候。由于Java和javascript的差异很可轻易使我们掉入陷阱。
3. 改良服务器客户端(Server-Modified Client)
意图(Intent)
通过少量服务器端处理来提供高级的客户端交互。
问题和方案(Problem and Solution)
有时,客户端的处理需要一些存储在数据库或后台的数据。应用程序可以预先把这些数据取出来放到一个数据结构中,当javascript需要时,就可以轻松的访问这个数据结构,而不需要每次都去和服务器交互一次。这样一旦一个页面被载入,客户端操作就可以迅速的访问他想要访问的数据了。例如,动态和有条件的装配一个下拉框,javascript就可以访问使用从后台数据库获得数据组装成的一个数组完成。
一些更复杂的应用程序可能需要后台来生成javascript函数。例如,用服务器控制客户端验证。
改良服务器客户端模式目的是为了处理javascript代码和使用Web层Java代码数据结构的变化。并不是用于生成Html元素,那是jsp的基本功能。(当然,灰色域也是一种隐式商业逻辑,如有条件生成的HTML导航元素)
效果(Consequences)
使用改良服务器客户端模式的优势就是,在没有冗余的情况下达到良好交互。使在一点控制客户端和服务器端逻辑成为可能。两个层之间的异常得以减少或消除。
使用这一模式的劣势在于增加了复杂度。因为生成javascript代码是相对简单,而维护生成这些代码的代码,复杂程度一般会增加一个数量级。
作用域模式(Scope Patterns)
作用域一般指一些信息的生命周期,经验告诉我们,我们碰到的作用域有下面几种:请求(Request)、会话(Session)和关系(Relationship)。
一个请求作用域变量在客户端请求返回之前都是可以访问的。一个会话作用域变量从用户登陆到会话未结束之前一直可以访问。最后,一个关系作用域变量,只要在应用程序中存在这个用户的具体资料,这个变量就一直存在。关系作用域并不是JSP规范定义的一种作用域类型,而是根据现实中抽象出来的,诸如个性化。(我们并不去过多考虑JSP定义的Page和Application这两个作用域)
4. 请求访问器
意图(Intent)
在多步骤的服务器端处理中,提供对请求作用域变量的统一访问的机制。
问题和方案(Problem and Solution)
大多数Web开发的初学者会发现JSP的一个残酷而又明显的缺陷之一就是没有一个像HttpServletRequest对象的getParameter()方法一样的设置器。这样就可能使请求在组件间传来传去时,在Web层进行一系列操作变得非凡头疼。有些操作可能还需要请求作用域变量进行修改,而缺少setParameter()方法,这将会变得极其糟糕。
请求对象的用于处理指定对象的getAttribute()(与getParameter()方法相比,它将返回一个指定字符串)和setAttribute()给了我们一线希望。可是,使用getAttribute()方法从一个新来自浏览器请求对象中获得参数是无效的,还必须调用getParameter()方法。
请求访问器模式被设计为,使用一个兼容的接口来对请求对象的检索、存储和重检索进行治理。更简单的说,这个模式就是,工具类的一个静态方法(可以叫getAttributeOrParameter