友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
Java编程思想第4版[中文版](PDF格式)-第138部分
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!
一端提供服务,我们可以用Java 编程,不必使用自己不熟悉的其他语言。由于 Java 具有优秀的移植能力,
所以不必关心具体容纳这个服务器是什么平台。
所有这些以及其他特性都在《Java Network Progr amming》一书中得到了详细讲述。该书由 Elliotte Rusty
Harold 编著,O'Reilly 于 1997 年出版。
15。10 练习
(1) 编译和运行本章中的JabberServer 和 JabberClient 程序。接着编辑一下程序,删去为输入和输出设计
的所有缓冲机制,然后再次编译和运行,观察一下结果。
(2) 创建一个服务器,用它请求用户输入密码,然后打开一个文件,并将文件通过网络连接传送出去。创建
一个同该服务器连接的客户,为其分配适当的密码,然后捕获和保存文件。在自己的机器上用 localhost
(通过调用InetAddress。getByName(null)生成本地 IP地址 127。0。0。1)测试这两个程序。
(3) 修改练习2 中的程序,令其用多线程机制对多个客户进行控制。
(4) 修改JabberClient,禁止输出刷新,并观察结果。
(5) 以ShowHTML。java 为基础,创建一个程序片,令其成为对自己Web 站点的特定部分进行密码保护的大
586
…………………………………………………………Page 588……………………………………………………………
门。
(6) (可能有些难度)创建一对客户/服务器程序,利用数据报(Datagram )将一个文件从一台机器传到另
一台(参见本章数据报小节末尾的叙述)。
(7) (可能有些难度)对VLookup。java 程序作一番修改,使我们能点击得到的结果名字,然后程序会自动取
得那个名字,并把它复制到剪贴板(以便我们方便地粘贴到自己的E…mail )。可能要回过头去研究一下IO
数据流的那一章,回忆该如何使用 Java 1。1 剪贴板。
587
…………………………………………………………Page 589……………………………………………………………
第 16 章 设计范式
本章要向大家介绍重要但却并不是那么传统的“范式”(Pattern)程序设计方法。
在向面向对象程序设计的演化过程中,或许最重要的一步就是“设计范式”(Design Pattern)的问世。它
在由Gamma,Helm 和 Johnson 编著的《Design Patterns》一书中被定义成一个“里程碑”(该书由
Addison…Wesley 于 1995 年出版,注释①)。那本书列出了解决这个问题的 23 种不同的方法。在本章中,我
们准备伴随几个例子揭示出设计范式的基本概念。这或许能激起您阅读《Design Pattern》一书的欲望。事
实上,那本书现在已成为几乎所有 OOP 程序员都必备的参考书。
①:但警告大家:书中的例子是用 C++写的。
本章的后一部分包含了展示设计进化过程的一个例子,首先是比较原始的方案,经过逐渐发展和改进,慢慢
成为更符合逻辑、更为恰当的设计。该程序(仿真垃圾分类)一直都在进化,可将这种进化作为自己设计方
案的一个原型——先为特定的问题提出一个适当的方案,再逐步改善,使其成为解决那类问题一种最灵活的
方案。16。1 范式的概念
在最开始,可将范式想象成一种特别聪明、能够自我适应的手法,它可以解决特定类型的问题。也就是说,
它类似一些需要全面认识某个问题的人。在了解了问题的方方面面以后,最后提出一套最通用、最灵活的解
决方案。具体问题或许是以前见到并解决过的。然而,从前的方案也许并不是最完善的,大家会看到它如何
在一个范式里具体表达出来。
尽管我们称之为“设计范式”,但它们实际上并不局限于设计领域。思考“范式”时,应脱离传统意义上分
析、设计以及实施的思考方式。相反,“范式”是在一个程序里具体表达一套完整的思想,所以它有时可能
出现在分析阶段或者高级设计阶段。这一点是非常有趣的,因为范式具有以代码形式直接实现的形式,所以
可能不希望它在低级设计或者具体实施以前显露出来(而且事实上,除非真正进入那些阶段,否则一般意识
不到自己需要一个范式来解决问题)。
范式的基本概念亦可看成是程序设计的基本概念:添加一层新的抽象!只要我们抽象了某些东西,就相当于
隔离了特定的细节。而且这后面最引人注目的动机就是“将保持不变的东西身上发生的变化孤立出来”。这
样做的另一个原因是一旦发现程序的某部分由于这样或那样的原因可能发生变化,我们一般都想防止那些改
变在代码内部繁衍出其他变化。这样做不仅可以降低代码的维护代价,也更便于我们理解(结果同样是降低
开销)。
为设计出功能强大且易于维护的应用项目,通常最困难的部分就是找出我称之为“领头变化”的东西。这意
味着需要找出造成系统改变的最重要的东西,或者换一个角度,找出付出代价最高、开销最大的那一部分。
一旦发现了“领头变化”,就可以为自己定下一个焦点,围绕它展开自己的设计。
所以设计范式的最终目标就是将代码中变化的内容隔离开。如果从这个角度观察,就会发现本书实际已采用
了一些设计范式。举个例子来说,继承可以想象成一种设计范式(类似一个由编译器实现的)。在都拥有同
样接口(即保持不变的东西)的对象内部,它允许我们表达行为上的差异(即发生变化的东西)。合成亦可
想象成一种范式,因为它允许我们修改——动态或静态——用于实现类的对象,所以也能修改类的运作方
式。
在《Design Patterns》一书中,大家还能看到另一种范式:“继承器”(即Iterator,Java 1。0 和 1。1 不
负责任地把它叫作Enumeration,即“枚举”;Java1。2 的集合则改回了“继承器”的称呼)。当我们在集合
里遍历,逐个选择不同的元素时,继承器可将集合的实施细节有效地隐藏起来。利用继承器,可以编写出通
用的代码,以便对一个序列里的所有元素采取某种操作,同时不必关心这个序列是如何构建的。这样一来,
我们的通用代码即可伴随任何能产生继承器的集合使用。
16。1。1 单子
或许最简单的设计范式就是“单子”(Singleton),它能提供对象的一个(而且只有一个)实例。单子在
Java 库中得到了应用,但下面这个例子显得更直接一些:
//: SingletonPattern。java
// The Singleton design pattern: you can
// never instantiate more than one。
588
…………………………………………………………Page 590……………………………………………………………
package c16;
// Since this isn't inherited from a Cloneable
// base class and cloneability isn't added;
// making it final prevents cloneability from
// being added in any derived classes:
final class Singleton {
private static Singleton s = new Singleton(47);
private int i;
private Singleton(int x) { i = x; }
public static Singleton getHandle() {
return s;
}
public int getValue() { return i; }
public void setValue(int x) { i = x; }
}
public class SingletonPattern {
public static void main(String'' args) {
Singleton s = Singleton。getHandle();
System。out。println(s。getValue());
Singleton s2 = Singleton。getHandle();
s2。setValue(9);
System。out。println(s。getValue());
try {
// Can't do this: pile…time error。
// Singleton s3 = (Singleton)s2。clone();
} catch(Exception e) {}
}
} ///:~
创建单子的关键就是防止客户程序员采用除由我们提供的之外的任何一种方式来创建一个对象。必须将所有
构建器都设为private (私有),而且至少要创建一个构建器,以防止编译器帮我们自动同步一个默认构建
器(它会自做聪明地创建成为“友好的”——friendly,而非 private)。
此时应决定如何创建自己的对象。在这儿,我们选择了静态创建的方式。但亦可选择等候客户程序员发出一
个创建请求,然后根据他们的要求动态创建。不管在哪种情况下,对象都应该保存为“私有”属性。我们通
过公用方法提供访问途径。在这里,getHandle()会产生指向 Singleton 的一个句柄。剩下的接口
(getValue()和 setValue())属于普通的类接口。
Java 也允许通过克隆(Clone)方式来创建一个对象。在这个例子中,将类设为 final 可禁止克隆的发生。
由于Singleton 是从Object 直接继承的,所以 clone()方法会保持 protected (受保护)属性,不能够使用
它(强行使用会造成编译期错误)。然而,假如我们是从一个类结构中继承,那个结构已经过载了clone()
方法,使其具有public 属性,并实现了Cloneable ,那么为了禁止克隆,需要过载clone(),并掷出一个
CloneNotSupportedException (不支持克隆违例),就象第12章介绍的那样。亦可过载 clone(),并简单地
返回 this。那样做会造成一定的混淆,因为客户程序员可能错误地认为对象尚未克隆,仍然操纵的是原来的
那个。
注意我们并不限于只能创建一个对象。亦可利用该技术创建一个有限的对象池。但在那种情况下,可能需要
解决池内对象的共享问题。如果不幸真的遇到这个问题,可以自己设计一套方案,实现共享对象的登记与撤
消登记。
16。1。2 范式分类
《Design Patterns》一书讨论了23 种不同的范式,并依据三个标准分类(所有标准都涉及那些可能发生变
化的方面)。这三个标准是:
589
…………………………………………………………Page 591……………………………………………………………
(1) 创建:对象的创建方式。这通常涉及对象创建细节的隔离,这样便不必依赖具体类型的对象,所以在新
添一种对象类型时也不必改动代码。
(2) 结构:设计对象,满足特定的项目限制。这涉及对象与其他对象的连接方式,以保证系统内的改变不会
影响到这些连接。
(3) 行为:对程序中特定类型的行动进行操纵的对象。这要求我们将希望采取的操作封装起来,比如解释一
种语言、实现一个请求、在一个序列中遍历(就象在继承器中那样)或者实现一种算法。本章提供了“观察
器”(Observer)和“访问器”(Visitor)的范式的例子。
《Design Patterns》为所有这 23 种范式都分别使用了一节,随附的还有大量示例,但大多是用 C++编写
的,少数用 Smalltalk 编写(如看过这本书,就知道这实际并不是个大问题,因为很容易即可将基本概念从
两种语言翻译到Java 里)。现在这本书并不打算重复《Design Patterns》介绍的所有范式,因为那是一本
独立的书,大家应该单独阅读。相反,本章只准备给出一些例子,让大家先对范式有个大致的印象,并理解
它们的重要性到底在哪里。
16。2 观察器范式
观察器(Observer )范式解决的是一个相当普通的问题:由于某些对象的状态发生了改变,所以一组对象都
需要更新,那么该如何解决?在Smalltalk 的MVC (模型-视图-控制器)的“模型-视图”部分中,或在
几乎等价的“文档-视图结构”中,大家可以看到这个问题。现在我们有一些数据(“文档”)以及多个视
图,假定为一张图(Plot )和一个文本视图。若改变了数据,两个视图必须知道对自己进行更新,而那正是
“观察器”要负责的工作。这是一种十分常见的问题,它的解决方案已包括进标准的java。util 库中。
在Java 中,有两种类型的对象用来实现观察器范式。其中,Observable 类用于跟踪那些当发生一个改变时
希望收到通知的所有个体——无论“状态”是否改变。如果有人说“好了,所有人都要检查自己,并可能要
进行更新”,那么 Observable 类会执行这个任务——为列表中的每个“人”都调用 notifyObservers()方
法。notifyObservers()方法属于基础类Observable 的一部分。
在观察器范式中,实际有两个方面可能发生变化:观察对象的数量以及更新的方式。也就是说,观察器范式
允许我们同时修改这两个方面,不会干扰围绕在它周围的其他代码。
下面这个例子类似于第 14章的ColorBoxes 示例。箱子(Boxes)置于一个屏幕网格中,每个都初始化一种随
机的颜色。此外,每个箱子都“实现”(implement)了“观察器”(Observer )接口,而且随一个
Observable 对象进行了注册。若点击一个箱子,其他所有箱子都会收到一个通知,指出一个改变已经发生。
这是由于Observable 对象会自动调用每个Observer 对象的 update()方法。在这个方法内,箱子会检查被点
中的那个箱子是否与自己紧邻。若答案是肯定的,那么也修改自己的颜色,保持与点中那个箱子的协调。
//: BoxObserver。java
// Demonstration of Observer pattern using
// Java's built…in observer classes。
import java。awt。*;
import java。awt。event。*;
import java。util。*;
// You must inherit a new type of Observable:
class BoxObservable extends Observable {
public void notifyObservers(Object b) {
// Otherwise it won't propagate changes:
setChanged();
super。notifyObservers(b);
}
}
public class BoxObserver extends Frame {
Observable notifier = new BoxObservable ();
public BoxObserver(int grid) {
setTitle(〃Demonstrates Observer pattern〃);
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!