【设计模式系列】抽象工厂模式

一、什么是抽象工厂模式

抽象工厂模式(Abstract Factory Pattern)是一种创建型设计模式,++它提供了一个接口,用于创建一系列相关或相互依赖的对象,而无需指定它们具体的类++。这种模式允许客户端使用抽象的接口来创建一组相关产品,而不需要知道或关心实际生产出的具体产品是什么,从而实现客户端与具体产品的解耦。

二、抽象工厂模式的角色

  1. 抽象产品族(Abstract Product)

    • 这是一组相关的抽象类或接口,它们定义了可能被创建的对象的公共接口。
    • 这些抽象产品通常包含在一个或多个产品等级结构中,每个产品等级结构代表一类产品。
  2. 具体产品(Concrete Product)

    • 这些是抽象产品的实现类,它们提供了抽象产品接口的具体实现。
    • 每个具体产品都是抽象产品接口的具体类,代表一个具体的对象。
  3. 抽象工厂(Abstract Factory)

    • 这是一个接口,它声明了一组方法,用于创建一系列相关的抽象产品。
    • 它不实现这些方法,而是将实现推迟到子类中。
    • 抽象工厂定义了可以被创建的产品集合,但不定义具体如何创建这些产品。
  4. 具体工厂(Concrete Factory)

    • 这些类实现了抽象工厂接口,并提供了具体工厂方法来创建具体的产品对象。
    • 每个具体工厂都对应一个产品族,它负责创建该族中的具体产品。
    • 具体工厂封装了具体产品的创建逻辑,客户端可以通过具体工厂来创建所需的产品,而无需知道具体的创建细节。

三、抽象工厂模式的典型应用场景

  1. 多系列产品创建: 当一个系统需要创建多个系列的相关产品,并且这些产品之间存在相互依赖关系时,使用抽象工厂模式可以确保客户端始终使用同一个系列的产品。

四、抽象工厂模式在Calendar中的应用

在抽象工厂模式中,Calendar 类可以被视为一个抽象工厂,它提供了创建各种具体日历实例的接口。GregorianCalendarBuddhistCalendarCalendar 类的具体实现,代表不同的日历系统。下面详细展开 Calendar 在抽象工厂模式中的应用,并给出包含 BuddhistCalendarGregorianCalendar 的代码示例。

概念解释

  • 抽象产品族(Abstract Product Family):在日历系统中,这可以是日期和时间的表示方式,包括年、月、日等。

  • 抽象产品(Abstract Product)Calendar 类本身,它定义了所有具体日历类必须实现的接口,如 getTime()setTime(Date date)get(int field) 等。

  • 具体产品(Concrete Product)GregorianCalendarBuddhistCalendar 是具体的产品类,它们实现了 Calendar 抽象类,并提供了特定日历系统的具体实现。

  • 抽象工厂(Abstract Factory)Calendar 类也可以被视为一个抽象工厂,因为它的静态方法 getInstance() 可以根据传入的 Locale 参数返回不同的 Calendar 实例。

  • 具体工厂(Concrete Factory) :在这个模式中,具体工厂由 JDK 内部实现,它根据 Locale 参数决定实例化 GregorianCalendar 还是 BuddhistCalendar

java 复制代码
//抽象工厂类
public abstract class Calendar {
    public static class Builder {
        public Builder setDate(int year, int month, int dayOfMonth) {
            return setFields(YEAR, year, MONTH, month, DAY_OF_MONTH, dayOfMonth);
        }

        public Builder setTimeOfDay(int hourOfDay, int minute, int second) {
            return setTimeOfDay(hourOfDay, minute, second, 0);
        }
    }

    public static Calendar getInstance()
    {
        return createCalendar(TimeZone.getDefault(), Locale.getDefault(Locale.Category.FORMAT));
    }

    public int getWeeksInWeekYear() {
        throw new UnsupportedOperationException();
    }
}

//具体工厂类
public class GregorianCalendar extends Calendar {
    @Override
    public int getWeeksInWeekYear() {
        GregorianCalendar gc = getNormalizedCalendar();
        int weekYear = gc.getWeekYear();
        if (weekYear == gc.internalGet(YEAR)) {
            return gc.getActualMaximum(WEEK_OF_YEAR);
        }

        // Use the 2nd week for calculating the max of WEEK_OF_YEAR
        if (gc == this) {
            gc = (GregorianCalendar) gc.clone();
        }
        gc.setWeekDate(weekYear, 2, internalGet(DAY_OF_WEEK));
        return gc.getActualMaximum(WEEK_OF_YEAR);
    }
}

//具体工厂类
public class BuddhistCalendar extends GregorianCalendar {

}

// 客户端
public class CalendarAbstractFactoryDemo {

    public static void main(String[] args) {
        // 创建一个默认的 GregorianCalendar 实例
        Calendar defaultCalendar = Calendar.getInstance();
        printCalendarInfo(defaultCalendar, "Default Calendar");

        // 创建一个 BuddhistCalendar 实例,用于泰国
        Locale thailandLocale = new Locale("th", "TH");
        Calendar thaiCalendar = Calendar.getInstance(thailandLocale);
        printCalendarInfo(thaiCalendar, "Thai Buddhist Calendar");
    }

    private static void printCalendarInfo(Calendar calendar, String calendarType) {
        // 设置日期
        calendar.setTime(new Date());
        System.out.println(calendarType + " - Year: " + calendar.get(Calendar.YEAR));
        System.out.println(calendarType + " - Month: " + calendar.getDisplayName(Calendar.MONTH, Calendar.LONG, Locale.ENGLISH));
        System.out.println(calendarType + " - Day: " + calendar.get(Calendar.DAY_OF_MONTH));
        System.out.println();
    }
}

在这个示例中:

  • Calendar.getInstance() 方法根据提供的 Locale 参数决定创建哪种具体的 Calendar 实例。如果没有指定 Locale,它通常会创建一个 GregorianCalendar 实例,这是大多数地区的默认日历系统。
  • 当我们传递泰国的 Locale 对象给 Calendar.getInstance() 方法时,它返回一个 BuddhistCalendar 实例,这是泰国使用的日历系统。
  • printCalendarInfo() 方法打印日历的详细信息,包括年、月、日。它展示了如何使用 Calendar 实例来获取日期信息。

这个例子展示了 Calendar 类如何作为一个抽象工厂,根据不同的 Locale 参数创建不同的日历实例。这种方式使得客户端代码能够灵活地根据不同的需求创建适当的日历对象,而不需要了解具体的日历实现细节。这种设计符合抽象工厂模式的原则,提供了一个创建一系列相关或相互依赖对象的接口,而不需要指定它们具体的类。

五、简单工厂模式与复杂工厂模式的区别

简单工厂模式和抽象工厂模式都是创建型设计模式,用于创建对象,但它们在解决问题的范围和方式上存在明显差异:

简单工厂模式

  • 解决问题的范围:简单工厂模式主要用于创建单一类型的产品。它适用于场景中对象的创建逻辑相对简单,或者需要创建的对象种类较少的情况。
  • 核心思想:简单工厂模式通过一个工厂类包含一个或多个方法,根据传入的参数来决定创建并返回哪种具体产品的实例。

抽象工厂模式

  • 解决问题的范围:抽象工厂模式用于创建多个产品族中相关或相互依赖的产品对象。它适用于场景中需要创建一系列相关或相互依赖的产品,且这些产品属于不同的产品等级结构。
  • 核心思想:抽象工厂模式定义了一个接口,用于创建一系列相关的产品,而不需要指定它们的具体类。具体工厂类实现了这个接口,并提供了创建具体产品的方法。
相关推荐
维天说24 分钟前
CLI-Switch 2026年3月版历史设计:Hook、TTY 隔离与 JSON 状态
java·服务器·json
Zane19941 小时前
并发 vs 并行:别再傻傻分不清了,一文讲透 Java 并发编程的第一课
java·后端
杨充2 小时前
11.DDD与战术建模
设计模式·开源·代码规范
杨充2 小时前
12.综合实战图片框架
设计模式·开源·代码规范
噢,我明白了2 小时前
java中Excel的导入和导出(EasyExcel)
java·开发语言·excel
吠品2 小时前
Zabbix Web界面误报Server未运行的排查与解决
java·服务器·数据库
啊湘2 小时前
天气查询API接口 按月Token鉴权 实时天气 物联网可用 文档齐全
java·后端·struts
Java内核笔记2 小时前
告别十亿美元的错误 : Spring Boot 4 空安全 (JSpecify) 实战
java·spring boot·后端
糖果店的幽灵3 小时前
langgraph的 MessagesState 解读
java·开发语言·人工智能·windows·langgraph
极光代码工作室4 小时前
基于SpringBoot的在线博客系统
java·springboot·web开发·后端开发