抽象工厂及其作用
工厂方法是一种创建型设计模式。所谓创建型设计模式是说针对创建对象方面的设计模式。在面向对象的编程语言里,我们通过对象间的相互协作,共同完成复杂的业务逻辑,因而对象之间存在着依赖关系。
在上一篇文章设计模式详解(一):工厂方法——Factory Method中,我们知道,工厂方法是通过一个方法扮演对象工厂的角色,为依赖方提供对象,避免直接调用所依赖类的构造器,从而实现解耦的目的。
在有些业务场景下,我们依赖于一组对象,这些对象就像一套零件一样,当变换实现时,往往需要成套换掉。
比如,我们模仿家具城售卖家具的场景,家具城可对外出售桌子、椅子,沙发三件套家具,用户可定制风格,如艺术、现在等。
public class FurnitureShop {
//... some business logic
public void sell(String style) {
Chair chair = provideChair(style);
Sofa sofa = provideSofa(style);
Table table = provideTable(style);
System.out.println("sell chair:" + chair + ",sofa:" + sofa + ",table:" + table);
}
private Chair provideChair(String style) {
switch (style) {
case "艺术":
return new ArtChair();
case "现代":
return new ModernChair();
//...
}
return new DefaultChair();
}
private Sofa provideSofa(String style) {
switch (style) {
case "艺术":
return new ArtSofa();
case "现代":
return new ModernSofa();
//...
}
return new DefaultSofa();
}
private Table provideTable(String style) {
switch (style) {
case "艺术":
return new ArtTable();
case "现代":
return new ModernTable();
//...
}
return new DefaultTable();
}
//... some business logic
}
观察上面的代码发现,Chair、Sofa、Table三种对象总是按照某种风格成套提供,当需要切换风格时,需要同步修改provideChair、provideSofa、provideTable三处,很不方便。另外,FurnitureShop类的主要职责时售卖家具,而不是创建相关的对象,但现在的实现里充斥大量负责创建对象的工厂方法。
抽象工厂设计模式是将一组互有联系,成套提供对象的工厂方法从依赖方抽离出来,放到一个单独的类中,这个类充当一个总管的角色,管理这些工厂方法。
上面的代码用抽象工厂的设计理念实现如下:
//定义家具套FurnitureSuit接口,担任抽象工厂的角色,提供同种风格的一套家具。
public interface FurnitureSuit {
Chair provideChair();
Sofa provideSofa();
Table provideTable();
}
根据需要实现不同的抽象工程类,如艺术风格
public class ArtFurnitureSuit implements FurnitureSuit {
@Override
public Chair provideChair() {
return new ArtChair();
}
@Override
public Sofa provideSofa() {
return new ArtSofa();
}
@Override
public Table provideTable() {
return new ArtTable();
}
}
最后是依赖方的代码,
public class FurnitureShop {
//... some business logic
public void sell(String style) {
FurnitureSuit suit = provideFurnitureSuit(style);
Chair chair = suit.provideChair();
Sofa sofa = suit.provideSofa();
Table table = suit.provideTable();
System.out.println("sell chair:" + chair + ",sofa:" + sofa + ",table:" + table);
}
private FurnitureSuit provideFurnitureSuit(String style) {
switch (style) {
case "艺术":
return new ArtFurnitureSuit();
case "现代":
return new ModernFurnitureSuit();
//...
}
return new DefaultFurnitureSuit();
}
//... some business logic
}
抽象工厂的好处
- 解耦。同工厂方法一样,抽象工厂避免对象之间实现类的强耦合,另外便于逻辑上成套提供服务的一组对象的切换,出自同一抽象工厂的对象之间必然是互相兼容。
- 符合单一职责的原则。类的设计原则之一便是单一职责。依赖方的主要业务功能并不是创建所依赖的对象,将这一组对象的创建任务交给抽象工厂。
- 符合开闭原则。后续引入更多的抽象工厂的实现类,不会影响到依赖方
抽象工厂的实现
关系图
Abstract Factory为抽象工厂接口定义,ConcreteFactoryX为抽象工厂的不同实现类。依赖方Client只依赖抽象工厂的接口,不依赖抽象工厂的实现类,更不依赖其提供的各种对象的实现类。
实现步骤
- 定义一个抽象工厂接口,该接口定义了一组工厂方法,返回共同服务的各个对象。而方法的返回值也是这些对象对应的接口或基类,而非具体实现类。
- 根据需要定义不同特性的抽象工厂类,它们统一实现抽象工厂接口,实现各个方法,返回符合某种特性的各个对象。
- 依赖方只依赖抽象工厂接口,当需要不同特性的对象组时,直接更换抽象接口的实现类即可。
抽象工厂的适用场景
- 当我们实现业务代码时,无法提前获知所依赖对象的具体类型,这通常发生在顶层设计的时候,此时可以通过工厂方法抽象定义所需的对象。
- 我们打包一个库给他人使用时,其中一些业务逻辑,我们无法确定用户是应该使用库里定义好的一些默认组件,还是想使用自己扩展后的组件(继承默认组件),可通过工厂方法解决该问题。
- 被依赖的对象可能比较特殊,占用一定的系统资源,所以不能任意创建,可在工厂方法内实现一定的控制逻辑,控制对象的创建过程,避免资源浪费。
抽象工厂举例
在java中,定义了一组与数据库交互的接口,即可JDBC,由于不同数据库厂商的内部原理及运转必然不同,java无从得知,只能定义抽象接口让不同数据库厂商去实现。
Connection接口代码数据库连接,封装了某种数据库连接下的各种功能,实现该接口的数据库需实现并返回与之匹配的SQL语句对象和数据库元数据对象等信息。
不同厂商实现的Connection不同,Connection扮演抽象工厂的角色。
public interface Connection extends Wrapper, AutoCloseable {
//...
Statement createStatement() throws SQLException;
DatabaseMetaData getMetaData() throws SQLException;
//...
}