定义
抽象工厂模式:提供一个创建一系列相关或者相互依赖对象的接口,而无需指定它们具体的类。
自己的的理解:
工厂方法模式是用来创建同一个产品的不同类型的,但是抽象工厂模式是用来创建一系列类的产品。产品种类单一,适合用工厂方法模式;如果有多个种类,各种类型时,通过抽象工厂模式来进行创建是很合适的。原来的工厂方法模式相当于只有一个维度,就是一种产品的各个实现,而现在抽象工厂模式相当于在工厂方法模式的基础上新增了一个维度,产品的种类。而抽象工厂是在工厂方法的基础上将产品的种类固定在了抽象类中的方法个数上,将各个产品的相似实现的方式抽象出一个工厂类,将原来不同产品但是实现的原理或方式相似的变成一个工厂就可以生产,子类实现时,就是原来相似的实现方式,创建一系列的产品,简化了如果采用工厂方式模式生成多个产品需要多个抽象工厂类的情况。同时也带来另一个问题,控制死了一个维度,另一个维度是灵活的。当控制死的维度(产品的种类)需要变化时,会造成所有的工厂类必须要更改的情况。后面优化的部分(反射+抽象方法)会解决这个问题,使其只需要更改一个类便可。
讲到这,我们便可以得出抽象工厂模式适合的场景(同时满足):
1.适合创建多个产品
2.这些产品各个实现的方式都是类似的(像下面实例中讲的一样,UserDao和DapartmentDao类都有MySQL、Oracle等不同的数据库对应的实现),方便合并抽象成一个工厂
3.扩展时,适合扩展产品不同的实现,不太适合产品种类的扩展(如果需要扩展种类需要使用后面优化的方案)
结构图
实例:
设计一个部门人员的管理系统(后续可能会更改数据库的类型)。
类图:
编码实现:
IFactory类:
public interface IFactory {
public IUserDao createUserDao();
public IDepartmentDao createDepartmentDao();
}
当数据库是MySQL时,生产操作一系列产品的工厂类
MySQLFactory 类:
public class MySQLFactory implements IFactory{
@Override
public IUserDao createUserDao() {
return new MySQLUserDao();
}
@Override
public IDepartmentDao createDepartmentDao() {
return new MySQLDepartmentDao();
}
}
当数据库是Oracle时,生产操作一系列产品的工厂类
OracleFactory类:
public class OracleFactory implements IFactory {
@Override
public IUserDao createUserDao() {
return new OracleUserDao();
}
@Override
public IDepartmentDao createDepartmentDao() {
return new OracleDepartmentDao();
}
}
操作用户类的接口
IUserDao接口:
public interface IUserDao {
public void Insert(User user);
public User getUser(int id);
}
数据库是MySQL时,User对应的操作类
MySQLUserDao 类
public class MySQLUserDao implements IUserDao{
@Override
public void Insert(User user) {
System.out.println("在MySQL中的User表添加一条记录!");
}
@Override
public User getUser(int id) {
System.out.println("从MySQL中的User表中取出一条数据!");
return null;
}
}
数据库是Oracle时,User类对应的操作类
OracleUserDao
public class OracleUserDao implements IUserDao {
@Override
public void Insert(User user) {
System.out.println("在Oracle中的User表中插入一条记录!");
}
@Override
public User getUser(int id) {
System.out.println("从Oracle中的User表中取出一条数据!");
return null;
}
}
操作部门类的接口
IDepartmentDao
public interface IDepartmentDao {
public void insert(Department department);
public Department getDepartment(int id);
}
数据库是MySQL时,Department对应的操作类
MySQLDepartmentDao 类
public class MySQLDepartmentDao implements IDepartmentDao{
@Override
public void insert(Department department) {
System.out.println("在MySQL中的Department表添加一条记录!");
}
@Override
public Department getDepartment(int id) {
System.out.println("从MySQL中的Department表中取出一条数据!");
return null;
}
}
数据库是Oracle时,Department对应的操作类
OracleDepartmentDao 类
public class OracleDepartmentDao implements IDepartmentDao {
@Override
public void insert(Department department) {
System.out.println("在Oracle中的Department表添加一条记录!");
}
@Override
public Department getDepartment(int id) {
System.out.println("从Oracle中的Department表中取出一条数据!");
return null;
}
}
客户端代码
public class Client {
public static void main(String[] args) {
IFactory factory = new MySQLFactory();
IUserDao userDao = factory.createUserDao();
IDepartmentDao departmentDao = factory.createDepartmentDao();
//插入一个用户
userDao.Insert(new User());
}
}
客户端需要知道得类
抽象工厂类、具体工厂类、各个产品抽象类。
优化(反射+抽象工厂类)
原来的工厂方法只能生产一种产品,需要生产多种产品,则工厂方法模式肯定需要多个抽象工厂类,而抽象工厂模式则是相当于把这些多个抽象工厂类再进行合并再抽象,抽象出一个可以生产多种产品的工厂类。但是如果再扩展时,需要在工厂在原来的基础多生产一种产品,这时候,需要的改动会涉及所有的工厂类(抽象工厂类和具体实现的工厂类),所以现在使用了反射加抽象工厂类,可以简化原来多个工厂类的情况,变成只需要一个工厂类,再有需求则只需要更改一个类就可以实现。
结构图:
编码实现
在上面代码的基础上去掉IFactory、MySQLFactory、OracleFactory三个类,添加一个DataAccess类,更改客户端代码。
DataAccess类:
public class DataAccess {
private String db;
public DataAccess(){
Properties properties = new Properties();
//得到输出流
//database.properties必须在src目录下或者在SourceFolder下,否则得加上包名
InputStream in =
DataAccess.class.getClassLoader().getResourceAsStream("database.properties");
try {
//加载
properties.load(in);
} catch (IOException e) {
e.printStackTrace();
}
//得到数据库的名字
db = properties.getProperty("databaseName");
}
public String getDb() {
return db;
}
public void setDb(String db) {
this.db = db;
}
public IUserDao createUserDao() throws ClassNotFoundException,
InstantiationException, IllegalAccessException{
//得到包名
String packageName = this.getClass().getPackage().getName();
//得到完整的类名
String className =packageName+"." +db+"UserDao";
//根据类名得到class
Class<?> clazz = Class.forName(className);
//使用class类创建实例
return (IUserDao) clazz.newInstance();
}
public IDepartmentDao createDepartmentDao() throws ClassNotFoundException,
InstantiationException,IllegalAccessException{
//得到包名
String packageName = this.getClass().getPackage().getName();
//得到完整的类名
String className =packageName+"." +db+"DepartmentDao";
//根据类名得到class
Class<?> clazz = Class.forName(className);
//使用class类创建实例
return (IDepartmentDao) clazz.newInstance();
}
}
客户端代码:
public class Client {
public static void main(String[] args) throws ClassNotFoundException,
InstantiationException, IllegalAccessException {
DataAccess dataAccess = new DataAccess();
IUserDao userDao = dataAccess.createUserDao();
userDao.Insert(new User());
}
}
客户端需要记住的类:
DataAccess类,各个产品的抽象类。