第15章 就不能不换DB吗?--抽象工厂模式

2018-07-11  本文已影响0人  落墨Zero

情境:在需要更改项目数据库时,代码尽量少的更改。

为了可以切换数据库,首先可以想到使用工厂方法模式,定义一个用于创建对象的接口,让子类决定实例化哪一个类。

代码如下

IUser接口,用于客户端访问,解除与具体数据库访问的耦合。

public interface IUser {

    void insert(User user);

}

SqlServerUser类,用于访问SqlServer的User

public class SqlServerUser implements IUser{

    @Override
    public void insert(User user){
        print("在SQL Server中给User表增加一条记录");
    }

}

AccessUser类,用于访问Access的User

public class AccessUser implements IUser{

    @Override
    public void insert(User user){
        print("在Access中给User表增加一条记录");
    }

}

IFactory接口,定义一个创建访问User表对象的工厂接口

public interface IFactory {

    IUser getUserFactory();

}

SqlServerFactory类,实现IFactory接口,实例化SqlServerUser

public class SqlServerFactory implements IFactory {
    @Override
    public IUser createUserFactory() {
        return new SqlServerUser();
    }
}

AccessFactory类,实现IFactory接口,实例化AccessUser

public class AccessFactory implements IFactory {
    @Override
    public IUser createUserFactory() {
        return new AccessUser();
    }
}

测试代码

public class Test {

    public static void main(String[] args) {
        User user = new User();
        //若要改成Access数据库,只需要将本句改成IFactory factory = new AccessFactory();
        IFactory factory = new SqlServerFactory();
        IUser userFactory = factory.createUserFactory();
        userFactory.insert(user);
    }

}

此时,由于多态的关系,使得声明IUser接口的对象userFactory事先根本不知道是在访问哪个数据库,却可以在运行时很好地完成工作,这就是所谓的业务逻辑与数据访问的解耦。
但是如果增加一个部门表(Department表),需要做下面的改动。

新增IDepartment接口,用于客户端访问,解除与具体数据库访问的耦合

public interface IDepartment {

    void insert(Department department);

}

新增SqlServerDepartment类,用于访问SqlServer的Department

public class SqlServerDepartment implements IDepartment{

    @Override
    public void insert(Department department){
        print("在SQL Server中给Department表增加一条记录");
    }

}

新增AccessDepartment类,用于访问Access的Department

public class AccessDepartment implements IDepartment{

    @Override
    public void insert(Department department){
        print("在Access中给Department表增加一条记录");
    }

}

修改IFactory接口

public interface IFactory {

    IUser createUserFactory();

    IDepartment createDepartmentFactory();
}

修改SqlServerFactory

public class SqlServerFactory implements IFactory {
    @Override
    public IUser createUserFactory() {
        return new SqlServerUser();
    }

    @Override
    public IDepartment createDepartmentFactory() {
        return new SqlServerDepartment();
    }
}

修改AccessFactory

public class AccessFactory implements IFactory {
    @Override
    public IUser createUserFactory() {
        return new AccessUser();
    }

    @Override
    public IDepartment createDepartmentFactory() {
        return new AccessDepartment();
    }
}

抽象工厂模式

抽象工厂模式(Abstract Factory),提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。[DP]

抽象工厂模式(Abstract Factory)结构图

抽象工厂模式结构图.png

AbstractProductA和AbstractProductB是两个抽象产品,之所以为抽象,是因为它们都有可能有两种不同的实现,就刚才的例子来说就是User和Department,而ProductA1、ProductA2和ProductB1、ProductB2就是对两个抽象产品的具体分类的实现,比如ProductA1可以理解为是SqlServerUser,而ProductB1是AccessUser。
IFactory是一个抽象工厂接口,它里面应该包含所有的产品创建的抽象方法。而ConcreteFactory1和ConcreteFactory2就是具体的工厂。就像SqlServerFactory和AccessFactory一样。
通常是在运行时刻再创建一个ConcreteFactory类的实例,这个具体的工厂再创建具有特定实现的产品对象,也就是说,为创建不同的产品对象,客户端应使用不同的具体工厂。

抽象工厂模式的优点与缺点

优点

1、易于交换产品系列,由于具体工厂类,在一个应用中只需要再初始化的时候出现一次,这就使得改变一个应用的具体工厂变得非常容易,它只需要改变具体工厂即可使用不同的产品配置。
2、它让具体的创建实例过程与客户端分离,客户端是通过他们的抽象接口操纵实例,产品的具体类名也被具体工厂的实现分离,不会出现在客户代码中。

缺点

每一个类的开始都要声明IFactory factory = new SqlServerFactory()新增功能,需要修改Factory相关类。

用简单工厂来改进抽象工厂

新增DataAccess类

public class DataAccess {

    private static final String db = "SqlServer";

    public static IUser createUser() {
        IUser result = null;
        switch (db) {
            case "SqlServer":
                result = new SqlServerUser();
                break;
            case "Access":
                result = new AccessUser();
                break;
        }
        return result;
    }

    public static IDepartment createDepartment() {
        IDepartment result = null;
        switch (db) {
            case "SqlServer":
                result = new SqlServerDepartment();
                break;
            case "Access":
                result = new AccessDepartment();
                break;
        }
        return result;
    }
}

测试代码

public class Test {

    public static void main(String[] args) {
        User user = new User();
        IUser userFactory = DataAccess.createUser();
        userFactory.insert(user);
    }

}

抛弃IFactory、SqlServerFactory、AccessFactory三个工厂类,取而代之的是DataAccess类,客户端没有出现任何一个SqlServer或Access的字样,达到解耦的目的。

用反射解决switch case

修改DataAccess类

public class DataAccess {

    private static final String fullName = "com.luomo.study.design.patten.absfactory.";

    private static final String db = "Access";

    public static IUser createUser() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        Class<?> cls = Class.forName(fullName+db+"User");
        return (IUser) cls.newInstance();
    }

    public static IDepartment createDepartment() throws IllegalAccessException, InstantiationException, ClassNotFoundException {
        Class<?> cls = Class.forName(fullName+db+"Department");
        return (IDepartment) cls.newInstance();
    }
}

所有在用简单工厂的地方,都可以考虑用反射技术来去除switch或if,解除分支判断带来的耦合。

上一篇下一篇

猜你喜欢

热点阅读