美文网首页
工厂模式 + 单例模式实战

工厂模式 + 单例模式实战

作者: 老衲呢 | 来源:发表于2020-03-09 20:40 被阅读0次

    一、绪论

    1、Tips

    这篇文章是基于上篇文章 《工厂模式为 MVC 解耦》之上的后续,建议先看上篇文章。

    2、提出问题

    上篇文章使用工厂模式和反射为保存账户的功能进行解耦,可以决解缺少某个类时编译不出错,但是运行抛异常,从而降低耦合。

    但是工厂模式还是有一定的问题的,我们先来看下在 AccountDemo 中,连续创建五次的 AccountServiceImpl 的对象内存地址分别是什么。

    • 修改下 AccountDemo 中的代码,其他不变,具体如下:
    public class AccountDemo {
    
        public static void main(String[] args) {
            for (int i = 0; i < 5; i++) {
                IAccountService as = (IAccountService) BeanFactory.getBean("accountService");
                System.out.println(as);
            }
    //            as.saveAccount();
        }
    }
    
    //*************************************************
    // 运行结果
    wiki.laona.service.impl.AccountServiceImpl@1540e19d
    wiki.laona.service.impl.AccountServiceImpl@677327b6
    wiki.laona.service.impl.AccountServiceImpl@14ae5a5
    wiki.laona.service.impl.AccountServiceImpl@7f31245a
    wiki.laona.service.impl.AccountServiceImpl@6d6f6e28
    
    • 从运行结果可以明显看出来,五个对象的内存地址都是不一样的,那就意味着每次调用 BeanFactory.getBean 方法都会创建一个新的对象。这样耦合是解决了但是内存开销也随之变大了。

    3、刨析一下

    原因其实这也不能理解,在 getBean 方法中,每次获取 bean 的对象实例都是通过反射 newInstance 新建一个实例,所以每次调用都会在内存中 new 一个新的对象,导致了内存消耗,这就是多例的存在的问题。解决这个问题,我们可以通过单例的思想解决。有关单例模式的内容可以查看博客《Java 设计模式 -- 单例模式》, 另外 getBeam() 方法如下:

     /**
      * 通过资源名获取类对象
      *
      * @param beanName 资源名
      * @return {@link Object} 类对象
      * @throws ClassNotFoundException {@link ClassNotFoundException} 未找到类对象异常
      */
    public static Object getBean(String beanName) {
        Object bean = null;
        String res = props.getProperty(beanName);
        try {
            bean = Class.forName(res).newInstance();
        } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
            e.printStackTrace();
        }
        return bean;
    }
    

    二、工厂模式结合单例模式

    改造工厂模式,我们只需要改动 BeanFactory 类就行了。

    1、主要思路

    • 通过 Map 保存 AccountService 和 AccountDao 的实例对象
    • 在 getBean 中返回 Map 中保存的AccountService 和 AccountDao 的实例对象。

    2、 代码实现

    2.1、建立保存

    • 代码实现:
    /**
     * 保存 AccountService 和 AccountDao 的实例对象的 Map 
     */
    private static Map<String, Object> beans;
    

    2.2、把实例对象保存到 Map 中

    • 这一部分在静态代码块中实现,代码如下:
    static {
        try {
            props = new Properties();
            InputStream is = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
            props.load(is);
            // 初始化 Map
            beans = new HashMap<>();
            // 获取 prop 中的实例对象实例
            Enumeration<Object> keys = props.keys();
            while (keys.hasMoreElements()) {
                // 获取 key
                String key = keys.nextElement().toString();
                // 获取 properties 文件中的实例路径
                String beanPath = props.getProperty(key);
                // 反射获取实例对象
                Object val = Class.forName(key).newInstance();
                // 保存 key 和 value 到 beans 中
                beans.put(key, val);
            }
        } catch (Exception e) {
            throw new ExceptionInInitializerError("初始化 Properties 文件失败~!");
        }
    }
    

    2.3、在 getBean 方法中返回对象实例

    • 通过 key 获取 Map 的对象实例,返回便可。
    • 代码实现如下:
    /**
     * 通过名字获取实例对象
     * @param beanPath 实例名称
     * @return {@link Object} 实例对象
     */
    public static Object getBean(String beanPath) {
        return beans.get(beanPath);
    }
    

    3、测试

    3.1、集成测试

    • 因为直接重写了 getBean 方法,所以在 AccountDemo 中无需修改,直接运行,此时获取的内存地址是一样,代码如下:
    public class AccountDemo {
    
        public static void main(String[] args) {
            IAccountService as = null;
            for (int i = 0; i < 5; i++) {
                as = (IAccountService) BeanFactory.getBean("accountService");
                System.out.println(as);
            }
            as.saveAccount();
        }
    }
    
    //*********************************
    // 运行结果
    wiki.laona.service.impl.AccountServiceImpl@1540e19d
    wiki.laona.service.impl.AccountServiceImpl@1540e19d
    wiki.laona.service.impl.AccountServiceImpl@1540e19d
    wiki.laona.service.impl.AccountServiceImpl@1540e19d
    wiki.laona.service.impl.AccountServiceImpl@1540e19d
    已保存账户~!
    

    三、小结

    工厂模式基础上再结合单例模式的编程思想,不但可以降低耦合,同时也可以节省内存消耗,为程序提供更高的鲁棒性。


    人若无名,专心练剑~!

    相关文章

      网友评论

          本文标题:工厂模式 + 单例模式实战

          本文链接:https://www.haomeiwen.com/subject/mysjdhtx.html