目录
抽象工厂模式(AbstractFactory)
产品族
理解
UML图
优缺点
应用场景
抽象工厂模式VS和工厂模式
实例
数据访问(工厂方法)
数据访问(抽象工厂)
用简单工厂改造抽象工厂
C#中的反射
Java实现反射+配置文件+抽象工厂数据访问程序
本质:选择产品族的实现
产品族:不同产品等级的产品集合。
图中一共有四个产品族,分布于三个产品等级结构中。
产品族+等级结构可以唯一确定一个产品。
抽象工厂模式:提供一个创建一系列相关或相互依赖对象的接口,而无需指定他们具体的类
抽象工厂是指一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象。
抽象工厂(Abstract Factory):
核心,与商业逻辑无关
具体工厂(Concrete Factory):
在客户端的调用下创建产品的实例,含有选择合适的产品对象的逻辑(与商业逻辑有关)
抽象产品(Abstract Product):
担任这个角色的类是工厂方法模式所创建的对象的父类,或它们共同拥有的接口。
具体产品(Concrete Product):
抽象工厂模式所创建的任何产品对象都是某一个具体产品类的实例。这是客户端最终需要的东西,其内部一定充满了应用系统的商业逻辑。
需要更换Mysql数据库时,只需要增加MysqlUser和MysqlFactory
package com.method;
public class User {
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public interface IUser {
void Insert(User user);
User getUser(int id);
}
public class SqlserverUser implements IUser{
@Override
public void Insert(User user) {
// TODO Auto-generated method stub
System.out.println("在SqlServer中给User表增加一条记录");
}
@Override
public User getUser(int id) {
// TODO Auto-generated method stub
System.out.println("在SqlServer中根据id得到User表一条记录");
return null;
}
}
public class AccessUser implements IUser{
@Override
public void Insert(User user) {
// TODO Auto-generated method stub
System.out.println("在Access中给User表增加一条记录");
}
@Override
public User getUser(int id) {
// TODO Auto-generated method stub
System.out.println("在Access中根据id得到User表一条记录");
return null;
}
}
public interface IFactory {
public IUser createUser();
}
public class SqlserverFactory implements IFactory{
@Override
public IUser createUser() {
// TODO Auto-generated method stub
return new SqlserverUser();
}
}
public class AccessFactory implements IFactory{
@Override
public IUser createUser() {
// TODO Auto-generated method stub
return new AccessUser();
}
}
实现了增加数据库还不够
如果数据库中要增加其它表,比如Dept表,就需要增加很多类 ,此时用到了抽象工厂模式
产品族:两种DBMS
产品等级:两张表
public interface IFactory {
public IUser createUser();
public IDept createDept();
}
public class SqlserverFactory implements IFactory{
@Override
public IUser createUser() {
// TODO Auto-generated method stub
return new SqlserverUser();
}
@Override
public IDept createDept() {
// TODO Auto-generated method stub
return new SqlserverDept();
}
}
public class AccessFactory implements IFactory{
@Override
public IUser createUser() {
// TODO Auto-generated method stub
return new AccessUser();
}
@Override
public IDept createDept() {
// TODO Auto-generated method stub
return new AccessDept();
}
}
public class Dept {
private int did;
private String dname;
public int getDid() {
return did;
}
public void setDid(int did) {
this.did = did;
}
public String getDname() {
return dname;
}
public void setDname(String dname) {
this.dname = dname;
}
}
public interface IDept {
void Insert(Dept dept);
User getDept(int did);
}
public class SqlserverDept implements IDept{
@Override
public void Insert(Dept dept) {
// TODO Auto-generated method stub
System.out.println("在Sqlserver中给Department表增加一条记录");
}
@Override
public User getDept(int did) {
System.out.println("在Sqlserver中根据id="+did+"得到Department表一条记录");
return null;
}
}
public class AccessDept implements IDept{
@Override
public void Insert(Dept dept) {
// TODO Auto-generated method stub
System.out.println("在Access中给Department表增加一条记录");
}
@Override
public User getDept(int did) {
// TODO Auto-generated method stub
System.out.println("在Access中根据id="+did+"得到得到Department表一条记录");
return null;
}
}
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
User user=new User();
IFactory factory1=new SqlserverFactory();
IUser iu=factory1.createUser();
iu.Insert(user);
iu.getUser(1);
Dept dept=new Dept();
IDept idept=factory1.createDept();
idept.Insert(dept);
idept.getDept(2);
}
}
用Database类代替IFactory,SqlserverFactory,AccessFactory三个类
客户端无需出现Access和Sqlserver,而是通过事先设置的db值来选择DBMS,实现了解耦
如果要增加Mysql数据库,就需要在Database类的switch中增加case"Mysql"语句
public class DataBase {
private static String db="Access";
public static IUser createUser() {
IUser result=null;
switch(db) {
case "Sqlserver":
result=new SqlserverUser();
break;
case "Access":
result=new AccessUser();
break;
}
return result;
}
public static IDept createDept() {
IDept result=null;
switch(db) {
case "Sqlserver":
result=new SqlserverDept();
break;
case "Access":
result=new AccessDept();
break;
}
return result;
}
}
public class Main {
public static void main(String[] args) {
// TODO Auto-generated method stub
User user=new User();
Dept dept=new Dept();
IUser iu=DataAccess.createUser();
iu.Insert(user);
iu.getUser(1);
IDept idept=DataAccess.createDept();
idept.Insert(dept);
idept.getDept(2);
}
}
有没有办法可以不写明以上switch语句中的case"SqlserverUser"这样的话呢?
由此我们引入反射
常规:当某个角色需要另一个角色的协助时,通常由调用者来创建被调用者的实例
依赖注入:创建被调用者的工作不再由调用者来完成(因此叫控制反转),创建被调用者实例的工作通常由Ioc容器来完成,然后注入调用者。
控制反转和依赖注入是同一个概念
Java反射允许程序在运行时透过Reflection APIs取得任何一个已知名称的class的内部信息
Java反射相关的API在包java.lang.reflect.*中,具体可以参考我这篇文章
关于XML文件解析可以参考我这篇文章
增加一个data2.xml,配置数据库名字
更改DataBase类,负责读取data2.xml文件中的数据库名字
Access
package com.reflect;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.util.Iterator;
public class DataBase {
private static String db;
public static String getDb(){
SAXReader reader = new SAXReader();
Object obj=null;
try {
//获取Document 对象
Document doc = reader.read("AbstractFactory/conf/data2.xml");
//获取 xml 根节点
Element root = doc.getRootElement();
//遍历根节点,获取子节点
for (Iterator iter = root.elementIterator(); iter.hasNext(); ) {
//获取database节点对象
Element dElement = (Element) iter.next();
//遍历database节点的子节点
for (Iterator innerIter = dElement.elementIterator(); innerIter.hasNext(); ) {
//获取databases节点的对象
Element innerElement = (Element) innerIter.next();
db= innerElement.getStringValue();
}
}
} catch (DocumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return db;
}
public static IUser createUser() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
String db=getDb();
Class c = Class.forName("com.reflect."+db+"User");
Object obj=c.newInstance();
return (IUser) obj;
}
public static IDept createDept() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
String db=getDb();
Class c = Class.forName("com.reflect."+db+"Dept");
Object obj=c.newInstance();
return (IDept) obj;
}
}
package com.reflect;
public class Main {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
User user = new User();
Dept dept = new Dept();
IUser iu = DataBase.createUser();
iu.Insert(user);
iu.getUser(1);
IDept id = DataBase.createDept();
id.Insert(dept);
id.getDept(1);
}
}
需要修改连接的数据库时,只需要修改data2.xml