多数据库切换?设计模式--抽象工厂引导下思路

缘起

某日,部门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或其他产品对象。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值