• 通过 JDK 源码学习灵活应用设计模式(上)


    如果大家觉得文章有错误内容,欢迎留言或者私信讨论~

      在真实的项目开发中,对于设计模式要学会活学活用,切不可死记硬背,生搬硬套设计模式的设计与实现。需要了解到:

    • 设计的过程是先有问题后有方案的

      当你接手某一项任务,首先要分析这项任务的痛点在哪里,比如可读性不好、可拓展性不好,然后再针对性的使用某一个设计模式看看是否合适。而不是想着这次任务与书中的某个案例相同就套用上去,切记不可过度设计。

    • 设计的应用场景是复杂代码

      设计模式常用的应用场景就是对代码进行解耦,可以说设计模式就是用来解决复杂代码问题而产生的。对于复杂代码,比如项目代码量多、开发周期长、参与开发的人员多,我们前期要多花点时间在设计上,越是复杂代码,花在设计上的时间就要越多。相反,如果你参与的只是一个简单的项目,代码量不多,开发人员也不多,那简单的问题用简单的解决方案就好,不要引入过于复杂的设计模式,将简单问题复杂化,切记不可过度设计。

    • 持续性的重构能够避免“破窗效应”

      引入设计模式虽然会提高代码的可拓展性,实际上还会降低代码的可读性,一旦我们引入某个复杂的设计,之后即便在很长一段时间都没有扩展的需求,那么就需要背负这个复杂的设计一路走下去。为了避免这个错误,持续的重构非常重要——当对要不要应用某种设计模式感到模棱两可的时候,你可以思考一下,如果暂时不用这种设计模式,随着代码的演进,当某一天不得不去使用它的时候,重构的代码是否很大,切记不可过度设计。

      所以为了加深自己对设计模式的应用场景与时机的了解,借用 JDK 源码去体会,在真实的项目开发中,要学会活学活用,切不可过于死板,生搬硬套设计模式的设计与实现,这点非常重要(开发前 2 年必须养成能够写一手适合易懂的能力)。

      话不多说,让我们开始吧。

    工厂模式在 Calendar 类中的应用

      通常映像里的工厂模式都是以 Factory 作为后缀来命名的,并且主要负责创建对象这样一件事情。但在实际的项目开发中,工厂类的设计更加灵活。那我们就来看下,工厂模式在 Java JDK 中的一个应用:java.util.Calendar。看命名我们无法猜测它是一个工厂类。

       Calendar 类提供了大量跟日期相关的功能代码,同时,又提供了一个 getInstance() 工厂方法,用来根据不同的 TimeZone 和 Locale 创建不同的 Calendar 子类对象。也就是说,功能代码和工厂方法代码耦合在了一个类中。

      Calendar 类的相关代码如下所示,大部分代码都已经省略,从代码中,我们可以看出,getInstance() 方法可以根据不同TimeZone 和 Locale,创建不同的 Calendar 子类对象,比如 BuddhistCalendar、JapaneseImperialCalendar、GregorianCalendar,这些细节完全封装在工厂方法中,使用者只需要传递当前的时区和地址,就能够获得一个 Calendar 类对象来使用,而获得的对象具体是哪个 Calendar 子类的对象,使用者在使用的时候并不关心。

    public abstract class Calendar implements Serializable, Cloneable, Comparable<Calendar> {
    	// .....
    	 public static Calendar getInstance() {
            return createCalendar(TimeZone.getDefault(), Locale.getDefault(Locale.Category.FORMAT));
        }
    	
    	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 中还有建造者模式的应用,将 Builder 实现为原始类的内部类,我们先来看一下代码的实现:

    public abstract class Calendar implements Serializable, Cloneable, Comparable<Calendar> {
    	// ....
    	public static class Builder {
    		private static final int NFIELDS = FIELD_COUNT + 1;
    		private static final int WEEK_YEAR = FIELD_COUNT;
    		private long instant;
    		private int[] fields;
    		private int nextStamp;
    		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;
    		}
    		//...省略n多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);
                }
                cal.setLenient(lenient);
                if (firstDayOfWeek != 0) {
                    cal.setFirstDayOfWeek(firstDayOfWeek);
                    cal.setMinimalDaysInFirstWeek(minimalDaysInFirstWeek);
                }
                if (isInstantSet()) {
                    cal.setTimeInMillis(instant);
                    cal.complete();
                    return cal;
                }
    
                if (fields != null) {
                    boolean weekDate = isSet(WEEK_YEAR)
                                           && fields[WEEK_YEAR] > fields[YEAR];
                    if (weekDate && !cal.isWeekDateSupported()) {
                        throw new IllegalArgumentException("week date is unsupported by " + type);
                    }
    
                    // Set the fields from the min stamp to the max stamp so that
                    // the fields resolution works in the Calendar.
                    for (int stamp = MINIMUM_USER_STAMP; stamp < nextStamp; stamp++) {
                        for (int index = 0; index <= maxFieldIndex; index++) {
                            if (fields[index] == stamp) {
                                cal.set(index, fields[NFIELDS + index]);
                                break;
                            }
                        }
                    }
    
                    if (weekDate) {
                        int weekOfYear = isSet(WEEK_OF_YEAR) ? fields[NFIELDS + WEEK_OF_YEAR] : 1;
                        int dayOfWeek = isSet(DAY_OF_WEEK)
                                        ? fields[NFIELDS + DAY_OF_WEEK] : cal.getFirstDayOfWeek();
                        cal.setWeekDate(fields[NFIELDS + WEEK_YEAR], weekOfYear, dayOfWeek);
                    }
                    cal.complete();
                }
    
                return cal;
            }
    	}
    }
    

      这里虽然工厂模式和建造者模式都是创建类型的设计模式——用来创建对象。但是工厂模式一般都是用来创建不同但类型相关的对象(比如继承同一父类或者接口的一组子类),而建造者模式则是用来“定制化”创建复杂对象的一种手段。
      可能建造者模式前半段代码与工厂模式十分相近,后段才是标准的建造者模式的代码。但这里是想提醒大家,不要过于学院派,设计模式的核心在于设计原则这套心法,甚至有时候都可以为了业务而违背设计原则。灵活运用,甚至适当的混用各种模式,根据具体的功能需求做灵活的调整。

    装饰器模式在 Collections 类中的应用

      除去之前文章讲过的 Java IO 类运用过装饰器模式,Collections 也是运用了这一设计模式。Collections 类是一个集合容器的工具类,提供了很多静态方法,用来创建各种集合容器,比如通过 unmodifiableColletion() 静态方法,来创建 UnmodifiableCollection 类对象。而这些容器类中的 UnmodifiableCollection 类、CheckedCollection 和 SynchronizedCollection 类,就是针对 Collection 类的装饰器类。

      提到的这三类 Collection 代码结构上几乎一样,所以,只拿其中的UnmodifiableCollection 类来举例讲解一下,下面摘抄了部分代码:

    public class Collections {
    	private Collections() {}
    	
    	public static <T> Collection<T> unmodifiableCollection(Collection<? extends T> c) {
            return new UnmodifiableCollection<>(c);
        }
    	
       static class UnmodifiableCollection<E> implements Collection<E>, Serializable {
            private static final long serialVersionUID = 1820017752578914078L;
    		private static final long serialVersionUID = 1820017752578914078L;
    		final Collection<? extends E> c;
    		
    		UnmodifiableCollection(Collection<? extends E> c) {
    			if (c==null)
    			throw new NullPointerException();
    			this.c = c;
    		}
    		
    		public int size() {return c.size();}
    		public boolean isEmpty() {return c.isEmpty();}
    		public boolean contains(Object o) {return c.contains(o);}
    		public Object[] toArray() {return c.toArray();}
    		public <T> T[] toArray(T[] a) {return c.toArray(a);}
    		public String toString() {return c.toString();}
    		
    		 public Iterator<E> iterator() {
                return new Iterator<E>() {
                    private final Iterator<? extends E> i = c.iterator();
    
                    public boolean hasNext() {return i.hasNext();}
                    public E next()          {return i.next();}
                    public void remove() {
                        throw new UnsupportedOperationException();
                    }
                    @Override
                    public void forEachRemaining(Consumer<? super E> action) {
                        // Use backing collection version
                        i.forEachRemaining(action);
                    }
                };
            }
    
            public boolean add(E e) {
                throw new UnsupportedOperationException();
            }
            public boolean remove(Object o) {
                throw new UnsupportedOperationException();
            }
    
            public boolean containsAll(Collection<?> coll) {
                return c.containsAll(coll);
            }
            public boolean addAll(Collection<? extends E> coll) {
                throw new UnsupportedOperationException();
            }
            public boolean removeAll(Collection<?> coll) {
                throw new UnsupportedOperationException();
            }
            public boolean retainAll(Collection<?> coll) {
                throw new UnsupportedOperationException();
            }
            public void clear() {
                throw new UnsupportedOperationException();
            }
    
            // Override default methods in Collection
            @Override
            public void forEach(Consumer<? super E> action) {
                c.forEach(action);
            }
            @Override
            public boolean removeIf(Predicate<? super E> filter) {
                throw new UnsupportedOperationException();
            }
            @SuppressWarnings("unchecked")
            @Override
            public Spliterator<E> spliterator() {
                return (Spliterator<E>)c.spliterator();
            }
            @SuppressWarnings("unchecked")
            @Override
            public Stream<E> stream() {
                return (Stream<E>)c.stream();
            }
            @SuppressWarnings("unchecked")
            @Override
            public Stream<E> parallelStream() {
                return (Stream<E>)c.parallelStream();
            }		
    	
    	}
    }
    

      装饰器模式中的装饰器类是对原始类功能的增强。尽管 UnmodifiableCollection 类可以算是对 Collection 类的一种功能增强,但这点还不具备足够的说服力来断定 UnmodifiableCollection 就是 Collection 类的装饰器类。
      实际上,最关键的一点是,UnmodifiableCollection 的构造函数接收一个 Collection 类对象,然后对其所有的函数进行了包裹(Wrap):重新实现(比如 add() 函数)或者简单封装(比如 stream() 函数)。而简单的接口实现或者继承,并不会如此来实现UnmodifiableCollection 类。所以,从代码实现的角度来说,UnmodifiableCollection 类是典型的装饰器类。

    总结

      尽管在之前的理论讲解中,都会了解到每个模式的经典代码实现,但是,在真实的项目开发中,这些模式的应用更加灵活,代码实现更加自由,可以根据具体的业务场景、功能需求,对代码实现做很大的调整,甚至还可能会对模式本身的设计思路做调整。

  • 相关阅读:
    ESP32设备驱动-VCNL4010光传感器驱动
    防火墙Ipsec vpn的配置
    免费好用的Mac电脑磁盘清理工具CleanMyMac
    Solidity 小白教程:20. 发送 ETH
    NTT简介
    Arthas快速入门
    神经网络解决优化问题,神经网络 样本不平衡
    docker的数据管理
    第四章redis配置文件的介绍
    【技术分享】OSPFv3基本原理
  • 原文地址:https://blog.csdn.net/qq_43654226/article/details/126942072