通过 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 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 源码学习灵活应用设计模式(上)相关推荐
- 【开源与项目实战:开源实战】77 | 开源实战一(下):通过剖析Java JDK源码学习灵活应用设计模式
上一节课,我们讲解了工厂模式.建造者模式.装饰器模式.适配器模式在 Java JDK 中的应用,其中,Calendar 类用到了工厂模式和建造者模式,Collections 类用到了装饰器模式.适配器 ...
- JDK源码学习-基础
JDK源码学习 目录 基础 1. 安装 1.1 下载JDK 1.2 配置环境变量 1.3 验证 2. 简单的程序 2.1 编写代码 2.2 编译文件 2.3 执行类 3. java基本类型 基础 1. ...
- JAVA JDK 源码学习
JAVA JDK 源码学习 ,以1.8为例,按照下面图片顺序依次学习: applet ,awt,beans,io,lang,math,net,nio,rmi,security,sql,text,tim ...
- JDK源码学习笔记——Integer
一.类定义 public final class Integer extends Number implements Comparable<Integer> 二.属性 private fi ...
- 非常实用,IDEA 搭建JDK源码学习环境(可修改+断点+笔记)
点击关注公众号,实用技术文章及时了解 来源:chenxiao.blog.csdn.net/article/details/104369824 在学习JDK源码的时候,自然少不了代码的调试. 阅读与调试 ...
- JDK源码学习笔记——String
1.学习jdk源码,从以下几个方面入手: 类定义(继承,实现接口等) 全局变量 方法 内部类 2.hashCode private int hash; public int hashCode() {i ...
- JDK源码学习路线~每天学一点~每天进步一点点
很多java开发的小伙伴都会阅读jdk源码,然而确不知道应该从哪读起.以下为小编整理的通常所需阅读的源码范围. 标题为包名,后面序号为优先级1-4,优先级递减 1.java.lang 1) Objec ...
- JDK源码学习之Arraylist与LinkedList
ArrayList和LinkedList是我们在开发过程中常用的两种集合类,本文将从底层源码实现对其进行简单介绍. 下图是Java集合类所涉及的类图. 一.ArrayList 从上面的集合类图可以看出 ...
- JDK源码学习笔记——Enum枚举使用及原理
一.为什么使用枚举 什么时候应该使用枚举呢?每当需要一组固定的常量的时候,如一周的天数.一年四季等.或者是在我们编译前就知道其包含的所有值的集合. 利用 public final static 完全可 ...
最新文章
- RuntimeError Assertion cur_target = 0 cur_target n_classes failed
- 小憩,味一二 ——08年3月编程手札
- hashmap为什么线程不安全_什么时候线程不安全?怎样做到线程安全?怎么扩展线程安全的类?...
- Effective Java 英文 第二版 读书笔记 Item 14:In public classes,use accessor methods,not public fields...
- mesos,marthon集群部署详细步骤
- poj 1325 Machine Schedule 最小顶点覆盖
- php mysql_fetch_field_PHP mysqli_fetch_field() 函数
- 深度学习VS机器学习——到底什么区别
- aspxgridview的取值
- 如何实现用户认证授权系统
- response.setContentType(“text/html;charset=utf-8“)后依然乱码的解决方法
- css compressor java_使用YUI Compressor压缩CSS/JS
- CQF笔记M1L5仿真和操作随机微分方程
- 安师大计算机专业排名多少,安师大的计算机专业怎么样
- day15爬虫(二手房数据)
- python图片转svg_Python3.7将普通图片(png)转换为SVG图片格式并且让你的网站Logo(图标)从此”动”起来...
- 彻底卸载360画报(流氓屏保软件)---亲测有效
- 电视厂商渐进式占领“高清奥运”
- 如何运用计算机教学教学的收获,计算机教学中行动感悟法的应用
- 如何提高云服务器性能,提高云服务器性能
热门文章
- E:Could not get lock /var/lib/apt/lists/lock - open (11: Resource temporarily unavailable)
- 2017cpu服务器性能排行,2017年4月CPU性能排行榜:AMD中端称王(附天梯图)
- 利用python爬取猫眼电影榜单TOP100
- 快速了解统计学之辛普森悖论
- 《Unity Magica Cloth从入门到详解》之(6)换装
- 水位报警仪电极式传感器感知水位分级式水位监测
- Band in a Box 2019+RealTracks+RealDrums 智能编曲软件免安装版含音色库
- 巴菲特致股东的一封信:1982年
- 图像处理:手写实现图像增广算法(旋转、亮度调整、裁剪与拼接)
- ip-guard邮箱发往外网的邮件必须抄送公司领导