使用工厂模式解耦

上篇博客JDBC的demo展示了程序之间的耦合(程序间的依赖性),那么如何进行解耦,就使用到工厂模式。

在开发过程中,项目主要分为业务层、持久层(dao,管数据访问的)和表现层。
Maven工程架构如下:

└── com
    └── eastnotes
        ├── dao
        │   ├── AccountDao.java
        │   └── impl
        │       └── AccountDaoImpl.java
        ├── service
        │   ├── AccountService.java
        │   └── impl
        │       └── AccountServiceImpl.java
        └── ui
            └── Client.java
dao层
/**
 * 账户的持久层操作类
 */
public class AccountDaoImpl implements AccountDao {

    public void saveAccount(){
        System.out.println("保存了账户");
    }

}
public interface IAccountDao {
 void saveAccount();
}
service层

service层负责业务逻辑,向dao层发送数据访问指令。

/**
 * 账户的业务层实现类
 */
public class AccountServiceImpl implements AccountService {

    private AccountDao accountDao = new AccountDaoImpl();
    public void saveAccount(){
        accountDao.saveAccount();
    }
}
public interface IAccountService {
	//模拟保存账户
	void saveAccount();
}

ui是表现层,用于调用业务层方法。

/**
 * 模拟一个表现层,用于调业务层
 */
public class Client {
    public static void main(String[] args) {
        AccountService as = new AccountServiceImpl();
        as.saveAccount();
    }
}

最终得到dao层方法的输出:

 保存了账户

Client类使用new关键字创建了一个业务层的实现类对象,在业务层实现类,AccountServiceImpl同样使用new关键字创建了一个持久层实现

AccountService as = new AccountServiceImpl();
private AccountDao accountDao = new AccountDaoImpl();

那么使用工厂模式解耦:
新建factory包,beanfactory类:用于解耦的工厂类。作用是创建Bean对象。
在这些组成部分中,有的类可以被重复使用,比如说service可以被很多的serverlet使用,dao可以被很多的service来使用。那么这些被重复使用的类就可以成为可重用组件,也就是Bean。所以,我们新创建的这个factory工厂类,就是来创建service和dao对象的。

解耦的思路分为两步:

  • 需要一个配置文件来配置我们的service和dao,配置文件的内容是一个个的键值对,键是唯一表示,值是这个类的全限定类名:唯一标志=全限定类名。
  • 读取配置文件,通过反射创建对象。配置文件可选xml或者是properties,在此处我们使用properties。

配置文件放在resources目录下,取名为bean.properties,文件内容如下:

accountService=com.eastnotes.service.impl.AccountServiceImpl
accountDao=com.eastnotes.dao.impl.AccountDaoImpl
├── java
│   └── com
│       └── eastnotes
│           ├── dao
│           │   ├── AccountDao.java
│           │   └── impl
│           │       └── AccountDaoImpl.java
│           ├── factory
│           │   └── BeanFactory.java
│           ├── service
│           │   ├── AccountService.java
│           │   └── impl
│           │       └── AccountServiceImpl.java
│           └── ui
│               └── Client.java
└── resources
    └── bean.properties
package com.eastnotes.factory;

import java.io.InputStream;
import java.util.Properties;

public class BeanFactory {
    //定义一个Properties对象
    private static Properties props;

    //使用静态代码块为Properties对象赋值
    static{
        try {
            //实例化对象
            props = new Properties();

            //获取Properties文件流对象
            InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
            props.load(in);
        }catch (Exception e){
            throw new ExceptionInInitializerError("初始化Properties失败");
        }
    }

    /**
     * 根据bean的名称获取bean对象
     * 使用static关键字可以通过类名来访问这个函数
     * @param beanname
     * @return
     */
    public static Object getBean(String beanname){
        Object bean = null;
        try {
            String beanPath = props.getProperty(beanname);
            bean = Class.forName(beanPath).newInstance();   //反射创建对象
        }catch (Exception e){
            e.printStackTrace();
        }
        return bean;
    }
}

写完这个可以提供Bean对象的工厂类之后,我们就可以改造业务层和表现层的对象创建方式了:
将业务层创建对象的方式改为:

private AccountDao accountDao = (AccountDao) BeanFactory.getBean("accountDao");

将表现层创建对象的方式改为:

AccountService as = (AccountService) BeanFactory.getBean("accountService");
工厂解耦问题

我们将表现层的代码改为下面:

public class Client {
    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            AccountService as = (AccountService) BeanFactory.getBean("accountService");
            System.out.println(as);
        }
    }
}

输出结果:

com.itheima.service.impl.AccountServiceImpl@4f023edb
com.itheima.service.impl.AccountServiceImpl@3a71f4dd
com.itheima.service.impl.AccountServiceImpl@7adf9f5f
com.itheima.service.impl.AccountServiceImpl@85ede7b
com.itheima.service.impl.AccountServiceImpl@5674cd4d

由此我们可以看出,通过工厂类得到的对象是多例的,因为每次生成的对象都不一样。单例对象从始至终都是一个对象,我们的Servlet就是一个单例对象。单例对象的类成员只会被初始化一次,而多例对象会被初始化多次。

因此多例对象的执行效率没有单例对象高。而单例对象存在线程安全问题。但是,在我们的Service和DAO中,我们并没有在类中定义成员变量,因此不存在线程问题,所以我们并不需要多例对象。
出现多例对象的问题出现在工厂类中的下面这个代码中:

bean = Class.forName(beanPath).newInstance(); 

我们通过反射使用newInstance创建对象的时候,每次都会调用默认构造函数。因此,当他创建完对象之后,我们应该马上把对象存起来。用什么存呢?Map容器

工厂解耦改进

首先,在工厂类中定义一个由static修饰的Map,用于存放我们要创建的对象,我们将它称之为容器。这个Map的key是String类型,value是Object类型。

然后,再静态代码块中,取出配置文件中的所有key,然后根据每个key获取value,也就是我们的全限定类名,然后通过反射创建每一个对象。此时key有了,value有了,我们就可以把它存入Map(容器)中。

静态代码块只在类加载的时候执行一次,等到它执行完,这个容器内已经装好了我们所有需要的Bean对象,那么当我们获取对象的时候就没有这么麻烦了。直接根据bean的名称从容器中取出对象,然后直接返回就好了。

package com.eastnotes.factory;

import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

public class BeanFactoryUpdate {
    //定义一个Properties对象
    private static Properties propres;

    //定义一个Map,用于存放我们要创建的对象,我们把它称之为容器,创建它是为了实现单例对象
    public static Map<String,Object> beans;

    //使用静态代码块为Properties对象赋值,在类加载的时候只执行一次
    static {
        try {
            //实例化对象
            propres = new Properties();
            //获取Properties文件的流对象
            InputStream in = BeanFactoryUpdate.class.getClassLoader().getResourceAsStream("bean.properties");
            propres.load(in);

            //实例化容器对象
            beans = new HashMap<String, Object>();

            //取出配置文件中所有的keys
            Enumeration keys = propres.keys();

            //遍历枚举
            while (keys.hasMoreElements()){
                //取出每个key
                String key = keys.nextElement().toString();
                //根据key获取value
                String beanPath = propres.getProperty(key);
                //反射创建对象
                try {
                    Object value = Class.forName(beanPath).newInstance();
                    //把key和value存入map中
                    beans.put(key,value);
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        } catch (IOException e) {
            throw new ExceptionInInitializerError("初始化Properties失败");
        }
    }

    //【单例模式下】根据bean的名称获取bean对象
    public static Object getBean(String beanName){
        return beans.get(beanName);
    }

}

这样改进后,此工厂在创建Bean对象的时候会使用单例的形式,当我们在表现层循环获取某个对象的时候,得到的将是同一个对象。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值