15 抽象工厂模式

1 定义

提供一个创建一系列相关的接口,而无需指定它们具体的类

2 优点和缺点

2.1优点

易于交换产品系列,这是最大的优点。例如IFactory factory=new AccessFactory(),只在初始化的时候出现一次,这样改变一个应用的具体工厂变得很容易,只需要改变具体工厂即可使用不同的产品配置。如果现在要改变数据库访问,只需要修改具体工厂就行了

  • 让具体的创建过程和客户端分离,客户端是通过它们的抽象接口操纵实例产品的具体类名也被具体工厂的实现分离,不会出现在客户端代码中。

  • 开放-封闭原则依赖倒转原则的良好运用。其实,这一点和工厂模式一样。

2.2 缺点

通过第4章和5.2.1节,可知,如果要增加一个Project项目表,至少要添加3个类,IProject,SqlServerProject,AccessProject,还需要修改IFactory,SqlServerFactory,AccessFactory,极不方便。所以有了利用简单工厂改进抽象工厂

3 UML图–以切换数据库场景为例

IUser是对user表的操作接口,SqlServerUser和AccessUser实现了IUser接口。类图中没有画出User表对应的类和Department表对应的类。
在这里插入图片描述

4 例子–切换数据库场景

4.1 场景

数据有2个来源,SqlServer和Access;产品有2个,User和Department。
下面给出针对上述需求的代码,采用的设计模式演化过程为:

  • 抽象工厂模式
  • 简单工厂模式+抽象工厂模式
  • 反射+配置+抽象工厂模式

4.2 代码

4.2.1 代码(抽象工厂模式)

Main

public class Main {
    public static void main(String[] args) {
        User user=new User();
        //IFactory factory=new SqlServerFactory();
        IFactory factory=new AccessFactory();
        IUser iu=factory.createUser();

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

User

public class User  {
    private int id;
    private String name;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Department

public class Department  {
    private int id;
    private String deptName;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getDeptName() {
        return deptName;
    }

    public void setDeptName(String deptName) {
        this.deptName = deptName;
    }
}

IFactory

public interface IFactory {
    IUser createUser();
    IDepartment createDepartment();
}

SqlServerFactory

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

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

AccessFactory

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

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

IUser

public interface IUser {
    public void insert(User product);

    public User getUser(int id);
}

IDepartment

public interface IDepartment {
    public void insert(Department product);

    public Department getDepartment(int id);
}

SqlServerUser

public class SqlServerUser implements IUser {
    @Override
    public void insert(User product) {
        System.out.println("在sql server中给user表增加一条记录");
    }

    @Override
    public User getUser(int id) {
        System.out.println("在sql server中根据id得到user表一条记录");
        return null;
    }
}

SqlServerDepartment

public class SqlServerDepartment implements IDepartment {
    @Override
    public void insert(Department product) {
        System.out.println("在sql server中给Department表增加一条记录");
    }

    @Override
    public Department getDepartment(int id) {
        System.out.println("在sql server中根据id得到Department表一条记录");
        return null;
    }
}

AccessUser

public class AccessUser implements IUser {
    @Override
    public void insert(User product) {
        System.out.println("在access中给user表增加一条记录");
    }

    @Override
    public User getUser(int id) {
        System.out.println("在access中根据id得到user表一条记录");
        return null;
    }
}

AccessDepartment

public class AccessDepartment implements IDepartment {
    @Override
    public void insert(Department product) {
        System.out.println("在access中给Department表增加一条记录");
    }

    @Override
    public Department getDepartment(int id) {
        System.out.println("在access中根据id得到Department表一条记录");
        return null;
    }
}

4.2.2 代码(简单工厂+抽象工厂)

由上面的抽象工厂可知,如果要增加一个Project项目表,至少要添加3个类,IProject,SqlServerProject,AccessProject,还需要修改IFactory,SqlServerFactory,AccessFactory,极不方便。所以有了利用简单工厂改进抽象工厂。抛弃了IFactory,SqlServerFactory,AccessFactory,取而代之的是DataAccess类

Main

//注意:本文件夹中是:简单工厂+抽象工厂
public class Main {
    public static void main(String[] args) {
        User user=new User();

        //在DataAccess中内置了,用哪个数据库
        IUser iu=DataAccess.createUser();

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

DataAccess

//注意:本文件夹中是:简单工厂+抽象工厂
public class DataAccess {
    private static String db="SqlServer";
    //private static String db="Access";

    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;
    }
}

4.2.3 代码(反射+配置+抽象工厂)

如果要增加Oracle数据库访问的话,原来的抽象工厂只增加一个OracleFactory就可以,在简单工厂+抽象工厂中,需要在每个方法中的switch中添加相应的case。所以产生了反射+抽象工厂DataAccess类用反射代替IFactory,SqlServerFactory,AccessFactory

//反射核心
String className=path+"."+db+"User";
return  (IUser)Class.forName(className).newInstance();

反射需要根据字符串来决定使用哪个数据库,所以字符串要放在配置文件中,这样切换数据库的时候,只需要修改配置文件即可不需要重新部署、启动程序

Main

//注意:本文件夹中是:反射+抽象工厂
public class Main {
    public static void main(String[] args) throws Exception {
        User user=new User();

        //在DataAccess中内置了,用哪个数据库
        IUser iu=DataAccess.createUser();

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

配置文件 Appconfig.properties

//db=SqlServer
db=Access

AppConfigHelper

public class AppConfigHelper {

    private static AppConfigHelper appConfigHelper;
    private static Properties prop = new Properties();

    private AppConfigHelper() {
        readProperties();
    }

    private static AppConfigHelper getSingle() {
        if (appConfigHelper != null) {
            appConfigHelper = null;
        }
        appConfigHelper = new AppConfigHelper();
        return appConfigHelper;
    }

    public static String getDB() {
        getSingle();
        return prop.getProperty("db");
    }

    private void readProperties() {
        //这里filepath得到的是项目目录:F:\bupt\project\leetcode\
        //String filepath = System.getProperty("user.dir");

        //这里得到的是项目编译后的class目录:F:\bupt\project\leetcode\out\production\leetcode\
        String filepath = Thread.currentThread().getContextClassLoader().getResource("").getPath();
        // 将文件路径中含有的%20替换为空格,避免出现java.io.fileNotFoundException
        filepath = filepath.replaceAll("%20", " ");
        try {
            //这里补充完整AppConfig.properties的目录
            filepath += "designPattern/AbstractFactoryPattern/ReflectionAddAbsFac/";
            prop.load(new FileInputStream(filepath + "AppConfig.properties"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

DataAccess

//注意:本文件夹中是:反射+配置+抽象工厂
public class DataAccess {
    private static final String path="designPattern.AbstractFactoryPattern.AbsFactoryPattern";
    private static String db="SqlServer";

    private static void setDB() {
        db=AppConfigHelper.getDB();
    }

    //利用反射,根据字符串确定实例化哪个类
    public static IUser createUser() throws Exception{
        setDB();
        String className=path+"."+db+"User";
        return  (IUser)Class.forName(className).newInstance();
    }

    //利用反射,根据字符串确定实例化哪个类
    public static IDepartment createDepartment() throws Exception{
        setDB();
        String className=path+"."+db+"Department";
        return  (IDepartment)Class.forName(className).newInstance();
    }

}

5 工厂模式和抽象工厂模式的适用场景

  • 工厂模式 适合具体功能类ConcreteProductX只有一个系列的情况,例如只有一个User类和User操作类的时候。
  • 抽象工厂模式 适合一个具体功能类ConcreteProductX有至少两个系列的情况,现在有很多表,而sql server和access又是不同的分类,抽象工厂模式 适合解决涉及到多个产品系列的问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值