设计模式学习:抽象工厂模式

文章介绍了抽象工厂模式在数据库如Sqlserver和Access之间切换的场景,以及如何通过该模式实现对user和department表的操作。当需要更换数据库或添加新的表操作时,需要扩展相应的接口和实现类。文章还提到通过简单工厂模式和配置文件进一步优化,减少了代码的修改,但仍有违背开闭原则的问题。
摘要由CSDN通过智能技术生成

抽象工厂模式:

抽象工厂模式(Abstract Factory)是以一个超级工厂创建其他工厂。它属于创建型模式。抽象工厂模式是工厂方法模式的升级版本,他用来创建一组相关或者相互依赖的对象。他与工厂方法模式的区别就在于,工厂方法模式针对的是一个产品等级结构;而抽象工厂模式则是针对的多个产品等级结构。

场景:原来的项目是一个user表并且使用的Sqlserver数据库,现要在换成使用Access数据库该怎么办?如果要添加一个department表又该怎么办?

类图:

 

demo:

public interface IUser {
    void Insert(User user);
    User getUser(int id);
}
//新增一个Idepartment接口
public interface IDepartment {
    void Insert(Department department);
    Department GetDepartment(int id);
}
public class SqlServerUser implements IUser{
    @Override
    public void Insert(User user) {
        System.out.println("SqlServerUser insert User");
    }

    @Override
    public User getUser(int id) {
        System.out.println("SqlServerUser get user from id == " + id);
        return null;
    }
}
//新增sqlserver对department表的操作
public class SqlServerDepartment implements IDepartment{
    @Override
    public void Insert(Department department) {
        System.out.println("AccessDepartment insert Department");
    }

    @Override
    public Department GetDepartment(int id) {
        System.out.println("AccessDepartment get department from id == " + id);
        return null;
    }
}
//新增Access数据库对user表的操作
public class AccessUser implements IUser{
    @Override
    public void Insert(User user) {
        System.out.println("AccessUser Insert user ");
    }

    @Override
    public User getUser(int id) {
        System.out.println("AccessUser get user from id == " + id);
        return null;
    }
}
//Access对department的操作
public class AccessDepartment implements IDepartment{
    @Override
    public void Insert(Department department) {
        System.out.println("AccessDepartment insert Department");
    }

    @Override
    public Department GetDepartment(int id) {
        System.out.println("AccessDepartment get department from id == " + id);
        return null;
    }
}
public interface IFactory {
    IUser CreateUser();
    //新增createDepartment()
    IDepartment CreateDepartment();
}
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();
    }
}
public class Main {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
       
        //方法1:
        User user = new User();
        //IFactory factory = new SqlServerFactory();
        IFactory factory = new AccessFactory();
        IUser iUser =  factory.CreateUser();
        iUser.Insert(user);

        Department department = new Department();
        IDepartment iDepartment = factory.CreateDepartment();
        iDepartment.Insert(department);

        //方法2
        User user2 = new User();
        IUser iUser2 = DataAccess.CreateUser().newInstance();
        iUser.Insert(user);
        iUser.getUser(1);
    }
}

先看主函数中的方法1:

换成Access数据库只需把SqlServerFactory改成AccessFactory即可

添加department表的操作只需通过工厂获取Idepartment对象即可对department表进行操作

优点:

1.易于交换产品系列,由于具体工厂,例如IFactory factory = new AccessFactory();在一个应用中只需要在初始化的时候出现一次,这就使得改变一个应用的具体工厂变得非常容易,只需要改变具体的工厂即可使用不同的产品配置。(抽象工厂模式将具体产品的创建延迟到具体工厂的子类中,这样将对象的创建封装起来,可以减少客户端与具体产品类之间的依赖,从而使系统耦合度低,这样更有利于后期的维护和扩展)

2.让具体的创建实例过程与客户端分离,客户端是通过它们的抽象接口操作实例,产品的具体类名也被具体工厂的实现分离,不会出现在客户端代码。(抽象工厂模式很难支持新种类产品的变化。这是因为抽象工厂接口中已经确定了可以被创建的产品集合,如果需要添加新产品,此时就必须去修改抽象工厂的接口,这样就涉及到抽象工厂类的以及所有子类的改变,这就违背了"开闭原则")

缺点:虽然方便切换两个数据库访问的代码,但是新增功能,比如新增Project表,则需至少增加三个类IProject,SqlServerProject,AccessProject,还需要更改IFactory,SqlServerFactory和AccessFactory需要大量改动代码

接下来看主函数的方法2:

在以上缺点上进行了改进

 创建DataAccess.java

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Properties;

public class DataAccess {
    private static String db = "Sqlserver";
    //private static final String db = "Access";

    public static Class<IUser> CreateUser() throws ClassNotFoundException {
        db = getProperties_2("src/resource/App.properties", "db.databasename");
        String classname = db + "User";
        System.out.println("classname==" + classname);
        Class<?> result = null;
        result = Class.forName(classname);
        /*switch (db) {
            case "Sqlserver":
                //result = new SqlServerUser();
                result = Class.forName("SqlServerUser");
                break;
            case "Access":
                //result = new AccessUser();
                result = Class.forName("AccessUser");
            default:
                break;
        }*/
        return (Class<IUser>) result;
    }

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

    //此方法最好单独写在一个类中,在此类中违背了单一职责原则。
    public static String getProperties_2(String filePath, String keyWord){
         Properties prop = new Properties();
         String value = null;
         try {
                 // 通过输入缓冲流进行读取配置文件
                 InputStream InputStream = new BufferedInputStream(new FileInputStream(new File(filePath)));
                 // 加载输入流
                 prop.load(InputStream);
                 // 根据关键字获取value值
                 value = prop.getProperty(keyWord);
             } catch (Exception e) {
                 e.printStackTrace();
             }
         return value;
     }
}

 在CreateApartment中使用了简单工厂模式,使客户端解藕,抛弃了IFactory,SqlServerFactory和AccessFactory。但是有功能新增时需要在DataAccess中新增case,违背了开闭原则。

以CreateUser为例在此基础上继续升级:

 创建APP.properties配置数据库文件

db.databasename=SqlServer

通过配置文件来读取数据库,更换数据库时只需修改配置文件,同时读取配置文件的信息,根据信息通过放射的方式来实例化SqlServerUser类,这样就可以根据字符串来动态的选择创建的对象,并且去除了switch的判断

类图:

附上拓展知识储备:

大部分内容来自《大话设计模式》

Java 反射详解和使用_AnWen03的博客-CSDN博客JAVA数据库使用配置文件建立MYSQL连接_创建数据库配置文件_bobyao123456的博客-CSDN博客

Java 读取 .properties 配置文件的几种方式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值