目录
一 工厂模式
二 Builder 模式
三 装饰器模式
四 模板模式
五 单例模式
是不是所有的工厂模式中都以 Factory 作为后缀来命名呢?
不是的,我们以 java.util.Calendar 为例。
public abstract class Calendar implements Serializable, Cloneable, Comparable {
//...
public static Calendar getInstance(TimeZone zone, Locale aLocale){
return createCalendar(zone, aLocale);
}
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 (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;
}
//...
}
上面代码中的 getInstance() 方法就是 Calendar 类的工厂方法,该方法可以根据不同 TimeZone 和 Locale 创建不同的 Calendar 子类对象。
为什么 Calendar 类是工厂类,却不以 Factory 作为后缀来命名呢?
因为 Calendar 不仅仅是一个工厂类,Calendar 类除了创建对象外,还有很多其他的功能,所以没有用 Factory 作为后缀来命名,是不是感觉实际的应用更灵活。
一般来说,Builder 模式有两种实现方式:
我们还是以 Calendar 来说明 Builder 模式的使用:
public abstract class Calendar implements Serializable, Cloneable, Comparable {
//...
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);
}
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;
}
}
}
Calendar 类既然已经有了 getInstance() 工厂方法来创建 Calendar 类对象,为什么还要用 Builder 来创建 Calendar 类对象呢?
哈哈,其实 getInstance() 方法使用的是工厂模式,而工厂模式是用来创建不同但是相同类型的对象。
Builder 模式用来创建一种类型的复杂对象,通过设置不同的参数,定制化创建不同的对象。
装饰器模式中的装饰器类是对原始类功能的增强,Java IO 类库是装饰器模式的非常经典的应用,下面以 Collections 类来说明。
Collections 类是集合容器的工具类,有很多静态方法用来创建各种集合容器。
public class Collections {
private Collections() {}
public static Collection unmodifiableCollection(Collection extends T> c) {
return new UnmodifiableCollection<>(c);
}
static class UnmodifiableCollection implements Collection, Serializable {
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[] toArray(T[] a) {return c.toArray(a);}
public String toString() {return c.toString();}
public Iterator iterator() {
return new Iterator() {
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) {
hrow 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 spliterator() {
return (Spliterator)c.spliterator();
}
@SuppressWarnings("unchecked")
@Override
public Stream stream() {
return (Stream)c.stream();
}
@SuppressWarnings("unchecked")
@Override
public Stream parallelStream() {
return (Stream)c.parallelStream();
}
}
}
尽管 UnmodifiableCollection 类可以算是对 Collection 类的一种功能增强,但这点还不具备足够的说服力来断定 UnmodifiableCollection 就是 Collection 类的装饰器类。
最关键的一点是,UnmodifiableCollection 的构造函数接收一个 Collection 类对象,然后对其所有的函数进行了包裹(Wrap):重新实现(比如 add() 函数)或者简单封装(比如 stream() 函数)。
而简单的接口实现或者继承,并不会如此来实现 UnmodifiableCollection 类。所以,从代码实现的角度来说,UnmodifiableCollection 类是典型的装饰器类。
模板模式常用在框架的设计中,提供框架的扩展点,在不对框架进行大的修改的前提下,基于扩展点定制化框架的功能。我们以 Collections 类的 sort() 函数来说明。
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class Demo {
public static void main(String[] args) {
List users = new ArrayList<>();
users.add(new User("Zhangsan", 19));
users.add(new User("Lisi", 20));
users.add(new User("Wangwu", 18));
Collections.sort(users, new AgeAscComparator());
print(users);
Collections.sort(users, new NameAscComparator());
print(users);
}
public static void print(List students) {
for (User s : students) {
System.out.println(s.getName() + " " + s.getAge());
}
}
public static class AgeAscComparator implements Comparator {
@Override
public int compare(User u1, User u2) {
return u1.getAge() - u2.getAge();
}
}
public static class NameAscComparator implements Comparator {
@Override
public int compare(User u1, User u2) {
return u1.getName().compareTo(u2.getName());
}
}
}
为什么说 Collections.sort() 函数用到了模板模式?
Collections.sort() 实现了对集合的排序,为了实现扩展性,将“比较大小”这部分逻辑,委派给用户来实现。我们可以把比较大小这部分逻辑看做整个排序逻辑的一部分,所以看做是模板模式。
JDK 中 java.lang.Runtime 类就是一个单例类。每个 Java 应用在运行时会启动一个 JVM 进程,每个 JVM 进程都只对应一个 Runtime 实例,用于查看 JVM 状态以及控制 JVM 行为。
进程内唯一,所以比较适合设计为单例。在编程的时候,我们不能自己去实例化一个 Runtime 对象,只能通过 getRuntime() 静态方法来获得。
/**
* Every Java application has a single instance of class
* Runtime
that allows the application to interface with
* the environment in which the application is running. The current
* runtime can be obtained from the getRuntime
method.
*
* An application cannot create its own instance of this class.
*
* @author unascribed
* @see java.lang.Runtime#getRuntime()
* @since JDK1.0
*/
public class Runtime {
private static Runtime currentRuntime = new Runtime();
public static Runtime getRuntime() {
return currentRuntime;
}
/** Don't let anyone else instantiate this class */
private Runtime() {}
//....
public void addShutdownHook(Thread hook) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new RuntimePermission("shutdownHooks"));
}
ApplicationShutdownHooks.add(hook);
}
//...
}
从上面代码我们可以看出,它使用了最简单的饿汉式的单例实现方式。