抽象工厂模式概述
抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在抽象工厂模式中,接口是负责创建一个相关对象的工厂,不需要显式指定它们的类。每个生成的工厂都能按照工厂模式提供对象。
抽象工厂模式提供了一种创建一系列相关或相互依赖对象的接口,而无需指定具体实现类。通过使用抽象工厂模式,可以将客户端与具体产品的创建过程解耦,使得客户端可以通过工厂接口来创建一族产品。
介绍
目的: 提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
主要解决: 主要解决接口选择的问题。
何时使用: 系统的产品有多于一个的产品族,而系统只消费其中某一族的产品。
如何解决: 在一个产品族里面,定义多个产品。
关键代码: 在一个工厂里聚合多个同类产品。
优点:
- 抽象工厂模式隔离了具体类的生成,使得客户并不需要知道什么被创建。由于这种隔离,更换一个具体工厂就变得相对容易。所有的具体工厂都实现了抽象工厂中定义的那些公共接口,因此只需改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。另外,应用抽象工厂模式可以实现高内聚低耦合的设计目的,因此抽象工厂模式得到了广泛的应用。
- 当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象。这对一些需要根据当前环境来决定其行为的软件系统来说,是一种非常实用的设计模式。
- 增加新的具体工厂和产品族很方便,无须修改已有系统,符合“开闭原则”。
缺点:
- 在添加新的产品对象时,难以扩展抽象工厂来生产新种类的产品,这是因为在抽象工厂角色中规定了所有可能被创建的产品集合,要支持新种类的产品就意味着要对该接口进行扩展,而这将涉及到对抽象工厂角色及其所有子类的修改,显然会带来较大的不便。
- 开闭原则的倾斜性(增加新的工厂和产品族容易,增加新的产品等级结构麻烦)。
使用场景:
- 一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有类型的工厂模式都是重要的。
- 系统中有多于一个的产品族,而每次只使用其中某一产品族。
- 属于同一个产品族的产品将在一起使用,这一约束必须在系统的设计中体现出来。
- 系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现。
应用实例:
- 前端页面的一键换肤,整个页面所有元素的样式一起换。
- 对不同数据库的表进行curd操作,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。
名词解释:
- 产品等级结构: 产品等级结构即产品的继承结构,如一个抽象类是电视机,其子类有海尔电视机、海信电视机、TCL电视机,则抽象电视机与具体品牌的电视机之间构成了一个产品等级结构,抽象电视机是父类,而具体品牌的电视机是其子类。
- 产品族: 在抽象工厂模式中,产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品,如海尔电器工厂生产的海尔电视机、海尔电冰箱,海尔电视机位于电视机产品等级结构中,海尔电冰箱位于电冰箱产品等级结构中,海尔电视机、海尔电冰箱构成了一个产品族。
结构
四种角色
抽象工厂(Abstract Factory)
提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法 newProduct() 来创建产品。
具体工厂(ConcreteFactory)
主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
抽象产品(Product)
定义了产品的规范,描述了产品的主要特性和功能。
具体产品(ConcreteProduct)
实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。
抽象工厂模式通常涉及一族相关的产品,每个具体工厂类负责创建该族中的具体产品。客户端通过使用抽象工厂接口来创建产品对象,而不需要直接使用具体产品的实现类。
结构图
案例实现
案例描述
在一个项目中用到了两种不同的数据库mysql和access,需要可以随时切换数据库。
案例类图
代码实现
抽象产品角色
User 抽象产品类
public interface User {
/**
* 插入
* @return Integer
*/
Integer insert();
/**
* 删除
* @return Integer
*/
Integer delete();
}
Company 抽象产品类
public interface Company {
/**
* 插入
*
* @return Integer
*/
Integer insert();
/**
* 删除
*
* @return Integer
*/
Integer delete();
}
具体产品角色
access数据库
AccessUser user具体产品
public class AccessUser implements User {
/**
* 插入
*
* @return Integer
*/
@Override
public Integer insert() {
System.out.println("Access\tuser\tinsert");
return 11;
}
/**
* 删除
*
* @return Integer
*/
@Override
public Integer delete() {
System.out.println("Access\tuser\tdelete");
return 12;
}
}
AccessCompany company具体产品
public class AccessCompany implements Company {
/**
* 插入
*
* @return Integer
*/
@Override
public Integer insert() {
System.out.println("Access\tcompany\tinsert");
return 11;
}
/**
* 删除
*
* @return Integer
*/
@Override
public Integer delete() {
System.out.println("Access\tcompany\tdelete");
return 12;
}
}
mysql数据库
MysqlUser user具体产品
public class MysqlUser implements User {
/**
* 插入
*
* @return Integer
*/
@Override
public Integer insert() {
System.out.println("Mysql\tuser\tinsert");
return 21;
}
/**
* 删除
*
* @return Integer
*/
@Override
public Integer delete() {
System.out.println("Mysql\tuser\tdelete");
return 22;
}
}
MysqlCompany company具体产品
public class MysqlCompany implements Company {
/**
* 插入
*
* @return Integer
*/
@Override
public Integer insert() {
System.out.println("Mysql\tcompany\tinsert");
return 21;
}
/**
* 删除
*
* @return Integer
*/
@Override
public Integer delete() {
System.out.println("Mysql\tcompany\tdelete");
return 22;
}
}
抽象工厂角色
AbstractFactory 抽象工厂
public interface AbstractFactory {
Company company();
User user();
}
具体工厂角色
mysql具体工厂(MysqlFactory)
public class MysqlFactory implements AbstractFactory {
@Override
public Company company() {
return new MysqlCompany();
}
@Override
public User user() {
return new MysqlUser();
}
}
Access具体工厂(AccessFactory)
public class AccessFactory implements AbstractFactory {
@Override
public Company company() {
return new AccessCompany();
}
@Override
public User user() {
return new AccessUser();
}
}
demo调用
public class AbstractFactoryPattern {
public static void main(String[] args) {
demo1();
}
private static void demo1(){
System.out.println(">>>>>>>>>>demo1<<<<<<<<<<");
AbstractFactory factory = new AccessFactory();
Company company = factory.company();
User user = factory.user();
company.insert();
company.delete();
user.insert();
user.delete();
factory = new MysqlFactory();
company = factory.company();
user = factory.user();
company.insert();
company.delete();
user.insert();
user.delete();
}
}
关于优化
新增产品等级时使用泛型和反射优化工厂
新增产品等级时,抽象工厂和具体工厂都需要新增对于产品方法,修改地方很多,使用泛型避免修改工厂,只进行产品等级的新增
但是使用反射时需要确定的包名和类名,而且在调用方还需要认识抽象产品,待进一步优化
修改抽象工厂
使用泛型接收需要返回的类型
public interface AbstractFactory<T> {
T getDb(String table);
}
修改具体工厂
使用反射,根据传入的参数创建对应的产品
MysqlFactory
public class MysqlFactory<T> implements AbstractFactory<T> {
private final String path = "design.abstractfactorypattern.javademo.product.concreteproduct.mysql.Mysql";
@Override
public T getDb(String table) {
table = firstUpperCase(table);
try {
return (T) Class.forName(path + table).getDeclaredConstructor().newInstance();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
private static String firstUpperCase(String str) {
char[] chars = str.toCharArray();
if (97 <= chars[0] && chars[0] <= 122) {
chars[0] ^= 32;
}
return String.valueOf(chars);
}
}
AccessFactory
public class AccessFactory<T> implements AbstractFactory<T> {
private final String path = "design.abstractfactorypattern.javademo.product.concreteproduct.access.Access";
@Override
public T getDb(String table) {
table = firstUpperCase(table);
try {
return (T) Class.forName(path + table).getDeclaredConstructor().newInstance();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
private static String firstUpperCase(String str) {
char[] chars = str.toCharArray();
if (97 <= chars[0] && chars[0] <= 122) {
chars[0] ^= 32;
}
return String.valueOf(chars);
}
}
demo调用测试
public class AbstractFactoryPattern {
public static void main(String[] args) {
demo2();
}
private static void demo2(){
System.out.println(">>>>>>>>>>demo2<<<<<<<<<<");
MysqlFactory<Company> mysqlFactory = new MysqlFactory<>();
Company db = mysqlFactory.getDb("Company");
db.insert();
db.delete();
}
}
客户端职责问题,结合简单工厂模式优化
使用简单工厂模式时,客户端不需要进行判断的操作,然而使用抽象工厂模式和工厂模式的时候,逻辑判断的操作回到了客户端,换言之,需要客户端决定实例化哪一个工厂,所以将抽象工厂和简单工厂结合起来,让逻辑判断的部分重新回到工厂中,此处继续使用反射来优化
去掉AbstractFactory、AccessFactory、MysqlFactory三个工厂,使用DbFactory工厂类来实现,创建对应的产品族
public class DbFactory {
private static final String PATH = "design.abstractfactorypattern.javademo.product.concreteproduct.access.";
private static final String PREFIX = "Access";
// private final String path = "design.abstractfactorypattern.javademo.product.concreteproduct.mysql.";
// private static final String PREFIX = "Mysql";
public static Company company() {
Company company = null;
try {
company = (Company) Class.forName(PATH + PREFIX + "Company").getDeclaredConstructor().newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return company;
}
public static User user() {
User user = null;
try {
user = (User) Class.forName(PATH + PREFIX + "User").getDeclaredConstructor().newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return user;
}
}
结合之后逻辑判断的职责回到了工厂中,只需要更换PATH和PREFIX就可以创建对应的产品族,但是在新增产品等级的时候,还是需要在DbFactory新增一个方法,所以同样使用泛型和反射解决
public class DbFactory<T> {
private static final String PATH = "design.abstractfactorypattern.javademo.product.concreteproduct.access.";
private static final String PREFIX = "Access";
// private final String path = "design.abstractfactorypattern.javademo.product.concreteproduct.mysql.";
// private static final String PREFIX = "Mysql";
public T getDb(String table) {
table = firstUpperCase(table);
try {
return (T) Class.forName(PATH + PREFIX + table).getDeclaredConstructor().newInstance();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
private static String firstUpperCase(String str) {
char[] chars = str.toCharArray();
if (97 <= chars[0] && chars[0] <= 122) {
chars[0] ^= 32;
}
return String.valueOf(chars);
}
}
demo调用测试
public class AbstractFactoryPattern {
public static void main(String[] args) {
demo3();
}
private static void demo3() {
System.out.println(">>>>>>>>>>demo3<<<<<<<<<<");
Company company = DbFactory.company();
company.insert();
company.delete();
User user = DbFactory.user();
user.delete();
user.insert();
company = new DbFactory<Company>().getDb("Company");
company.insert();
company.delete();
user = new DbFactory<User>().getDb("User");
user.delete();
user.insert();
}
}