设计模式之抽象工厂模式
2015-11-03 22:33
302 查看
最近在看《大话设计模式》时,书上以一个例子作引子,这个引子很有意思,我记录一下。
假如有个项目,开始是用SQLserver作数据库,现在因为某种原因,要更换到Access,或者oracle(oracle更恶心),那如何做?
这几种数据库都有各自不同的框架和方法(因为我们不会直接操作SQL语句,那样非常脆弱)。因此,对于简单的插入表的方法就会完全不一样。
想想怎么做?很直观的想法是,将数据表示层与业务逻辑层分离,也就是抽象的思想。业务逻辑层如果根本无法区分到底使用的是什么数据库,那我们的目的就达到了。
在这个例子中,不管什么数据库,方法都是一样的。比如创建表,也许具体代码不一样,但是这个方法是一定要有的。除此之外,还有对各个表的操作,增删改查,同样,也许数据库操作的方法并不一样,但是增删改查的基本方法是要有的。
因此创建一个抽象工厂类
OK,抽象工厂模式就这样了,然后在客户端使用的时候,就使用IDatabase db = new AccessDatabase();
ITableA tableA = db.createTableA();再之后,客户端就完全屏蔽了数据库的差异。对它而言,只要操作tableA 就可以了。
以上就是抽象工厂模式。
下面分析一下它的优缺点:
优点在于它隔离了业务逻辑层和数据库连接层。此后无论再变成SQLite或者别的什么数据库,业务逻辑代码都不用更改了。
缺点在于,比如新添一张表,就要在抽象类中添加一个createtableC的方法,然后在各个数据库中都增加相应方法,然后在再新建一个tableC的类,这个类再实现增删改查的方法。然后就像晓峰所说的,类会越来越多,,越来越难以维护。
而且还有一个问题,比如在客户端程序中,有100个地方用来new AccessDatabase(),那当变成Oracle时,将会有100个地方要改变的。
如何解决上述问题呢?
我们可以想到使用简单工厂模式,
这样,当要更改数据库时,只要将这里属性中的database换成希望的database名字即可。然后再新加个switch case即可,当然了,新建数据库后,创建数据库的表的方法,以及表中所要包含的方法名一个都不能少。【这是需求要求所致,没有办法】。这种方法的结果就是客户端完全不必关心到底采用的是什么数据库。
但是上面代码还有问题:就是每新加一个数据库,都要新加一个switch case,然后还要再更改属性值。能不能不再添加switch case?
有一个方法,就是反射,反射是另外一种创建对象的方法,我们平时创建对象时,需要知道类名,然后new 类名()创建了对象,那反射呢?是使用一个字符串,对于Java而言,就是class.forName(“完整类名”).newInstance();这样就创建了一个类。
看到有什么好处了么?使用反射,可以通过动态提供类名来完成对象的创建,而这个动态的类名是一个字符串。因此,如果使用反射机制,那么上述代码可以变为:
OK,现在如果要变成Oracle数据库,只需要将database名变成 com.dacas.oracle即可。
这种方法还有缺陷,那就是每次做修改都要拿到源码,然后更改源码。如果拿不到源码,或者以及编译好了,那就抓瞎了。
所以最好的方法是使用配置文件。
对于Java而言,使用properties配置文件是最好不过的了,它使用一个键值对来存储。
比如配置文件为:database.properties,文件内容为:database=com.dacas.Access
在上述代码中就变成了:
OK,这样一来,利用反射与简单工厂模式就完整的解决了上述难题。
假如有个项目,开始是用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,这样一来,利用反射与简单工厂模式就完整的解决了上述难题。
相关文章推荐
- PropertyChangeListener简单理解
- 什么是设计模式
- 设计模式之创建型模式 - 特别的变量问题
- 七、设计模式——装饰模式
- 设计模式总结
- 设计模式之创建型模式
- 浅谈设计模式的学习
- PHP设计模式之装饰者模式代码实例
- php设计模式之单例模式实例分析
- 介绍php设计模式中的工厂模式
- PHP设计模式之适配器模式代码实例
- 深入浅出23种设计模式
- 浅谈c#设计模式之单一原则
- C#设计模式之观察者模式实例讲解
- C#设计模式之单例模式实例讲解
- 深入理解JavaScript系列(28):设计模式之工厂模式详解
- 面向对象设计模式的核心法则
- JavaScript设计模式之单件模式介绍
- 深入理解JavaScript系列(25):设计模式之单例模式详解
- JavaScript设计模式之外观模式实例