Java实现抽象工厂模式

15.3 用工厂方法模式的数据访问程序

现在想用不同的数据库连接,获得数据,但是每个数据库连接的方法可能有些不同。

想用工厂方法兼容不同的数据库。

现在想在User表获得记录和添加记录。SqlServer和Access都有user表。

这里写图片描述

User接口,用户客户端访问,解除和具体数据库的耦合

public interface IUser {
    void insert(User user);

    User getUser(int id);
}

SqlserverUser类,用于访问SQL server的user

public class SqlserverUser implements IUser {

    void insert(User user){
        println("在SQL server中给user加条记录");
    }

    User getUser(int id){
        println("根据ID获得USER表中一条记录");
        return null;
    }
}

AccessUser类也是类似的。

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

public interface IFactory {
    IUser createUser();
}

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

public class SqlServerFactory implements IFactory {

    public IUser createUser(){
        return new SqlserverUser();
    }
}

AccessFactory类也是类似的。

客户端代码

扫描二维码关注公众号,回复: 870746 查看本文章
public class Main {
    public static void main(String[] args){
        User user = new User();

        //如果是Access数据库,则改成new AccessFactory();
        IFactory factory = new SqlServerFactory();

        IUser iu = factory.createUser();

        iu.insert(user);
        iu.getUser(1);
    }
}

问题
但是现在数据库里不可能只有一个User表,很可能有其他表,比如部门表。那就需要增加好多类了。

15.4 用抽象工厂模式的数据访问程序

这里写图片描述

//接口
public interface IDepartment {

    void insert(Department department);

    User getUser(int id);
}

//Sqlserver的实现
class SqlserverDepartment implements IDepartment {

    public void insert(Department department){
         println("在SQL server中给department加条记录");
    }

    public User getUser(int id){
         println("根据ID获得department表中一条记录");
        return null;
    }
}

//IFactory接口中增加一个接口方法
public interface IFactory {
    IUser createUser();

    IDepartment createDepartment();
}

//SqlServerFactory类,实例化createDepartment方法
public class SqlServerFactory implements IFactory {

    public IUser createUser(){
        return new SqlserverUser();
    }

    public IDepartment createDepartment(){
        return new SqlserverDepartment();
    }
}

只有一个Uuser类和User操作类的时候,是只需要工厂方法模式的,但是现在数据库有很多表而数据库也有不同的,所以解决这种涉及多个产品系列的问题,就用抽象工厂模式

15.5 抽象工厂模式

提供一个创建一系列相关或相互依赖对象的接口,而不需指定它们具体的类。
这里写图片描述
AbstractProductA和AbstractProductB是两个抽象产品,因为下面可能有多种不同的实现,就像刚才的User和Department,而不同的ProductA1,ProductA2等等,就是抽象产品的具体分类实现。例如SqlserverUser理解为ProductA1,AccessUser是ProductB1。

IFactory是一个抽象工厂接口,里面应包含所有的产品创建的抽象方法。

ConcreteFactory1和ConcreteFactory2就是具体工厂,就像SqlserverFactory和AccessFactory一样。

运行时再创建一个ConcreteFactory类的实例,为创建不同的产品对象,客户端应使用不同的具体工厂。

15.6 抽象工厂模式的优点和缺点

优点

  1. 易于交换产品系列,具体工厂类,例如IFactory factory = new AccessFactory(),在一个应用中只需在初始化出现一次,使得改变一个应用的具体工厂变得很容易。
  2. 让具体的创建实例过程和客户端分离,客户端只是通过它们的抽象接口操作实例,产品的具体类名也被具体工厂的实现分离,不会出现在客户代码。

缺点

  1. 如果增加一个产品的话,例如又增加项目表Project。要增加很多类,AbstractProduct和ConcreteProduct。还要修改类Factory,ConcreteFactory。很麻烦
  2. 在客户端如果我有100个ConcreteFactory,可能就要调用100次。

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

去除IFactory,SqlserverFactory和AccessFactory三个工厂,用DataAccess类取代,用简单工厂模式来实现。

这里写图片描述

public class DataAccess {
    //数据库名称,可替换成Access
    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;
            default:break;
        }
        return result;
    }

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

客户端代码

public class Main {
    public static void main(String[] args){
        User user = new User();
        Department dept = new Department();

        //直接得到实际数据库访问实例,不存任何依赖
        IUser iu = DataAccess.createUser();

        iu.insert(user);
        iu.getUser(1);

        //...同Department
    }
}

问题
如果增加一个ConcreteFactory,本来抽象工厂增加一个具体工厂类只需要实现一下就好了,现在就需要在DataAccess类中每个方法的switch加case

15.8 用反射+抽象工厂的数据访问程序

将DataAccess中使用switch的部分,都替换成通过反射获取。

这里写图片描述

public class DataAccess {
    //数据库名称,可替换成Access
    private static final String db = "Sqlserver";

    public static IUser createUser(){
        //SqlserverUser
        String className = "com.wt.design.product."+ db + "User";
        Class iuserClass = Class.forName(className); 
        return (IUser)iuserClass.newInstance();
    }

    public static IDepartment createDepartment() {
        //...
    }
}

如果有新的表Project呢?增加Project相关表,再修改DataAccess,在其中增加一个public static IProject createProject()方法就可以了。

DataAccess中的db变量(表示用哪个数据库的),可以通过设置properties属性来改进成可动态修改的即可。

猜你喜欢

转载自blog.csdn.net/a464700300/article/details/79901390