缘起
某日,部门Leader找到小明:“小明,我们公司不是用的SQL Server的数据库吗,但是后面可能会改,比如去使用Access或Mysql或其他的,你觉得该怎么去设计这个代码呢?”
小明一脸所思,陷入了思考中…
Leader:“或者你先这样,先把最基本的访问数据库的示例写一下。”
小明听后,几分钟后代码出炉。
User类
public class User {
private int id;
private String name;
..getter/setter
}
SqlServerUser类,用于操作User表,假设只有新增用户和查询用户的方法。
public class SqlServerUser {
public void insert(User user) {
System.out.println("Sql server插入了一条用户数据");
}
public void getUser(int id) {
System.out.println("Sql server查询一条用户数据");
}
}
- 客户端调用
User user = new User();
SqlServerUser sqlServerUser = new SqlServerUser();
sqlServerUser.insert(user);
sqlServerUser.getUser(1);
改造
Leader:“你看这里,是不是最基本的实现了Sql server数据库的操作,假如换成别的你还要改一大堆对吧,那么你是不是可以把每个数据库的操作都封装起来呢,而且它们所有数据库执行的操作结果都是一致的。当我需要哪个数据库的操作时,就实例化哪个数据库的实例去使用就完事儿了”
小明听后,立即想到了之前的工厂模式。
- IUser接口:用户客户端访问,解除与具体数据库访问的耦合
public interface IUser {
void insert(User user);
User getUser(int id);
}
- SqlServerUser:用于访问Sqlserver的User
public class SqlServerUser implements IUser{
public void insert(User user) {
System.out.println("Sql server插入了一条用户数据");
}
public User getUser(int id) {
System.out.println("Sql server查询一条用户数据");
return null;
}
}
- AccessUser:用于Access的User
public class AccessUser implements IUser{
public void insert(User user) {
System.out.println("Access插入了一条用户数据");
}
public User getUser(int id) {
System.out.println("Access查询一条用户数据");
return null;
}
}
- IFactory:定义一个创建访问User表对象的抽象的工厂接口
public interface IFactory {
IUser createUser();
}
- SqlserverFactory:实现IFactory接口,实例化SqlServerUser
public class SqlServerFactory implements IFactory {
@Override
public IUser createUser() {
return new SqlServerUser();
}
}
- AccessFactory:实现IFactory接口,实例化AccessUser
public class AccessFactory implements IFactory {
@Override
public IUser createUser() {
return new AccessUser();
}
}
- 客户端调用
User user = new User();
IFactory factory = new SqlServerFactory();
IUser iUser = factory.createUser();
iUser.insert(user);
iUser.getUser(1);
再增部门
Leader看后,点了点头,“假如我再新增一个部门表的数据库操作呢”
小明继续编码中。。。
- 部门
public class Dept {
private int id;
private String name;
}
- IDept
public interface IDept {
void insert(Dept dept);
Dept getDept(int id);
}
- SqlserverDept:访问Sqlserver的部门数据
public class SqlServerDept implements IDept{
@Override
public void insert(Dept dept) {
System.out.println("Sqlserver 新增一条部门数据");
}
@Override
public Dept getDept(int id) {
System.out.println("Sqlserver查询一条部门数据");
return null;
}
}
- AccessDept
public class AccesssDept implements IDept{
@Override
public void insert(Dept dept) {
System.out.println("Access 新增一条部门数据");
}
@Override
public Dept getDept(int id) {
System.out.println("Access查询一条部门数据");
return null;
}
}
- IFactory
public interface IFactory {
IUser createUser();
IDept createDept();
}
- SqlServerFactory
public class SqlServerFactory implements IFactory {
@Override
public IUser createUser() {
return new SqlServerUser();
}
@Override
public IDept createDept() {
return new SqlServerDept();
}
}
- AccessFactory
public class AccessFactory implements IFactory {
@Override
public IUser createUser() {
return new AccessUser();
}
@Override
public IDept createDept() {
return new AccesssDept();
}
}
- 客户端
User user = new User();
IFactory factory = new SqlServerFactory();
IUser iUser = factory.createUser();
iUser.insert(user);
iUser.getUser(1);
IDept dept = factory.createDept();
dept.insert(new Dept());
dept.getDept(1);
Leader:“很好,这样子后面如果有很多表,也只需要多增加一些类就行了,只要能把代码灵活性提高,多增加一些类也没有啥问题。”
而且你这种代码结构实际是使用了一种抽象工厂模式:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
像上面我们的IUser和IDept其实就是两中产品的抽象,那么实际的SqlServerUser、SqlServerDept、AccessUser、AccessDept就是他们的具体分类的实现。IUser和IDept实际就是两种不同的产品分类,然后具体的产品实现由子类进行衍生。
IFactory类就是我们的抽象工厂类了,它里面应该包含所有的产品创建的抽象方法。那么SqlserverFactory和AccessFactory就是两种产品的工厂,具体生成哪种产品由它们来决定。
优缺点
优点:最大的好处是易于交换产品系列,由于具体工厂类,在一个应用中只需要在初始化的时候出现一次,这就使得改变一个应用的具体工厂变得非常容易,它只需要改变具体工厂即可使用不同的产品配置。
缺点:显而易见,每次如果有一个新的表,至少需要增加三个类,而且还需要改动至少三处。这样大批量的改动显然是非常丑陋的做法。
简单工厂改进抽象工厂
直接去除IFatory和子Factory,用DataAccess取而代之它们。
public class DataAccess {
private static String db = "Sqlserver";
public static IUser createUser() {
IUser res = null;
switch (db) {
case "Sqlserver":
res = new SqlServerUser();
break;
case "Access":
res = new AccessUser();
break;
}
return res;
}
public static IDept createDept() {
IDept res = null;
switch (db) {
case "Sqlserver":
res = new SqlServerDept();
break;
case "Access":
res = new AccesssDept();
break;
}
return res;
}
}
- 客户端
User user = new User();
IUser iUser = DataAccess.createUser();
iUser.insert(user);
iUser.getUser(1);
IDept dept = DataAccess.createDept();
dept.insert(new Dept());
dept.getDept(1);
客户端中没有出现一个Sqlserver或Access的字样,将耦合度降到最低。
Tips:在DataAccess类中db字段若不想硬编码到代码中,可以在项目资源文件夹中创建一个文件,读取文件的db属性,使用反射的方式动态生成需要的SqlserverUser或其他产品对象。