一、场景
生活中总是存在着一些依赖于产品系列的问题,不同的系列中的产品可能相互不兼容,在使用时经常需要使用同一系列产品来完成工作。例如:单反摄像机的品牌有多种,如佳能、尼康等。单反摄像机配件是玩摄影的一个很大的开支,为了使拍出的照片更加漂亮,除了购买机身外还需要购买一些配件,如镜头、支架等。而不同品牌的这些配件是不相兼容,因此买了佳能的单反相机不能使用尼康的镜头。所以,一般购买某个品牌的单反后,就会购买其相应的配件。
生活中还有一些别的相似场景,如:
电脑组装。电脑组装时需要考虑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的产品Apublic class ProductA1 implements AbstractProductA { public ProductA1(){ System.out.println("ProductA1"); }}//系列2的产品Apublic class ProductA2 implements AbstractProductA { public ProductA2(){ System.out.println("ProductA2"); }}//系列1的产品Bpublic class ProductB1 implements AbstractProductB { public ProductB1(){ System.out.println("ProductB1"); }}// 系列2的产品Bpublic 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