设计模式之抽象工厂模式

最近在看《大话设计模式》时,书上以一个例子作引子,这个引子很有意思,我记录一下。
假如有个项目,开始是用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,这样一来,利用反射与简单工厂模式就完整的解决了上述难题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值