工厂模式优化:反射机制+配置文件下的工厂模式进行解耦合

本文介绍了如何通过工厂模式优化对象创建过程,以Mybatis的SqlSession为例,展示了从直接使用构造函数到使用工厂类再到引入XML配置文件进行解耦合的步骤。优化后的工厂类利用反射和配置文件,降低了代码的耦合度,提高了灵活性。
摘要由CSDN通过智能技术生成

一、引入

工厂模式是我们最常用的实例化对象模式了,它是用工厂中的方法代替new创建对象的一种设计模式。

我们以Mybatis的SqlSession接口为例,它有一个实现类DefaultSqlSession,如果我们要创建该接口的实例对象:

SqlSession sqlSession = new DefaultSqlSession();

可是,实际情况是,通常我们都要在创建SqlSession实例时做点初始化的工作,比如解析XML,封装连接数据库的信息等等。

在创建对象时,如果有一些不得不做的初始化操作时,我们首先到的是,可以使用构造函数,这样生成实例就写成:

SqlSession sqlSession = new DefaultSqlSession(传入配置文件的路径);

但是,如果创建sqlSession实例时所做的初始化工作不是像赋值这样简单的事,可能是很长一段代码,如果也写入构造函数中,那你的代码很难看了(这时候就需要代码Refactor重构)。

SqlSessionFactory factory = new SqlSessionFactoryBuilder().build("SqlMapConfig.xml");
SqlSession sqlSession = factory.openSession();

所以,Mybatis框架在使用时为我们提供了SqlSessionFactory工厂类,通过openSession()方法获取到SqlSession对象。

二、简单工厂

本篇主要侧重优化,简单工厂内容不赘述了,简单来说就是通过方法调用来获得对象。

  • 前提类和接口
//创建账户的业务层接口
public interface AccountService {
     //保存账户
    void saveAccount();
}
//创建账户的业务层实现类
public class AccountServiceImpl implements AccountService{
    @Override
    public void saveAccount() {
        System.out.println("保存了账户");
    }
}
//创建账户的持久层接口
public interface AccountDao {
   //保存账户
    void saveAccount();
}
//创建账户的持久层实现类
public class AccountDaoImpl implements AccountDao {
    @Override
    public void saveAccount() {
        System.out.println("保存了账户");
    }
}
  • 工厂类
public class BeanFactory {
    //传入接口名,返回实现类对象
    public static Object getBean(String InterfaceName) {
        if (InterfaceName.equals("AccountDao")) {
            return new AccountDaoImpl();
        } else if(InterfaceName.equals("AccountService")){
            return new AccountServiceImpl();
        }
    }
}
  • 测试
public class AccountTest {
    public static void main(String[] args) {
        //创建业务层对象, 调用save方法,不在需要new对象了
        //AccountService accountService = new AccountServiceImpl();

      	AccountService accountService = (AccountService) BeanFactory.getBean("AccountService");
        accountService.saveAccount();

        AccountDao accountDao = (AccountDao) BeanFactory.getBean("AccountDao");
        accountDao.saveAccount();
    }
}

三、优化:进一步解耦合(重点)

除了创建对象时,不再使用new的方式以外,工厂类中还可以有进一步的优化空间

  • 在我们使用一些不是频繁创建的对象时,采用反射的方式创建对象显然更加合理。而反射创建时需要提供创建类的全限定类名,这个名称如果写在java代码中,造成的结果就是修改仍然避免不了修改源码。所以,我们需要使用配置文件,把要创建类的全限定类名用配置文件配置起来。
  • 解耦的思路总结:
    • 第一:通过工厂类
    • 第二:使用反射创建对象
    • 第三:创建对象用到的全限定类名用配置文件配置起来

还是继续用上面的类和接口以及测试程序,只需要将工厂更新以及新增xml配置文件即可

那么具体的优化步骤是什么呢?

  • Bean工厂优化步骤:
  1. 解析xml,获取所有对象的全限定类型(全名称)
  2. 反射创建对象,存储到容器中,使用map集合(键:接口名 值:接口实现类对象)
  3. 用户需要对象时,从容器中根据键interface直接获取对应值即可
  • beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <!-- 配置 service -->
    <bean interface="AccountService" fullClassName="com.zstu.service.impl.AccountServiceImpl"></bean>
    <!-- 配置 dao -->
    <bean interface="AccountDao" fullClassName="com.zstu.dao.impl.AccountDaoImpl"></bean>
</beans>
  • 优化后的工厂类
public class BeanFactory {
   // (键:接口名  值:接口实现类对象) : 反射创建好的对象
   private static Map<String,Object> map = new HashMap<>();
   //静态代码块:类加载时执行,只会执行一次
   static {
       //解析xml,获取所有对象的全限定类型(全名称)
       SAXReader reader = new SAXReader();
       try {
           //获取文档对象
           Document doc = reader.read(BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml"));
           //获取根节点
           Element rootElement = doc.getRootElement();
           //获取根节点下的所有的子节点
           List<Element> elements = rootElement.elements();
           //遍历
           for (Element element : elements) {
               String interfaceValue = element.attributeValue("interface");
               String fullClassNameValue = element.attributeValue("fullClassName");
               //反射创建对象,存储到容器中
               Class clazz = Class.forName(fullClassNameValue);
               Object obj = clazz.newInstance();
               //存储到容器中,使得以后可以直接通过接口名获取对象
               map.put(interfaceValue, obj);
           }
       } catch (Exception e) {
           e.printStackTrace();
       }
   }
   //传入接口名,返回实现类对象
   public static Object  getBean(String id){
       return map.get(id);
   }
}
  • 测试程序
public class AccountTest {
  public static void main(String[] args) {
      //创建业务层对象, 调用save方法,不在需要new对象了
      //AccountService accountService = new AccountServiceImpl();

    	AccountService accountService = (AccountService) BeanFactory.getBean("AccountService");
      accountService.saveAccount();

      AccountDao accountDao = (AccountDao) BeanFactory.getBean("AccountDao");
      accountDao.saveAccount();
  }

这样的好处是,程序中不仅没有new对象,并且将耦合性降到最低。

对于配置文件的解析,这里是xml文件,使用的是dom4j包解析的,如果是properties文件的解析,可以看我之前写的一篇文章:传送门:读取properties配置文件的几种方法

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值