抽象工厂模式(Abstract Factory Pattern)

抽象工厂模式是一种创建型设计模式,提供了一个创建一系列相关或相互依赖对象的接口,无需指定它们的具体类。此模式隔离了具体类的生成,使得客户端可以使用工厂接口创建一组产品,无需关心产品的具体实现。模式的优点包括解耦客户端和具体产品,易于更换产品族,且符合开闭原则。缺点在于扩展新产品等级结构时较为困难。常见应用场景包括多数据库操作、前端页面主题切换等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

抽象工厂模式概述

抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在抽象工厂模式中,接口是负责创建一个相关对象的工厂,不需要显式指定它们的类。每个生成的工厂都能按照工厂模式提供对象。
抽象工厂模式提供了一种创建一系列相关或相互依赖对象的接口,而无需指定具体实现类。通过使用抽象工厂模式,可以将客户端与具体产品的创建过程解耦,使得客户端可以通过工厂接口来创建一族产品

介绍

目的: 提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
主要解决: 主要解决接口选择的问题。
何时使用: 系统的产品有多于一个的产品族,而系统只消费其中某一族的产品。
如何解决: 在一个产品族里面,定义多个产品。
关键代码: 在一个工厂里聚合多个同类产品。
优点:

  1. 抽象工厂模式隔离了具体类的生成,使得客户并不需要知道什么被创建。由于这种隔离,更换一个具体工厂就变得相对容易。所有的具体工厂都实现了抽象工厂中定义的那些公共接口,因此只需改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。另外,应用抽象工厂模式可以实现高内聚低耦合的设计目的,因此抽象工厂模式得到了广泛的应用。
  2. 当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象。这对一些需要根据当前环境来决定其行为的软件系统来说,是一种非常实用的设计模式。
  3. 增加新的具体工厂和产品族很方便,无须修改已有系统,符合“开闭原则”。

缺点:

  1. 在添加新的产品对象时,难以扩展抽象工厂来生产新种类的产品,这是因为在抽象工厂角色中规定了所有可能被创建的产品集合,要支持新种类的产品就意味着要对该接口进行扩展,而这将涉及到对抽象工厂角色及其所有子类的修改,显然会带来较大的不便。
  2. 开闭原则的倾斜性(增加新的工厂和产品族容易,增加新的产品等级结构麻烦)。

使用场景:

  1. 一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有类型的工厂模式都是重要的。
  2. 系统中有多于一个的产品族,而每次只使用其中某一产品族。
  3. 属于同一个产品族的产品将在一起使用,这一约束必须在系统的设计中体现出来。
  4. 系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现。

应用实例:

  1. 前端页面的一键换肤,整个页面所有元素的样式一起换。
  2. 对不同数据库的表进行curd操作,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。

名词解释:

  1. 产品等级结构: 产品等级结构即产品的继承结构,如一个抽象类是电视机,其子类有海尔电视机、海信电视机、TCL电视机,则抽象电视机与具体品牌的电视机之间构成了一个产品等级结构,抽象电视机是父类,而具体品牌的电视机是其子类。
  2. 产品族: 在抽象工厂模式中,产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品,如海尔电器工厂生产的海尔电视机、海尔电冰箱,海尔电视机位于电视机产品等级结构中,海尔电冰箱位于电冰箱产品等级结构中,海尔电视机、海尔电冰箱构成了一个产品族。

结构

四种角色

抽象工厂(Abstract Factory)
提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法 newProduct() 来创建产品。
具体工厂(ConcreteFactory)
主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
抽象产品(Product)
定义了产品的规范,描述了产品的主要特性和功能。
具体产品(ConcreteProduct)
实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。
抽象工厂模式通常涉及一族相关的产品,每个具体工厂类负责创建该族中的具体产品。客户端通过使用抽象工厂接口来创建产品对象,而不需要直接使用具体产品的实现类。

结构图


案例实现

案例描述

在一个项目中用到了两种不同的数据库mysql和access,需要可以随时切换数据库。

案例类图

代码实现

抽象产品角色

User 抽象产品类

public interface User {

    /**
     * 插入
     * @return Integer
     */
    Integer insert();

    /**
     * 删除
     * @return Integer
     */
    Integer delete();
}

Company 抽象产品类

public interface Company {
    /**
     * 插入
     *
     * @return Integer
     */
    Integer insert();

    /**
     * 删除
     *
     * @return Integer
     */
    Integer delete();

}

具体产品角色

access数据库
AccessUser user具体产品

public class AccessUser implements User {
    /**
     * 插入
     *
     * @return Integer
     */
    @Override
    public Integer insert() {
        System.out.println("Access\tuser\tinsert");
        return 11;
    }

    /**
     * 删除
     *
     * @return Integer
     */
    @Override
    public Integer delete() {
        System.out.println("Access\tuser\tdelete");
        return 12;
    }
}

AccessCompany company具体产品

public class AccessCompany implements Company {
    /**
     * 插入
     *
     * @return Integer
     */
    @Override
    public Integer insert() {
        System.out.println("Access\tcompany\tinsert");
        return 11;
    }

    /**
     * 删除
     *
     * @return Integer
     */
    @Override
    public Integer delete() {
        System.out.println("Access\tcompany\tdelete");
        return 12;
    }
}

mysql数据库
MysqlUser user具体产品

public class MysqlUser implements User {
    /**
     * 插入
     *
     * @return Integer
     */
    @Override
    public Integer insert() {
        System.out.println("Mysql\tuser\tinsert");
        return 21;
    }

    /**
     * 删除
     *
     * @return Integer
     */
    @Override
    public Integer delete() {
        System.out.println("Mysql\tuser\tdelete");
        return 22;
    }
}

MysqlCompany company具体产品

public class MysqlCompany implements Company {
    /**
     * 插入
     *
     * @return Integer
     */
    @Override
    public Integer insert() {
        System.out.println("Mysql\tcompany\tinsert");
        return 21;
    }

    /**
     * 删除
     *
     * @return Integer
     */
    @Override
    public Integer delete() {
        System.out.println("Mysql\tcompany\tdelete");
        return 22;
    }
}

抽象工厂角色

AbstractFactory 抽象工厂

public interface AbstractFactory {

    Company company();

    User user();
}

具体工厂角色

mysql具体工厂(MysqlFactory)

public class MysqlFactory implements AbstractFactory {
    
    @Override
    public Company company() {
        return new MysqlCompany();
    }

    @Override
    public User user() {
        return new MysqlUser();
    }
}

Access具体工厂(AccessFactory)

public class AccessFactory implements AbstractFactory {


    @Override
    public Company company() {
        return new AccessCompany();
    }


    @Override
    public User user() {
        return new AccessUser();
    }
}

demo调用

public class AbstractFactoryPattern {

    public static void main(String[] args) {
        demo1();
    }

    private static void demo1(){
        System.out.println(">>>>>>>>>>demo1<<<<<<<<<<");
        AbstractFactory factory = new AccessFactory();
        Company company = factory.company();
        User user = factory.user();
        company.insert();
        company.delete();
        user.insert();
        user.delete();
        factory = new MysqlFactory();
        company = factory.company();
        user = factory.user();
        company.insert();
        company.delete();
        user.insert();
        user.delete();
    }
}


关于优化

新增产品等级时使用泛型和反射优化工厂
新增产品等级时,抽象工厂和具体工厂都需要新增对于产品方法,修改地方很多,使用泛型避免修改工厂,只进行产品等级的新增
但是使用反射时需要确定的包名和类名,而且在调用方还需要认识抽象产品,待进一步优化
修改抽象工厂
使用泛型接收需要返回的类型

public interface AbstractFactory<T> {

    T getDb(String table);
}

修改具体工厂
使用反射,根据传入的参数创建对应的产品
MysqlFactory

public class MysqlFactory<T> implements AbstractFactory<T> {

    private final String path = "design.abstractfactorypattern.javademo.product.concreteproduct.mysql.Mysql";
    
    @Override
    public T getDb(String table) {
        table = firstUpperCase(table);
        try {
            return (T) Class.forName(path + table).getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    private static String firstUpperCase(String str) {
        char[] chars = str.toCharArray();
        if (97 <= chars[0] && chars[0] <= 122) {
            chars[0] ^= 32;
        }
        return String.valueOf(chars);
    }
}

AccessFactory

public class AccessFactory<T> implements AbstractFactory<T> {

    private final String path = "design.abstractfactorypattern.javademo.product.concreteproduct.access.Access";

    @Override
    public T getDb(String table) {
        table = firstUpperCase(table);
        try {
            return (T) Class.forName(path + table).getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    private static String firstUpperCase(String str) {
        char[] chars = str.toCharArray();
        if (97 <= chars[0] && chars[0] <= 122) {
            chars[0] ^= 32;
        }
        return String.valueOf(chars);
    }

}

demo调用测试

public class AbstractFactoryPattern {

    public static void main(String[] args) {
        demo2();
    }

    private static void demo2(){
        System.out.println(">>>>>>>>>>demo2<<<<<<<<<<");
        MysqlFactory<Company> mysqlFactory = new MysqlFactory<>();
        Company db = mysqlFactory.getDb("Company");
        db.insert();
        db.delete();
    }
}

客户端职责问题,结合简单工厂模式优化
使用简单工厂模式时,客户端不需要进行判断的操作,然而使用抽象工厂模式和工厂模式的时候,逻辑判断的操作回到了客户端,换言之,需要客户端决定实例化哪一个工厂,所以将抽象工厂和简单工厂结合起来,让逻辑判断的部分重新回到工厂中,此处继续使用反射来优化
去掉AbstractFactory、AccessFactory、MysqlFactory三个工厂,使用DbFactory工厂类来实现,创建对应的产品族

public class DbFactory {

    private static final String PATH = "design.abstractfactorypattern.javademo.product.concreteproduct.access.";
    private static final String PREFIX = "Access";
//    private final String path = "design.abstractfactorypattern.javademo.product.concreteproduct.mysql.";
//    private static final String PREFIX = "Mysql";

    public static Company company() {
        Company company = null;
        try {
            company = (Company) Class.forName(PATH + PREFIX + "Company").getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return company;
    }

    public static User user() {
        User user = null;
        try {
            user = (User) Class.forName(PATH + PREFIX + "User").getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return user;
    }
}

结合之后逻辑判断的职责回到了工厂中,只需要更换PATH和PREFIX就可以创建对应的产品族,但是在新增产品等级的时候,还是需要在DbFactory新增一个方法,所以同样使用泛型和反射解决

public class DbFactory<T> {

    private static final String PATH = "design.abstractfactorypattern.javademo.product.concreteproduct.access.";
    private static final String PREFIX = "Access";
//    private final String path = "design.abstractfactorypattern.javademo.product.concreteproduct.mysql.";
//    private static final String PREFIX = "Mysql";

    public T getDb(String table) {
        table = firstUpperCase(table);
        try {
            return (T) Class.forName(PATH + PREFIX + table).getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    private static String firstUpperCase(String str) {
        char[] chars = str.toCharArray();
        if (97 <= chars[0] && chars[0] <= 122) {
            chars[0] ^= 32;
        }
        return String.valueOf(chars);
    }
}

demo调用测试

public class AbstractFactoryPattern {

    public static void main(String[] args) {
        demo3();
    }

    private static void demo3() {
        System.out.println(">>>>>>>>>>demo3<<<<<<<<<<");
        Company company = DbFactory.company();
        company.insert();
        company.delete();
        User user = DbFactory.user();
        user.delete();
        user.insert();

        company = new DbFactory<Company>().getDb("Company");
        company.insert();
        company.delete();
        user = new DbFactory<User>().getDb("User");
        user.delete();
        user.insert();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值