工厂方法和简单工厂模式
前言
GOF( Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides)联合出版的书中提到的设计模式一共有23种,可以分为三大类:创建型模式(Creational Patterns)、结构型模式(Structural Patterns)、行为型模式(Behavioral Patterns)。
工厂模式的特点
这一章我们主要介绍创建型模式之工厂模式(Factory Pattern)。它的特点是,将对象的使用者和提供者进行隔离,从而达到解耦的作用。
比如我们在刚开始使用Java的时候,总会在代码里使用new
关键字来创建对象,如果创建出来的对象要进行一系列的初始化操作,还需要我们自己来做。如果后期代码需要修改,则需要修改多处使用到了该对象的地方,因此出现了工厂模式。
工厂——顾名思义就是生产产品的地方,而Java中的工厂就是生产对象的地方。Java中的工厂通常是一个类或者对象,它提供一个接口来获取对象,例如Factory.getBean()
。当然,这个Factory里的代码也是需要我们自己来写的~~
简单工厂模式
简单工厂模式不在23种设计模式之中,但是它是工厂模式的起点,而且应用范围也很广。
一个例子
由于某种原因,我们系统将用户分别存储在MySQL和Oracle数据库中了,根据不同的情况需要从两个数据库中取值。根据简单工厂模式,我们可以这样设计:
0. 工具类
用户类并不是我们工厂模式必须的,只是这个例子中用到的一个工具:
public class User {
// 用户信息略...
}
1. 对象接口
public interface UserDao {
// 根据用户名获取用户
User getUser(String username);
// 例如修改用户等其他方法略..
}
2. 业务类
- 连接MySQL数据库用的
public class MysqlUserDao implements UserDao {
@Override
public User getUser(String username) {
// ..省略一些列操作
System.out.println("使用Mysql数据库查找用户");
return new User();
}
}
- 连接Oracle数据库用的
public class OracleUserDao implements UserDao {
@Override
public User getUser(String username) {
// ..省略一些列操作
System.out.println("使用Oracle数据库查找用户");
return new User();
}
}
3. 工厂类
public class UserDaoFactory {
public static UserDao getBean(String type) {
if ("mysql".equals(type)) {
return new MysqlUserDao();
}
if ("oracle".equals(type)) {
return new OracleUserDao();
}
throw new RuntimeException("找不到对应的类型");
}
}
4. 测试
// 需要连接MySQL数据库时
UserDao userDao = UserDaoFactory.getBean("mysql");
userDao.getUser("test");
// 需要连接MySQL数据库时
UserDao userDao1 = UserDaoFactory.getBean("oracle");
userDao1.getUser("test");
简单工厂模式总结:
首先,简单工厂模式通常用来生产某一类的对象,这些对象通常是某个接口的子类。所以我们需要先创建一个通用的接口,然后再创建实际的业务类。
其次,在工厂中我们提供一个静态方法,返回类型是接口的类型。根据工厂类静态方法中传入的不同字符串,返回具体的子类。
逻辑和过程都非常的简单,而且应用范围也很广。但是也有一定的缺陷,比如说如果传入的字符串拼错了怎么办?【这个可以通过维护一个常量来解决】还有就是如果需要增加子类,那么就需要修改工厂类中的代码。
工厂方法模式
按照刚才的业务和需求,我们这次使用工厂方法模式来解决。
0. 工具类
用户类并不是我们工厂模式必须的,只是这个例子中用到的一个工具:
public class User {
// 用户信息略...
}
1. 对象接口
public interface UserDao {
// 根据用户名获取用户
User getUser(String username);
// 例如修改用户等其他方法略..
}
2. 业务类
- 连接MySQL数据库用的
public class MysqlUserDao implements UserDao {
@Override
public User getUser(String username) {
// ..省略一些列操作
System.out.println("使用Mysql数据库查找用户");
return new User();
}
}
- 连接Oracle数据库用的
public class OracleUserDao implements UserDao {
@Override
public User getUser(String username) {
// ..省略一些列操作
System.out.println("使用Oracle数据库查找用户");
return new User();
}
}
3. 工厂类接口
public interface UserDaoFactory {
UserDao getBean();
}
4. 为每个类型的业务类创建自己的工厂
- MysqlUserDao工厂
public class MysqlUserDaoFactory implements UserDaoFactory {
@Override
public UserDao getBean() {
return new MysqlUserDao();
}
}
- OracleUserDao工厂
public class OracleUserDaoFactory implements UserDaoFactory{
@Override
public UserDao getBean() {
return new OracleUserDao();
}
}
5.测试
// 创建工厂对象
MysqlUserDaoFactory mysqlUserDaoFactory = new MysqlUserDaoFactory();
// 获取业务类对象
UserDao userDao1 = mysqlUserDaoFactory.getBean();
userDao1.getUser("test");
// 创建工厂对象
OracleUserDaoFactory oracleUserDaoFactory = new OracleUserDaoFactory();
// 获取业务类对象
UserDao userDao2 = oracleUserDaoFactory.getBean();
userDao2.getUser("test");
工厂方法模式总结
工厂方法模式其实也比较简单。
-
为业务接口创建一个生产该接口的工厂接口
-
为每个业务类创建一个工厂,有多少个业务类就要创建多少个工厂
跟简单工厂模式的对比:
缺点:
结构上,工厂方法模式比简单工厂要复杂。
代码量上,由于每个业务类都需要创建属于自己的工厂,所以带么有不少的冗余。
优点:
如果需要新增业务类不用修改原来的代码,直接新增一个工厂即可。
那么使用哪一种比较好呢?通常使用简单工厂模式比较多,但是如果系统变化较快或者有其他原因,也可以选择使用工厂方法模式。