根据JDK源码Calendar来看工厂模式和建造者模式

本文涉及的产品
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
实时计算 Flink 版,5000CU*H 3个月
实时数仓Hologres,5000CU*H 100GB 3个月
简介: 根据JDK源码Calendar来看工厂模式和建造者模式

Calendar是jdk提供的日历类。

public class Main {

  public static void main(String[] args) {
  
    Calendar calendar = Calendar.getInstance();
    
    int year = calendar.get(Calendar.YEAR);
    // 取月份要加1
    int month = calendar.get(Calendar.MONTH) + 1;
    int day = calendar.get(Calendar.DAY_OF_MONTH);
    int hour = calendar.get(Calendar.HOUR_OF_DAY);
    int minute = calendar.get(Calendar.MINUTE);
    int seconds = calendar.get(Calendar.SECOND);
    // 1-7分别代表 -- 星期日,星期一,星期二,星期三,星期四,星期五,星期六
    int week = calendar.get(calendar.DAY_OF_WEEK);

    // 年-月-日
    System.out.println("year = " + year);
    System.out.println("month = " + month);
    System.out.println("day = " + day);
    //时-分-秒
    System.out.println("hour = " + hour);
    System.out.println("minute = " + minute);
    System.out.println("seconds = " + seconds);   
    
    // 星期
    System.out.println("week = " + week);

  }
}
Calendar instance = Calendar.getInstance();
        int year = instance.get(Calendar.YEAR);
        int minute = instance.get(Calendar.MINUTE);
        System.out.println("当前年份-->"+year);
        System.out.println("当前分钟-->"+minute);

工厂模式

  Calendar instance = Calendar.getInstance();
     image.png     
根据zone 和 locale 创建一个Calendar子对象,但是具体的实现细节和创建了哪个子对象调用者无需关心,完全封装在了工厂的方法中
private static Calendar createCalendar(TimeZone zone,
                                           Locale aLocale)
    {
        CalendarProvider provider =
            LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale)
                                 .getCalendarProvider();
        if (provider != null) {
            try {
                return provider.getInstance(zone, aLocale);
            } catch (IllegalArgumentException iae) {
                // fall back to the default instantiation
            }
        }

        Calendar cal = null;

        if (aLocale.hasExtensions()) {
            String caltype = aLocale.getUnicodeLocaleType("ca");
            if (caltype != null) {
                switch (caltype) {   // 根据情况不同来工厂决定创建不同的对象
                case "buddhist":
                cal = new BuddhistCalendar(zone, aLocale);
                    break;
                case "japanese":
                    cal = new JapaneseImperialCalendar(zone, aLocale);
                    break;
                case "gregory":
                    cal = new GregorianCalendar(zone, aLocale);
                    break;
                }
            }
        }
        if (cal == null) {
            // If no known calendar type is explicitly specified,
            // perform the traditional way to create a Calendar:
            // create a BuddhistCalendar for th_TH locale,
            // a JapaneseImperialCalendar for ja_JP_JP locale, or
            // a GregorianCalendar for any other locales.
            // NOTE: The language, country and variant strings are interned.
            if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
                cal = new BuddhistCalendar(zone, aLocale);
            } else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"
                       && aLocale.getCountry() == "JP") {
                cal = new JapaneseImperialCalendar(zone, aLocale);
            } else {
                cal = new GregorianCalendar(zone, aLocale);
            }
        }
        return cal;
    }

Calendar calendar = Calendar.getInstance();getInstance() 方法进去:

建造者


从以上分析,Calendar使用了工厂模式,根据不同的情况生产不同的对象,那么这些对象是怎么被生产出来的呢。

答案就是:建造者模式
建造者模式,这里将建造者的类为原始类的内部类 使用建造者模式来设置很多的参数,根据对应的参数来建造不同的对象

image.png

public static class Builder {
    private static final int NFIELDS = FIELD_COUNT + 1; // +1 for WEEK_YEAR
    private static final int WEEK_YEAR = FIELD_COUNT;

    private long instant;
    // Calendar.stamp[] (lower half) and Calendar.fields[] (upper half) combined
    private int[] fields;
    // Pseudo timestamp starting from MINIMUM_USER_STAMP.
    // (COMPUTED is used to indicate that the instant has been set.)
    private int nextStamp;
    // maxFieldIndex keeps the max index of fields which have been set.
    // (WEEK_YEAR is never included.)
    private int maxFieldIndex;
    private String type;
    private TimeZone zone;
    private boolean lenient = true;
    private Locale locale;
    private int firstDayOfWeek, minimalDaysInFirstWeek;

    public Builder() {
    }

    public Builder setInstant(long instant) {
        if (fields != null) {
            throw new IllegalStateException();
        }
        this.instant = instant;
        nextStamp = COMPUTED;
        return this; //返回自身,链式调用
    }

    public Builder setInstant(Date instant) {
        return setInstant(instant.getTime()); // NPE if instant == null
    }

这里只贴出了部分set方法,下面还有很多的set方法就不展示了。


public Calendar build() {
            if (locale == null) {
                locale = Locale.getDefault();
            }
            if (zone == null) {
                zone = TimeZone.getDefault();
            }
            Calendar cal;
            if (type == null) {
                type = locale.getUnicodeLocaleType("ca");
            }
            if (type == null) {
                if (locale.getCountry() == "TH"
                    && locale.getLanguage() == "th") {
                    type = "buddhist";
                } else {
                    type = "gregory";
                }
            }
            switch (type) {
            case "gregory":
                cal = new GregorianCalendar(zone, locale, true);
                break;
            case "iso8601":
                GregorianCalendar gcal = new GregorianCalendar(zone, locale, true);
                // make gcal a proleptic Gregorian
                gcal.setGregorianChange(new Date(Long.MIN_VALUE));
                // and week definition to be compatible with ISO 8601
                setWeekDefinition(MONDAY, 4);
                cal = gcal;
                break;
            case "buddhist":
                cal = new BuddhistCalendar(zone, locale);
                cal.clear();
                break;
            case "japanese":
                cal = new JapaneseImperialCalendar(zone, locale, true);
                break;
            default:
                throw new IllegalArgumentException("unknown calendar type: " + type);
            }


很清晰:使用了建造者,根据不同的属性来构造一个复杂的对象。
     image.png     

why

为什么既然使用了工厂模式来创建对象,那么又进一步的使用建造者模式呢?
我觉得两者并不冲突,工厂模式可以选择创建什么对象,建造者模式可以进一步选择创建对象的属性。
一个经典的例子:去餐厅吃饭,根据口味选择了干锅,但是对于干锅来说也可以选择哪些配菜。
对于Calendar 而言,根据不同的type来创建了不同的Calendar子类。根据Set方法来定制化参数。

反思

对于设计模式而言,并不是一味的死板。并不是设计模式的模型是一层不变的, 工厂模式就一定要Factory,只是一种思想。多种设计模式也可以混用在一起。

值得思考的是为什么要有设计模式,为什么要这么设计,这么设计可以解决什么问题。而不是工厂模式是什么样,建造者模式是什么样。照着抄的话就过于死板。



相关文章
|
6月前
|
安全 前端开发 Java
JDK源码级别彻底剖析JVM类加载机制
JDK源码级别彻底剖析JVM类加载机制
|
6月前
|
缓存 Dubbo Java
趁同事上厕所的时间,看完了 Dubbo SPI 的源码,瞬间觉得 JDK SPI 不香了
趁同事上厕所的时间,看完了 Dubbo SPI 的源码,瞬间觉得 JDK SPI 不香了
|
3月前
|
算法 安全 Java
深入JDK源码:揭开ConcurrentHashMap底层结构的神秘面纱
【8月更文挑战第24天】`ConcurrentHashMap`是Java并发编程中不可或缺的线程安全哈希表实现。它通过精巧的锁机制和无锁算法显著提升了并发性能。本文首先介绍了早期版本中使用的“段”结构,每个段是一个带有独立锁的小型哈希表,能够减少线程间竞争并支持动态扩容以应对高并发场景。随后探讨了JDK 8的重大改进:取消段的概念,采用更细粒度的锁控制,并引入`Node`等内部类以及CAS操作,有效解决了哈希冲突并实现了高性能的并发访问。这些设计使得`ConcurrentHashMap`成为构建高效多线程应用的强大工具。
52 2
|
设计模式 Java 程序员
太爆了!阿里最新出品2023版JDK源码学习指南,Github三天已万赞
最近后台收到很多粉丝私信,说的是程序员究竟要不要去读源码?当下行情,面试什么样的薪资/岗位才会被问到源码? 对此,我的回答是:一定要去读,并且要提到日程上来! 据不完全统计,现在市面上不管是初级,中级,还是高级岗,面试的时候都有可能会问到源码中的问题,它已经成为程序员常规必备的一个技术点。如果你当下想通过一个面试,或者想把中级薪资要到相对于比较高的话,源码这块就必须要会。
144 0
|
5月前
|
Java Spring
深入解析Spring源码,揭示JDK动态代理的工作原理。
深入解析Spring源码,揭示JDK动态代理的工作原理。
58 0
|
6月前
|
算法 Java 索引
【数据结构与算法】4、双向链表(学习 jdk 的 LinkedList 部分源码)
【数据结构与算法】4、双向链表(学习 jdk 的 LinkedList 部分源码)
67 0
|
6月前
|
Java Linux iOS开发
Spring5源码(27)-静态代理模式和JDK、CGLIB动态代理
Spring5源码(27)-静态代理模式和JDK、CGLIB动态代理
48 0
|
6月前
|
消息中间件 Oracle Dubbo
Netty 源码共读(一)如何阅读JDK下sun包的源码
Netty 源码共读(一)如何阅读JDK下sun包的源码
127 1
|
6月前
|
算法 安全 Java
ConcurrentLinkedQueue的源码解析(基于JDK1.8)
ConcurrentLinkedQueue的源码解析(基于JDK1.8) ConcurrentLinkedQueue是Java集合框架中的一种线程安全的队列,它是通过CAS(Compare and Swap)算法实现的并发队列。在并发场景下,ConcurrentLinkedQueue能够保证队列的线程安全性,同时性能也很不错。
|
6月前
|
Java
LinkedBlockingDeque的源码解析(基于JDK1.8)
LinkedBlockingDeque的源码解析(基于JDK1.8) LinkedBlockingDeque是Java中的一个阻塞双端队列,它继承自AbstractQueue类并实现了BlockingDeque接口。在多线程环境下,LinkedBlockingDeque能够提供高效的并发访问能力。下面我们来看一下它的源码实现。