跟着MyBatis 学习 设计模式七大原则

以下是 MyBatis 框架中体现的 SOLID 设计模式七大原则 的分类、技术原理及代码示例:


1. 单一职责原则 (Single Responsibility Principle, SRP)

定义:一个类应该只有一个引起它变化的原因。
MyBatis 实现:通过 Mapper 接口和 XML 映射文件分离 SQL 操作与业务逻辑。

// Mapper 接口(单一职责:定义 SQL 操作)
public interface UserMapper {
    User getUserById(Long id);
    void insertUser(User user);
}

// 业务逻辑类(单一职责:处理业务)
@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;

    public User getUser(Long id) {
        return userMapper.getUserById(id); // 调用 Mapper 接口
    }
}

2. 开闭原则 (Open-Closed Principle, OCP)

定义:软件实体应对扩展开放,对修改关闭。
MyBatis 实现:通过插件机制(Interceptor)扩展功能,无需修改核心代码。

// 自定义插件(扩展点)
@Intercepts({
    @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
public class LoggingInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) {
        System.out.println("Executing SQL: " + ((MappedStatement) invocation.getArgs()[0]).getSql());
        return invocation.proceed(); // 调用原始方法
    }
}

// 配置插件(无需修改 MyBatis 核心代码)
@Configuration
public class MyBatisConfig {
    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);
        factoryBean.setPlugins(new Interceptor[]{new LoggingInterceptor()}); // 注册插件
        return factoryBean.getObject();
    }
}

3. 里氏替换原则 (Liskov Substitution Principle, LSP)

定义:子类必须能够替换其父类而不影响程序正确性。
MyBatis 实现:通过 TypeHandler 接口及其实现类保证类型替换性。

// 父类接口
public interface TypeHandler<T> {
    void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType);
    T getResult(ResultSet rs, String columnName);
}

// 子类实现(枚举类型处理器)
public class EnumTypeHandler<E extends Enum<E>> implements TypeHandler<E> {
    private final Class<E> type;

    public EnumTypeHandler(Class<E> type) {
        this.type = type;
    }

    @Override
    public void setParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) {
        ps.setString(i, parameter.name()); // 枚举名称存入数据库
    }

    @Override
    public E getResult(ResultSet rs, String columnName) {
        String name = rs.getString(columnName);
        return Enum.valueOf(type, name); // 从数据库读取枚举名称
    }
}

// 客户端代码(可替换为其他 TypeHandler 实现)
@MappedTypes(UserStatus.class)
public class UserStatusTypeHandler extends EnumTypeHandler<UserStatus> {
    public UserStatusTypeHandler(Class<UserStatus> type) {
        super(type);
    }
}

4. 接口隔离原则 (Interface Segregation Principle, ISP)

定义:客户端不应依赖它不需要的接口。
MyBatis 实现:通过细粒度接口(如 ResultSetHandlerParameterHandler)替代臃肿接口。

// 细粒度接口(符合 ISP)
public interface ResultSetHandler {
    Object handleResultSets(Statement stmt);
}

public interface ParameterHandler {
    Object getParameterObject();
    void setParameters(PreparedStatement ps);
}

// 违反 ISP 的反例:臃肿接口
public interface StatementHandler {
    Statement prepare(Connection connection);
    void parameterize(Statement statement);
    Object handleResultSets(Statement stmt); // 客户端可能不需要结果集处理
}

5. 依赖倒置原则 (Dependency Inversion Principle, DIP)

定义:高层模块不应依赖低层模块,二者都应依赖抽象。
MyBatis 实现:通过 SqlSession 接口注入抽象,而非具体实现。

// 抽象层
public interface SqlSession {
    <T> T selectOne(String statement, Object parameter);
    void insert(String statement, Object parameter);
}

// 具体实现(由 MyBatis 内部实现)
public class DefaultSqlSession implements SqlSession {
    // 实现 SQL 操作逻辑
}

// 高层模块依赖抽象
@Service
public class UserService {
    private final SqlSession sqlSession;

    @Autowired // 注入抽象而非具体实现
    public UserService(SqlSession sqlSession) {
        this.sqlSession = sqlSession;
    }

    public User getUser(Long id) {
        return sqlSession.selectOne("getUserById", id);
    }
}

6. 迪米特法则 (Law of Demeter, LoD)

定义:一个对象应对其他对象有尽可能少的了解。
MyBatis 实现:通过 Configuration 对象隐藏数据库配置细节。

// 业务代码无需了解配置细节
@Service
public class OrderService {
    private final SqlSession sqlSession;

    @Autowired
    public OrderService(SqlSession sqlSession) {
        this.sqlSession = sqlSession;
    }

    public void placeOrder(Order order) {
        sqlSession.insert("insertOrder", order); // 仅知道需要插入订单
    }
}

// 配置细节由 MyBatis 内部管理
@Configuration
public class MyBatisConfig {
    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);
        factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mappers/*.xml"));
        return factoryBean.getObject();
    }
}

7. 合成复用原则 (Composite Reuse Principle, CRP)

定义:优先使用对象组合而非类继承。
MyBatis 实现:通过 Configuration 对象组合 MapperRegistryInterceptorChain 等组件。

// 通过组合复用功能(符合 CRP)
public class Configuration {
    protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
    protected final InterceptorChain interceptorChain = new InterceptorChain();

    public void addMapper(Class<?> type) {
        mapperRegistry.addMapper(type); // 组合 Mapper 注册表
    }

    public void addInterceptor(Interceptor interceptor) {
        interceptorChain.addInterceptor(interceptor); // 组合拦截器链
    }
}

// 违反 CRP 的反例:通过继承复用
public class BadConfiguration extends MapperRegistry {
    private final InterceptorChain interceptorChain = new InterceptorChain();

    public void addInterceptor(Interceptor interceptor) {
        interceptorChain.addInterceptor(interceptor);
    }
}

总结

MyBatis 框架通过以下方式践行 SOLID 原则:

  1. SRP:通过 Mapper 接口和 XML 映射文件分离 SQL 操作与业务逻辑。
  2. OCP:通过插件机制(Interceptor)扩展功能,无需修改核心代码。
  3. LSP:通过 TypeHandler 接口及其实现类保证类型替换性。
  4. ISP:通过细粒度接口(如 ResultSetHandler)替代臃肿接口。
  5. DIP:通过 SqlSession 接口注入抽象,而非具体实现。
  6. LoD:通过 Configuration 对象隐藏数据库配置细节。
  7. CRP:通过 Configuration 对象组合 MapperRegistryInterceptorChain 等组件。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值