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

911 阅读3分钟

Calendar简单使用

Calendar是jdk提供的日历类。

     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();

getInstance 就是使用了工厂模式,我们进入源码里面查看

根据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使用了工厂模式,根据不同的情况生产不同的对象,那么这些对象是怎么被生产出来的呢。

答案就是:建造者模式

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

**

代码:

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);
            }

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

WHY?

为什么既然使用了工厂模式来创建对象,那么又进一步的使用建造者模式呢?

我觉得两者并不冲突,工厂模式可以选择创建什么对象,建造者模式可以进一步选择创建对象的属性。

一个经典的例子:去餐厅吃饭,根据口味选择了干锅,但是对于干锅来说也可以选择哪些配菜。

对于Calendar 而言,根据不同的type来创建了不同的Calendar子类。根据Set方法来定制化参数。

反思

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

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