设计模式系列之三:抽象工厂模式

本文涉及的产品
云数据库 RDS SQL Server,基础系列 2核4GB
RDS SQL Server Serverless,2-4RCU 50GB 3个月
推荐场景:
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
简介:

前言

在设计模式有三个模式是与工厂模式相关的,分别是:简单工厂模式、工厂方法模式以及抽象工厂模式。在前面的文章中已经谈到前面两种,这里就对抽象工厂模式介绍一下。抽象工厂模式就是提供一个创建一系列相关或者相互依赖的接口(也就是抽象类),而无需指定具体的类。简单来说,就是当我们需要创建一个具体的对象的时候,我们不必指定该具体的对象,只需要使用它的上层接口直接调用就行。好像还是很抽象哦,好吧,为了更清晰领悟这个设计模式,我们还是通过一个案例来说明

问题背景

某公司开发了一个A软件,数据库使用的是SQLServer。后由于客户要求需要使用Oracle数据库,原来的数据要迁移到Oracle中,在迁移的过程中遇到很多问题,比如语法错误,关键字滥用,函数不支持等问题。请设计一组程序,实现数据的无缝迁移。

简介

通过使用我们的工厂方法模式,我们已经较好的解决了用户使用不同数据库的问题,现在我们已经获得了要创建的数据库对象,那么接下来我们就需要向数据库中添加数据了,假设都需要添加部门信息,有的用户需要使用Oracle,有的用户需要使用SqlServer。考虑抽象工厂模式的代码结构:

抽象工厂模式结构图

抽象工厂模式实现

在修改代码结构之前,为了更好的对比,来看一下我们现在的代码结构:

初始类图

修改之前(基于抽象工厂模式)

原始代码v1.0:

public interface IFactory {
//创建数据库对象
DBObject createDBObject();
//创建部门
IDepartment createDepartment();
}

public class OracleFactory implements IFactory{
@Override
public DBObject createDBObject() {
    return new OracleObject();
}
@Override
public IDepartment createDepartment() {
    return new OracleDepartment();
}
}

public class SqlServerFactory implements IFactory{
@Override
public DBObject createDBObject() {
    return new SqlServerObejct();
}
@Override
public IDepartment createDepartment() {
    return new SqlServerDepartment();
}
}

public abstract class DBObject {
//添加用户的方法
public abstract void insertUser(User user);

//查找用户信息的方法
public abstract User findUserById(int id);
}

public class OracleObject extends DBObject{
@Override
public void insertUser(User user) {
    System.out.println("使用Oracle添加用户");
}
@Override
public User findUserById(int id) {
    System.out.println("使用Oracle通过id找到用户");
    return null;
}
}

public class SqlServerObejct extends DBObject{
@Override
public void insertUser(User user) {
    System.out.println("使用SQLServer添加用户");
}
@Override
public User findUserById(int id) {
    System.out.println("使用SQLServer通过id找到用户");
    return null;
}
}

public interface IDepartment {
//创建部门
void insertDept(Department dept);
//查找部门
Department findDeptById(int id);
}

public class OracleDepartment implements IDepartment {
@Override
public void insertDept(Department dept) {
    System.out.println("使用Oracle对象插入一条部门信息");
}
@Override
public Department findDeptById(int id) {
    System.out.println("使用Oracle对象查找id为" + id + "的部门信息");
    return null;
}
}

public class SqlServerDepartment implements IDepartment {
@Override
public void insertDept(Department dept) {
    System.out.println("使用SqlServer对象添加一条部门信息");
}
@Override
public Department findDeptById(int id) {
    System.out.println("使用SqlServer对象查找id为" + id + "的部门信息");
    return null;
}
}

//测试方法
public static void main(String[] args){
    IFactory factory = new OracleFactory();
    IFactory factory2 = new SqlServerFactory();

    IDepartment department = factory.createDepartment();
    IDepartment department2 = factory2.createDepartment();

    department.insertDept(dept);
    department.findDeptById(1);
    department2.insertDept(dept);
    department2.findDeptById(2);        
}

第一次修改(基于简单工厂模式)

根据抽象工厂模式结构图以及需求,修改代码如下:
1) 增加一个创建部门的抽象工厂,这里使用接口
2) 分别创建基于Oracle以及基于SqlServer的部门实现类(已经在上面的代码中实现)
3) 创建一个数据库工具类,根据条件创建需要的对象
修改的代码v1.1

public class DBAccess {
//这里固定DBTYPE的值,如果要使用其他数据库,修改这个字段的值就好
private static final String DBTYPE = "oracle";
public static DBObject createDBObject(){
    switch (DBTYPE){
        case "oracle":
            return new OracleObject();
        case "sqlserver":
            return new SqlServerObejct();
    }
    return null;
}

public static IDepartment createDepartment(){
    switch (DBTYPE) {
    case "oracle":
        return new OracleDepartment();
    case "sqlserver":
        return new SqlServerDepartment();
    }
    return null;
}
}
//测试方法
public static void main (String[] args){
    User user = new User();
    Department dept = new Department();

    //这里创建出来的DBObject就是上面DBTYPE指定值的数据库对象
    DBObject dbObject = DBAccess.createDBObject();
    dbObject.insertUser(user);
    dbObject.findUserById(1);

    IDepartment department = DBAccess.createDepartment();
    department.insertDept(dept);
    department.findDeptById(1);
}

这里通过DBAccess类直接替代了原来的IFactory、OracleFactory、SqlServerFactory三个类,这里实际上是简单工厂的实现方法,把判断逻辑转移到DBAccess中,客户端不需要做任何修改,要更换数据库只需要在DBAccess中把DBTYPE的值修改就ok。在这点上,好像简单工厂占优势,但是不尽然,如果要在程序中添加对MySQL数据库的支持,如果使用原来抽象工厂模式。只需要添加一个MySQLFactory类就行,现在就需要在DBAccess类中修改逻辑判断了(在这一点违背了封闭开放原则)。那么有没有一种方法,不要增加对switch的判断就可以根据DBTYPE的值创建相应的对象呢?也就是说,在程序运行期间,根据类的字符串表示创建类的实例。答案是肯定的,在Java中我们可以利用反射技术做到这一点,所以我们对v1.1版的代码进行进一步的修改:

第二次修改(基于反射技术)

v1.2

public class DBAccess {
    private static final String DBTYPE = "Oracle";
    public static DBObject createDBObject() throws ClassNotFoundException, InstantiationException, IllegalAccessException{
        return (DBObject) Class.forName("com.rhwayfun.GoF." + DBTYPE + "Object").newInstance();
    }

    public static IDepartment createDepartment() throws InstantiationException, IllegalAccessException, ClassNotFoundException{
        return (IDepartment) Class.forName("com.rhwayfun.GoF." + DBTYPE + "Department").newInstance();
    }
}

现在,如果需要增加对MySQL的支持只需要修改Oracle为MySQL就行,不过还是得创建MySQLObject类以及MySQLDepartment类,但是与上面简单工厂模式的修改不同,这里只是在原来代码的基础增加,是扩展而不是修改。所以没有违背开放-封闭原则,现在这版的代码比之前好多了,但是DBTYPE的值仍然不可避免要进行修改,事实上还有一种通过配置文件的方式可以保存原来的代码不变而实现这个功能,下面对代码进行第三次修改:

第三次修改(基于配置文件+反射技术)

首先创建db.properties配置文件

DBTYPE=Oracle

v1.3版代码

public class DBAccess {
    public static DBObject createDBObject() throws ClassNotFoundException, InstantiationException, IllegalAccessException, IOException{
        return (DBObject) Class.forName("com.rhwayfun.GoF." + readPro() + "Object").newInstance();
    }

    public static IDepartment createDepartment() throws InstantiationException, IllegalAccessException, ClassNotFoundException, IOException{
        return (IDepartment) Class.forName("com.rhwayfun.GoF." + readPro() + "Department").newInstance();
    }

    private static String readPro() throws IOException{
        Properties pro = new Properties();
        InputStream in = DBAccess.class.getClassLoader().getResourceAsStream("com/rhwayfun/GoF/db.properties");
        pro.load(in);
        return (String) pro.get("DBTYPE");
    }
}

客户端代码保持不变,实际测试成功。

抽象工厂模式小结

从v1.0到v1.3我们对工厂模式做一个简单的小结:

  • 所有使用简单工厂的都可以考虑使用反射技术加以优化
  • 抽象工厂的最大好处是便于交换产品系列,要创建不同的产品,我们可以使用不同的工厂创建。所以当我们需要不同产品线的产品的时候只需要换一个工厂就可以轻松实现
  • 抽象工厂模式与工厂方法模式把创建实例的过程与客户端分离,用户操作的只是抽象接口,而具体的创建过程则封装到了具体的工厂去实现。很好的体现了开放-封闭的原则
  • 抽象工厂模式与抽象工厂模式都存在一个缺点,就是需要增加新功能的时候,要创建的类很多。
  • 简单工厂模式可以通过反射技术克服上诉缺点,但使用反射会降低程序的性能,所以虽然使用反避免了创建多个类的麻烦,却也同时降低了程序的性能,所谓有得必有失。在实际的开发中还是需要仔细衡量的。
相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
6月前
|
设计模式 PHP
php设计模式--抽象工厂模式(二)
php设计模式--抽象工厂模式(二)
33 0
|
5月前
|
设计模式
**工厂模式与抽象工厂模式**都是创建型设计模式,用于封装对象创建,减少耦合
【6月更文挑战第23天】**工厂模式与抽象工厂模式**都是创建型设计模式,用于封装对象创建,减少耦合。工厂模式专注于单个对象,通过具体工厂创建具体产品,适用于简单对象创建;抽象工厂则关注一系列相关产品,提供创建一族对象的接口,适用于处理多个不兼容产品族。选择模式基于问题域的复杂性,单个产品需求时用工厂模式,多产品族时用抽象工厂模式。
32 5
|
6月前
|
设计模式 Java
【设计模式】JAVA Design Patterns——Abstract Factory(抽象工厂模式)
【设计模式】JAVA Design Patterns——Abstract Factory(抽象工厂模式)
|
6月前
|
设计模式 Java
Java一分钟之-设计模式:工厂模式与抽象工厂模式
【5月更文挑战第17天】本文探讨了软件工程中的两种创建型设计模式——工厂模式和抽象工厂模式。工厂模式提供了一个创建对象的接口,延迟实例化到子类决定。过度使用或违反单一职责原则可能导致问题。代码示例展示了如何创建形状的工厂。抽象工厂模式则用于创建一系列相关对象,而不指定具体类,但添加新产品可能需修改现有工厂。代码示例展示了创建颜色和形状的工厂。根据需求选择模式,注意灵活性和耦合度。理解并恰当运用这些模式能提升代码质量。
59 2
|
2月前
|
设计模式 Java
Java设计模式-抽象工厂模式(5)
Java设计模式-抽象工厂模式(5)
|
6月前
|
设计模式 Java
【设计模式系列笔记】抽象工厂模式
抽象工厂模式(Abstract Factory Pattern)是一种设计模式,属于创建型模式之一。它提供了一种方式来创建一系列相关或相互依赖的对象,而无需指定它们具体的类。抽象工厂模式通过引入抽象的工厂接口,使得客户端代码可以使用抽象的接口来创建一组相关的产品,而不关心这些产品的具体实现。
212 4
|
3月前
|
设计模式 Java
Java 设计模式之谜:工厂模式与抽象工厂模式究竟隐藏着怎样的神奇力量?
【8月更文挑战第30天】在Java编程中,设计模式为常见问题提供了高效解决方案。工厂模式与抽象工厂模式是常用的对象创建型设计模式,能显著提升代码的灵活性、可维护性和可扩展性。工厂模式通过定义创建对象的接口让子类决定实例化哪个类;而抽象工厂模式则进一步提供了一个创建一系列相关或相互依赖对象的接口,无需指定具体类。这种方式使得系统更易于扩展和维护。
37 1
|
3月前
|
设计模式 XML 存储
【三】设计模式~~~创建型模式~~~抽象工厂模式(Java)
文章详细介绍了抽象工厂模式,这是一种创建型设计模式,用于提供一个接口以创建一系列相关或相互依赖的对象,而不指定它们具体的类。通过代码示例和结构图,文章展示了抽象工厂模式的动机、定义、结构、优点、缺点以及适用场景,并探讨了如何通过配置文件和反射机制实现工厂的动态创建。
【三】设计模式~~~创建型模式~~~抽象工厂模式(Java)
|
3月前
|
设计模式 Java C语言
设计模式-----------工厂模式之抽象工厂模式(创建型)
抽象工厂模式是一种创建型设计模式,它提供了一个接口用于创建一系列相关或相互依赖的对象,而无需指定具体类,从而增强了程序的可扩展性并确保客户端只使用同一产品族的产品。
设计模式-----------工厂模式之抽象工厂模式(创建型)
|
3月前
|
设计模式 存储 XML
[设计模式]创建型模式-抽象工厂模式
[设计模式]创建型模式-抽象工厂模式

热门文章

最新文章