面向接口及单例工厂随笔

面向接口及单例工厂

单例工厂是工厂模式的一种,表示生产出的产品为单例产品。java

  在上古web开发中,后端是servlet、service、dao这三层。servlet依赖service,service依赖dao,为何说依赖,由于该层持有另外一层的对象:web

//UserServlet
public class UserServlet extends HttpServlet {
    //一个UserServlet中持有一个UserService的实现类对象
    //这里UserService为接口,指向其实现类
    private UserService userService  = new UserServiceImpl();

    //其余代码
    ...
}

//Userservice
public interface UserService{
    private UserDao userDao = new UserDaoImpl();

    ...
}

//UserDao
public interface UserDao{
    //do something
}

  这里为何将UserService和UserDao声明为接口,并让其引用实现类呢?后端

  咱们要知道的是,Java语言是编译型语言,.java源代码文件是须要编译成.class以后才能运行,若是咱们须要切换业务模式,好比须要使用UserService2的业务模式,对于上面用new的方式去建立对象,咱们就须要去修改源代码,而后从新编译才能运行。而在项目大的时候,编译是很是耗费时间的。服务器

  对于时间就是金钱的互联网行业,在你编译的时候,你的对手可能就把你的顾客给抢走了,那么有没有一种方法能够快速切换呢?多线程

  有,就是使用反射 + 配置文件的方式来获取对象,当须要不一样的对象时只须要修改配置文件,而不用从新编译:工具

public class UserServlet extends HttpServlet throws Exception {
    //1.读取配置,参数是.properties文件的文件名
    ResourceBundle rb = ResourceBundle.getBundle("service");
    //获取配置文件中的类路径
    String classPath = rb.getString("UserService");
    //经过类路径来得到实例,也就是建立对象
    private UserService userService  = Class.forName(classPath).newInstance();

    //其余代码
    ...
}

service.properties文件:优化

UserService = com.bilibili.service.impl.UserServiceImpl

  这样当咱们想获取不一样的UserServiceImpl实例时只须要修改配置文件并重启服务器便可。线程

  这也是为何上面使用接口引用其实现类,若是直接使用实现类去引用的话则不能达到这种效果。code

  上面的优化后虽然不用从新编译了,可是问题又来了:除了UserServlet中这样反射得到了一个对象,另外一个Servlet中——ProductServlet也须要一个相似的ProductService对象,甚至Service中也须要dao对象,这些代码都很像,那么咱们可不能够把这一部分给单独抽取出来,做为工具类使用能?对象

尝试一下:

public class BeansFactory {
    public static Object getInstance(String beanName){

        //1.读取配置
        ResourceBundle rb = ResourceBundle.getBundle("beans");
        String classPath = rb.getString(beanName);
        //2.反射建立对象
        Object o = null;
        try {
            o = Class.forName(classPath).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
        return o;
    }
}

  咱们把这一部分逻辑类似的代码做为一个类的静态方法来使用,这个方法是专门用来生产对象的,咱们把这种模式叫作工厂模式

  上面的方式虽然很不错,但仍是存在一些问题的:若是不止一个Servlet中须要使用UserService的实现类对象,好比Produce中也须要使用,是否是每一个须要使用的地方都要用这种方式持有一个对象呢?
  对于每一个须要使用到UserService的地方来讲,咱们只关注UserService中的方法,咱们只是想使用它的功能,咱们不关注这个对象怎么建立,也不关注是不是同一个对象,若是每一个须要使用到的地方都去 getgetInstance来得到一个对象,这样会形成资源的浪费,那么能不能只建立一个对象来供许多地方使用能?

能够,这就是单例工厂:

public class BeansFactory {
    //咱们把建立过的对象使用Map保存起来
    private static Map<String,Object> map = new HashMap<>();

    //这里使用synchronized修饰能够防止多线程问题
    public static synchronized Object getInstance2(String beanName){
        //当有地方须要得到对象时,优先从map中获取
        Object o = map.get(beanName);
        //o为null表明map中没有这种对象,此时咱们就去建立这个对象并保存在map中
        if(o == null){
            //若是获取不到,再去反射机制建立,而且保存到map。
            //1.读取配置
            ResourceBundle rb = ResourceBundle.getBundle("beans");
            String classPath = rb.getString(beanName);
            //2.反射建立对象
            try {
                o = Class.forName(classPath).newInstance();
                //保存
                map.put(beanName,o);
            } catch (Exception e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            }
        }

        return o;
    }
}

这样咱们获取的的就是同一个对象了。

相关文章
相关标签/搜索