虽然这篇文章和java没什么关系,但我还是要贴出来和大家一起分享。因为这样感人的文章现在已经不多见了。这孩子太可怜,这个孩子的母亲也很伟大,为了这样的人间真情,我不禁落泪了....
一天,正走在路上,手机响了,话筒里是个稚嫩的小女孩的声音:“爸爸,你快回来吧,我好想你啊!”凭直觉,我知道又是个打错的电话,因为我没有女儿,只有个6岁的独生子。这年头发生此类事情也实在是不足为奇。我没好气的说了声:“打错了!”便挂断了电话。
接下来几天里,这个电话竟时不时地打过来,搅得我心烦,有时态度粗暴的回绝,有时干脆不接。
那天,这个电话又一次次打来,与往常不同的是,在我始终未接的情况下,那边一直在坚持不懈的拨打着。我终于耐住性子开始接听,还是那个女孩有气无力的声音:“爸爸,你快回来吧,我好想你啊!妈妈说这个电话没打错,是你的手机号码,爸爸我好疼啊!妈妈说你工作忙,天天都是她一个人在照顾我,都累坏了,爸爸我知道你很辛苦,如果来不了,你就在电话里再亲妞妞一次好吗?”孩子天真的要求不容我拒绝,我对着话筒响响地吻了几下,就听到孩子那边断断续续的声音:“谢谢……爸爸,我好……高兴,好……幸福……”
就在我逐渐对这个打错的电话发生兴趣时,接电话的不是女孩而是一个低沉的女声:“对不起,先生,这段日子一定给您添了不少麻烦,实在对不起!我本想处理完事情就给您打电话道歉的。这孩子的命很苦,生下来就得了骨癌,她爸爸不久前又……被一场车祸夺去了生命,我实在不敢把这个消息告诉她,每天的化疗,时时的疼痛,已经把孩子折磨得够可怜的了。当疼痛最让她难以忍受的时候,她嘴里总是呼喊着以前经常鼓励她要坚强的爸爸,我实在不忍心看孩子这样,那天就随便编了个手机号码……”
“那孩子现在怎么样了?”我迫不及待地追问。
“妞妞已经走了,您当时一定是在电话里吻了她,因为她是微笑着走的,临走时小手里还紧紧攥着那个能听到‘爸爸’声音的手机……”
不知什么时候,我的眼前已模糊一片……
数据库操作现在是项目开发的根本,学习Java首先应该学会怎么样连接数据库,用Java连接数据库可不像用Delphi这类工具那样设几个属性就OK,说简单也简单,说复杂,其实也挺复杂的,而且很麻烦,如果是初学,根本不能保证第一次就连接成功,下面以SQL Server 2000为例,说说Java连接数据库的基本方法,也记录一下心得。
1、下载SQL Server 2000 driver for JDBC
SQL Server 2000 Driver For JDBC Downloads
该驱动截止目前有四个版本,建议下载最新的SP3版。
该驱动安装成功后,请将安装目录下的lib目录下的三个.jar文件加到CLASSPATH中;如果你使用的是JBuilder或Eclipse,将这三个文件根据IDE的提示加到工程中也可。
2、升级你的SQL Server 2000,为其打上最新的补丁。
这一步可能不是必需的,因操作系统环境而定,在不打补丁的情况,有时可以正常连接,有时却不能,所以建议还是安装最新的SQL Server 2000补丁(SP4)和JDBC驱动(SP3)。
如果你的程序在运行时提示:Error establishing socket,一般情况下,打上SQL Server 2000的补丁就可解决。
3、驱动的加载方法
在建立连接之前,要先加载SQL Server 2000 JDBC的驱动,代码形式如下:
Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver");
在此注意,forName方法的参数字符串必须完全相同于以上内容,大小写是区分的,其实这个串就是驱动类的完整名称:包名+类名。
4、获得一个连接
在操作数据库之前,要先获得与数据库的一个连接,使用如下代码格式:
DriverManager.getConnection(连接字符串, 登录用户名, 登录密码);
例:
DriverManager.getConnection("jdbc:microsoft:sqlserver://localhost:1433; DatabaseName=pubs", "sa", "");
在此处关键的是连接字符串的内容,localhost部分即服务器的名字,可以更改;1433部分为SQL Server使用的端口号,根据实际情况修改即可;DatabaseName即为要连接的数据库的名字,在此注意DatabaseName之前的是分号,而不是冒号。
5、代码实例
// 导入Java SQL包,连接数据库必需;
import java.sql.*;
public class TestDB {
public static void main(String[] args) {
String driverName = "com.microsoft.jdbc.sqlserver.SQLServerDriver";
String dbURL = "jdbc:microsoft:sqlserver://localhost:1433; DatabaseName=pubs";
String userName = "sa";
String userPwd = "";
Connection dbConn
try {
Class.forName(driverName);
dbConn = DriverManager.getConnection(dbURL, userName, userPwd);
System.out.println("Connection Successful!");
}
Catch (Exception e) {
e.printStackTrace();
}
}
}
6、可能出现的问题
如果以上的代码运行后,输出"Connection Successful!",那就代表一切正常,连接数据库成功,你可以进行Statement、ResultSet的操作了;反之的话,一定是出现了相应的异常。
如果提示错误"Error establishing socket",请根据之前的说明安装相应的SQL Server 2000补丁即可。
如果提示"ClassNotFoundException",那一定是 Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver"); 该段代码拼写有误,或者是SQL Server 2000 Driver For JDBC Lib目录下的三个.jar文件未加入到CLASSPATH中。
工厂模式有简单工厂模式,工厂方法模式和抽象工厂模式几种形态。其中简单工厂模式和工厂方法模式已经在前面作过介绍。在这里,我们来介绍抽象工厂模式。
抽象工厂模式是所有形态的工厂模式中最为抽象和最具广泛性的一种形态。
抽象工厂模式的定义
抽象工厂模式是工厂方法模式的进一步扩广化和抽象化。我们给出抽象工厂模式的类图定义如下。
图1. 抽象工厂模式的类图定义 |
从上图可以看出,简单工厂模式涉及到以下的角色
抽象工厂(AbstractFactory)类或接口
担任这个角色的是工厂方法模式的核心,它是与应用程序无关的。任何在模式中创立对象的工厂类必须实现这个接口,或继承这个类。
实工厂类 (Conrete Factory)
担任这个角色的是与应用程序紧密相关的,直接在应用程序调用下,创立产品实例的那样一些类。
抽象产品 (Abstract Product)
担任这个角色的类是工厂方法模式所创立的对象的父类,或它们共同拥有的接口。
实产品 (Concrete Product)
担任这个角色的类是工厂方法模式所创立的任何对象所属的类。
怎么这个类图和工厂方法模式的类图看起来是一样的?
是的,图是一样的,但是含义有很大的不同。必须指出,在抽象工厂模式中,抽象产品 (AbstractProduct) 可能是一个或多个,从而构成一个或多个产品族(Product Family)。 在只有一个产品族的情况下,抽象工厂模式实际上退化到工厂方法模式。在上面的类图中,只给出了一个产品族,相当于位图中的一个点,而完整的位图应当是三维的,如下图。
图2. 抽象工厂模式的位图 |
从位图可以清楚地看到,与纸面垂直的数轴,即第三维轴,是代表产品族的数轴。上面的位图中展示的是有两个产品族,族A和族B的情形。
在只有一个产品族时,第三维就坍缩掉,位图也就只剩下两维。这时抽象工厂模式就退化得与工厂方法模式一模一样。
在什么情形下应当使用抽象工厂模式
在以下情况下,应当考虑使用抽象工厂模式。
首先,一个系统应当不依赖于产品类实例被创立,组成,和表示的细节。这对于所有形态的工厂模式都是重要的。
其次,这个系统的产品有多于一个的产品族。
第三,同属于同一个产品族的产品是设计成在一起使用的。这一约束必须得在系统的设计中体现出来。
最后,不同的产品以一系列的接口的面貌出现,从而使系统不依赖于接口实现的细节。
其中第二丶第三个条件是我们选用抽象工厂模式而非其它形态的工厂模式的关键性条件。
抽象工厂模式在小花果园系统中的实现
现在,我们在佛罗里达的渡假小屋修整好啦。接下来,一项重要而光荣的工作,就是开发小屋后面的小花园。这意味着,我们有两处小花园需要照料,一处在北方地区,另一处在亚热带地区。抽象工厂模式正好适用于我们的情况。
图3. 抽象工厂模式应用于小花果园系统中。三种不同的背景颜色可以区分工厂类,蔬菜类(第一产品族),和水果类的类图(第二产品族) |
两处花园就相当于两个产品族。显然,给北方花园的植物是要种植在一起的,给南方花园的植物是要另种植在一起的。这种分别应当体现在系统的设计上面。这就满足了应当使用抽象工厂模式的第二和第三个条件。
package com.javapatterns.abstractfactory;
public interface Gardener {} |
代码清单1. 接口 Gardener。
package com.javapatterns.abstractfactory;
public class NorthenGardener implements Gardener { public VeggieIF createVeggie(String name) { return new NorthernVeggie(name); }
public FruitIF createFruit(String name) { return new NorthernFruit(name); }
} |
代码清单2. 实工厂类 NorthenGardener。
package com.javapatterns.abstractfactory;
public class TropicalGardener implements Gardener { public VeggieIF createVeggie(String name) { return new TropicalVeggie(name); }
public FruitIF createFruit(String name) { return new TopicalFruit(name); }
} |
代码清单3. 实工厂类 TropicalGardener。
package com.javapatterns.abstractfactory;
public interface VeggieIF {} |
代码清单4. 接口 VeggieIF。
package com.javapatterns.abstractfactory;
public class NorthernVeggie implements VeggieIF { public NorthernVeggie(String name) { this.name = name; }
public String getName(){ return name; }
public void setName(String name){ this.name = name; }
private String name; } |
代码清单5. 实产品类 NorthernVeggie。实产品类 NorthernFruit 与此极为类似,故略去。
package com.javapatterns.abstractfactory;
public class TropicalVeggie implements VeggieIF { public TropicalVeggie(String name) { this.name = name;}
public String getName(){ return name; }
public void setName(String name){ this.name = name; }
private String name; } |
代码清单6. 实产品类 TropicalVeggie。实产品类 TropicalFruit 与此极为类似,故略去。
笔者对植物的了解有限,为免遗笑大方,在上面的系统里采用了简化处理。没有给出高纬度和低纬度的水果类或蔬菜类的具体名称。
抽象工厂模式的另一个例子
这个例子讲的是微型计算机的生产。产品族有两个,PC(IBM系列)和Mac(MacIntosh系列)。显然,我们应该使用抽象工厂模式,而不是工厂方法模式,因为后者适合于处理只有一个产品族的情形。
图4. 抽象工厂模式应用于微型计算机生产系统中。两种不同的背景颜色可以区分两类产品族,及其对应的实工厂类 |
关于模式的实现
在抽象实现工厂模式时,有下面一些值得注意的技巧。
第一丶实工厂类可以设计成单态类。很显然,在小花果园系统中,我们只需要 NorthenGardener 和TropicalGardener 的一个实例就可以了。关于单态类的知识,请见<爪哇语言单态类创立性模式>。
第二丶在实现抽象工厂模式时,产品类往往分属多于一个的产品族,而针对每一族,都需要一个实工厂类。在很多情况下,几个实工厂类都彼此相象,只有些微的差别。
这时,笔者建议使用原始模型(Prototype)模式。这一模式会在以后介绍,届时作者会进一步阐述这一点。
第三丶设计更加灵活的实工厂。以微型计算机生产系统为例,PCProducer 是一个实工厂类,它的不灵活之处在于,每一种产品都有一个工厂方法。CPU 有createCPU(),RAM 有createRAM(),等等。如果一个已有的系统需要扩充,比如增加硬盘这一新产品,我们就需要增加一系列的接口 (createHD())丶类(HD, PCHD, MacHD)和方法。这似乎不很理想。
一个解决的办法是,把createCPU(),createRAM(), createHD()这几个方法合并为一个createPart(String type)方法。这个合并后的方法返还一个Part接口。所有的产品都要实现这一接口,而CPU,RAM,和HD接口则不再需要了。每一个实产品都需要有一个属性,表明它们的种类是CPU,RAM,和HD。
这样做的结果是,数据类型的丰富结构被扁平化了。客户端拿到的永远是一个Part接口。这对客户端而言不很安全。
第四丶抽象工厂类可以配备静态方法,以返还实工厂。设计的方法有两种。
一种是以一个静态方法,按照参量的值,返回所对应的实工厂。静态方法的数据类型是抽象方法类。
另一种是以每一个实工厂类都配备一个静态方法,其数据类型是该实工厂类。
问答题
第1题。如上面的讨论,抽象工厂类可以配备一个静态方法,按照参量的值,返回所对应的实工厂。请把微型计算机生产系统的抽象工厂类按照这一方案改造,给出UML类图和源代码。
第2题。如上面的讨论,抽象工厂类可以配备一系列静态方法对应一系列的实工厂。请把微型计算机生产系统的抽象工厂类按照这一方案改造,给出UML类图和源代码。
第3题。如上面的讨论,实工厂类可以设计成单态类。请在第1题的基础上把微型计算机生产系统的实工厂类按照这一方案改造,给出UML类图和源代码。
问答题答案
第1题。微型计算机生产系统的抽象工厂原本是接口,现在需要改造成抽象类。
图5. 三种不同的背景颜色可以区分抽象工厂类,两类产品族,及其对应的实工厂类。ComputerProducer 类图中类名为斜体表明该类是抽象的,而getProducer()的下划线表明该方法是静态的 |
package com.javapatterns.abstractfactory.exercise1;
public class ComputerProducer { public static ComputerProducer getProducer(String which) { if (which.equalsIgnoreCase("PC")) { return new PCProducer(); } else (which.equalsIgnoreCase("Mac")) { return new MacProducer(); } } } |
代码清单7. 抽象类 ComputerProducer 的方法 getProducer(String which)。
第2题。略。
第3题。本题答案是在第1题基础之上的。
图6. 三种不同的背景颜色可以区分抽象工厂类,两类产品族,及其对应的实工厂类。ComputerProducer 类图中类名为斜体表明该类是抽象的,而getProducer()的下划线表明该方法是静态的。MacProducer 和 PCProducer 的构造子是私有的,因此这两个类必须自己将自己实例化。 |
package com.javapatterns.abstractfactory.exercise3;
abstract public class ComputerProducer { public static ComputerProducer getProducer(String which) { if (which.equalsIgnoreCase("PC")) { return PCProducer.getInstance(); } else (which.equalsIgnoreCase("Mac")) { return MacProducer.getInstance(); } } } |
代码清单8.抽象工厂类ComputerProducer。
package com.javapatterns.abstractfactory.exercise3;
public class MacProducer extends ComputerProducer { private MacProducer() { }
public CPU createCPU() {}
public RAM createRAM() {}
private static final m_MacProducer = new MacProducer();
} |
代码清单9. 实工厂类 MacProducer 是单态类。读过笔者<单态创立性模式>一节的读者应当知道,这里使用的单态类实现方法是饿汉式方法。
package com.javapatterns.abstractfactory.exercise3;
public class PCProducer extends ComputerProducer { private PCProducer() { }
public CPU createCPU() {}
public RAM createRAM() {}
private static final m_PCProducer = new PCProducer(); } |
代码清单10. 实工厂类 PCProducer 是单态类,使用的单态类实现方法是饿汉式方法。
各产品类没有变化,因此不在此重复。