抽象工厂模式:
抽象工厂模式(Abstract Factory)是以一个超级工厂创建其他工厂。它属于创建型模式。抽象工厂模式是工厂方法模式的升级版本,他用来创建一组相关或者相互依赖的对象。他与工厂方法模式的区别就在于,工厂方法模式针对的是一个产品等级结构;而抽象工厂模式则是针对的多个产品等级结构。
场景:原来的项目是一个user表并且使用的Sqlserver数据库,现要在换成使用Access数据库该怎么办?如果要添加一个department表又该怎么办?
类图:
demo:
public interface IUser {
void Insert(User user);
User getUser(int id);
}
//新增一个Idepartment接口
public interface IDepartment {
void Insert(Department department);
Department GetDepartment(int id);
}
public class SqlServerUser implements IUser{
@Override
public void Insert(User user) {
System.out.println("SqlServerUser insert User");
}
@Override
public User getUser(int id) {
System.out.println("SqlServerUser get user from id == " + id);
return null;
}
}
//新增sqlserver对department表的操作
public class SqlServerDepartment implements IDepartment{
@Override
public void Insert(Department department) {
System.out.println("AccessDepartment insert Department");
}
@Override
public Department GetDepartment(int id) {
System.out.println("AccessDepartment get department from id == " + id);
return null;
}
}
//新增Access数据库对user表的操作
public class AccessUser implements IUser{
@Override
public void Insert(User user) {
System.out.println("AccessUser Insert user ");
}
@Override
public User getUser(int id) {
System.out.println("AccessUser get user from id == " + id);
return null;
}
}
//Access对department的操作
public class AccessDepartment implements IDepartment{
@Override
public void Insert(Department department) {
System.out.println("AccessDepartment insert Department");
}
@Override
public Department GetDepartment(int id) {
System.out.println("AccessDepartment get department from id == " + id);
return null;
}
}
public interface IFactory {
IUser CreateUser();
//新增createDepartment()
IDepartment CreateDepartment();
}
public class SqlServerFactory implements IFactory{
@Override
public IUser CreateUser() {
return new SqlServerUser();
}
@Override
public IDepartment CreateDepartment() {
return new SqlServerDepartment();
}
}
//添加一个AccessFactory工厂类
public class AccessFactory implements IFactory{
@Override
public IUser CreateUser() {
return new AccessUser();
}
@Override
public IDepartment CreateDepartment() {
return new AccessDepartment();
}
}
public class Main {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
//方法1:
User user = new User();
//IFactory factory = new SqlServerFactory();
IFactory factory = new AccessFactory();
IUser iUser = factory.CreateUser();
iUser.Insert(user);
Department department = new Department();
IDepartment iDepartment = factory.CreateDepartment();
iDepartment.Insert(department);
//方法2
User user2 = new User();
IUser iUser2 = DataAccess.CreateUser().newInstance();
iUser.Insert(user);
iUser.getUser(1);
}
}
先看主函数中的方法1:
换成Access数据库只需把SqlServerFactory改成AccessFactory即可
添加department表的操作只需通过工厂获取Idepartment对象即可对department表进行操作
优点:
1.易于交换产品系列,由于具体工厂,例如IFactory factory = new AccessFactory();在一个应用中只需要在初始化的时候出现一次,这就使得改变一个应用的具体工厂变得非常容易,只需要改变具体的工厂即可使用不同的产品配置。(抽象工厂模式将具体产品的创建延迟到具体工厂的子类中,这样将对象的创建封装起来,可以减少客户端与具体产品类之间的依赖,从而使系统耦合度低,这样更有利于后期的维护和扩展)
2.让具体的创建实例过程与客户端分离,客户端是通过它们的抽象接口操作实例,产品的具体类名也被具体工厂的实现分离,不会出现在客户端代码。(抽象工厂模式很难支持新种类产品的变化。这是因为抽象工厂接口中已经确定了可以被创建的产品集合,如果需要添加新产品,此时就必须去修改抽象工厂的接口,这样就涉及到抽象工厂类的以及所有子类的改变,这就违背了"开闭原则")
缺点:虽然方便切换两个数据库访问的代码,但是新增功能,比如新增Project表,则需至少增加三个类IProject,SqlServerProject,AccessProject,还需要更改IFactory,SqlServerFactory和AccessFactory需要大量改动代码
接下来看主函数的方法2:
在以上缺点上进行了改进
创建DataAccess.java
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Properties;
public class DataAccess {
private static String db = "Sqlserver";
//private static final String db = "Access";
public static Class<IUser> CreateUser() throws ClassNotFoundException {
db = getProperties_2("src/resource/App.properties", "db.databasename");
String classname = db + "User";
System.out.println("classname==" + classname);
Class<?> result = null;
result = Class.forName(classname);
/*switch (db) {
case "Sqlserver":
//result = new SqlServerUser();
result = Class.forName("SqlServerUser");
break;
case "Access":
//result = new AccessUser();
result = Class.forName("AccessUser");
default:
break;
}*/
return (Class<IUser>) result;
}
public static IDepartment CreateDepartment() {
IDepartment result = null;
switch (db) {
case "Sqlserver":
result = new SqlServerDepartment();
break;
case "Access":
result = new AccessDepartment();
default:
break;
}
return result;
}
//此方法最好单独写在一个类中,在此类中违背了单一职责原则。
public static String getProperties_2(String filePath, String keyWord){
Properties prop = new Properties();
String value = null;
try {
// 通过输入缓冲流进行读取配置文件
InputStream InputStream = new BufferedInputStream(new FileInputStream(new File(filePath)));
// 加载输入流
prop.load(InputStream);
// 根据关键字获取value值
value = prop.getProperty(keyWord);
} catch (Exception e) {
e.printStackTrace();
}
return value;
}
}
在CreateApartment中使用了简单工厂模式,使客户端解藕,抛弃了IFactory,SqlServerFactory和AccessFactory。但是有功能新增时需要在DataAccess中新增case,违背了开闭原则。
以CreateUser为例在此基础上继续升级:
创建APP.properties配置数据库文件
db.databasename=SqlServer
通过配置文件来读取数据库,更换数据库时只需修改配置文件,同时读取配置文件的信息,根据信息通过放射的方式来实例化SqlServerUser类,这样就可以根据字符串来动态的选择创建的对象,并且去除了switch的判断
类图:
附上拓展知识储备:
大部分内容来自《大话设计模式》
Java 反射详解和使用_AnWen03的博客-CSDN博客JAVA数据库使用配置文件建立MYSQL连接_创建数据库配置文件_bobyao123456的博客-CSDN博客