设计模式_11:抽象工厂模式
来源:互联网 发布:网络的坏处有哪些500字 编辑:程序博客网 时间:2024/05/21 11:35
场景:假设现在有一个专门操作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 { 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
- 设计模式_11:抽象工厂模式
- 抽象工厂设计模式
- 设计模式-----抽象工厂
- 抽象工厂设计模式
- 抽象工厂设计模式
- 抽象工厂设计模式
- 设计模式 抽象工厂
- 设计模式--抽象工厂
- 设计模式--抽象工厂
- 设计模式 抽象工厂
- 设计模式-抽象工厂
- 设计模式-> 抽象工厂
- 抽象工厂设计模式
- 设计模式--抽象工厂
- 【设计模式】抽象工厂
- 设计模式---抽象工厂
- 【设计模式-抽象工厂】
- 设计模式-抽象工厂
- 网站的架构模式
- 2017杭州云栖大会倒计时!研发效能主题专场五大亮点抢先看
- 用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。
- 本地java链接远程服务器Linux上redis出错解决方案
- Activity工作流学习要点
- 设计模式_11:抽象工厂模式
- 剑指offer-树的子结构
- Android用TabLayout实现类似网易选项卡动态滑动效果
- 汇编语言 第三版 王爽 jmp转移指令笔记 思维导图总结
- 多线程
- 设计模式之6大原则(5)-迪米特法则
- PG(HGDB)中创建schema及修改schema的属主及名称
- eclipse如何导出WAR包
- Servlet监听器