设计模式之抽象工厂模式
一、场景
生活中总是存在着一些依赖于产品系列的问题,不同的系列中的产品可能相互不兼容,在使用时经常需要使用同一系列产品来完成工作。例如:单反摄像机的品牌有多种,如佳能、尼康等。单反摄像机配件是玩摄影的一个很大的开支,为了使拍出的照片更加漂亮,除了购买机身外还需要购买一些配件,如镜头、支架等。而不同品牌的这些配件是不相兼容,因此买了佳能的单反相机不能使用尼康的镜头。所以,一般购买某个品牌的单反后,就会购买其相应的配件。
生活中还有一些别的相似场景,如:
-
电脑组装。电脑组装时需要考虑CUP、主板、内存等兼容性问题,不同的系列如Intell和AMD等有所不同,可能会引起兼容性问题。
-
操作系统软件。一个软件不能同时在不同操作系统上运行,必须针对不同的电脑系统开发相应的软件程序。如eclipse需要针对mac、window和linux设计软件,因为不同的系列使用的软件不同。
-
多个数据库切换问题。不同的数据库,如SQLServer、Access、MySql和Orcal等在连接方式、使用细节上有些不同,所以在编程中针对不同的数据库要定义相应的数据库访问操作。
二、定义和意图
抽象工厂模式:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
抽象工厂与工厂方法的区别是,抽象工厂是针对多个系列产品,而工厂方法是针对同个系列产品。
三、结构
AbstractFactory:定义了创建抽象产品对象的操作接口。
public interface AbstractFactory {
AbstractProductA createProductA();
AbstractProductB createProductB();
}
ConcreteFactory:实现创建具体产品对象的操作
public class ConcreteFactory1 implements AbstractFactory {
@Override
public AbstractProductA createProductA() {
return new ProductA1();
}
@Override
public AbstractProductB createProductB() {
return new ProductB1();
}
}
public class ConcreteFactory2 implements AbstractFactory {
@Override
public AbstractProductA createProductA() {
return new ProductA2();
}
@Override
public AbstractProductB createProductB() {
return new ProductB2();
}
}
AbstractProduct: 为一类产品对象声明接口
public interface AbstractProductA {
}
public interface AbstractProductB {
}
ConcreteProduct:具体的产品对象
//系列1的产品A
public class ProductA1 implements AbstractProductA {
public ProductA1(){
System.out.println("ProductA1");
}
}
//系列2的产品A
public class ProductA2 implements AbstractProductA {
public ProductA2(){
System.out.println("ProductA2");
}
}
//系列1的产品B
public class ProductB1 implements AbstractProductB {
public ProductB1(){
System.out.println("ProductB1");
}
}
// 系列2的产品B
public class ProductB2 implements AbstractProductB {
public ProductB2(){
System.out.println("ProductB2");
}
}
Client:使用AbstractFactory和AbstractProduct类声明的接口。
四、举例
以数据库的用户User和部门Dept的操作为例,不同数据库的SQL可能有些不同,因此需要为不同的数据库定义User和Dept的访问操作。
通过客户端client调用不同的工厂获取不同数据库的访问方式。具体如下:
DbFactory,创建访问不同数据库表的抽象工厂,
public interface IFactory {
IDept getIDept();
IUser getIUser();
}
MySqlFactory访问Mysql数据的对象工厂,AccessFacotry是访问Access数据库数据的对象工厂。
public class MysqlFactory implements IFactory {
@Override
public IDept getIDept() {
return new MySqlDeptDao();
}
@Override
public IUser getIUser() {
return new MysqlUserDao();
}
}
public class AccessFactory implements IFactory {
@Override
public IDept getIDept() {
return new AccessDeptDao();
}
@Override
public IUser getIUser() {
return new AccessUserDao();
}
}
访问User表的接口,和访问Dept的接口。
public interface IUser {
//添加用户
boolean addUser(User user);
//删除用户
boolean delUser(User user);
}
public interface IDept {
//插入部门
boolean addDept(Dept dept);
//删除部门
boolean delDept(Dept dept);
}
具体实现:
public class MySqlDeptDao implements IDept {
@Override
public boolean addDept(Dept dept) {
System.out.println("mysql 中添加dept");
return true;
}
@Override
public boolean delDept(Dept dept) {
System.out.println("mysql 中删除dept");
return true;
}
}
public class MysqlUserDao implements IUser{
@Override
public boolean addUser(User user) {
System.out.println("mysql 中添加user");
return true;
}
@Override
public boolean delUser(User user) {
System.out.println("mysql 中删除user");
return true;
}
}
public class AccessDeptDao implements IDept{
@Override
public boolean addDept(Dept dept) {
System.out.println("access 中添加dept");
return true;
}
@Override
public boolean delDept(Dept dept) {
System.out.println("access 中删除dept");
return true;
}
}
public class AccessUserDao implements IUser{
@Override
public boolean addUser(User user) {
System.out.println("access 中添加user");
return true;
}
@Override
public boolean delUser(User user) {
System.out.println("access 中删除user");
return true;
}
}
客户端的实现:
public class Client {
public static void main(String[] args){
Dept dept = new Dept();
//只要更换IFactory 就可以切换不同的数据库处理。这边可以采用配置和反射机制改进
IFactory dbFactory = new MysqlFactory();
IDept deptDao = dbFactory.getIDept();
deptDao.addDept(dept);
}
}
五、优缺点
抽象工厂有以下优点:
-
易于交换产品系列。如上面的例子中,只要切换不同的具体工厂类就可以得到不同的数据库访问方式。
-
有利于保持产品的一致性。 因为具体工厂能够生产同一系列的产品,如MysqlFactory主要生产对Mysql数据库不同表访问的操作类,因此能够保持高度同一。
-
分离了具体类,实现了低耦合。将用户类和具体实现类分开,使得用户不知道对象的创建细节等,降低耦合性。
抽象工厂的不足:
-
难以支持新来的产品。 例如上例中只支持对User表和Dept表的操作,如果有新的表需要操作,意味着需要在很多地方添加方法,破坏了开放-封闭原则。
-
要求每个产品系列都要有一个新的具体工厂子类,即使这些系列产品差别很小也要为不同系列创建具体子类。这样也很容易造成类爆炸问题。
六、参考
[1] 四人帮《设计模式》
[2] 《大话设计模式》
[3] http://www.cnblogs.com/java-my-life/archive/2012/03/28/2418836.html