您的位置:首页 > 其它

设计模式之抽象工厂模式

2015-11-03 22:33 302 查看
最近在看《大话设计模式》时,书上以一个例子作引子,这个引子很有意思,我记录一下。

假如有个项目,开始是用SQLserver作数据库,现在因为某种原因,要更换到Access,或者oracle(oracle更恶心),那如何做?

这几种数据库都有各自不同的框架和方法(因为我们不会直接操作SQL语句,那样非常脆弱)。因此,对于简单的插入表的方法就会完全不一样。

想想怎么做?很直观的想法是,将数据表示层与业务逻辑层分离,也就是抽象的思想。业务逻辑层如果根本无法区分到底使用的是什么数据库,那我们的目的就达到了。

在这个例子中,不管什么数据库,方法都是一样的。比如创建表,也许具体代码不一样,但是这个方法是一定要有的。除此之外,还有对各个表的操作,增删改查,同样,也许数据库操作的方法并不一样,但是增删改查的基本方法是要有的。

因此创建一个抽象工厂类

//抽象数据库类
IDatabase{
ITableA createTableA();
ITableB createTableB();
}
//具体Access数据库
AccessDatabase implements IDatabase{
ITableA createTableA(){
......

}
ITableB createTableB(){
......
}
}
//具体oracle数据库
OracleDatabase implement IDatabase{

ITableA createTableA(){
......

}
ITableB createTableB(){
......
}
}

//抽象的表A
//因为表可以由Access创建,也可以由oracle创建
ITableA{
insert();
update();
delete();
select();
}
//具体的表A
//由AccessDatabase创建
AccessTableA implements ITableA{
。。。。
}
//具体的表A
//由OracleDatabase创建
OracleTableA implements ITableA{
.....
}

//抽象表B
。。。。


OK,抽象工厂模式就这样了,然后在客户端使用的时候,就使用IDatabase db = new AccessDatabase();

ITableA tableA = db.createTableA();再之后,客户端就完全屏蔽了数据库的差异。对它而言,只要操作tableA 就可以了。

以上就是抽象工厂模式。

下面分析一下它的优缺点:

优点在于它隔离了业务逻辑层和数据库连接层。此后无论再变成SQLite或者别的什么数据库,业务逻辑代码都不用更改了。

缺点在于,比如新添一张表,就要在抽象类中添加一个createtableC的方法,然后在各个数据库中都增加相应方法,然后在再新建一个tableC的类,这个类再实现增删改查的方法。然后就像晓峰所说的,类会越来越多,,越来越难以维护。

而且还有一个问题,比如在客户端程序中,有100个地方用来new AccessDatabase(),那当变成Oracle时,将会有100个地方要改变的。

如何解决上述问题呢?

我们可以想到使用简单工厂模式,

class Factory{
private String database="Access";

public ITableA createTableA(){
if(database.equalsOf("Access")){
return new AccessDatabase().createTableA();
}else if(database.equalsOf("Oracle")){
return new OracleDatabase().createTableA();
}
}
public ITableB createTableB(){
.....
}
}


这样,当要更改数据库时,只要将这里属性中的database换成希望的database名字即可。然后再新加个switch case即可,当然了,新建数据库后,创建数据库的表的方法,以及表中所要包含的方法名一个都不能少。【这是需求要求所致,没有办法】。这种方法的结果就是客户端完全不必关心到底采用的是什么数据库。

但是上面代码还有问题:就是每新加一个数据库,都要新加一个switch case,然后还要再更改属性值。能不能不再添加switch case?

有一个方法,就是反射,反射是另外一种创建对象的方法,我们平时创建对象时,需要知道类名,然后new 类名()创建了对象,那反射呢?是使用一个字符串,对于Java而言,就是class.forName(“完整类名”).newInstance();这样就创建了一个类。

看到有什么好处了么?使用反射,可以通过动态提供类名来完成对象的创建,而这个动态的类名是一个字符串。因此,如果使用反射机制,那么上述代码可以变为:

class Factory{
private String database="com.dacas.Access";

public ITableA createTableA(){
IFactory factory = (IFactory)Class.forName(database).newInstance();
return factory.createTableA();
}
public ITableB createTableB(){
.....
}
}


OK,现在如果要变成Oracle数据库,只需要将database名变成 com.dacas.oracle即可。

这种方法还有缺陷,那就是每次做修改都要拿到源码,然后更改源码。如果拿不到源码,或者以及编译好了,那就抓瞎了。

所以最好的方法是使用配置文件。

对于Java而言,使用properties配置文件是最好不过的了,它使用一个键值对来存储。

比如配置文件为:database.properties,文件内容为:database=com.dacas.Access

在上述代码中就变成了:

class Factory{
private String database;

public Factory(){
Properties pro = new Properties();
FileInputStream in = new FileInputStream("database.propreties");
pro.load(in);
in.close();
this.database = pro.getProperty(database);
}
public ITableA createTableA(){
IFactory factory = (IFactory)Class.forName(database).newInstance();
return factory.createTableA();
}
public ITableB createTableB(){
.....
}
}


OK,这样一来,利用反射与简单工厂模式就完整的解决了上述难题。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  设计模式