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

本文涉及的产品
实时计算 Flink 版,5000CU*H 3个月
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
大数据开发治理平台 DataWorks,不限时长
简介: 根据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,只是一种思想。多种设计模式也可以混用在一起。

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



相关文章
|
4月前
|
缓存 Dubbo Java
趁同事上厕所的时间,看完了 Dubbo SPI 的源码,瞬间觉得 JDK SPI 不香了
趁同事上厕所的时间,看完了 Dubbo SPI 的源码,瞬间觉得 JDK SPI 不香了
|
7月前
|
Java 容器
阿里内部流传的JDK源码剖析手册!GitHub已获上千万的访问量
相信现在已经有很多小伙伴知道了“微软”要对JDK下手了! JDK是什么? jdk是Java语言的软件开发工具包,主要用于移动设备、嵌入式设备上的java应用程序。jdk是整个java开发的核心,它包含了JAVA的运行环境和JAVA工具。相对而言,没有jdk的话,无法编译Java程序(指java源码.java文件),如果想只运行Java程序(指class或jar或其它归档文件),要确保已安装相应的JRE。
200 0
|
3月前
|
缓存 Java Spring
Spring 源码阅读 66:基于 JDK 的 AOP 代理如何获取拦截器链(4)- 将 Advice 封装为拦截器
【1月更文挑战第1天】本文分析了 Advice 被封装成 MethodInterceptor 的过程,Spring AOP 用到的五种 Advice 中,有些本身就是 MethodInterceptor 的实现类,而有些需要通过适配器的封装。
44 0
|
7月前
|
设计模式 Java 程序员
太爆了!阿里最新出品2023版JDK源码学习指南,Github三天已万赞
最近后台收到很多粉丝私信,说的是程序员究竟要不要去读源码?当下行情,面试什么样的薪资/岗位才会被问到源码? 对此,我的回答是:一定要去读,并且要提到日程上来! 据不完全统计,现在市面上不管是初级,中级,还是高级岗,面试的时候都有可能会问到源码中的问题,它已经成为程序员常规必备的一个技术点。如果你当下想通过一个面试,或者想把中级薪资要到相对于比较高的话,源码这块就必须要会。
94 0
|
1月前
|
算法 Java 索引
【数据结构与算法】4、双向链表(学习 jdk 的 LinkedList 部分源码)
【数据结构与算法】4、双向链表(学习 jdk 的 LinkedList 部分源码)
31 0
|
3月前
|
Java Linux iOS开发
Spring5源码(27)-静态代理模式和JDK、CGLIB动态代理
Spring5源码(27)-静态代理模式和JDK、CGLIB动态代理
23 0
|
3月前
|
XML Java 数据格式
Spring 源码阅读 70:基于 JDK 的 AOP 代理拦截器链执行(4)- 容易被忽略的 ExposeInvocationInterceptor
【1月更文挑战第5天】本文分析了 Spring AOP 拦截器链中的一个特殊拦截器 ExposeInvocationInterceptor 的注册的时机以及它的作用。至此,基于 JDK 的 AOP 代理拦截器链执行的逻辑就分析完了。
393 0
|
3月前
|
Java 索引 Spring
Spring 源码阅读 69:基于 JDK 的 AOP 代理拦截器链执行(3)- MethodInterceptor 分析
【1月更文挑战第4天】本文详细分析了 Spring AOP 中五种增强类型对应的拦截器中增强方法的执行逻辑,结合上一篇中分析的 ReflectiveMethodInvocation 中proceed方法的执行逻辑,就组成了完整的拦截器链递归调用的逻辑。
34 0
|
3月前
|
Java 索引 Spring
Spring 源码阅读 68:基于 JDK 的 AOP 代理拦截器链执行(2)- ReflectiveMethodInvocation 分析
【1月更文挑战第3天】本文分析了 ReflectiveMethodInvocation 类中的proceed方法,通过对这个方法的分析,了解了连接器链中的增强逻辑是如何逐层执行的,以及目标方法是什么时候被调用的。
32 0