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

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

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

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

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

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

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

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

  所以为了加深自己对设计模式的应用场景与时机的了解,借用 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 Gregoriangcal.setGregorianChange(new Date(Long.MIN_VALUE));// and week definition to be compatible with ISO 8601setWeekDefinition(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();}@Overridepublic void forEachRemaining(Consumer<? super E> action) {// Use backing collection versioni.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@Overridepublic void forEach(Consumer<? super E> action) {c.forEach(action);}@Overridepublic boolean removeIf(Predicate<? super E> filter) {throw new UnsupportedOperationException();}@SuppressWarnings("unchecked")@Overridepublic Spliterator<E> spliterator() {return (Spliterator<E>)c.spliterator();}@SuppressWarnings("unchecked")@Overridepublic Stream<E> stream() {return (Stream<E>)c.stream();}@SuppressWarnings("unchecked")@Overridepublic Stream<E> parallelStream() {return (Stream<E>)c.parallelStream();}        }
}

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

总结

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

通过 JDK 源码学习灵活应用设计模式(上)相关推荐

  1. 【开源与项目实战:开源实战】77 | 开源实战一(下):通过剖析Java JDK源码学习灵活应用设计模式

    上一节课,我们讲解了工厂模式.建造者模式.装饰器模式.适配器模式在 Java JDK 中的应用,其中,Calendar 类用到了工厂模式和建造者模式,Collections 类用到了装饰器模式.适配器 ...

  2. JDK源码学习-基础

    JDK源码学习 目录 基础 1. 安装 1.1 下载JDK 1.2 配置环境变量 1.3 验证 2. 简单的程序 2.1 编写代码 2.2 编译文件 2.3 执行类 3. java基本类型 基础 1. ...

  3. JAVA JDK 源码学习

    JAVA JDK 源码学习 ,以1.8为例,按照下面图片顺序依次学习: applet ,awt,beans,io,lang,math,net,nio,rmi,security,sql,text,tim ...

  4. JDK源码学习笔记——Integer

    一.类定义 public final class Integer extends Number implements Comparable<Integer> 二.属性 private fi ...

  5. 非常实用,IDEA 搭建JDK源码学习环境(可修改+断点+笔记)

    点击关注公众号,实用技术文章及时了解 来源:chenxiao.blog.csdn.net/article/details/104369824 在学习JDK源码的时候,自然少不了代码的调试. 阅读与调试 ...

  6. JDK源码学习笔记——String

    1.学习jdk源码,从以下几个方面入手: 类定义(继承,实现接口等) 全局变量 方法 内部类 2.hashCode private int hash; public int hashCode() {i ...

  7. JDK源码学习路线~每天学一点~每天进步一点点

    很多java开发的小伙伴都会阅读jdk源码,然而确不知道应该从哪读起.以下为小编整理的通常所需阅读的源码范围. 标题为包名,后面序号为优先级1-4,优先级递减 1.java.lang 1) Objec ...

  8. JDK源码学习之Arraylist与LinkedList

    ArrayList和LinkedList是我们在开发过程中常用的两种集合类,本文将从底层源码实现对其进行简单介绍. 下图是Java集合类所涉及的类图. 一.ArrayList 从上面的集合类图可以看出 ...

  9. JDK源码学习笔记——Enum枚举使用及原理

    一.为什么使用枚举 什么时候应该使用枚举呢?每当需要一组固定的常量的时候,如一周的天数.一年四季等.或者是在我们编译前就知道其包含的所有值的集合. 利用 public final static 完全可 ...

最新文章

  1. RuntimeError Assertion cur_target = 0 cur_target n_classes failed
  2. 小憩,味一二 ——08年3月编程手札
  3. hashmap为什么线程不安全_什么时候线程不安全?怎样做到线程安全?怎么扩展线程安全的类?...
  4. Effective Java 英文 第二版 读书笔记 Item 14:In public classes,use accessor methods,not public fields...
  5. mesos,marthon集群部署详细步骤
  6. poj 1325 Machine Schedule 最小顶点覆盖
  7. php mysql_fetch_field_PHP mysqli_fetch_field() 函数
  8. 深度学习VS机器学习——到底什么区别
  9. aspxgridview的取值
  10. 如何实现用户认证授权系统
  11. response.setContentType(“text/html;charset=utf-8“)后依然乱码的解决方法
  12. css compressor java_使用YUI Compressor压缩CSS/JS
  13. CQF笔记M1L5仿真和操作随机微分方程
  14. 安师大计算机专业排名多少,安师大的计算机专业怎么样
  15. day15爬虫(二手房数据)
  16. python图片转svg_Python3.7将普通图片(png)转换为SVG图片格式并且让你的网站Logo(图标)从此”动”起来...
  17. 彻底卸载360画报(流氓屏保软件)---亲测有效
  18. 电视厂商渐进式占领“高清奥运”
  19. 如何运用计算机教学教学的收获,计算机教学中行动感悟法的应用
  20. 如何提高云服务器性能,提高云服务器性能

热门文章

  1. E:Could not get lock /var/lib/apt/lists/lock - open (11: Resource temporarily unavailable)
  2. 2017cpu服务器性能排行,2017年4月CPU性能排行榜:AMD中端称王(附天梯图)
  3. 利用python爬取猫眼电影榜单TOP100
  4. 快速了解统计学之辛普森悖论
  5. 《Unity Magica Cloth从入门到详解》之(6)换装
  6. 水位报警仪电极式传感器感知水位分级式水位监测
  7. Band in a Box 2019+RealTracks+RealDrums 智能编曲软件免安装版含音色库
  8. 巴菲特致股东的一封信:1982年
  9. 图像处理:手写实现图像增广算法(旋转、亮度调整、裁剪与拼接)
  10. ip-guard邮箱发往外网的邮件必须抄送公司领导