1. 程式人生 > >多態和簡單對象工廠

多態和簡單對象工廠

個數 保存 添加 抽象類 close div date ssl col

  Java 的反射技術和多態特性是框架開發、組件解耦的核心,在這方面,Spring 的 IOC 和 DI 為我們提供了一個極好的學習範例,Spring 的 IOC 使用反射技術創建、管理對象,DI 使用多態技術為組件註入依賴對象。

  在沒有學習 Spring 之前,簡單的解決方案是使用一個 .properties 文件保存程序中使用的接口、實現類類型鍵值信息,然後在程序中使用一個全局 Properties 對象保存這些信息,並且使用反射技術把這些實現類初始化、提供一個靜態的方法獲取指定接口的實現類對象,在組件中就可以使用依賴對象的鍵獲取需要的對象。

  這樣的方案帶來的好處就是:當我們需要修改某個組件的實現方式時,比如把之前 JDBC 的 DAO 實現改為 Hibernate 實現,只要把這些新的實現類放到 classpath 下,把 .properties 文件對應接口的實現類類型改成新的 Hibernate 實現類,而不需要修改依賴組件的代碼。

1、接口和抽象類

  共同點:

  (1)二者都不能創建對象;

  (2)抽象方法要被實現,不能是靜態的,也不能是私有的;

  (3)一個類如果沒有實現父類或接口的全部抽象方法,那麽該類只能為抽象類

  區別:

  (1)接口可以繼承接口,而且可以多繼承,但是不能實現;類只能繼承一個父類或抽象類,但可以實現多個接口;

  (2)抽象類可以有構造方法,接口不能有構造方法;

  (3)接口都是抽象方法;抽象類可以沒有抽象方法,而且一個類如果有抽象方法,那麽這個類就必須是抽象類;

  (4)接口只能定義公共靜態的常量;抽象類中可以定義普通變量;

  (5)接口更適合做子系統、組件、模塊的技術、功能規範;抽象類更適合做通用業務邏輯的抽象和封裝。比如:DAO、Service 層的核心功能就需要使用接口來規範;而DAO層實現類都具有的一些通用邏輯就可以放在一個抽象類中,然後讓具體的DAO實現類來繼承這個抽象類。

2、方法的重寫和重載

  方法的重寫 Override 和重載 Overload 都是 Java 多態的不同表現。重寫 Override 是父類與子類之間多態性的一種表現,重載 Overload 是一個類中多態性的一種表現。

  重寫:也叫做覆蓋。子類中的方法與父類中的某一方法具有相同的方法名、返回值類型和參數列表,則該方法將覆蓋父類方法。有幾點需要註意:

  (1)子類方法不能拋出比父類方法更多或“更大”的異常;

  (2)子類方法的訪問權限級別不能比父類方法更嚴格;

  (3)一定要有相同的返回值類型;

  (4)參數列表需要相同,如果不同,那麽這個方法相當於重載,而不是重寫

  重載:一個類中定義了多個同名的方法,它們的參數個數或參數類型不同,這種情況就是方法重載。但是和返回值的類型無關。

3、示例代碼概述

  目錄結構如下:

  技術分享圖片

4、DAO接口和默認實現

  UserDao接口

 1 public interface UserDao {
 2 
 3     public void add();
 4 
 5     public void del();
 6 
 7     public void update();
 8 
 9     public void get();
10 }

  UserDaoImpl 實現類

 1 public class UserDaoImpl implements UserDao {
 2 
 3     public void add() {
 4         System.out.println("使用的是demo.dao.impl.UserDaoImpl的添加方法");
 5     }
 6 
 7     public void del() {
 8 
 9         System.out.println("使用的是demo.dao.impl.UserDaoImpl的刪除方法");
10     }
11 
12     public void update() {
13 
14         System.out.println("使用的是demo.dao.impl.UserDaoImpl的更新方法");
15     }
16 
17     public void get() {
18 
19         System.out.println("使用的是demo.dao.impl.UserDaoImpl的獲取方法");
20     }
21 }

5、Service接口和默認實現

  UserService 接口

 1 public interface UserService {
 2 
 3     public void addUser();
 4 
 5     public void delUser();
 6 
 7     public void updateUser();
 8 
 9     public void getUser();
10 }

  UserServiceImpl 實現類,註意在這裏需要使用對象工廠註入依賴的 DAO 對象

 1 public class UserServiceImpl implements UserService {
 2 
 3     private UserDao userDao;
 4 
 5     public UserServiceImpl() {
 6         super();
 7         this.userDao = (UserDao) ObjectFactory.getObject("userDao");
 8     }
 9 
10     public void addUser() {
11         this.userDao.add();
12         System.out.println("使用的是demo.service.impl.UserServiceImpl的addUser方法");
13     }
14 
15     public void delUser() {
16         this.userDao.del();
17         System.out.println("使用的是demo.service.impl.UserServiceImpl的delUser方法");
18     }
19 
20     public void updateUser() {
21         this.userDao.update();
22         System.out
23                 .println("使用的是demo.service.impl.UserServiceImpl的updateUser方法");
24     }
25 
26     public void getUser() {
27         this.userDao.get();
28         System.out.println("使用的是demo.service.impl.UserServiceImpl的getUser方法");
29     }
30 }

6、ObjectFactory 類和 objects.properties

  首先,看一下 objects.properties 配置文件,主要配置程序使用的接口實現類類型信息

  技術分享圖片

1 userDao=demo.dao.impl.UserDaoImpl
2 userService=demo.service.impl.UserServiceImpl

  我們需要寫個 ObjectFactory 類讀取配置並實例化對象,然後保存到全局“工廠”

技術分享圖片
 1 public final class ObjectFactory {
 2     
 3     /**
 4      * 保存接口的鍵(通常使用接口的簡單類名首字母小寫)和實現類對象信息<br/>
 5      * 
 6      * 使用者可以調用getObject方法傳入配置文件中key獲取對應實現類對象<br/>
 7      */
 8     private static Map<String, Object> objectMap = new HashMap<String, Object>();
 9     
10     /**
11      * 用於加載配置文件
12      */
13     private static Properties prop = new Properties();
14     
15     static {
16         try {
17             // 讀取配置文件
18             prop.load(ObjectFactory.class.getClassLoader().getResourceAsStream("objects.properties"));
19             // 叠代properties,對實現類逐一進行實例化
20             // 然後把實現類對象保存到objectMap中
21             for(Object k : prop.keySet()) {
22                 String key = k.toString();
23                 // 獲取值,即實現類的權限定名
24                 String className = prop.get(key).toString();
25                 // 加載類並實例化
26                 Class<?> cls = Class.forName(className);
27                 Object obj = cls.newInstance();
28                 // 把實現類對象保存到objectMap
29                 objectMap.put(key, obj);
30             }
31         } catch (IOException e) {
32             e.printStackTrace();
33             throw new RuntimeException("加載全局接口實現類配置文件失敗", e);
34         } catch (ClassNotFoundException e) {
35             e.printStackTrace();
36             throw new RuntimeException("有實現類未找到", e);
37         } catch (InstantiationException e) {
38             e.printStackTrace();
39             throw new RuntimeException("類實例化出錯", e);
40         } catch (IllegalAccessException e) {
41             e.printStackTrace();
42             throw new RuntimeException("類訪問出錯", e);
43         }
44     }
45     
46     /**
47      * 根據指定接口的鍵(通常使用接口的簡單類名首字母小寫)獲取對應配置的實現類對象
48      */
49     public static Object getObject(String key) {
50         return objectMap.get(key);
51     }
52 }
View Code

7、Servlet 和 JSP

  DecoupleServlet 類,需要使用對象工廠獲取 Service 對象

 1 public class DecoupleServlet extends HttpServlet {
 2 
 3     private static final long serialVersionUID = 1L;
 4 
 5     public DecoupleServlet() {
 6         super();
 7     }
 8 
 9     protected void doGet(HttpServletRequest request,
10             HttpServletResponse response) throws ServletException, IOException {
11         doPost(request, response);
12     }
13 
14     protected void doPost(HttpServletRequest request,
15             HttpServletResponse response) throws ServletException, IOException {
16 
17         request.setCharacterEncoding("UTF-8");
18         response.setContentType("text/html; charset=utf-8");
19 
20         UserService userService = (UserService) ObjectFactory
21                 .getObject("userService");
22 
23         userService.addUser();
24 
25         request.setAttribute("service", userService.getClass().getName());
26 
27         request.getRequestDispatcher("demo.jsp").forward(request, response);
28     }
29 }

  index.jsp 和 demo.jsp 省略。

  啟動項目後訪問 http://localhost:8080/demo/

  技術分享圖片

  點擊頁面鏈接

  技術分享圖片

  從頁面和控制臺輸出,可以看出程序確實使用了默認的 DAO 和 Service

8、修改 DAO 實現類配置

  首先,編寫一個新的 DAO 實現類 UserHibernateDao

 1 public class UserHibernateDao implements UserDao {
 2 
 3     public void add() {
 4         System.out.println("使用的是demo.dao.hibernate.UserHibernateDao的添加方法");
 5     }
 6 
 7     public void del() {
 8         System.out.println("使用的是demo.dao.hibernate.UserHibernateDao的刪除方法");
 9     }
10 
11     public void update() {
12         System.out.println("使用的是demo.dao.hibernate.UserHibernateDao的更新方法");
13     }
14 
15     public void get() {
16         System.out.println("使用的是demo.dao.hibernate.UserHibernateDao的獲取方法");
17     }
18 }

  修改配置

1 # userDao=demo.dao.impl.UserDaoImpl
2 userDao=demo.dao.hibernate.UserHibernateDao
3 userService=demo.service.impl.UserServiceImpl

  重啟項目,重新訪問

技術分享圖片

  可以看到控制臺輸出,已經成功地使用了新的 DAO 實現類。

多態和簡單對象工廠