抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

创建型模式

简单工厂

定义: 由一个工厂对象决定创建出哪一种产品类的实例.

类型: 创建型, 但不属于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.connectionjava.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中获取的SqlSessionConfiguration, 肯定属于同一个产品族.

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中使用构建者模式的例子

StringBuilderStringBuffer 都使用了建筑者模式.两者不同之处在于, 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);
    }
}

评论