原创文章,同步发自作者个人博客,转载请注明出处 http://www.jasongj.com/design_pattern/abstract_factory/

抽象工厂模式解决的问题

上文《工厂方法模式》中提到,在工厂方法模式中一种工厂只能创建一种具体产品。而在抽象工厂模式中一种具体工厂可以创建多个种类的具体产品。

抽象工厂模式

抽象工厂模式介绍

抽象工厂模式(Factory Method Pattern)中,抽象工厂提供一系列创建多个抽象产品的接口,而具体的工厂负责实现具体的产品实例。抽象工厂模式与工厂方法模式最大的区别在于抽象工厂中每个工厂可以创建多个种类的产品。

抽象工厂模式类图

抽象工厂模式类图如下 (点击可查看大图)

抽象工厂模式角色划分

  • 抽象产品(或者产品接口),如上文类图中的IUserDao,IRoleDao,IProductDao
  • 具体产品,如PostgreSQLProductDao
  • 抽象工厂(或者工厂接口),如IFactory
  • 具体工厂,如果MySQLFactory
  • 产品族,如Oracle产品族,包含OracleUserDao,OracleRoleDao,OracleProductDao

抽象工厂模式使用方式

与工厂方法模式类似,在创建具体产品时,客户端通过实例化具体的工厂类,并调用其创建目标产品的方法创建具体产品类的实例。根据依赖倒置原则,具体工厂类的实例由工厂接口引用,具体产品的实例由产品接口引用。具体调用代码如下

package com.jasongj.client;

import com.jasongj.bean.Product;
import com.jasongj.bean.User;
import com.jasongj.dao.role.IRoleDao;
import com.jasongj.dao.user.IUserDao;
import com.jasongj.dao.user.product.IProductDao;
import com.jasongj.factory.IDaoFactory;
import com.jasongj.factory.MySQLDaoFactory; public class Client { public static void main(String[] args) {
IDaoFactory factory = new MySQLDaoFactory(); IUserDao userDao = factory.createUserDao();
User user = new User();
user.setUsername("demo");
user.setPassword("demo".toCharArray());
userDao.addUser(user); IRoleDao roleDao = factory.createRoleDao();
roleDao.getRole("admin"); IProductDao productDao = factory.createProductDao();
Product product = new Product();
productDao.removeProduct(product); } }

抽象工厂模式案例解析

本文所述抽象工厂模式示例代码可从作者Github下载

上例是J2EE开发中常用的DAO(Data Access Object),操作对象(如User和Role,对应于数据库中表的记录)需要对应的DAO类。

在实际项目开发中,经常会碰到要求使用其它类型的数据库,而不希望过多修改已有代码。因此,需要为每种DAO创建一个DAO接口(如IUserDao,IRoleDao和IProductDao),同时为不同数据库实现相应的具体类。

调用方依赖于DAO接口而非具体实现(依赖倒置原则),因此切换数据库时,调用方代码无需修改。

这些具体的DAO实现类往往不由调用方实例化,从而实现具体DAO的使用方与DAO的构建解耦。实际上,这些DAO类一般由对应的具体工厂类构建。调用方不依赖于具体工厂而是依赖于抽象工厂(依赖倒置原则,又是依赖倒置原则)。

每种具体工厂都能创建多种产品,由同一种工厂创建的产品属于同一产品族。例如PostgreSQLUserDao,PostgreSQLRoleDao和PostgreSQLProductDao都属于PostgreSQL这一产品族。

切换数据库即是切换产品族,只需要切换具体的工厂类。如上文示例代码中,客户端使用的MySQL,如果要换用Oracle,只需将MySQLDaoFactory换成OracleDaoFactory即可。

抽象工厂模式优点

  • 因为每个具体工厂类只负责创建产品,没有简单工厂中的逻辑判断,因此符合单一职责原则。
  • 与简单工厂模式不同,抽象工厂并不使用静态工厂方法,可以形成基于继承的等级结构。
  • 新增一个产品族(如上文类图中的MySQLUserDao,MySQLRoleDao,MySQLProductDao)时,只需要增加相应的具体产品和对应的具体工厂类即可。相比于简单工厂模式需要修改判断逻辑而言,抽象工厂模式更符合开-闭原则。

抽象工厂模式缺点

  • 新增产品种类(如上文类图中的UserDao,RoleDao,ProductDao)时,需要修改工厂接口(或者抽象工厂)及所有具体工厂,此时不符合开-闭原则。抽象工厂模式对于新的产品族符合开-闭原则而对于新的产品种类不符合开-闭原则,这一特性也被称为开-闭原则的倾斜性。

抽象工厂模式与OOP原则

已遵循的原则

  • 依赖倒置原则(工厂构建产品的方法均返回产品接口而非具体产品,从而使客户端依赖于产品抽象而非具体)
  • 迪米特法则
  • 里氏替换原则
  • 接口隔离原则
  • 单一职责原则(每个工厂只负责创建自己的具体产品族,没有简单工厂中的逻辑判断)
  • 开闭原则(增加新的产品族,不像简单工厂那样需要修改已有的工厂,而只需增加相应的具体工厂类)

未遵循的原则

  • 开闭原则(虽然对新增产品族符合开-闭原则,但对新增产品种类不符合开-闭原则)

Java设计模式系列

Java设计模式(三) 抽象工厂模式的更多相关文章

  1. java设计模式(三)--抽象工厂模式

    转载:http://zz563143188.iteye.com/blog/1847029 前面的工厂方法模式虽然清晰,但还是感觉有些繁琐,通常使用的还是抽象工厂模式. 工厂方法模式有一个问题就是,类的 ...

  2. Java 设计模式之抽象工厂模式(三)

    原文地址:Java 设计模式之抽象工厂模式(三) 博客地址:http://www.extlight.com 一.前言 上篇文章 <Java 设计模式之工厂模式(二)>,介绍了简单工厂模式和 ...

  3. java设计模式三种工厂模式简单介绍

    一.简单工厂模式 概述:简单工厂模式的创建意图就是,把对类的创建初始化全都交给一个工厂来执行,而用户不需要去关心创建的过程是什么样的,只用告诉工厂我想要什么就行了.而这种方法的缺点也很明显,违背了设计 ...

  4. Java设计模式系列-抽象工厂模式

    原创文章,转载请标注出处:https://www.cnblogs.com/V1haoge/p/10755412.html 一.概述 抽象工厂模式是对工厂方法模式的再升级,但是二者面对的场景稍显差别. ...

  5. java设计模式之抽象工厂模式

    上一篇文章(http://www.cnblogs.com/liaoweipeng/p/5768197.html)讲了简单工厂模式,但是简单工厂模式存在一定的问题,如果想要拓展程序,必须对工厂类进行修改 ...

  6. Java 设计模式之抽象工厂模式

    抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂.该超级工厂又称为其他工厂的工厂.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式. 在抽 ...

  7. java设计模式---三种工厂模式

    工厂模式提供创建对象的接口. 工厂模式分为三类:简单工厂模式(Simple Factory), 工厂方法模式(Factory Method)和抽象工厂模式(Abstract Factory).GOF在 ...

  8. Java设计模式(3)——抽象工厂模式

    抽象工厂模式是所有形态的工厂模式中最为抽象和最其一般性的.抽象工厂模式可以向客户端提供一个接口,使得客户端在不必指定产品的具体类型的情况下,能够创建多个产品族的产品对象. 一.产品族和产品等级结构 为 ...

  9. [java] java 设计模式(2):抽象工厂模式(Abstract Factory)

    工厂方法模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要拓展程序,必须对工厂类进行修改,这违背了闭包原则,所以,从设计角度考虑,有一定的问题,如何解决?就用到抽象工厂模式,创建多个工厂类,这 ...

  10. java设计模式之抽象工厂模式学习

    工厂模式有个问题就是,类的创建依赖工厂.要想增加一个工厂类,就要修改原来的代码,这违背了闭包原则.所以,从设计角度考虑,有一定的问题,如何解决?就用到抽象工厂模式,创建多个工厂类,这样一旦需要增加新的 ...

随机推荐

  1. redis-JedisPoolConfig配置

    项目之前要使用redis这个临时缓存,下面是从别的大神那里转来的一些基本配置属性的说明:   JedisPoolConfig config = new JedisPoolConfig(); //连接耗 ...

  2. gradle环境配置、

    话不多说,直接上流程. 1.下载 gradle.zip文件,我以为已经为大家准备好了各个版本的下载地址. 链接: http://pan.baidu.com/s/1hqjIVlE 密码: 8ccb 本人 ...

  3. Leetcode_实现zigzag的转换_20161228

    #include<iostream> //#include<valarray> #include<vector> #include<string> us ...

  4. [转]bash快捷键

    生活在 Bash shell 中,熟记以下快捷键,将极大的提高你的命令行操作效率. 编辑命令 Ctrl + a :移到命令行首 Ctrl + e :移到命令行尾 Ctrl + f :按字符前移(右向) ...

  5. $(document).ready() 与 window.onload 之间的区别

    1.执行时机 window.onload 是网页中所有的元素都加载到浏览器后才执行 $(document).ready() 是dom完全就续就可以调用 例如:如果给一副图片添加点击事件,window. ...

  6. DOM操作 ——如何添加、移除、移动、复制、创建和查找节点等。

    DOM操作 --如何添加.移除.移动.复制.创建和查找节点等. (1)创建新节点 createDocumentFragment() //创建一个DOM片段 createElement() //创建一个 ...

  7. kendo-ui学习笔记——题记

    1.Kendo UI基于最新技术HTML5.CSS3和JavaScript标准设计开发.2.官方网址:http://www.kendoui.com/3.API网上查阅英文版网址:http://docs ...

  8. Fiddler-1 安装

    1 进入Fiddler官网:http://www.telerik.com/fiddler 点击[Free download]:填写一些信息后就可以下载. 2 双击安装包--下一步dinghanhua下 ...

  9. GridView不能添加头布局,并且scrollView与GridView冲突导致一些页面无法融合

    此贴为标记贴 方便下次使用 在项目需求中原本是用ScrollView来进行整个页面的滑动,ScrollView里面包含的有图片轮播,文字轮播,与2列GridView的item 问题 使用原生的Grid ...

  10. __autoload的小tip

    __autoload魔法函数不仅在如 $a=new testClass();时可以触发.在 class a extends b时 如果b类的定义在当前页未找到,也可以触发这个函数