参考书籍《大话设计模式》
案例
假设现在有基于Mysql数据库操作用户的的MysqlUserServiceImpl和基于Oracle数据库操作用户的OracleUserServiceImpl,代码如下:
public class User {
}
public interface UserService {
void insert(User user);
User findById(Integer id);
}
public class MysqlUserServiceImpl implements UserService {
@Override
public void insert(User user) {
System.out.println("mysql 插入 user");
}
@Override
public User findById(Integer id) {
System.out.println("mysql 获取 user");
return null;
}
}
public class OracleUserServiceImpl implements UserService {
@Override
public void insert(User user) {
System.out.println("oracle 插入 user");
}
@Override
public User findById(Integer id) {
System.out.println("oracle 获取 user");
return null;
}
}
客户端的代码如下,可以在客户端选择自己需要的数据库对应的服务。
public class Client {
public static void main(String[] args) {
// 如果客户端想使用基于Mysql的服务
// ......这里省略若干代码(在创建对应的产品时,可能有其他逻辑代码)
// UserService userService = new MysqlUserServiceImpl();
// 如果客户端想使用基于Oracle的服务
// ......这里省略若干代码(在创建对应的产品时,可能有其他逻辑代码)
UserService userService = new OracleUserServiceImpl();
userService.insert(new User());
userService.findById(1);
}
}
对于这样的代码,有许多弊端。
- 客户端需要知道具体的服务(产品)。比如这里的MysqlUserServiceImpl和OracleUserServiceImpl
- 代码重复。如果有多个客户端,都这样创建服务(产品),因为在创建服务时可能还有其他逻辑代码,就会造成大量代码重复。
这些问题可以使用简单工厂解决。
简单工厂
提供一个工厂类来创建不同类型的产品对象,根据输入的参数决定返回哪个具体的产品。
一个工厂类负责创建所有产品,适用于产品种类较少的情况。
public interface UserService {
void insert(User user);
User findById(Integer id);
}
public class OracleUserServiceImpl implements UserService {
@Override
public void insert(User user) {
System.out.println("oracle 插入 user");
}
@Override
public User findById(Integer id) {
System.out.println("oracle 获取 user");
return null;
}
}
public class MysqlUserServiceImpl implements UserService {
@Override
public void insert(User user) {
System.out.println("mysql 插入 user");
}
@Override
public User findById(Integer id) {
System.out.println("mysql 获取 user");
return null;
}
}
public class UserServiceFactory {
public static UserService getUserService(String databaseType) {
UserService userService = null;
switch (databaseType) {
case "Mysql":
userService = new MysqlUserServiceImpl();
break;
case "Oracle":
userService = new OracleUserServiceImpl();
break;
}
return userService;
}
}
public class Client {
public static void main(String[] args) {
UserService userService = UserServiceFactory.getUserService("Mysql");
userService.insert(new User());
userService.findById(1);
}
}
在简单工厂代码中,客户端依旧可以自行选择使用哪种数据库,同时让客户端的代码和业务代码隔离出来,变得更加简洁。
但是简单工厂也有自己的弊端。
- 违背了"开闭原则"。如果现在想要增加一个基于SqlServer数据库的服务SqlServerUserServiceImpl,那么我们得去修改工厂,在switch分支中增加代码。这违法了"开闭原则"(对扩展开放,对修改关闭)。
- 不同产品的创建都在同一个工厂中,会使这个工厂变得臃肿。每个产品创建时可能有其他逻辑代码(比如在创建MysqlUserServiceImpl后,调用它的set方法设置它的属性),那么这个工厂就会变得庞大复杂。
工厂模式
工厂方法模式将对象的创建延迟到子类中进行。具体的工厂类负责创建产品实例,而客户端只需要依赖抽象工厂接口,具体的工厂实现类会创建相应的具体产品。
通过工厂接口和具体工厂类,允许子类决定创建哪种产品,适用于需要扩展产品类型的情况。
现在我们把前面的简单工厂类UserServiceFactory定义成一个接口了,然后定义具体的工厂创建对应的产品。这样如果现在要新增一个基于Sqlserver的服务,只要新增一个具体的工厂SqlserverUserServiceFactory实现UserServiceFactory工厂,新增一个具体的产品SqlserverUserServiceImpl实现UserService。
public interface UserServiceFactory {
UserService getUserService();
}
public class OracleUserServiceFactory implements UserServiceFactory {
@Override
public UserService getUserService() {
// ......这里省略若干代码(在创建对应的产品时,可能有许多前置逻辑代码)
return new OracleUserServiceImpl();
}
}
public class MysqlUserServiceFactory implements UserServiceFactory {
@Override
public UserService getUserService() {
// ......这里省略若干代码(在创建对应的产品时,可能有许多前置逻辑代码)
return new MysqlUserServiceImpl();
}
}
public class SqlserverUserServiceFactory implements UserServiceFactory {
@Override
public UserService getUserService() {
// ......这里省略若干代码(在创建对应的产品时,可能有许多前置逻辑代码)
return new SqlServerUserServiceImpl();
}
}
public class Client {
public static void main(String[] args) {
// 客户端想要使用mysql的服务,创建对应的工厂即可
// UserServiceFactory userServiceFactory = new MysqlUserServiceFactory();
// 客户端想要使用oracle的服务,创建对应的工厂即可
// UserServiceFactory userServiceFactory = new OracleUserServiceFactory();
// 客户端想要使用sqlserver的服务,创建对应的工厂即可
UserServiceFactory userServiceFactory = new SqlserverUserServiceFactory();
UserService userService = userServiceFactory.getUserService();
userService.insert(new User());
userService.findById(1);
}
}
这就解决了简单工厂中违背“开闭原则”问题。把创建产品放到对应的具体工厂,就能把创建某个产品时的其他逻辑代码维护到具体的工厂,就不会出现所有产品的创建的代码都在一个工厂类(简单工厂就是把所有产品的创建维护在一个工厂中)。
缺陷:
- 复杂度增高,新增一个产品时需要新增对应的工厂和产品类。
- 客户端需要知道具体的工厂。(这个和一开始没有使用简单工厂案例有点类似,那个是客户端需要知道具体的产品)
抽象工厂
用于创建一系列相关产品,适用于多个产品系列的情况。
假设现在新增一个专门管理部门的服务。对应代码如下:
public interface DeptService {
void insert(Dept dept);
Dept findById(Integer id);
}
public class MysqlDeptServiceImpl implements DeptService {
@Override
public void insert(Dept dept) {
System.out.println("mysql 插入 dept");
}
@Override
public Dept findById(Integer id) {
System.out.println("mysql 获取 dept");
return null;
}
}
public class OracleDeptServiceImpl implements DeptService {
@Override
public void insert(Dept dept) {
System.out.println("oracle 插入 dept");
}
@Override
public Dept findById(Integer id) {
System.out.println("oracle 获取 dept");
return null;
}
}
public interface UserService {
void insert(User user);
User findById(Integer id);
}
public class MysqlUserServiceImpl implements UserService {
@Override
public void insert(User user) {
System.out.println("mysql 插入 user");
}
@Override
public User findById(Integer id) {
System.out.println("mysql 获取 user");
return null;
}
}
public class OracleUserServiceImpl implements UserService {
@Override
public void insert(User user) {
System.out.println("oracle 插入 user");
}
@Override
public User findById(Integer id) {
System.out.println("oracle 获取 user");
return null;
}
}
public interface ServiceFactory {
UserService getUserService();
DeptService getDeptService();
}
public class MysqlServiceFactory implements ServiceFactory {
@Override
public UserService getUserService() {
// ......这里省略若干代码(在创建对应的产品时,可能有许多前置逻辑代码)
return new MysqlUserServiceImpl();
}
@Override
public DeptService getDeptService() {
return new MysqlDeptServiceImpl();
}
}
public class OracleServiceFactory implements ServiceFactory {
@Override
public UserService getUserService() {
// ......这里省略若干代码(在创建对应的产品时,可能有许多前置逻辑代码)
return new OracleUserServiceImpl();
}
@Override
public DeptService getDeptService() {
return new OracleDeptServiceImpl();
}
}
public class Client {
public static void main(String[] args) {
ServiceFactory serviceFactory = new OracleServiceFactory();
UserService userService = serviceFactory.getUserService();
DeptService deptService = serviceFactory.getDeptService();
userService.insert(new User());
userService.findById(1);
deptService.insert(new Dept());
deptService.findById(1);
}
}
抽象工厂主要管理的是一系列的产品,比如这里基于Mysql的MysqlDeptServiceImpl和MysqlUserServiceImpl就是属于同一系列的产品。
抽象工厂这样做的好处:
- 便于切换产品的系列,可以避免依赖导致的错误。假设没有抽象工厂,那你正好使用了基于mysql的MysqlUserServiceImpl和基于oracle的OracleDeptServiceImpl,两个service之间存在某种依赖,会因为数据库的不同可能导致某些错误。
- 客户端不需要知道具体的产品。对于客户端来说,只知道是UserService,不需要知道是MysqlUserServiceImpl还是OracleUserServiceImpl
弊端:
- 新增产品复杂度变高。假设现在需要增加一个role表,需要创建RoleService、MysqlRoleServiceImpl、OracleRoleServiceImpl。需要改动ServiceFactory、MysqlServiceFactory、OracleServiceFactory。才能实现
- 客户端多时,切换产品系列复杂。假设现在有100个客户端,都使用的是
ServiceFactory serviceFactory = new OracleServiceFactory();
现在要改成Mysql的,那么就要修改100行代码为ServiceFactory serviceFactory = new MysqlServiceFactory();
简单工厂+反射优化抽象工厂
public interface DeptService {
void insert(Dept dept);
Dept findById(Integer id);
}
public class OracleDeptServiceImpl implements DeptService {
@Override
public void insert(Dept dept) {
System.out.println("oracle 插入 dept");
}
@Override
public Dept findById(Integer id) {
System.out.println("oracle 获取 dept");
return null;
}
}
public class MysqlDeptServiceImpl implements DeptService {
@Override
public void insert(Dept dept) {
System.out.println("mysql 插入 dept");
}
@Override
public Dept findById(Integer id) {
System.out.println("mysql 获取 dept");
return null;
}
}
public interface UserService {
void insert(User user);
User findById(Integer id);
}
public class OracleUserServiceImpl implements UserService {
@Override
public void insert(User user) {
System.out.println("oracle 插入 user");
}
@Override
public User findById(Integer id) {
System.out.println("oracle 获取 user");
return null;
}
}
public class MysqlUserServiceImpl implements UserService {
@Override
public void insert(User user) {
System.out.println("mysql 插入 user");
}
@Override
public User findById(Integer id) {
System.out.println("mysql 获取 user");
return null;
}
}
public class ServiceFactory {
private final static String dataType = "Oracle";
private final static String packedPath = "org.aqiu.design1.demo4";
public static UserService getUserService() throws Exception {
return (UserService) Class.forName(packedPath + "." + dataType + "UserServiceImpl").newInstance();
}
public static DeptService getDeptService() throws Exception {
return (DeptService) Class.forName(packedPath + "." + dataType + "DeptServiceImpl").newInstance();
}
}
public class Client {
public static void main(String[] args) throws Exception {
UserService userService = ServiceFactory.getUserService();
DeptService deptService = ServiceFactory.getDeptService();
userService.insert(new User());
userService.findById(1);
deptService.insert(new Dept());
deptService.findById(1);
}
}
优化后的代码,去掉了抽象工厂和具体工厂。现在只有一个简单工厂,利用反射机制,复杂不同产品的创建,同时保持了抽象工厂的特点(能够管理一系列产品)。
这样解决了抽象工厂的弊端:
- 解决新增产品复杂度变高。现在新增产品,只要创建RoleService、MysqlRoleServiceImpl、OracleRoleServiceImpl,改一下ServiceFactory类,就能实现。
- 解决客户端多时,切换产品系列复杂。现在只要修改ServiceFactory类的属性dataType,就能让任意多的客户端从使用mysql改为使用oracle。甚至我们可以编写一个配置文件,让这个字段的值从配置文件中读取。