设计模式_11:抽象工厂模式
2017-10-12 14:50
375 查看
场景:假设现在有一个专门操作mysql数据库user表的dao,可以对其进行插入和查询,,以下是代码:
运行结果:
向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
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
相关文章推荐
- 设计模式(2)-创建型-简单工厂,工厂,抽象工厂模式(Factory)
- c#设计模式--抽象工厂模式
- .NET简谈设计模式之(抽象工厂模式)
- 初始设计模式11——外观模式
- 设计模式:抽象工厂模式
- 设计模式——抽象工厂模式AbstractFactory
- 设计模式之——抽象工厂模式
- 设计模式练习--抽象工厂模式
- java23种常用设计模式之抽象工厂模式(Abstract Factory)2
- C# 设计模式----抽象工厂模式
- 设计模式--2: Net设计模式实例之抽象工厂模式(Abstract Factory Pattern)
- 23种设计模式(3):抽象工厂模式
- Java设计模式之抽象工厂模式
- JAVA设计模式三抽象工厂模式
- [创建型模式系列]Abstract factory of Design Pattern模式设计之抽象工厂模式
- 设计模式解密(11)- 命令模式 - 扩展篇(撤销命令)
- 23种设计模式(3):抽象工厂模式
- 23种设计模式(11):责任连模式
- 设计模式之抽象工厂模式
- 设计模式之工厂模式和抽象工厂模式