AbstractFactory模式即抽象工厂模式,因为《图解设计模式》一书中的示例程序并不是很能直观地理解复杂难懂的抽象工厂模式,我会使用一个简单更容易理解的例程进行说明。
示例
示例的程序来源于《大话设计模式》。
背景是小菜已经写好了给一家企业做的电子商务网站,使用SQL Server作为数据库。而后,小菜所在的公司接到了另一家公司类似需求的项目,但是数据库要规定使用Access。这样小菜就要修改代码,大鸟循循善诱将使用抽象工厂模式重构的思路授予了小菜~
原本小菜的代码是这样的:
User类
// User类
public class User {
private int id;
private String name;
public int getId(){
return id;
}
public void setId(int id){
this.id = id;
}
public int getName(){
return name;
}
public void setName(String name){
this.name = name;
}
}
SqlserverUser类
public class SqlserverUser {
public void insert(User user){
System.out.println("在 SQL Server 中给User表中增加一条记录");
}
public User getUser(int id){
System.out.println("在 SQL Server 中根据ID得到User表的一条记录");
return null;
}
}
Main类
public class Main {
public static void main(String[] args) {
User user = new User();
SqlserverUser su = new SqlserverUser(); // 与SQL Server耦合
su.insert(user); // 插入用户
su.getUser(1); // 得到ID为 1 的用户
}
}
小菜这样写的问题就在于:Main 相当于Client 客户端,客户端的代码与Sqlserver 高度耦合,所以这样自然就出现了如果公司要更换数据库,就要改动大量代码的情况。
接下来,我们不妨看一下如何使用抽象工厂模式是如何对代码进行封装与解耦的。
我们具有User类以及Department部门类
User 类和 Department 类
// User类
public class User {
private int id;
private String name;
public int getId(){
return id;
}
public void setId(){
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
// Department 类
public class Department {
private int depId;
private String depName;
public int getDepId() {
return depId;
}
public void setDepId(int depId) {
this.depId = depId;
}
public String getDepName() {
return depName;
}
public void setDepName(String depName) {
this.depName = depName;
}
}
首先是抽象工厂中的接口:
IFactory 接口
public interface IFactory {
IUser createUser();
IDepartment createDepartment();
}
IUser 接口
public interface IUser {
void insert(User user);
User getUser(int id);
}
IDepartment 接口
public interface IDepartment {
void insert(Department department);
Department getDepartment(int id);
}
以及实现了接口的具体的工厂:
SqlserverFactory
public class SqlserverFactory implements IFactory {
@Override
public IUser createUser() {
return new SqlserverUser();
}
@Override
public IDepartment createDepartment() {
return new SqlserverDepartment();
}
}
SqlserverUser
public class SqlserverUser implements IUser {
@Override
public void insert(User user) {
System.out.println("在 SQL Server 中给User表中增加一条记录");
}
@Override
public User getUser(int id) {
System.out.println("在 SQL Server 中根据ID得到User表的一条记录");
return null;
}
}
SqlserverDepartment
public class SqlserverDepartment implements IDepartment {
@Override
public void insert(Department department) {
System.out.println("在SQL Server中给 Department 表增加一条记录");
}
@Override
public Department getDepartment(int id) {
System.out.println("在SQL Server中根据 ID 得到 Department 表一条记录");
return null;
}
}
AccessFactory
public class AccessFactory implements IFactory {
@Override
public IUser createUser() {
return new AccessUser();
}
@Override
public IDepartment createDepartment() {
return new AccessDepartment();
}
}
AccessUser
public class AccessUser implements IUser {
@Override
public void insert(User user) {
System.out.println("在 Access 中给User表中增加一条记录");
}
@Override
public User getUser(int id) {
System.out.println("在 Access 中根据ID得到User表的一条记录");
return null;
}
}
AccessDepartment 类
public class AccessDepartment implements IDepartment{
@Override
public void insert(Department department) {
System.out.println("在 Access 中给 Department 表增加一条记录");
}
@Override
public Department getDepartment(int id) {
System.out.println("在 Access 中根据 ID 得到 Department 表一条记录");
return null;
}
}
Main类
public class Main {
public static void main(String[] args) {
User user = new User();
Department dept = new Department();
IFactory factory = new SqlserverFactory ();
IUser iu = factory.createUser();
iu.insert(user);
iu.getUser(1);
IDepartment id = factory.createDepartment();
id.insert(dept);
id.getDepartment(1);
}
}
这样一来,在Client即:Main类中,我们就可以做到将Sql Server数据库 或 Access数据库 与程序彻底解耦。当我们需要将Sql Server数据库换成 Access数据库的时候,我们只需要将代码改动为:
public class Main {
public static void main(String[] args) {
User user = new User();
Department dept = new Department();
// IFactory factory = new SqlserverFactory ();
IFactory factory = new AccessFactory();
IUser iu = factory.createUser();
iu.insert(user);
iu.getUser(1);
IDepartment id = factory.createDepartment();
id.insert(dept);
id.getDepartment(1);
}
}
即可。
Abstract Factory模式中的角色
AbstractFactory
AbstractFactory 即抽象工厂,这个角色负责定义用于生成抽象产品的接口。在示例程序中,由IFactory扮演这一个角色。
AbstractProduct
AbstractProduct 角色负责定义AbstractFactory 角色所生成的抽象产品,示例程序中由IUser和IDepartment接口承担此角色。
ConcreteFactory
具体工厂,ConcreteFactory 角色负责实现 AbstractFactory 角色。在示例程序中,由SqlserverFactory 和 AccessFactory 扮演此角色。
ConcreteProduct
ConcreteProduct角色负责实现AbstractProduct角色的接口的接口,示例程序中由SqlserverUser,SqlserverDepartment,AccessUser,AccessDepartment 扮演这个角色。
Client
Client委托者,Client角色会调用AbstractFactory抽象工厂和AbstractProduct 角色的接口来进行工作,隐藏了具体的产品和工厂的实现细节,在示例程序中,Main类承担这个角色。
AbstractFactory 模式UML图
对AbstractFactory 模式的一些思考
AbstractFactory模式的优缺点
优点
- 便于交换产品
拿示例程序来说,小菜的公司要求将Sqlserver替换为Access。对于Client角色而言,我们只需要修改一个具体的工厂类就可以了,即:
将这段代码
IFactory factory = new SqlserverFactory ();
更改为:
IFactory factory = new AccessFactory();
因为在一个应用中,一个工厂的初始化只需要一次,这样就使得变更产品非常容易实现。
- 封装性强
Client角色通过抽象接口来操纵实例,而实际的工厂和实际的产品的具体细节被接口方法所替代,用示例程序进行说明:客户只知道IUser和IDepartment,而不必非要知道是Sql Server的的具体实现还是Access的具体实现,AbstractFactory模式将封装性做到了极致。
缺点
凡事都有两面性,AbstractFactory也有一定的缺陷,试想一下,如果我们需要新增一个项目表:我们就需要新增IProject,SqlserverProject,AccessProject并且还要修改IFactory,SqlserverFactory以及AccessFactory的代码才能够完全实现,已经编写完成的具体工厂越多,我们修改的工作量就会越大,这就是AbstractFactory带来的缺陷:维护成本高。
抽象工厂模式是一个需要深刻理解的设计模式,后续我还会继续补充,大家加油。
网友评论