MyBatis 中的设计模式,这次我总结全了

本文探讨了MyBatis框架中使用的设计模式及其应用场景,包括单例模式、工厂模式、建造者模式等,并分析了这些模式如何帮助解决实际开发中的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

系列文章目录

这是 MyBatis 源码之旅的第六篇文章,MyBatis 版本号为 3.5.6,源码分析注释已上传到 Github ,前五篇的文章目录如下,建议按照顺序阅读。
  1. MyBatis 初探,使用 MyBatis 简化数据库操作(超详细)
  2. MyBatis Mapper 接口方法执行原理分析
  3. 一条 SQL 是如何在 MyBatis 中执行的
  4. 谈谈 MyBatis 的插件,除了分页你可能还有这些使用场景
  5. MyBatis 缓存机制分析,MyBatis 真的有二级缓存?

前言

软件开发的流程一般可分为分析、设计、实现,设计模式在处于设计或代码实现阶段,以设计思想、设计原则作为指导,相对来说更为具象,是前人对经常遇到的设计问题总结出的一套解决方案,多数设计模式用来解决代码的扩展性问题,在框架中使用的场景较多。

MyBatis 作为一个小巧的持久层框架,在其中也使用了几个设计模式,这里把我能识别出的设计模式做一个总结。很多有关设计模式的书为了便于读者理解设计模式经常会举出一些简单的案例,这样看来好像懂了,但实战时又好像什么都不会,通过 MyBatis 这些设计模式的总结,希望达到深入理解设计模式的目的,知道为什么使用这些设计模式?解决了什么问题?在什么场景下使用?这样读者在遇到相同问题的时候自然就知道选择什么样的设计模式。


单例模式

单例模式是表示一个类只有一个实例,按照单例的范围可以分为线程内单例、进程内单例、集群内单例。更多单例模式的信息可参见《Java 中创建单例的几种方式》

ErrorContext

MyBatis 中使用的单例模式只有一个 ErrorContext,这是一个线程级别的单例模式,MyBatis 同样使用了 ThreadLocal 来实现,MyBatis 使用它作为解析 xml 配置或执行 SQL 时的线程上下文信息,如当前正在解析的资源文件、当前执行的 SQL 等等,具体代码如下。

public class ErrorContext {

    private static final ThreadLocal<ErrorContext> LOCAL = ThreadLocal.withInitial(ErrorContext::new);
    
	... 省略部分字段
	
    private ErrorContext() {
    }

    public static ErrorContext instance() {
        return LOCAL.get();
    }

	... 省略部分实例方法
}

工厂模式

工厂模式用以创建多个类型相似的不同对象(同一个类的多个子类),又可以细分为简单工厂、工厂方法、抽象工厂。具体可参见《设计模式之工厂模式》。这个设计模式在 MyBatis 中使用较多。

SqlSessionFactory

SqlSession 表示 MyBatis 与数据库的一次会话,MyBatis 中默认的 SqlSession 是 DefaultSqlSession,MyBatis 使用 SqlSessionFactory 作为工厂创建 SqlSession,具体的设计模式为工厂方法模式,SqlSessionFactory 仅在 MyBatis 内部使用,并未留给用户扩展。实现代码如下。

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();

}

public class DefaultSqlSessionFactory implements SqlSessionFactory {
	...省略实现
}

public class SqlSessionManager implements SqlSessionFactory, SqlSession {
	...省略实现
}

TransactionFactory

Transaction 表示在某次会话中,MyBatis 执行的一个事务,默认的实现是 JdbcTransaction,为了创建 Transaction,MyBatis 抽象出一个 TransactionFactory 作为工厂类,为了允许用户配置,因此设计为工厂方法模式。

MyBatis 中的实现代码如下。

public interface TransactionFactory {

    default void setProperties(Properties props) {
        // NOP
    }

    Transaction newTransaction(Connection conn);
    Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit);

}

public class JdbcTransactionFactory implements TransactionFactory {
	... 省略实现代码
}

public class ManagedTransactionFactory implements TransactionFactory {
    ... 省略实现代码
}

DataSourceFactory

DataSource 是 JDBC 规范中用于获取 Connection 的类,MyBatis 内置了一些 DataSource,如 UnpooledDataSource、PooledDataSource。为了支持获取不同的 DataSource ,MyBatis 抽象出创建 DataSource 的 DataSourceFactory,为了允许用户进行扩展和配置,同样使用了工厂方法模式。

具体实现如下。

public interface DataSourceFactory {

    void setProperties(Properties props);

    DataSource getDataSource();

}

public class UnpooledDataSourceFactory implements DataSourceFactory {
	...省略实现代码
}

public class PooledDataSourceFactory extends UnpooledDataSourceFactory {
	...省略实现代码
}

public class JndiDataSourceFactory implements DataSourceFactory {
	...省略实现代码
}

其他工厂模式

MyBatis 的工厂模式使用较多,其他对于工厂模式使用的地方简单介绍如下,具体实现感兴趣的小伙伴可以直接参阅源码。

  • ObjectFactory:MyBatis 从数据库查询出数据后,需要根据配置将记录转换为对象,MyBatis 使用 ObjectFactory 创建对象,具体为工厂方法模式。

  • ObjectWrapperFactory:ObjectWrapper 是对普通对象的包装,MyBatis 执行 SQL 前需要获取对象的属性值然后设置参数,从数据库查询到数据后又需要存入对象。由于不同的对象如 collection、map、普通 object 设置和获取属性的方法不一样,因此 MyBatis 抽象出 ObjectWrapper,以统一的方法设置获取属性值。使用 ObjectWrapperFactory 作为工厂创建不同的 ObjectWrapper 实例。

  • VFS.VFSHolder:VFS 用于在不同的 Web 容器环境中获取文件资源,VFSHolder 使用简单工厂模式创建 VFS。

  • ReflectorFactory:Reflector 用于设置/获取属性,ReflectorFactory 作为简单工厂创建 Reflector 的实例。

  • ExceptionFactory:这是一个根据现有异常信息及 ErrorContext 信息包装新的运行时异常的简单工厂。

  • MapperProxyFactory:我们定义的 Mapper 为接口,为了可以直接使用,MyBatis 使用 MapperProxyFactory 作为简单工厂创建 Mapper 接口的代理 MapperProxy。

  • LogFactory:为了支持不同的日志实现框架,MyBatis 抽象出一个 Log 作为日志类,LogFactory 作为简单工厂根据配置创建出具体的日志实现类的对象。

  • ProxyFactory:这是一个创建数据库记录对应对象的代理的工厂,以便用于延迟初始化对象的复杂属性,具体使用了抽象工厂,根据配置选择 JDK 或 Javassist 创建代理。


建造者模式

建造者模式可以解决类的构造方法参数过多的问题,具体可参见《设计模式之建造者模式》

在 MyBatis 中,建造者模式主要被用来创建复杂的配置,并且 MyBatis 对建造者模式灵活运用,除了使用最经典的方式实现,还对建造者模式进行了修改。23种设计模式较为灵活,事实上我们使用的时候也不必拘泥于固定的模式。

经典的建造者模式

经典的建造者模式实现,MyBatis 主要用来创建配置。

Environment.Builder

Envrionment 是 MyBatis 执行 SQL 的环境,持有持有事务工厂 TransactionFactory 和数据源 DataSource。MyBatis 创建 Environment 使用的建造者模式代码如下。

public final class Environment {
    private final String id;

    private final TransactionFactory transactionFactory;

    private final DataSource dataSource;

    public Environment(String id, TransactionFactory transactionFactory, DataSource dataSource) {
        ...省略校验及属性赋值代码
    }

    public static class Builder {

        private final String id;

        private TransactionFactory transactionFactory;

        private DataSource dataSource;

        public Builder(String id) {
            this.id = id;
        }

        public Builder transactionFactory(TransactionFactory transactionFactory) {
            this.transactionFactory = transactionFactory;
            return this;
        }

        public Builder dataSource(DataSource dataSource) {
            this.dataSource = dataSource;
            return this;
        }

        public Environment build() {
            return new Environment(this.id, this.transactionFactory, this.dataSource);
        }

    }
    ...省略 get 方法
}

这里 Environment 的构造方法使用了 public 进行修饰,事实上 MyBatis 内部只使用了 Builder 创建 Environment,修改为 private 也许更为合适。并且 Environment 的参数并不多,不排除 MyBatis 对此有过度设计的嫌疑。

其他

其他对经典的建造者模式的实现和 Environment.Builder 套路类似,包括如下使用的地方。

  • MappedStatement.Builder
  • ParameterMapping.Builder
  • Discriminator.Builder
  • ResultMap.Builder
  • CacheBuilder

改进后的建造者模式

改进后的建造者模式 MyBatis 则主要用来对 xml 或注解进行解析生成配置。

以 SQLSessionFactoryBuilder 为例,实现代码如下。

public class SqlSessionFactoryBuilder {

    public SqlSessionFactory build(Reader reader) {
        return build(reader, null, null);
    }

    public SqlSessionFactory build(Reader reader, String environment) {
        return build(reader, environment, null);
    }

    public SqlSessionFactory build(Reader reader, Properties properties) {
        return build(reader, null, properties);
    }

    public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
        try {
            XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
            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);
    }
}

和经典的实现方式不同,SqlSessionFactoryBuilder 并非直接通过方法设置属性,而是委派 XMLConfigBuilder 解析 xml 配置,进而创建 SqlSessionFactory。其他很多与配置相关的建造者模式实现也是直接委派其他类解析配置或者直接解析配置。其他改进后的建造者模式主要包括如下。

  • XMLConfigBuilder
  • XMLMapperBuilder
  • XMLScriptBuilder
  • SqlSourceBuilder
  • XMLStatementBuilder

代理

代理类似于我们日常生活中接触到的中介,它常用于在不改变原始类的情况下为原始类添加新的功能。关于更多代理的内容,可以参见《Java 中创建代理的几种方式》

MyBatis 对代理使用的场景并不局限于功能增强,具体如下。

功能增强

SQL 日志打印

为了打印 SQL 相关的日志,MyBatis 为 JDBC 规范中的各种类型提供了代理,在执行对应方法的时候就会进行日志打印。代理类包括如下。

  • ConnectionLogger:打印 Connection 类#prepareStatement#prepareCall方法执行日志。
  • PreparedStatementLogger:打印 PreparedStatement 类执行的 SQL 及参数。
  • StatementLogger:打印 Statement 类执行的 SQL。
  • ResultSetLogger:打印 ResultSet 返回的记录条数。

以 ConnectionLogger 为例,其实现代码如下。

public final class ConnectionLogger extends BaseJdbcLogger implements InvocationHandler {

    private final Connection connection;

    private ConnectionLogger(Connection conn, Log statementLog, int queryStack) {
        super(statementLog, queryStack);
        this.connection = conn;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] params)
        throws Throwable {
        try {
            if (Object.class.equals(method.getDeclaringClass())) {
                return method.invoke(this, params);
            }
            if ("prepareStatement".equals(method.getName()) || "prepareCall".equals(method.getName())) {
                if (isDebugEnabled()) {
                    debug(" Preparing: " + removeExtraWhitespace((String) params[0]), true);
                }
                PreparedStatement stmt = (PreparedStatement) method.invoke(connection, params);
                stmt = PreparedStatementLogger.newInstance(stmt, statementLog, queryStack);
                return stmt;
            }
            ... 省略部分代码
        } catch (Throwable t) {
            throw ExceptionUtil.unwrapThrowable(t);
        }
    }

    public static Connection newInstance(Connection conn, Log statementLog, int queryStack) {
        InvocationHandler handler = new ConnectionLogger(conn, statementLog, queryStack);
        ClassLoader cl = Connection.class.getClassLoader();
        return (Connection) Proxy.newProxyInstance(cl, new Class[]{Connection.class}, handler);
    }   
}

ConnectionLogger 作为 Connection 的代理对象,实现了 JDK 动态代理所需的 InvocationHandler,自身作为工厂提供了创建 Connection 代理实例的方法。在 Connection 相关方法执行时,就会进行日志打印。

插件实现

代理模式同样在 MyBatis 中的插件实现中有使用。

我们自定义的插件需要实现 Interceptor,这个 Interceptor 会被放在 InterceptorChain 中。

public class InterceptorChain {

    private final List<Interceptor> interceptors = new ArrayList<>();

    public Object pluginAll(Object target) {
        for (Interceptor interceptor : interceptors) {
            target = interceptor.plugin(target);
        }
        return target;
    }

    public void addInterceptor(Interceptor interceptor) {
        interceptors.add(interceptor);
    }

    public List<Interceptor> getInterceptors() {
        return Collections.unmodifiableList(interceptors);
    }
}

在 MyBatis 执行 SQL 的生命周期中,InterceptorChain#pluginAll会为生命周期中使用到的 ParameterHandler、ResultSetHandler、StatementHandler、Executor 创建代理对象,从而可以在生命周期中插入我们自定义的逻辑,如分页、数据库字段加密等。具体可参见谈谈 MyBatis 的插件,除了分页你可能还有这些使用场景

生成接口实现

在 MyBatis 的前身 IBatis 中,我们调用 SqlSession 中的方法执行 SQL,方法参数中使用字符串硬编码的方式定位要执行的 SQL,很容易出现拼写错误,为了解决这个问题,MyBatis 允许我们创建 Mapper 接口,然后 MyBatis 为我们的 Mapper 创建代理,底层仍然是调用了 SqlSession 的方法。

Mapper 接口的代理类为 MapperProxy,它由 MapperProxyFactory 通过简单工厂创建。

类加载处理

除了上述 MyBatis 中代理的使用场景,为了保证 DriverManager 中注册的 Driver 由系统类加载器加载,MyBatis 还为其内部使用到的 Drvier 提供了代理。具体如下。

    private static class DriverProxy implements Driver {
        private Driver driver;

        DriverProxy(Driver d) {
            this.driver = d;
        }

        @Override
        public boolean acceptsURL(String u) throws SQLException {
            return this.driver.acceptsURL(u);
        }

		...省略部分方法
    }

装饰器

装饰器的实现和静态代理的代码实现很相似,但是它们的使用场景不太一样,装饰器主要用在对原有功能的增强,而不是为原有类型添加新的功能。关于更多装饰器模式的内容,可以参考《透过 Java IO 流学习装饰器模式》

MyBatis 对装饰器模式的使用如下。

数据源

MyBatis 内置了不支持池的数据源 UnpooledDataSource 和支持池的数据源 PooledDataSource。由于这两者部分实现类似,因此 PooledDataSource 使用装饰器模式,对 UnpooledDataSource 进行增强,提供了池,以便减少反复获取释放连接的资源消耗。

缓存

为了加快查询以及解决循环引用问题,MyBatis 添加了缓存功能,在 mapper xml 文件中可以配置不同的缓存参数,如刷新间隔、缓存数量、清除策略等等,根据不同的参数,需要创建不同的缓存对象。

由于 MyBatis 事先并不知道用户会进行哪些配置,如果为各种配置的组合创建不同的缓存类,那么将导致 MyBatis 中存在较多的的缓存类,并且也会导致实现的复杂。为了解决这个问题,MyBatis 使用装饰器模式,通过为原有 Cache 进行装饰,以支持不同的参数,这样不同的参数就可以创建出不同的 Cache。

以支持日志打印的 LoggingCache 为例,其实现如下。

public class LoggingCache implements Cache {

    private final Log log;

    private final Cache delegate;

    public LoggingCache(Cache delegate) {
        this.delegate = delegate;
        this.log = LogFactory.getLog(getId());
    }

    @Override
    public Object getObject(Object key) {
        requests++;
        final Object value = delegate.getObject(key);
        if (value != null) {
            hits++;
        }
        if (log.isDebugEnabled()) {
            log.debug("Cache Hit Ratio [" + getId() + "]: " + getHitRatio());
        }
        return value;
    }
    ...省略部分代码
}

组合模式

组合模式将一组对象组织成树形结构,以表示一种“部分 - 整体”的层次结构。由于其用于处理树形结构的数据,因此使用场景来说相对受限。组合模式并无固定的代码模板,每种场景不太相同。

MyBatis 主要将组合模式应用在 mapper xml 文件内容的解析,由于 xml 标签可以相互嵌套,因此可以表示树形结构,使用组合模式最为合适不过。MyBatis 将 xml 中的节点抽象为 SqlNode,解析节点代码的入口为XMLLanguageDriver#createSqlSource,感兴趣的朋友可自行阅读源码。


适配器模式

适配器模式将不兼容的接口转换为兼容的接口,多是因为旧的接口设计不合理所致。更多适配器模式的内容,可以参考《设计模式之适配器模式》

在 Java 的开源库中,关于日志的框架众多,包括 log4j、log4j2、commons-loging 等等,为了支持不同的日志实现,MyBatis 使用适配器模式提供了新的日志接口 Log,以便对不同的日志实现进行整合。运行时根据配置或项目中存在的日志实现,使用不同的日志框架。

以支持 FIFO 的 FifoCache 为例,其实现代码如下。

public class FifoCache implements Cache {

    private final Cache delegate;

    private final Deque<Object> keyList;

    private int size;

    public FifoCache(Cache delegate) {
        this.delegate = delegate;
        this.keyList = new LinkedList<>();
        this.size = 1024;
    }

    @Override
    public void putObject(Object key, Object value) {
        cycleKeyList(key);
        delegate.putObject(key, value);
    }
    
    private void cycleKeyList(Object key) {
        keyList.addLast(key);
        if (keyList.size() > size) {
            Object oldestKey = keyList.removeFirst();
            delegate.removeObject(oldestKey);
        }
    }
	...省略部分代码
}

责任链

责任链模式将请求的发送与接收者解耦,由链上的每个对象处理请求。更多关于责任链模式的内容,可以参考《设计模式之责任链模式》

MyBatis 使用责任链模式主要用来实现插件,在 MyBatis 执行 SQL 的生命周期中,将插件组成一个链,在特定的生命周期中执行额外操作。

MyBatis 对插件的责任链模式实现如下。

public class InterceptorChain {

    private final List<Interceptor> interceptors = new ArrayList<>();

    public Object pluginAll(Object target) {
        for (Interceptor interceptor : interceptors) {
            target = interceptor.plugin(target);
        }
        return target;
    }

    public void addInterceptor(Interceptor interceptor) {
        interceptors.add(interceptor);
    }

    public List<Interceptor> getInterceptors() {
        return Collections.unmodifiableList(interceptors);
    }

}

模板方法

模板方法定义了算法的骨架,将算法中的某些步骤推迟到子类来实现。主要用于复用和扩展。更多关于模板方法的内容,可以参考《设计模式之模板方法》

MyBatis 对模板方法的使用主要有两块,包括如下。

  • BaseExecutor:Executor 负责组装 SQL 执行所需的的各模块,完成整个流程。为了复用代码,把重复的逻辑提取到模板方法中。
  • BaseTypeHandler:TypeHandler 用于设置 JDBC 中 SQL 的参数,以及从 ResultSet 中获取值。

以 BaseTypeHandler 为例,其实现代码如下。

public abstract class BaseTypeHandler<T> extends TypeReference<T> implements TypeHandler<T> {

    @Override
    public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
        if (parameter == null) {
            if (jdbcType == null) {
                throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters.");
            }
            try {
                ps.setNull(i, jdbcType.TYPE_CODE);
            } catch (SQLException e) {
                throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . "
                    + "Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. "
                    + "Cause: " + e, e);
            }
        } else {
            try {
            	// 子类实现
                setNonNullParameter(ps, i, parameter, jdbcType);
            } catch (Exception e) {
                throw new TypeException("Error setting non null for parameter #" + i + " with JdbcType " + jdbcType + " . "
                    + "Try setting a different JdbcType for this parameter or a different configuration property. "
                    + "Cause: " + e, e);
            }
        }
    }

    public abstract void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
    
	...省略部分代码
}

迭代器模式

迭代器模式用于遍历对象,大多数编程语言都使用迭代器模式实现对集合对象的遍历。

MyBatis 中的迭代器和传统的迭代器模式实现不太相同,它主要用迭代器实现对嵌套的属性遍历。MyBatis 中使用 PropertyTokenizer 表示嵌套的属性,其实现如下。

public class PropertyTokenizer implements Iterator<PropertyTokenizer> {

    /**
     * 属性名称,如 props
     */
    private String name;

    /**
     * 带索引的属性名称,如 props[1]
     */
    private final String indexedName;

    /**
     * 索引,如 1
     */
    private String index;

    /**
     * 子属性,如 props[1].cd 中的 cd
     */
    private final String children;

    public PropertyTokenizer(String fullname) {
        int delim = fullname.indexOf('.');
        if (delim > -1) {
            name = fullname.substring(0, delim);
            children = fullname.substring(delim + 1);
        } else {
            name = fullname;
            children = null;
        }
        indexedName = name;
        delim = name.indexOf('[');
        if (delim > -1) {
            index = name.substring(delim + 1, name.length() - 1);
            name = name.substring(0, delim);
        }
    }

	...省略get方法

    @Override
    public boolean hasNext() {
        return children != null;
    }

    @Override
    public PropertyTokenizer next() {
        return new PropertyTokenizer(children);
    }

    @Override
    public void remove() {
        throw new UnsupportedOperationException("Remove is not supported, as it has no meaning in the context of properties.");
    }
}

总结

MyBatis 中使用的设计模式和其场景具有一定关系,并且 MyBatis 对设计模式的使用并不拘泥于传统的实现方式,MyBatis 还结合场景进行了一些改进。不过也有一些代码不排除 MyBatis 过度使用设计模式的嫌疑。MyBatis 作为一个小巧的框架,结合官网文档,其源码阅读难度较低,理解其底层实现也便于我们日常排查问题,推荐大家阅读。

<think>好的,我现在需要回答用户关于Java设计模式的问题。用户之前已经询问过高效开发和安测试的内容,现在转向设计模式,可能是在学习软件工程的最佳实践,或者在实际开发中遇到了设计上的挑战。我需要确保回答结构清晰,涵盖设计模式的基本概念、常见模式及其应用场景,并且给出具体的Java示例。 首先,用户可能希望了解设计模式的整体概况,所以我应该先介绍设计模式的定义、分类以及它们的重要性。接着,分门别类地讲解创建型、结构型和行为型模式,每类中选择几个常用模式详细说明,比如单例、工厂、适配器、观察者等。每个模式需要包括意图、结构、实现示例以及优缺点,这样用户能面理解。 用户之前的问题中有代码示例,所以这次回答也应包含Java代码片段,但要注意使用正确的格式,比如代码块。同时,根据系统级指令,所有行内数学表达式需要用$...$,但设计模式可能较少涉及数学,所以可能不需要。不过要注意LaTeX语法的正确性,比如类名或方法名可能需要使用\texttt{},但用户可能更希望看到直接的代码示例。 需要确保内容准确,比如单例模式的双重检查锁定是否正确,是否考虑了线程安和Java内存模型的影响。另外,工厂模式和抽象工厂的区别要明确,避免混淆。适配器模式和代理模式的区别也需要解释清楚。 用户可能需要实际应用场景的例子,所以每个模式的应用场景要具体,比如Spring框架中使用哪些设计模式,这样帮助用户联系实际。同时,要指出设计模式的常见误用或注意事项,比如过度使用模式可能导致代码复杂化。 最后,总结部分要强调设计模式的选择原则,比如根据问题选择模式,而不是为了使用而使用。可能还需要推荐学习资源,如《设计模式:可复用面向对象软件的基础》一书,或者在线教程。 检查是否有遗漏的重要模式,比如装饰器、策略、责任链等,是否需要在概述中提到,但详细讲解可能集中在最常用的几个。同时,注意语言要口语化,避免过于技术化的术语,但保持专业性。确保回答符合用户可见层的结构清晰,分步骤解释,帮助用户逐步理解。 现在组织内容,先分三大类,每类选3-4个模式,每个模式的结构包括意图、结构图(可能用文字描述)、Java示例、应用场景、优缺点。确保代码示例正确,比如单例模式的双重检查锁定使用volatile和synchronized,工厂方法中的接口和实现类,观察者模式中的Subject和Observer接口等。 最后检查是否符合用户的系统级指令,特别是LaTeX格式和代码格式是否正确,使用中文回答,避免markdown,但可能需要用代码块标记。同时,结构清晰,分点明确,帮助用户高效获取信息。</think>以下是Java设计模式的系统化解析,结合代码示例与实践场景说明: --- ### 一、设计模式核心价值 1. **设计模式定义** 解决软件设计中**重复出现问题**的通用方案模板,提升代码**可维护性、扩展性、复用性** 2. **三大分类原则** - **创建型**:解耦对象创建过程(如工厂模式) - **结构型**:优化对象组合结构(如适配器模式) - **行为型**:改善对象间协作(如观察者模式) --- ### 二、创建型模式(5种典型) #### 1. 单例模式(Singleton) **意图**:确保类仅有一个实例,并提供局访问点 **实现**(线程安双重校验锁): ```java public class Singleton { private static volatile Singleton instance; private Singleton() {} public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } } ``` **应用场景**:数据库连接池、日志处理器 **注意事项**:反射攻击防御、序列化安 #### 2. 工厂方法模式(Factory Method) **意图**:定义创建对象的接口,让子类决定实例化哪个类 **结构**: ```java interface Product { void operate(); } class ConcreteProductA implements Product { public void operate() { System.out.println("ProductA"); } } abstract class Creator { public abstract Product createProduct(); } class ConcreteCreatorA extends Creator { public Product createProduct() { return new ConcreteProductA(); } } ``` **典型应用**:Spring的BeanFactory、JDBC的DriverManager #### 3. 建造者模式(Builder) **意图**:分步骤创建复杂对象,避免构造器参数爆炸 **示例**: ```java public class Computer { private String CPU; private String RAM; // 其他组件... public static class Builder { private Computer computer = new Computer(); public Builder setCPU(String cpu) { computer.CPU = cpu; return this; } public Computer build() { validate(); return computer; } } } // 使用:new Computer.Builder().setCPU("i9").build(); ``` **优势**:支持链式调用、参数灵活组合 --- ### 三、结构型模式(4种关键模式) #### 1. 适配器模式(Adapter) **意图**:转换接口使不兼容类协同工作 **类适配器实现**: ```java class LegacyPrinter { void printDocument() { /* 旧式打印 */ } } interface ModernPrinter { void print(); } class PrinterAdapter extends LegacyPrinter implements ModernPrinter { public void print() { super.printDocument(); } } ``` **应用场景**:第三方库接口适配、系统升级兼容 #### 2. 装饰器模式(Decorator) **意图**:动态添加职责,替代继承扩展 **IO流案例**: ```java InputStream in = new FileInputStream("test.txt"); in = new BufferedInputStream(in); // 添加缓冲功能 in = new GZIPInputStream(in); // 添加压缩解压功能 ``` **设计要点**:保持组件接口一致性,多层装饰可叠加 #### 3. 代理模式(Proxy) **意图**:控制对象访问,增强原有功能 **动态代理实现**: ```java public class LogProxy implements InvocationHandler { private Object target; public Object bind(Object target) { this.target = target; return Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), this ); } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("方法调用前记录日志"); return method.invoke(target, args); } } ``` **应用场景**:AOP切面编程、RPC远程调用 --- ### 四、行为型模式(5种核心模式) #### 1. 观察者模式(Observer) **意图**:定义对象间的一对多依赖,状态变化自动通知 **Java内置实现**: ```java class NewsPublisher extends Observable { void publishNews(String msg) { setChanged(); notifyObservers(msg); } } class Subscriber implements Observer { public void update(Observable o, Object arg) { System.out.println("收到新闻:" + arg); } } ``` **应用**:GUI事件处理、Spring事件驱动模型 #### 2. 策略模式(Strategy) **意图**:定义算法族,使其可互换 **支付场景示例**: ```java interface PaymentStrategy { void pay(int amount); } class AlipayStrategy implements PaymentStrategy { public void pay(int amount) { /* 支付宝支付逻辑 */ } } class PaymentContext { private PaymentStrategy strategy; public void executePay(int amount) { strategy.pay(amount); } } ``` **优势**:避免多重条件判断,符合开闭原则 #### 3. 责任链模式(Chain of Responsibility) **意图**:将请求沿处理链传递,直到有对象处理它 **审批流程实现**: ```java abstract class Approver { protected Approver next; public void setNext(Approver next) { this.next = next; } public abstract void processRequest(PurchaseRequest request); } class Manager extends Approver { public void processRequest(PurchaseRequest req) { if (req.getAmount() <= 1000) { // 处理请求 } else if (next != null) { next.processRequest(req); } } } ``` **典型应用**:Java过滤器链、Spring Security鉴权 --- ### 五、模式选择原则 1. **避免模式滥用** - 当简单代码能解决问题时,不要强制使用模式 - 例如:不需要为仅有一个实现的接口使用策略模式 2. **模式组合实践** - 工厂方法 + 原型模式:通过克隆方式快速生成对象 - 观察者 + 中介者模式:简化多对象间通信 3. **框架中的模式应用** - Spring:工厂模式(BeanFactory)、代理模式(AOP) - MyBatis:构建者模式(SqlSessionFactoryBuilder) --- ### 六、反模式警示 1. **上帝对象(God Object)** - 问题:单个类承担过多职责 - 解决:拆分职责,应用单一职责原则 2. **过度设计(Over-Engineering)** - 问题:提前引入不必要的模式 - 案例:为可能永远不会扩展的功能使用抽象工厂 --- ### 总结 掌握设计模式需理解其**本质思想**而非死记结构: 1. **创建型模式**:聚焦对象创建过程的控制与优化 2. **结构型模式**:解决类与对象之间的组合关系问题 3. **行为型模式**:处理对象间的职责分配与通信机制 推荐结合《Head First设计模式》与真实项目实践,逐步培养**模式识别能力**和**架构设计直觉**。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大鹏cool

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值