定义

提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。在抽象工厂模式中,每一个具体工厂都提供了多个工厂方法用于产生多种不同类型的产品。

类型

创建型

角色

抽象工厂:它声明了一组用于创建一族产品的方法,每一个方法对应一种产品。创建所有产品的工厂,创建充电宝,创建手机。
具体工厂:它实现了在抽象工厂中声明的创建产品的方法,生成一组具体产品,这些产品构成了一个产品族,每一个产品都位于某个产品等级结构中。加一个品牌方工厂,增加小米牌工厂,用于生产小米牌的充电宝和手机。
抽象产品:它为每种产品声明接口,在抽象产品中声明了产品所具有的业务方法。
比如手机都可以打电话,充电。
具体产品:它定义具体工厂生产的具体产品对象,实现抽象产品接口中声明的业务方法。小米牌的手机打电话是怎么打的,小米手机是怎么充电的(无线还是有线,快充还是慢充)

在抽象工厂中声明了多个工厂方法,用于创建不同类型的产品,抽象工厂可以是接口,也可以是抽象类或者具体类
具体工厂实现了抽象工厂,每一个具体的工厂方法可以返回一个特定的产品对象,而同一个具体工厂所创建的产品对象构成了一个产品族

设计模式之抽象工厂模式_oracle


直接说名词太绕口:我举超市的例子,正方形,圆形,椭圆代表不同的商品,比如衣服,充电宝,手机。抽象工厂就是一个大超市,里面可以给我们提供各种商品。

但是手机有小米手机,华为手机,vivo手机。所以一个具体工厂就代表一个品牌。

小米也可以产自己的充电宝,自己的手机,自己的衣服(虽然衣服现在还没有)。

扩展产品族,就是品牌方想要扩展业务了,比如小米想做衣服了,就要修改小米工厂的代码,违背了开闭原则。
增加一个产品族是不会修改以前的代码的,我们增加一个品牌方工厂就可以了。
比如增加魅族工厂,就再增加魅族手机,充电宝的相关实现类,而不用修改以前的代码,符合开闭原则。

所以当产品族扩展比较频繁时,抽象工厂模式就不太适用了。
具体应用场景用什么方案:随机应变是永远的准则。而前辈们的经验已经为大多数场景设计了优良的解决方案。

这里代码列举部署不同数据库的例子。
同样的系统,在数据库业务上有的公司想用mysql数据库,有的公司想用oracle数据库。为了避免更换数据库而修改多处系统代码的情况发生。应该怎么去设计数据库的crud业务。
像这种增加产品族而非扩展产品族的业务用抽象工厂刚好合适

示例

数据库表的实体类,User类和Department类

public class Department {
private String departmentName;
public String getDepartmentName() {
return departmentName;
}
public void setDepartmentName(String departmentName) {
this.departmentName = departmentName;
}
}

public class User {
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
private String name;
private int age;
}

抽象工厂DatabaseFactory:定义所有产品的生产,这里是访问不同数据库表的类的生产

public interface DatabaseFactory {
DataUser createUser();
DataDepartment createDepartment();
}

具体工厂:MysqlFactory和OracleFactory分别生成不同的类实例来访问不同数据库的表

public class MysqlFactory implements DatabaseFactory {
@Override
public DataUser createUser() {
return new MysqlUser();
}
@Override
public DataDepartment createDepartment() {
return new MysqlDepartment();
}
}

public class OracleFactory implements DatabaseFactory {
@Override
public DataUser createUser() {
return new OracleUser();
}
@Override
public DataDepartment createDepartment() {
return new OracleDepartment();
}
}

抽象产品:数据库访问接口,DataTable接口定义基本的crud行为。
DataDepartment和DataUser接口可以在此基础上再添加独立的行为。这是接口细分。
要依据业务而定

public interface DataTable<T> {
//访问数据库的顶级接口,定义基本的增删查改功能
boolean Insert();
T getById();
boolean DeleteById();
boolean Update();
}

public interface DataDepartment extends DataTable{
//可以扩展行为
boolean uniqueQuery();
}
public interface DataUser extends DataTable{

}

具体产品:MysqlDepartment,MysqlUser,OracleDepartment,OracleUser

public class OracleUser implements DataUser{
@Override
public boolean Insert() {
System.out.printf("Oracle插入User表成功\n");
return true;
}
@Override
public User getById() {
User user = new User();
System.out.printf("Oracle查询User表成功\n");
return user;
}
@Override
public boolean DeleteById() {
System.out.printf("Oracle删除User表成功\n");
return true;
}
@Override
public boolean Update() {
System.out.printf("Oracle更新User表成功\n");
return true;
}
}
public class OracleDepartment implements DataDepartment{
@Override
public boolean Insert() {
System.out.printf("Oracle插入Department表成功\n");
return true;
}

@Override
public Department getById() {

Department department = new Department();
System.out.printf("Oracle查询Department表成功\n");
return department;
}

@Override
public boolean DeleteById() {
System.out.printf("Oracle删除Department表成功\n");
return true;
}

@Override
public boolean Update() {
System.out.printf("Oracle更新Department表成功\n");
return true;
}


@Override
public boolean uniqueQuery() {
System.out.println("Department区别于User表的独特业务");
return true;
}
}
public class MysqlUser implements DataUser {


@Override
public boolean Insert() {
System.out.printf("Mysql插入User表成功\n");
return true;
}

@Override
public User getById() {

User user = new User();
System.out.printf("Mysql查询User表成功\n");
return user;
}

@Override
public boolean DeleteById() {
System.out.printf("Mysql删除User表成功\n");
return true;
}

@Override
public boolean Update() {
System.out.printf("Mysql更新User表成功\n");
return true;
}
}
public class MysqlDepartment implements DataDepartment{

@Override
public boolean Insert() {
System.out.printf("Mysql插入Department表成功\n");
return true;
}

@Override
public Department getById() {
Department department = new Department();
System.out.printf("Mysql查询Department表成功\n");
return department;
}

@Override
public boolean DeleteById() {
System.out.printf("Mysql删除Department表成功\n");
return true;
}

@Override
public boolean Update() {
System.out.printf("Mysql更新Department表成功\n");
return true;
}


@Override
public boolean uniqueQuery() {
System.out.println("Department区别于User表的独特业务");
return true;
}
}

客户端代码:

public class Test {
public static void main(String[] args){
DatabaseFactory factory = new OracleFactory();
DataUser dataUser = factory.createUser();
dataUser.Insert();
DataDepartment dataDepartment = factory.createDepartment();
dataDepartment.Insert();
}
}

如果不想修改客户端代码,也可以利用反射机制和配置文件,当需要修改具体工厂的时候就不需要修改客户端代码,只改配置文件即可

String factoryName = "com.xt.designmode.creational.abstractfactory.factory.MysqlFactory";
// 从配置文件或数据库等外部渠道获取具体工厂类名

// 通过反射机制获取工厂类
Class c = Class.forName(factoryName);
DatabaseFactory databaseFactory = (DatabaseFactory) c.newInstance();
DataUser dataUser1 = databaseFactory.createUser();
dataUser1.Insert();

抽象工厂模式总结

主要优点如下:

  • 抽象工厂模式隔离了具体类的生成,使得客户并不需要知道什么被创建。由于这种隔离,更换一个具体工厂就变得相对容易,所有的具体工厂都实现了抽象工厂中定义的那些公共接口,因此只需改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。
  • 当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象。
  • 增加新的产品族很方便,无须修改已有系统,符合”开闭原则”。

主要缺点如下:

  • 增加新的产品等级结构麻烦,需要对原有系统进行较大的修改,甚至需要修改抽象层代码,这显然会带来较大的不便,违背了”开闭原则”。

适用场景:

  • 一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有类型的工厂模式都是很重要的,用户无须关心对象的创建过程,将对象的创建和使用解耦。
  • 系统中有多于一个的产品族,而每次只使用其中某一产品族。可以通过配置文件等方式来使得用户可以动态改变产品族,也可以很方便地增加新的产品族。
  • 属于同一个产品族的产品将在一起使用,这一约束必须在系统的设计中体现出来。同一个产品族中的产品可以是没有任何关系的对象,但是它们都具有一些共同的约束,如同一操作系统下的按钮和文本框,按钮与文本框之间没有直接关系,但它们都是属于某一操作系统的,此时具有一个共同的约束条件:操作系统的类型。
  • 产品等级结构稳定,设计完成之后,不会向系统中增加新的产品等级结构或者删除已有的产品等级结构。(也就是不会随意更改自己公司的业务,如小米公司一直做手机电脑,而不轻易添加衣服食品等业务,但是也不会轻易放弃手机电脑等业务)

解决的问题:
增加了产品族的定义,于是有了可以创建一系列产品的顶级抽象工厂。
和创建具体品牌产品的具体工厂。解决了工厂方法设计模式的每个工厂只能创建一类产品的弊端。

​​工厂方法模式​​

应用:

  • java.sql.Connection