情境:在需要更改项目数据库时,代码尽量少的更改。
为了可以切换数据库,首先可以想到使用工厂方法模式,定义一个用于创建对象的接口,让子类决定实例化哪一个类。
代码如下
IUser接口,用于客户端访问,解除与具体数据库访问的耦合。
public interface IUser {
void insert(User user);
}
SqlServerUser类,用于访问SqlServer的User
public class SqlServerUser implements IUser{
@Override
public void insert(User user){
print("在SQL Server中给User表增加一条记录");
}
}
AccessUser类,用于访问Access的User
public class AccessUser implements IUser{
@Override
public void insert(User user){
print("在Access中给User表增加一条记录");
}
}
IFactory接口,定义一个创建访问User表对象的工厂接口
public interface IFactory {
IUser getUserFactory();
}
SqlServerFactory类,实现IFactory接口,实例化SqlServerUser
public class SqlServerFactory implements IFactory {
@Override
public IUser createUserFactory() {
return new SqlServerUser();
}
}
AccessFactory类,实现IFactory接口,实例化AccessUser
public class AccessFactory implements IFactory {
@Override
public IUser createUserFactory() {
return new AccessUser();
}
}
测试代码
public class Test {
public static void main(String[] args) {
User user = new User();
//若要改成Access数据库,只需要将本句改成IFactory factory = new AccessFactory();
IFactory factory = new SqlServerFactory();
IUser userFactory = factory.createUserFactory();
userFactory.insert(user);
}
}
此时,由于多态的关系,使得声明IUser接口的对象userFactory事先根本不知道是在访问哪个数据库,却可以在运行时很好地完成工作,这就是所谓的业务逻辑与数据访问的解耦。
但是如果增加一个部门表(Department表),需要做下面的改动。
新增IDepartment接口,用于客户端访问,解除与具体数据库访问的耦合
public interface IDepartment {
void insert(Department department);
}
新增SqlServerDepartment类,用于访问SqlServer的Department
public class SqlServerDepartment implements IDepartment{
@Override
public void insert(Department department){
print("在SQL Server中给Department表增加一条记录");
}
}
新增AccessDepartment类,用于访问Access的Department
public class AccessDepartment implements IDepartment{
@Override
public void insert(Department department){
print("在Access中给Department表增加一条记录");
}
}
修改IFactory接口
public interface IFactory {
IUser createUserFactory();
IDepartment createDepartmentFactory();
}
修改SqlServerFactory
public class SqlServerFactory implements IFactory {
@Override
public IUser createUserFactory() {
return new SqlServerUser();
}
@Override
public IDepartment createDepartmentFactory() {
return new SqlServerDepartment();
}
}
修改AccessFactory
public class AccessFactory implements IFactory {
@Override
public IUser createUserFactory() {
return new AccessUser();
}
@Override
public IDepartment createDepartmentFactory() {
return new AccessDepartment();
}
}
抽象工厂模式
抽象工厂模式(Abstract Factory),提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。[DP]
抽象工厂模式(Abstract Factory)结构图
抽象工厂模式结构图.pngAbstractProductA和AbstractProductB是两个抽象产品,之所以为抽象,是因为它们都有可能有两种不同的实现,就刚才的例子来说就是User和Department,而ProductA1、ProductA2和ProductB1、ProductB2就是对两个抽象产品的具体分类的实现,比如ProductA1可以理解为是SqlServerUser,而ProductB1是AccessUser。
IFactory是一个抽象工厂接口,它里面应该包含所有的产品创建的抽象方法。而ConcreteFactory1和ConcreteFactory2就是具体的工厂。就像SqlServerFactory和AccessFactory一样。
通常是在运行时刻再创建一个ConcreteFactory类的实例,这个具体的工厂再创建具有特定实现的产品对象,也就是说,为创建不同的产品对象,客户端应使用不同的具体工厂。
抽象工厂模式的优点与缺点
优点
1、易于交换产品系列,由于具体工厂类,在一个应用中只需要再初始化的时候出现一次,这就使得改变一个应用的具体工厂变得非常容易,它只需要改变具体工厂即可使用不同的产品配置。
2、它让具体的创建实例过程与客户端分离,客户端是通过他们的抽象接口操纵实例,产品的具体类名也被具体工厂的实现分离,不会出现在客户代码中。
缺点
每一个类的开始都要声明IFactory factory = new SqlServerFactory()新增功能,需要修改Factory相关类。
用简单工厂来改进抽象工厂
新增DataAccess类
public class DataAccess {
private static final String db = "SqlServer";
public static IUser createUser() {
IUser result = null;
switch (db) {
case "SqlServer":
result = new SqlServerUser();
break;
case "Access":
result = new AccessUser();
break;
}
return result;
}
public static IDepartment createDepartment() {
IDepartment result = null;
switch (db) {
case "SqlServer":
result = new SqlServerDepartment();
break;
case "Access":
result = new AccessDepartment();
break;
}
return result;
}
}
测试代码
public class Test {
public static void main(String[] args) {
User user = new User();
IUser userFactory = DataAccess.createUser();
userFactory.insert(user);
}
}
抛弃IFactory、SqlServerFactory、AccessFactory三个工厂类,取而代之的是DataAccess类,客户端没有出现任何一个SqlServer或Access的字样,达到解耦的目的。
用反射解决switch case
修改DataAccess类
public class DataAccess {
private static final String fullName = "com.luomo.study.design.patten.absfactory.";
private static final String db = "Access";
public static IUser createUser() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
Class<?> cls = Class.forName(fullName+db+"User");
return (IUser) cls.newInstance();
}
public static IDepartment createDepartment() throws IllegalAccessException, InstantiationException, ClassNotFoundException {
Class<?> cls = Class.forName(fullName+db+"Department");
return (IDepartment) cls.newInstance();
}
}
所有在用简单工厂的地方,都可以考虑用反射技术来去除switch或if,解除分支判断带来的耦合。
网友评论