面向接口及單例工廠隨筆

面向接口及單例工廠

單例工廠是工廠模式的一種,表示生產出的產品爲單例產品。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;
    }
}

這樣咱們獲取的的就是同一個對象了。

相關文章
相關標籤/搜索