大话设计模式笔记(十二)の抽象工厂模式

2019-07-29| 发布者: admin| 查看: |

模拟访问数据库“新增用户”和“得到用户”,用户类假设只有id和name两个字段。

/**
 * 用户类
 * created by callmedevil on 2019/7/28.
public class user {
 private int id;
 private string name;
 // 省略 get set 方法
}
/**
 * 假设sqlserver 连接,用于操作user表
 * created by callmedevil on 2019/7/28.
public class sqlserveruser {
 public void insert{
 system.out.println{
 system.out.println {
 user user = new user;
 sqlserveruser su = new sqlserveruser;
 su.insert;
 su.getuser);
}
在sql server中给user表增加一条记录
在sql server中根据id得到user表一条记录

如果需要连接别的数据库,那么这个写法无法扩展,下面使用工厂方法模式实现

/**
 * 用于客户端访问,解除与具体数据库访问的耦合
 * created by callmedevil on 2019/7/28.
public interface iuser {
 void insert;
 user getuser;
}
/**
 * 用于访问sql server 的user
 * created by callmedevil on 2019/7/28.
public class sqlserveruser implements iuser {
 @override
 public void insert {
 system.out.println {
 system.out.println {
 system.out.println {
 system.out.println;
}
/**
 * 实现ifactory 接口,实例化sqlserveruser
 * created by callmedevil on 2019/7/28.
public class sqlserverfactory implements ifactory {
 @override
 public iuser createuser {
 return new sqlserveruser;
}
/**
 * 实现ifactory 接口,实例化accessuser
 * created by callmedevil on 2019/7/28.
public class accessfactory implements ifactory {
 @override
 public iuser createuser {
 return new accessuser;
}
public class test {
 public static void main {
 user user = new user;
 // 若要更改成 access 数据库,只需要将此处改成
 // ifactory factory = new accessfactory;
 ifactory factory = new sqlserverfactory;
 iuser iuser = factory.createuser;
 iuser.insert;
 iuser.getuser;
}

测试结果同上。

如果要增加一个部门表,需要怎么改?

/**
 * 部门表
 * created by callmedevil on 2019/7/28.
public class department {
 private int id;
 private string name;
 // 省略 get set 方法
}
/**
 * 用于客户端访问,解除与具体数据库访问的耦合
 * created by callmedevil on 2019/7/28.
public interface idepartment {
 void insert;
 department getdepartment;
}
/**
 * 用于访问sqlserver 的department
 * created by callmedevil on 2019/7/28.
public class sqlserverdepartment implements idepartment {
 @override
 public void insert {
 system.out.println {
 system.out.println {
 system.out.println {
 system.out.println;
 idepartment createdepartment; //增加的接口方法
}
/**
 * 实现ifactory 接口,实例化sqlserveruser
 * created by callmedevil on 2019/7/28.
public class sqlserverfactory implements ifactory {
 @override
 public iuser createuser {
 return new sqlserveruser;
 @override
 public idepartment createdepartment {
 return new sqlserverdepartment; //增加了sqlserverdepartment 工厂
}
/**
 * 实现ifactory 接口,实例化accessuser
 * created by callmedevil on 2019/7/28.
public class accessfactory implements ifactory {
 @override
 public iuser createuser {
 return new accessuser;
 @override
 public idepartment createdepartment {
 return new accessdepartment; //增加了accessdepartment 工厂
}
public class test {
 public static void main {
 user user = new user;
 department dept = new department;
 // 只需确定实例化哪一个数据库访问对象给 factory
 ifactory factory = new accessfactory;
 // 则此时已于具体的数据库访问解除了依赖
 iuser iuser = factory.createuser;
 iuser.insert;
 iuser.getuser;
 idepartment idept = factory.createdepartment;
 idept.insert;
 idept.getdepartment;
}
在access 中给user表增加一条记录
在access 中根据id得到user表一条记录
在access 中给department 表增加一条记录
在access 中根据id得到department表一条记录

提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

实际上上面的修改实现已经满足抽象工厂模式的实现方式,此处不再举例。

如果还要添加对项目表的访问,那么需要增加三个类,iproject、sqlserverproject、accessproject,还需要更改 ifactory、isqlserverfactory、accessfactory 才可以完全实现,这太糟糕了。编程是门艺术,这样大批量的改动,显然是非常丑陋的做法。

去除ifactory、sqlserverfactory、accessfactory,改为一个 dataaccess,用一个简单工厂模式来实现。

/**
 * 统一管理数据库访问
 * created by callmedevil on 2019/7/28.
public class dataaccess {
 // 数据库名称,可替换成 access
 private static final string db = sqlserver 
// private static final string db = access 
 public static iuser createuser {
 iuser user = null;
 switch  {
 case sqlserver :
 user = new sqlserveruser;
 break;
 case access :
 user = new accessuser;
 break;
 default:
 break;
 return user;
 public static idepartment createdepartment {
 idepartment department = null;
 switch  {
 case sqlserver :
 department = new sqlserverdepartment;
 break;
 case access :
 department = new accessdepartment;
 break;
 default:
 break;
 return department;
}
public class test {
 public static void main {
 user user = new user;
 department dept = new department;
 // 直接得到实际的数据库访问实例,而不存在任何的依赖
 iuser iuser = dataaccess.createuser;
 iuser.insert;
 iuser.getuser;
 idepartment idept = dataaccess.createdepartment;
 idept.insert;
 idept.getdepartment;
}
在sql server中给user表增加一条记录
在sql server中根据id得到user表一条记录
在sql server中给department 表增加一条记录
在sql server中根据id得到department表一条记录

虽然解决了抽象工厂模式中需要修改太多地方的问题,但又回到了简单工厂模式一开始的问题了,就是如果要连接 oracle 数据库,那么需要修改的地方则是 dataaccess 类中所有方法的 swicth 中加 case 分支了。

# 数据库名称,可更改成 access
db=sqlserver
/**
 * 统一管理数据库访问
 * created by callmedevil on 2019/7/28.
public class dataaccess {
 // 数据库名称,从配置文件中获取
 private static string db;
 public static iuser createuser throw____ception {
 if  == ) {
 return null;
 // 拼接具体数据库访问类的权限定名
 string classname = com.___. + db + user 
 return  class.forname.newinstance;
 public static idepartment createdeptment throw____ception {
 if  == ) {
 return null;
 // 拼接具体数据库访问类的权限定名
 string classname = com.___. + db + department 
 return  class.forname.newinstance;
 public static string getdb {
 return db;
 public static void setdb {
 dataaccess.db = db;
}
public class test {
 public static void main throw____ception {
 // 加载配置文件
 properties properties = new properties;
 inputstream is = new fileinputstream); // 配置文件所在路径,当前方式采用绝对路径获取
 properties.load;
 is.close;
 string db = properties.getproperty;
 dataaccess.setdb;
 user user = new user;
 iuser iuser = dataaccess.createuser;
 iuser.insert;
 iuser.getuser;
 department dept = new department;
 idepartment idept = dataaccess.createdeptment;
 idept.insert;
 idept.getdepartment;
}
在sql server中给user表增加一条记录
在sql server中根据id得到user表一条记录
在sql server中给department 表增加一条记录
在sql server中根据id得到department表一条记录

现在如果我们增加了 oracle 数据库访问,相关类的增加是不可避免的,这点无论用任何办法都解决不了,不过这叫扩展,开放-封闭原则告诉我们,对于扩展,我们开放,但对于修改,我们应该尽量关闭,就目前实现方式而言,只需要将配置文件中改为 oracle 即可达到目的,客户端也不需要任何修改。

所有在用简单工厂的地方,都可以考虑用反射技术来去除 switch 或 if,解除分支判断带来的耦合。

可以发现到目前为止,就“工厂”而言,已经包含了三种设计模式:

对上述模式的不同点将在后续推出,本文篇幅已经过长,此处先不叙述。