写文章的初心主要是用来帮助自己快速的回忆这个模式该怎么用,主要是下面的UML图可以起到大作用,在你学习过一遍以后可能会遗忘,忘记了不要紧,只要看一眼UML图就能想起来了。同时也请大家多多指教。
抽象工厂模式(Abstract Factory)
是一种创建型模式。
目录
一、概述
二、优点
三、举例
一、概述
1、提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。2、和工厂方法模式不同的是,有多种类型的对象(产品)需要被实例化,同时工厂也被定义了多个不同产品创建的接口。
1.1、主要的角色分两种,但从代码(或技术)实现的角度看(为了充分使用面向对象语言的3大特性封装、继承、多态,还另外需要抽象类或接口)可能有4个:
- 工厂的抽象类或接口(单个) + 工厂的实现类(多个):控制创建哪些产品以及如何创建产品的类
- 产品的抽象类或接口(多个) + 产品实现类(多个):那些需要被创建(实例化)的类
1.2、直观的理解上这些角色之间的关系如下:
1.3、通过技术实现的角度看,对象之间关系的UML图如下:
二、优点
- 使得发起请求的对象和具体创建实例的过程分离
三、举例
假设一个部门系统升级,涉及到两个表:部门表和用户表。原先这两表在数据库A里,升级后新加了个数据库B,也有部门表和用户表。现在为了兼容性,A和B两个库,和库里的两个表(部门表和用户表)都需要使用。数据库如下:
3.1、为了实现对不同数据库的不同表的访问和连接,分析步骤:
1、分析上述问题:
- 抓住上面关键:有两个数据库,每个数据库又有两个表;
- 两个数据库是不同的,但库里的两个表是相同的,都是一个用户表一个部门表;
- 如果把两个库同名的表看成一样,那么逻辑上访问的就只有两个表,所以为了实现访问2个不同表,我们需要定义2个不同的接口,然后针对不同的数据库,我们还又需要设计不同实现类;
- 我们发现这样下来会设计很多类,那么在真正使用时需要创建很多不同对象的实例,会比较麻烦,此时就尝试用抽象工厂模式,将使用和创建分离开来。
2、针对问题的设计要素:
产品的抽象类或接口(多个) + 产品实现类(多个):
- 一个访问用户表抽象类或接口;
- 一个访问部门表抽象类或接口;
- 针对 访问数据库A里的用户表 和 访问数据库B里的用户表 的实现类各一个;
- 针对 访问数据库A里的部门表 和 访问数据库B里的部门表 的实现类各一个;
工厂的抽象类或接口(单个) + 工厂的实现类(多个):
- 一个用来创建连接数据库工厂的抽象类或接口;
- 一个用来创建访问数据库A的实例对象的工厂;
- 一个用来创建访问数据库B的实例对象的工厂。
(注:先挖个坑,有时候可能不确定应该抽象哪个,那就无脑将那个相对不容易变化的和容易变化的分别抽象出来再进行分析就行。但实际上我相关经验很少,我也不知道怎么样才算更好,像本例是将访问用户表和访问部门表抽象出来当接口,但感觉反着来抽象出数据库A和数据库B当接口也一样。具体怎么样的抽象更好我目前也是菜鸟还不知道该怎么回答,等我以后知道答案了一定会填这个坑的。
不过说这么多,也请大家放心向下看,本例我有借鉴一些教材书,这样子是没有问题的。之所以加了这个注,是想到可能有人会想问这个问题,为什么要这样定义,所以才在此说明。)
3.2、对象之间的关系用UML图表示如下:
3.3、Java实现代码如下(建议你在本地试一下,加深印象):
用户bean(举例就默认为空了,也不会影响到本例运行):
public class User {
//用户表里的属性和字段
}
部门bean(举例就默认为空了,也不会影响到本例运行):
public class Department {
//部门表里的属性和字段
}
访问用户表接口:
public interface IUser {
public void insertUser(User user);//添加
public User selectUserById(int id);//查找
}
访问部门表接口:
public interface IDepartment {
public void insertDepartment(Department department);//添加
public Department selectDepartmentById(int id);//查找
}
用来访问数据库A里的用户表的类:
public class DatabaseAUser implements IUser{
@Override
public void insertUser(User user) {
System.out.println("向数据库A里的User表中增加一条数据");
}
@Override
public User selectUserById(int id) {
System.out.println("根据id查询数据库A里的User表中一条数据");
return null;
}
}
用来访问数据库B里的用户表的类:
public class DatabaseBUser implements IUser{
@Override
public void insertUser(User user) {
System.out.println("向数据库B里的User表中增加一条数据");
}
@Override
public User selectUserById(int id) {
System.out.println("根据id查询数据库B里的User表中一条数据");
return null;
}
}
用来访问数据库A里的部门表的类:
public class DatabaseADepartment implements IDepartment {
@Override
public void insertDepartment(Department department) {
System.out.println("向数据库A里的Department表中增加一条数据");
}
@Override
public Department selectDepartmentById(int id) {
System.out.println("根据id查询数据库A里的Department表中一条数据");
return null;
}
}
用来访问数据库B里的部门表的类:
public class DatabaseBDepartment implements IDepartment {
@Override
public void insertDepartment(Department department) {
System.out.println("向数据库B里的Department表中增加一条数据");
}
@Override
public Department selectDepartmentById(int id) {
System.out.println("根据id查询数据库B里的Department表中一条数据");
return null;
}
}
工厂接口:
public interface IFactory {
public IUser createUserConnect();//创建User表的访问连接实例
public IDepartment createDepartmentConnect();//创建Department表的访问连接实例
}
创建数据库A的访问实例的工厂:
public class DatabaseAFactory implements IFactory {
@Override
public IUser createUserConnect() {
return new DatabaseAUser();
}
@Override
public IDepartment createDepartmentConnect() {
return new DatabaseADepartment();
}
}
创建数据库B的访问实例的工厂:
public class DatabaseBFactory implements IFactory {
@Override
public IUser createUserConnect() {
return new DatabaseBUser();
}
@Override
public IDepartment createDepartmentConnect() {
return new DatabaseBDepartment();
}
}
主程序(发起请求的类):
public class Main {
public static void main(String[] args) {
User user = new User();
Department department = new Department();
IFactory factoryA = new DatabaseAFactory();//创建数据库A连接实例的工厂
IUser iUserA = factoryA.createUserConnect();
iUserA.insertUser(user);//给数据库A的用户表增加数据
iUserA.selectUserById(1);//查询数据库A的用户表里的数据
IDepartment iDepartmentA = factoryA.createDepartmentConnect();
iDepartmentA.insertDepartment(department);//给数据库A的部门表增加数据
iDepartmentA.selectDepartmentById(1);//查询数据库A的部门表里的数据
System.out.println("==========分界线==========");
IFactory factoryB = new DatabaseBFactory();//创建数据库B连接实例的工厂
IUser iUserB = factoryB.createUserConnect();
iUserB.insertUser(user);//给数据库B的用户表增加数据
iUserB.selectUserById(1);//查询数据库B的用户表里的数据
IDepartment iDepartmentB = factoryB.createDepartmentConnect();
iDepartmentB.insertDepartment(department);//给数据库B的部门表增加数据
iDepartmentB.selectDepartmentById(1);//查询数据库B的部门表里的数据
}
}
在Java中还可以使用反射+配置文件进一步简化设计,可以把工厂压缩到只有一个,这里就不再举例了,可以把上面的Java例子复制到你本地,运行main函数试一下加深理解。这些代码都是我自己学习的时候根据一些教材手敲的,不存在bug可以直接运行。
如果觉得本文还不错,就请点个赞吧!如果有建议,也请评论指教和讨论!