用户注册系统的Refactoring(重整)过程(3)
板桥里人 http://www.jdon.com 2002/06/15
在前两篇中,我们展示了用户注册系统中的密码验证这部分的重整重构过程,使用了工厂模式。
模式是达到我们目标的手段之一,为达到“封装不变的”这个终极目标,手段还有各种各样,其中就包括数据库设计的重整。
数据库的缺陷在系统实施过程后,不可避免的会遇到,修改结构,在传统编程方法中,修改数据库结构,会带来相当麻烦的繁琐工作量,所以,这也是程序员害怕的。
但是,这是必须的,那么我们看看利用完全面向对象的Java是如何实现的。由于我们的数据库操作都封装在Javabean中,所以,很显然,修改数据库结构,不会涉及到前台的Jsp程序,这是让我们很欣慰的事情。
数据库结构缺陷的发现也是在需求变化的情况下发现的,因为老板会对你说,你这个用户注册系统做得不错,我们另外的面向律师的系统和面向制造业公司的系统也要用你这套用户注册系统,你应该稍微修改一下可以用。
但是你回来研究后发现,面向律师和面向制造业的系统的用户除了联系信息基本一致,都要有地址电话传真等,其他要求用户输入的信息是不一样的。
需要改数据库了,原来一个profile数据库已经顶不住,而且,让一个数据库接纳不同性质的用户也不是一件让人放心的事情。
我们要对“数据库”进行refactor(重整〕:
对数据库重整,实际是对数据对象Model(或者称实体对象)实行提炼,由于数据表是数据对象Model持久化的一个方式,而我们一般习惯都是从从数据表入手,所以重整数据Model从数据库开始。
无论什么用户注册,其联系地址电话等是不变的,那我们需要将这个对象分离出来后进行封装,取名叫Address
Address数据库如下:
CREATE TABLE address (
addressid int(20) NOT NULL AUTO_INCREMENT,
customerid int(20) NOT NULL ,
addressdes varchar(150) NOT NULL,
fullname varchar(150) NOT NULL, #person first name or
company name
address varchar(250) NOT NULL, #详细地址
address2 varchar(250) NOT NULL,
city varchar(50) NOT NULL,
state varchar(50) NOT NULL,
zipcode varchar(50) NOT NULL,
phone varchar(50) NOT NULL,
fax varchar(50) NOT NULL,
country varchar(50) NOT NULL,
key customerid (customerid),
PRIMARY KEY (addressid)
); |
那么我们面对律师 和制造业公司的用户数据库:
CREATE TABLE lawprofile (
userid int(20) NOT NULL ,
barnumber varchar(50) NOT NULL,
factsofcase text NOT NULL, #案例
description text NOT NULL, #详细描述
regdate datetime DEFAULT '0000-00-00 00:00:00' NOT NULL,
PRIMARY KEY (userid),
UNIQUE KEY email (email)
);
CREATE TABLE companyprofile (
userid int(20) NOT NULL ,
alias varchar(100) NOT NULL, #公司简称
weburl varchar(100) NOT NULL, #网址
description text NOT NULL, #描述
catalogid varchar(50) NOT NULL, #公司所属类别
regdate datetime DEFAULT '0000-00-00 00:00:00' NOT NULL,
PRIMARY KEY (userid),
UNIQUE KEY email (email)
);
|
另外我们当然需要一个userid产生器,产生自增的userid,然后插入需要userid的四个表中:
lawprofile companyprofile password passwordassist
数据库管理功能为我们上面的数据库修改提供了方便的倒数据功能。
最担心的还是程序,程序是否影响很大?
因为其他程序是用可能用到Address中的信息的。
我们设计一个AddressIF接口:
public interface AddressIF{
public String getZipcode();
public String getCity();
public String getState();
public String getAddress();
public String getPhone();
public String getFax();
public String getCountry();
} |
它的具体实现concrete就是从Address数据库中获取联系信息,对于老的系统,只有一个profile的那么当然是从profile数据库中获取联系信息,这里也是做个工厂方法,可以达到新老系统的交替。
到目前为止,我们已经固化两个我们认为相对不变的对象,一个是用户的注册联系信息,一个是密码验证体系。我们反回头再看看当初的需求分析:
1.增加 删除用户资料
2.忘记密码后可查询密码
3.密码修改
4.密码验证
后面三条和第一条中主要部分我们都已经解决,变化的就是,这套用户注册系统应用到不同用户场景(context)中,可能变化的就是用户有其自己特殊的信息需要输入,就象上面的律师和制造业公司一样。
那么我们如何在程序中表达这个思想,也就是说,我们希望我们已经“固化”的东西成为框架Framework,其他程序员再使用该系统时,只要继承延伸该框架就可以。
那我们做一个抽象类(Abastract class)在这个抽象类中,我们将固化事务写入,需要继承和具体实现的由其他程序员针对具体应用做拓展:
public abstract class UserInfo extends AddressDb implements
UserIF{
protected String password = "";
public void setPassword(String password) { this.password
= password; }
public String getPassword() { return password; }
protected String email="";
public void setEmail(String email) { this.email =
email; }
public String getEmail() {
return email;
}
/**
* for every different users ,this method don't need
be changed
*
*/
public int insert() throws Exception
{
//得到userid
userid = (int)SequenceManager.nextID();
if (userid==0)
throw new Exception("can't get userid from
SequenceManager");
try {
insertMember();
//完成地址插入数据库
insertAddress();
//完成插入密码验证数据库
AuthorizationIF auth=
AuthorizationFactory.getInstance(AuthorizationIF.authtypeone);
auth.insertPassword(userid,email,password);
}catch (Exception ex) {
throw new Exception("insert()"+ex.getMessage());
}
return this.userid;
}
public abstract void insertMember() throws Exception;
} |
在insert()方法中,我们已经完成了 联系地址Address 以及password email等固化信息的插入,剩余的要做的就是insertMember();
我们看看对于律师用户是如何继承这个抽象类的:
public class CompanyDb extends
UserInfo{
.....
private String alias="";
private static String ADD_MEMBER = "insert into
company "+
"(userid,email,alias,weburl,description,itemids,regdate)"
+
"values (?,?,?,?,?,?,?)";
public void insertMember() throws Exception {
ResultSet rs=null;
try {
Mysql mysql = new Mysql(ADD_MEMBER);
mysql.setInt(1,userid);
mysql.setString(2,email);
mysql.setString(3,alias);
mysql.setString(4,weburl);
mysql.setString(5,description);
mysql.setString(6,itemids);
java.text.SimpleDateFormat formatter = new java.text.SimpleDateFormat
(AuthorizationIF.entimeformat);
String regdate = formatter.format(new java.util.Date());
mysql.setString(7,regdate);
mysql.executeUpdate();
mysql.close();
mysql = null;
}catch (Exception ex) {
throw new Exception("insert()"+ex.getMessage());
}
}
|
在服务器后端的这些翻天覆地的变化中,相关Jsp页面变化不大,如果用户信息输入的项目变化,signup.jsp基本没有变化。
如果有变化,以面向制造业用户为例,也只是要要做一个新的javabean就是上面的CompanyDb,然后在原来的signup.jsp中增加几个表单内容。仅此而已。
这三篇系列文章,主要展示了本人实践中的一个refactoring实例,随着XP方法的运用普及,会有更多实践实例发表出来,也会对旧有的软件体系形成最强烈的冲击和提升。
由于没有校验,文中笔误难免,来信指出,谢谢。
案例下载
JavaBean和JSP/JDBC操作数据库
|