创建型模式
简单工厂
定义: 由一个工厂对象决定创建出哪一种产品类的实例.
类型: 创建型, 但不属于GOF23种设计模式.
适用场景:
- 工厂类负责创建的对象比较少.
- 客户端(应用层)只知道传入工厂类的参数, 对于如何创建对象(逻辑)不关心.
优点: 只需要传入一个正确的参数, 就可以获取你所需要的对象而不须知道其创建细节.
缺点: 工厂类的职责相对过重, 增加新的产品, 需要修改工厂类的判断逻辑, 违背开闭原则.
Simple Factory Demo UML类图
JDK中使用简单工厂的例子
java.util.Calendar.java
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;
}
}
}
// ...
return cal;
}
工厂方法模式
定义: 定义一个创建对象的接口(或抽象类), 但让实现这个接口的类来决定实例化哪个类.工厂方法让类的实例化推迟到子类中执行.
类型: 创建型
适用场景:
- 创建对象需要大量的重复代码.
- 客户端(应用层)不依赖于产品类实例如何被创建、实现的细节.
- 一个类通过子类来指定创建哪个对象.
子类对象覆盖父类对象, 从而使系统更容易扩展.
对象的创建被委托给多个工厂子类中的某一个实现.
可以把工厂类名存储到配置文件或者数据库中来实现动态创建.
关心产品等级结构.
优点:
- 用户只需要关心产品对应的工厂, 无须关心创建细节.
- 加入新产品符合开闭原则, 提高可扩展性.
缺点:
- 类的个数容易过多, 增加复杂度
- 增加了系统抽象性和理解难度
产品等级和产品族
例如: Java视频+Java手记都属于Java, 属于同一产品族;而Java的视频, Python的视频属于同一产品等级.美的的冰箱, 海尔的冰箱属于同一产品等级;而海尔的冰箱, 海尔的空调属于同一产品族.
Factory Method Demo UML类图
JDK中使用工厂方法例子
Collection.java
中的 iterator
方法.ArrayList
是实现这个方法的子类, 就是说是具体的实现工厂.
public interface Collection<E> extends Iterable<E> {
// ....
Iterator<E> iterator();
// ...
}
而具体产品就是 Itr
, 实现了 Iterator
接口.同时也使用了迭代器模式.
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
// ...
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
// prevent creating a synthetic constructor
Itr() {}
public boolean hasNext() {
return cursor != size;
}
// ...
}
// ...
}
URLStreamHandlerFactory
也是用了工厂方法.DefaultFactory
是其实现类.URLStreamHandler
是其抽象产品, Handler
是其产品的具体实现.
public interface URLStreamHandlerFactory {
URLStreamHandler createURLStreamHandler(String protocol);
}
private static class DefaultFactory implements URLStreamHandlerFactory {
private static String PREFIX = "sun.net.www.protocol";
public URLStreamHandler createURLStreamHandler(String protocol) {
String name = PREFIX + "." + protocol + ".Handler";
try {
@SuppressWarnings("deprecation")
Object o = Class.forName(name).newInstance();
return (URLStreamHandler)o;
} catch (ClassNotFoundException x) {
// ignore
} catch (Exception e) {
// For compatibility, all Exceptions are ignored.
// any number of exceptions can get thrown here
}
return null;
}
}
Logback中使用工厂方法的例子
Slf4j
中的 LoggerFactory
也使用了工厂方法.org.slf4j.LoggerFactory.java
public static Logger getLogger(String name) {
ILoggerFactory iLoggerFactory = getILoggerFactory();
return iLoggerFactory.getLogger(name);
}
ch.qos.logback.classic.LoggerContext.java
实现 org.slf4j.ILoggerFactory
接口
JDK14LoggerFactory
也同样实现了此接口.产品对应就是Logger
.
public final Logger getLogger(final String name) {
// ...
// if we are asking for the root logger, then let us return it without
// wasting time
if (Logger.ROOT_LOGGER_NAME.equalsIgnoreCase(name)) {
return root;
}
int i = 0;
Logger logger = root;
// check if the desired logger exists, if it does, return it
// without further ado.
Logger childLogger = (Logger) loggerCache.get(name);
// if we have the child, then let us return it without wasting time
if (childLogger != null) {
return childLogger;
}
//..
}
抽象工厂模式
定义: 抽象工厂模式提供了一个创建一系列相关或相互依赖对象的接口.
无须指定他们具体的类.
类型: 创建型
适用场景:
- 客户端(应用层)不依赖于产品类实例如何被创建、实现等细节.
- 强调一系列相关的产品对象(属于同一产品族)一起使用创建对象需要大量重复代码.
- 提供一个产品类的库, 所有的产品以同样的接口出现, 从而使客户端不依赖具体实现.
优点:
- 具体产品在应用层代码隔离, 无须关心创建细节.
- 将一个系列的产品族统一到一起创建.
缺点:
- 规定了所有可能被创建的产品集合, 产品族中扩展新的产品困难, 需要修改抽象工厂的接口, 修改涉及范围广.
- 增加了系统的抽象性和理解难度.
关心产品族结构.
产品等级结构与产品族
Abstract Factory Demo UML类图
JDK中使用抽象工厂的例子
java.sql.connection
和 java.sql.statement
都是抽象工厂.
public interface Connection extends Wrapper, AutoCloseable {
// ...
Statement createStatement() throws SQLException;
PreparedStatement prepareStatement(String sql)
throws SQLException;
// ...
}
只要是从 ConnectionImpl
中获取到的都属于Mysql
产品族的.
public class ConnectionImpl implements JdbcConnection, SessionEventListener, Serializable {
// ...
@Override
public java.sql.Statement createStatement() throws SQLException {
return createStatement(DEFAULT_RESULT_SET_TYPE, DEFAULT_RESULT_SET_CONCURRENCY);
}
@Override
public java.sql.Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
StatementImpl stmt = new StatementImpl(getMultiHostSafeProxy(), this.database);
stmt.setResultSetType(resultSetType);
stmt.setResultSetConcurrency(resultSetConcurrency);
return stmt;
}
// ...
}
Mybatis中使用抽象工厂的例子
org.apache.ibatis.session.SqlSessionFactory
使用了抽象工厂.只要通过 SqlSessionFactory
中获取的SqlSession
和Configuration
, 肯定属于同一个产品族.
public interface SqlSessionFactory {
SqlSession openSession();
SqlSession openSession(boolean autoCommit);
SqlSession openSession(Connection connection);
SqlSession openSession(TransactionIsolationLevel level);
SqlSession openSession(ExecutorType execType);
SqlSession openSession(ExecutorType execType, boolean autoCommit);
SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
SqlSession openSession(ExecutorType execType, Connection connection);
Configuration getConfiguration();
}
其中一个实现类 DefaultSqlSessionFactory
.从源码可以看出, 实际上返回的是 DefaultSqlSession
.
public class DefaultSqlSessionFactory implements SqlSessionFactory {
// ...
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
// ...
}
public class DefaultSqlSession implements SqlSession {
private final Configuration configuration;
private final Executor executor;
private final boolean autoCommit;
private boolean dirty;
private List<Cursor<?>> cursorList;
public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
this.configuration = configuration;
this.executor = executor;
this.dirty = false;
this.autoCommit = autoCommit;
}
// ...
}
单例模式
定义: 保证一个类仅有一个实例, 并提供一个全局访问点.
类型: 创建型
使用场景:
- 想确保任何情况下都绝对只有一个实例
优点:
- 在内存里只有一个实例, 减少了内存开销
- 可以避免对资源的多重占用
- 设置了全局访问点, 严格控制访问
缺点:
- 没有接口, 扩展困难
重点:
- 私有构造器
- 线程安全
- 延迟加载
- 序列化和反序列化安全
- 反射
相关设计模式
- 单例模式和工厂模式
- 单例模式和享元模式
多线程调试
打点时设置
选择线程
F8 逐行调试
F9 进入调试
当加锁之后:
另一个线程是无法访问这方法的.被 Thread-1 阻塞了.
Double Check
单线程的情况下, 2和3的重排序对结果并没有影响.
多线程情况
静态内部类
JVM保证只创建一个实例, 保证线程安全.
序列化破坏单例模式
@Test
public void singleton() throws IOException, ClassNotFoundException {
// 序列化破坏单例模式
HungrySingleton instance = HungrySingleton.INSTANCE();
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton_file"));
oos.writeObject(instance);
File file = new File("singleton_file");
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
// 重点是这个 readObject() 方法
// 实际是通过反射创建的.整个过程实际已经创建的, 只是返回对象还是原来那个而已
HungrySingleton newInstance = (HungrySingleton) ois.readObject();
// 返回false, 说明对象被再创建了
log.info(String.valueOf(instance == newInstance));
}
追踪源码ObjectInputStream
public class ObjectInputStream extends InputStream implements ObjectInput, ObjectStreamConstants
{
// ...
private final Object readObject(Class<?> type)
throws IOException, ClassNotFoundException
{
// ....
try {
Object obj = readObject0(type, false);
}
// ...
}
// ...
private Object readObject0(Class<?> type, boolean unshared) throws IOException {
// ...
try {
switch (tc) {
// ...
// 关注这里
case TC_OBJECT:
if (type == String.class) {
throw new ClassCastException("Cannot cast an object to java.lang.String");
}
return checkResolve(readOrdinaryObject(unshared));
// ...
} finally {
depth--;
bin.setBlockDataMode(oldMode);
}
}
// ...
private Object readOrdinaryObject(boolean unshared) throws IOException
{
// ...
Object obj;
try {
// 创建对象
obj = desc.isInstantiable() ? desc.newInstance() : null;
} catch (Exception ex) {
throw (IOException) new InvalidClassException(
desc.forClass().getName(),
"unable to create instance").initCause(ex);
}
// ...
// 注意 hasReadResolveMethod 这个方法
if (obj != null &&
handles.lookupException(passHandle) == null &&
desc.hasReadResolveMethod())
{
Object rep = desc.invokeReadResolve(obj);
if (unshared && rep.getClass().isArray()) {
rep = cloneArray(rep);
}
if (rep != obj) {
// Filter the replacement object
if (rep != null) {
if (rep.getClass().isArray()) {
filterCheck(rep.getClass(), Array.getLength(rep));
} else {
filterCheck(rep.getClass(), -1);
}
}
handles.setObject(passHandle, obj = rep);
}
}
return obj;
}
// ...
}
ObjectStreamClass
public class ObjectStreamClass implements Serializable {
private ObjectStreamClass(final Class<?> cl) {
// ...
if (serializable) {
AccessController.doPrivileged(new PrivilegedAction<>() {
public Void run() {
// ...
domains = getProtectionDomains(cons, cl);
writeReplaceMethod = getInheritableMethod(
cl, "writeReplace", null, Object.class);
// 获取 readResolve 方法
readResolveMethod = getInheritableMethod(
cl, "readResolve", null, Object.class);
return null;
}
});
// ...
}
Object invokeReadResolve(Object obj) throws IOException, UnsupportedOperationException
{
requireInitialized();
if (readResolveMethod != null) {
try {
// 执行 readResolve 方法
return readResolveMethod.invoke(obj, (Object[]) null);
} catch (InvocationTargetException ex) {
Throwable th = ex.getTargetException();
if (th instanceof ObjectStreamException) {
throw (ObjectStreamException) th;
} else {
throwMiscException(th);
throw new InternalError(th); // never reached
}
} catch (IllegalAccessException ex) {
// should not occur, as access checks have been suppressed
throw new InternalError(ex);
}
} else {
throw new UnsupportedOperationException();
}
}
}
反射攻击
如果是在类初始化时就创建好的单例, 可以在构造函数判断是否为null来防御反射攻击;如果是延迟加载的, 对于反射攻击是无法避免的.
JDK中使用单例的例子
Runtime
使用饿汉式单例模式.
public class Runtime {
private static final Runtime currentRuntime = new Runtime();
public static Runtime getRuntime() {
return currentRuntime;
}
}
Desktop
使用容器单例模式, 加了锁保证线程安全.
public class Desktop {
// ...
public static synchronized Desktop getDesktop(){
if (GraphicsEnvironment.isHeadless()) throw new HeadlessException();
if (!Desktop.isDesktopSupported()) {
throw new UnsupportedOperationException("Desktop API is not " +
"supported on the current platform");
}
sun.awt.AppContext context = sun.awt.AppContext.getAppContext();
Desktop desktop = (Desktop)context.get(Desktop.class);
if (desktop == null) {
desktop = new Desktop();
context.put(Desktop.class, desktop);
}
return desktop;
}
// ...
}
Spring中使用单例的例子
AbstractFactoryBean
public abstract class AbstractFactoryBean<T>
implements FactoryBean<T>, BeanClassLoaderAware, BeanFactoryAware, InitializingBean, DisposableBean {
@Nullable
private T singletonInstance;
// ...
@Override
public final T getObject() throws Exception {
if (isSingleton()) {
return (this.initialized ? this.singletonInstance : getEarlySingletonInstance());
}
else {
return createInstance();
}
}
// ...
}
Mybatis中使用单例的例子
ErrorContext
使用了ThreadLocal
单例模式.保证了每个线程上下文各自的错误数据.
public class ErrorContext {
// ...
private static final ThreadLocal<ErrorContext> LOCAL = ThreadLocal.withInitial(ErrorContext::new);
public static ErrorContext instance() {
return LOCAL.get();
}
// ...
}
原型模式
定义: 指原型实例指定创建对象的种类, 并且通过拷贝这些原型创建新的对象.
不需要知道任何创建细节, 不调用构造函数.
类型: 创建型
适用场景:
- 类初始化消耗较多资源.
- new产生的一个对象需要非常繁琐的过程(数据准备, 访问权限等).
- 构造函数比较复杂.
- 循环体中产生大量对象时.
优点:
- 性能比直接new一个对象高.
- 简化创建过程.
缺点:
- 必须配备克隆方法.
- 对克隆复杂对象或对克隆出的对象进行复杂改造时, 容易引入风险.
- 深拷贝、浅拷贝要使用得当.
JDK中使用原型模式的例子
Object
public class Object {
// ...
protected native Object clone() throws CloneNotSupportedException;
// ...
}
ArrayList
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
// ...
/**
* Returns a shallow copy of this {@code ArrayList} instance. (The
* elements themselves are not copied.)
*
* @return a clone of this {@code ArrayList} instance
*/
public Object clone() {
try {
ArrayList<?> v = (ArrayList<?>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}
// ...
}
直接看实现 Cloneable
接口的类
建造者模式
定义: 将一个复杂对象的构建与它的表示分离, 使得同样构建过程可以创建不同的表示.
用户只需要指定需要建造的类型就可以得到它们, 建造过程以及细节不需要知道.
类型: 创建型
使用场景:
- 如果一个对象有非常复杂的内部结构(很多属性).
- 想把复杂对象的创建和使用分离.
优点:
- 封装性好, 创建和使用分离.
- 扩展性好, 建造类之间独立, 一定程度上解耦.
缺点:
- 产生多余的Builder对象.
- 产品内部发生改变, 建造者都要修改, 成本较大.
建造者和工厂模式区别:
- 建造者模式更注重方法的调用顺序, 工厂模式注重创建产品.
- 创建对象的力度不同.建造者可以创建一些复杂的产品, 工厂模式创建出来的都是一种类型的.
- 建造者如果顺序会导致产出不同, 需要注重顺序, 而工厂模式则不需要.
Builder Demo UML类图
V1 版本
V2版本
JDK中使用构建者模式的例子
StringBuilder
和 StringBuffer
都使用了建筑者模式.两者不同之处在于, StringBuilder
是线程不安全的, 而StringBuffer
是线程安全的.
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, Comparable<StringBuilder>, CharSequence
{
// ...
@HotSpotIntrinsicCandidate
public StringBuilder() {
super(16);
}
// ...
public StringBuilder append(StringBuffer sb) {
super.append(sb);
return this;
}
// ...
}
Guava中使用构建者模式的例子
Immutableset
使用了构建者模式.
public abstract class ImmutableSet<E> extends ImmutableCollection<E> implements Set<E> {
// ...
public static class Builder<E> extends ImmutableCollection.Builder<E> {
// ...
@Override
@CanIgnoreReturnValue
public Builder<E> add(E element) {
checkNotNull(element);
copyIfNecessary();
impl = impl.add(element);
return this;
}
// ...
}
// ...
}
CacheBuilder
@GwtCompatible(emulated = true)
public final class CacheBuilder<K, V> {
// ...
public static CacheBuilder<Object, Object> newBuilder() {
return new CacheBuilder<>();
}
// ...
}
Spring中使用构建者模式的例子
BeanDefinitionBuilder
public final class BeanDefinitionBuilder {
// ...
public static BeanDefinitionBuilder genericBeanDefinition() {
return new BeanDefinitionBuilder(new GenericBeanDefinition());
}
// ...
}
MyBatis中使用构建者模式的例子
SqlSessionFactoryBuilder
public class SqlSessionFactoryBuilder {
// ...
// 解析xml配置文件
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
try {
// 这个也是使用构建者模式了
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
// 调用下面的build方法把配置传过去
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
reader.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
// ...
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
}