您的位置:首页 > 其它

设计模式_11:抽象工厂模式

2017-10-12 14:50 375 查看
场景:假设现在有一个专门操作mysql数据库user表的dao,可以对其进行插入和查询,,以下是代码:

public class Main {

public static void main(String[] args) {
User user = new User(0, "Lisa");
MysqlUserDao mysqlUserDao = new MysqlUserDao();
mysqlUserDao.insertUser(user);
mysqlUserDao.queryUserById(0);
}

}

//实体类
class User {

private int id;
private String name;

public User(int id, String name) {
this.id = id;
this.name = 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;
}

@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}

//操作Mysql的dao层
class MysqlUserDao {

public void insertUser(User user){
System.out.println("向Mysql数据库User表中插入一条数据:" + user);
}

public User queryUserById(int id) {
System.out.println("在Mysql数据库User表中查询一条id为"+ id +"的数据");
return new User(id, "测试");
}

}


运行结果:

向Mysql数据库User表中插入一条数据:User{id=0, name='Lisa'}
在Mysql数据库User表中查询一条id为0的数据

但是如果要换成使用另一种数据库(比如Access)的话,是需要改动很多代码的,因为MysqlUserDao mysqlUserDao = new MysqlUserDao()
已经写死dao的类型是MysqlUserDao,如果整个项目有100多个地方有这段代码,就意味着要改动100多次类型,不符合开闭原则,因此,而且不同数据库之间的sql函数和语法都有部分差异,所以先用工厂方法模式来改良一下:

public class Main {

public static void main(String[] args) {
User user = new User(0, "Lisa");

IUserDaoFactory mysqlUserDaoFactory = new MysqlUserDaoFactory();
//这里改成IUserDao,这样mysqlUserDao事先就不用知道访问的是哪个数据库了
IUserDao mysqlUserDao = mysqlUserDaoFactory.createUserDao();
mysqlUserDao.insertUser(user);
mysqlUserDao.queryUserById(0);
System.out.println("这里只需把new MysqlDaoFactory()改成new AccessDaoFactory()即可完成dao的切换");
IUserDaoFactory accessUserDaoFactory = new AccessUserDaoFactory();
IUserDao accessUserDao = accessUserDaoFactory.createUserDao();
accessUserDao.insertUser(user);
accessUserDao.queryUserById(0);

}

}

//用户实体类
class User {

private int id;
private String name;

public User(int id, String name) {
this.id = id;
this.name = name;
}

@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}

//抽象工厂接口
interface IUserDaoFactory {
IUserDao createUserDao();
}

//具体的MysqlDao工厂
class MysqlUserDaoFactory implements IUserDaoFactory {
@Override
public IUserDao createUserDao() {
return new MysqlUserDao();
}
}

//具体的accessDao工厂
class AccessUserDaoFactory implements IUserDaoFactory {
@Override
public IUserDao createUserDao() {
return new AccessUserDao();
}
}

//抽象dao接口
interface IUserDao {
void insertUser(User user);
User queryUserById(int id);
}

//操作Mysql的dao层
class MysqlUserDao implements IUserDao{

@Override
public void insertUser(User user){
System.out.println("向Mysql数据库User表中插入一条数据:" + user);
}

@Override
public User queryUserById(int id) {
System.out.println("在Mysql数据库User表中查询一条id为"+ id +"的数据");
return new User(id, "mysql测试");
}
}

//操作Access的dao层
class AccessUserDao implements IUserDao{
@Override
public void insertUser(User user){
System.out.println("向Access数据库User表中插入一条数据:" + user);
}

@Override
public User queryUserById(int id) {
System.out.println("在Access数据库User表中查询一条id为"+ id +"的数据");
return new User(id, "access测试");
}

}运行结果:
向Mysql数据库User表中插入一条数据:User{id=0, name='Lisa'}
在Mysql数据库User表中查询一条id为0的数据
这里只需把new MysqlDaoFactory()改成new AccessDaoFactory()即可完成dao的切换
向Access数据库User表中插入一条数据:User{id=0, name='Lisa'}
在Access数据库User表中查询一条id为0的数据

显然,数据库中不可能只有一个User表,比如说还有个Department部门表,我们可以加多一个对部门表操作的dao试试:

public class Main {

public static void main(String[] args) {
User user = new User(0, "Lisa");
Department department = new Department(0, "财务部");

IDaoFactory mysqlUserDaoFactory = new MysqlDaoFactory();
IUserDao mysqlUserDao = mysqlUserDaoFactory.createUserDao();
mysqlUserDao.insertUser(user);
mysqlUserDao.queryUserById(0);
IDepartmentDao mysqlDepartmentDao = mysqlUserDaoFactory.createDepartmentDao();
mysqlDepartmentDao.insertDepartment(department);
mysqlDepartmentDao.queryDepartmentById(0);
System.out.println("这里只需把new MysqlDaoFactory()改成new AccessDaoFactory()即可完成dao的切换");
IDaoFactory accessUserDaoFactory = new AccessDaoFactory();
IUserDao accessUserDao = accessUserDaoFactory.createUserDao();
accessUserDao.insertUser(user);
accessUserDao.queryUserById(0);
IDepartmentDao accessDepartmentDao = accessUserDaoFactory.createDepartmentDao();
accessDepartmentDao.insertDepartment(department);
accessDepartmentDao.queryDepartmentById(0);

}

}

//用户实体类
class User {

private int id;
private String name;

public User(int id, String name) {
this.id = id;
this.name = name;
}

@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}

//部门实体类
class Department {
private int id;
private String name;

public Department(int id, String name) {
this.id = id;
this.name = name;
}

@Override
public String toString() {
return "Department{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}

//抽象工厂接口
interface IDaoFactory {
IUserDao createUserDao();
IDepartmentDao createDepartmentDao();
}

//具体的MysqlDao工厂
class MysqlDaoFactory implements IDaoFactory {
@Override
public IUserDao createUserDao() {
return new MysqlUserDao();
}

@Override
public IDepartmentDao createDepartmentDao() {
return new MysqlDepartmentDao();
}
}

//具体的accessDao工厂
class AccessDaoFactory implements IDaoFactory {
@Override
public IUserDao createUserDao() {
return new AccessUserDao();
}

@Override
public IDepartmentDao createDepartmentDao() {
return new AccessDepartmentDao();
}
}

//抽象dao接口
interface IUserDao {
void insertUser(User user);
User queryUserById(int id);
}

interface IDepartmentDao {
void insertDepartment(Department department);
Department queryDepartmentById(int id);
}

//操作Mysql的dao层
class MysqlUserDao implements IUserDao{

@Override
public void insertUser(User user){
System.out.println("向Mysql数据库User表中插入一条数据:" + user);
}

@Override
public User queryUserById(int id) {
System.out.println("在Mysql数据库User表中查询一条id为"+ id +"的数据");
return new User(id, "mysql测试");
}
}

class MysqlDepartmentDao implements IDepartmentDao {
@Override
public void insertDepartment(Department department) {
System.out.println("向Mysql数据库Department表中插入一条数据:" + department);
}

@Override
public Department queryDepartmentById(int id) {
return new Department(0, "mysql测试");
}
}

//操作Access的dao层
class AccessUserDao implements IUserDao{
@Override
public void insertUser(User user){
System.out.println("向Access数据库User表中插入一条数据:" + user);
}

@Override
public User queryUserById(int id) {
System.out.println("在Access数据库User表中查询一条id为"+ id +"的数据");
return new User(id, "access测试");
}

}

class AccessDepartmentDao implements IDepartmentDao {
@Override
public void insertDepartment(Department department) {
System.out.println("向Access数据库Department表中插入一条数据:" + department);
}

@Override
public Department queryDepartmentById(int id) {
return new Department(0, "access测试");
}
}运行结果:
向Mysql数据库User表中插入一条数据:User{id=0, name='Lisa'}
在Mysql数据库User表中查询一条id为0的数据
向Mysql数据库Department表中插入一条数据:Department{id=0, name='财务部'}
这里只需把new MysqlDaoFactory()改成new AccessDaoFactory()即可完成dao的切换
向Access数据库User表中插入一条数据:User{id=0, name='Lisa'}
在Access数据库User表中查询一条id为0的数据
向Access数据库Department表中插入一条数据:Department{id=0, name='财务部'}


增加了部门dao后,其实上面就是抽象工厂模式了,抽象工厂模式是工厂模式的一种,专门用来解决多种产品的问题,例子中的具体产品就是MysqlUserDao/AccessUserDao和MysqlDepartmentDao/AccessDepartmentDao。
这样做的好处是方便进行整个产品系列的切换,如上面注释所说:只需把new MysqlDaoFactory()改成new AccessDaoFactory()即可完成dao的切换

工厂类在应用中的初始化只进行一次,它之后所生产的产品类型就决定下来了。另外一个好处是:它让具体的实例创建过程和客户端分离,客户端是通过他们的抽象接口操纵实例,产品的具体雷鸣也被具体工厂实现分离,不会出现在客户端代码中,所以上面例子的客户端所认识的只有IUserDao和IDepartmentDao,至于他们是用Mysql还是Access实现的就不知道了。

不过缺点还是有的:如果客户端程序类不只有一个,new MysqlDaoFactory()还是要改多次的,并且每添加一个表(比如说Agent),就意味着要做以下更改:增加IAgentDao、MysqlAgentDao、AccessAgentDao和修改IDaoFactory、MysqlDaoFactory、AccessDaoFactory,造成添加和改动的类过多,可以结合简单工厂模式进行优化:

public class Main {

public static void main(String[] args) {
User user = new User(0, "Lisa");
Department department = new Department(0, "财务部");

IUserDao accessUserDao = DaoFactory.createUserDao();
accessUserDao.insertUser(user);
accessUserDao.queryUserById(0);
IDepartmentDao accessDepartmentDao = DaoFactory.createDepartmentDao();
accessDepartmentDao.insertDepartment(department);
accessDepartmentDao.queryDepartmentById(0);

}

}

//用户实体类
class User {

private int id;
private String name;

public User(int id, String name) {
this.id = id;
this.name = name;
}

@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}

//部门实体类
class Department {
private int id;
private String name;

public Department(int id, String name) {
this.id = id;
this.name = name;
}

@Override
public String toString() {
return "Department{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}

/**
* 这里改成了简单工厂模式
* 去掉了IDaoFactory、MysqlDaoFactory、AccessDaoFactory
* 改用switch判断该返回哪个dao
* */
class DaoFactory {

private static final String dbType = "mysql"; //access

public static IUserDao createUserDao() {
switch (dbType) {
case "mysql":
return new MysqlUserDao();
case "access":
return new AccessUserDao();
default:
return null;
}
}

public static IDepartmentDao createDepartmentDao() {
switch (dbType) {
case "mysql":
return new MysqlDepartmentDao();
case "access":
return new AccessDepartmentDao();
default:
return null;
}
}

}

//抽象dao接口
interface IUserDao {
void insertUser(User user);
User queryUserById(int id);
}

interface IDepartmentDao {
void insertDepartment(Department department);
Department queryDepartmentById(int id);
}

//操作Mysql的dao层
class MysqlUserDao implements IUserDao{

@Override
public void insertUser(User user){
System.out.println("向Mysql数据库User表中插入一条数据:" + user);
}

@Override
public User queryUserById(int id) {
System.out.println("在Mysql数据库User表中查询一条id为"+ id +"的数据");
return new User(id, "mysql测试");
}
}

class MysqlDepartmentDao implements IDepartmentDao {
@Override
public void insertDepartment(Department department) {
System.out.println("向Mysql数据库Department表中插入一条数据:" + department);
}

@Override
public Department queryDepartmentById(int id) {
return new Department(0, "mysql测试");
}
}

//操作Access的dao层
class AccessUserDao implements IUserDao{
@Override
public void insertUser(User user){
System.out.println("向Access数据库User表中插入一条数据:" + user);
}

@Override
public User queryUserById(int id) {
System.out.println("在Access数据库User表中查询一条id为"+ id +"的数据");
return new User(id, "access测试");
}

}

class AccessDepartmentDao implements IDepartmentDao {
@Override
public void insertDepartment(Department department) {
System.out.println("向Access数据库Department表中插入一条数据:" + department);
}

@Override
public Department queryDepartmentById(int id) {
return new Department(0, "access测试");
}
}


这样,客户端连Mysql和Access的字样都没有了,实现进一步的解耦,但这样会比之前抽象工厂多一个问题:如果增加一个Oracle数据库访问,就要在switch上添加分支了,不符合开闭原则,可以通过反射来解决:
package dao;

public class Main {

public static void main(String[] args) throws IllegalAccessException, InstantiationException, ClassNotFoundException {
User user = new User(0, "Lisa");
Department department = new Department(0, "财务部");

IUserDao accessUserDao = DaoFactory.createUserDao();
accessUserDao.insertUser(user);
accessUserDao.queryUserById(0);
IDepartmentDao accessDepartmentDao = DaoFactory.createDepartmentDao();
accessDepartmentDao.insertDepartment(department);
accessDepartmentDao.queryDepartmentById(0);

}

}

//用户实体类
class User {

private int id;
private String name;

public User(int id, String name) {
this.id = id;
this.name = name;
}

@Override
public String toString() {
return "dao.User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}

//部门实体类
class Department {
private int id;
private String name;

public Department(int id, String name) {
this.id = id;
this.name = name;
}

@Override
public String toString() {
return "dao.Department{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}

/**
* 这里改成了简单工厂模式
* 去掉了IDaoFactory、MysqlDaoFactory、AccessDaoFactory
* 改用反射来决定调用哪个dao
* */
class DaoFactory {

private static final String packageName = "dao";
private static final String dbType = "Mysql"; //Access

public static IUserDao createUserDao() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
String className = packageName + "." + dbType + "UserDao";
return (IUserDao) Class.forName(className).newInstance();
}

public static IDepartmentDao createDepartmentDao() throws ClassNotFoundException, IllegalAccessException, InstantiationException
ccb2
{
String className = packageName + "." + dbType + "DepartmentDao";
return (IDepartmentDao) Class.forName(className).newInstance();
}

}

//抽象dao接口
interface IUserDao {
void insertUser(User user);
User queryUserById(int id);
}

interface IDepartmentDao {
void insertDepartment(Department department);
Department queryDepartmentById(int id);
}

//操作Mysql的dao层
class MysqlUserDao implements IUserDao{

@Override
public void insertUser(User user){
System.out.println("向Mysql数据库User表中插入一条数据:" + user);
}

@Override
public User queryUserById(int id) {
System.out.println("在Mysql数据库User表中查询一条id为"+ id +"的数据");
return new User(id, "mysql测试");
}
}

class MysqlDepartmentDao implements IDepartmentDao {
@Override
public void insertDepartment(Department department) {
System.out.println("向Mysql数据库Department表中插入一条数据:" + department);
}

@Override
public Department queryDepartmentById(int id) {
return new Department(0, "mysql测试");
}
}

//操作Access的dao层
class AccessUserDao implements IUserDao{
@Override
public void insertUser(User user){
System.out.println("向Access数据库User表中插入一条数据:" + user);
}

@Override
public User queryUserById(int id) {
System.out.println("在Access数据库User表中查询一条id为"+ id +"的数据");
return new User(id, "access测试");
}

}

class AccessDepartmentDao implements IDepartmentDao {
@Override
public void insertDepartment(Department department) {
System.out.println("向Access数据库Department表中插入一条数据:" + department);
}

@Override
public Department queryDepartmentById(int id) {
return new Department(0, "access测试");
}
}

其实现在还是有违背开闭原则的,因为切换数据库访问还是需要更改工厂类的dbType然后再重新编译,我们可以通过配置文件来解决这个问题:
配置文件在文件夹resources里的config.xml:

<?xml version="1.0" encoding="UTF-8"?>
<config>
<databaseSetting>
<databasePackage>dao</databasePackage>
<databaseName>Mysql</databaseName>
</databaseSetting>
</config>然后DaoFactory作以下改动就可以了:
/**
* 这里改成了简单工厂模式
* 去掉了IDaoFactory、MysqlDaoFactory、AccessDaoFactory
* 改用反射来决定调用哪个dao
* */
class DaoFactory {

private static String packageName; // = "dao";
private static String dbType; // = "Mysql"; //Access

static {
File configFile = new File("resources/config.xml");
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder builder;
try {
builder = dbf.newDocumentBuilder();
Document document = builder.parse(configFile);
packageName = document.getElementsByTagName("databasePackage").item(0).getFirstChild().getNodeValue();
dbType = document.getElementsByTagName("databaseName").item(0).getFirstChild().getNodeValue();
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

public static IUserDao createUserDao() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
String className = packageName + "." + dbType + "UserDao";
return (IUserDao) Class.forName(className).newInstance();
}

public static IDepartmentDao createDepartmentDao() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
String className = packageName + "." + dbType + "DepartmentDao";
return (IDepartmentDao) Class.forName(className).newInstance();
}

}


来到这里,我们做到了通过修改配置文件即可切换数据库访问方式了O(∩_∩)O
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: