多态和简单对象工厂

Posted 用户不存在!

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多态和简单对象工厂相关的知识,希望对你有一定的参考价值。

 

  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 实现类。

 

以上是关于多态和简单对象工厂的主要内容,如果未能解决你的问题,请参考以下文章

面向编程对象的好处及应用简单工厂模式(1-3)

设计模式——简单工厂模式

简单工厂类,有继承虚函数重写基类指针指向子类对象会发生多态

简单工厂模式(静态工厂方法模式)

简单工厂模式

简单工厂方法