在工厂方法模式中,我们使用一个工厂创建一个产品,也就是说一个具体的工厂对应一个具体的产品。但是有时候我们需要一个工厂能够提供多个产品对象,而不是单一的对象,这个时候我们就需要使用抽象工厂模式。
抽象工厂模式是所有形态的工厂模式中最为抽象和最具一般性的,抽象工厂迷失可以向客户端提供一个接口,使得客户端在不必指定产品的具体类型的情况下,能够创建多个产品族的产品对象。
在讲解抽象工厂模式之前,我们需要厘清两个概念:
产品等级结构。产品的等级结构也就是产品的继承结构。例如一个为空调的抽象类,它有海尔空调、格力空调、美的空调等一系列的子类,那么这个抽象类空调和他的子类就构成了一个产品等级结构。
产品族。产品族是在抽象工厂模式中的。在抽象工厂模式中,产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品。比如,海尔工厂生产海尔空调。海尔冰箱,那么海尔空调则位于空调产品族中。
产品等级结构和产品族结构示意图如下:
UML结构图
模式结构说明:
AbstractFactory:抽象工厂。抽象工厂定义了一个接口,所有的具体工厂都必须实现此接口,这个接口包含了一组方法用来生产产品。
ConcreteFactory:具体工厂。具体工厂是用于生产不同产品族。要创建一个产品,客户只需要使用其中一个工厂完全不需要实例化任何产品对象。
AbstractProduct:抽象产品。这是一个产品家族,每一个具体工厂都能够生产一整组产品。
Product:具体产品。
还是用之前的水果的例子
现在有两个产品族: 南方水果, 北方水果
产品结构 :苹果、香蕉
package com.dongguo.absfactory.fruit1;
/**
* @author Dongguo
* @date 2021/8/22 0022-10:15
* @description:
*/
public interface Fruit {
public void eat();
}
package com.dongguo.absfactory.fruit1;
/**
* @author Dongguo
* @date 2021/8/22 0022-10:23
* @description:
*/
public abstract class Apple implements Fruit{
public abstract void eat();
}
package com.dongguo.absfactory.fruit1;
/**
* @author Dongguo
* @date 2021/8/22 0022-10:16
* @description:
*/
public abstract class Banana implements Fruit{
public abstract void eat();
}
package com.dongguo.absfactory.fruit1;
/**
* @author Dongguo
* @date 2021/8/22 0022-11:08
* @description:
*/
public class NorthApple extends Apple {
@Override
public void eat() {
System.out.println("吃南方的苹果");
}
}
package com.dongguo.absfactory.fruit1;
/**
* @author Dongguo
* @date 2021/8/22 0022-11:08
* @description:
*/
public class SouthApple extends Apple{
@Override
public void eat() {
System.out.println("吃北方的苹果");
}
}
package com.dongguo.absfactory.fruit1;
/**
* @author Dongguo
* @date 2021/8/22 0022 11:10
* @description:
*/
public class NorthBanana extends Banana {
@Override
public void eat() {
System.out.println("吃南方的香蕉");
}
}
package com.dongguo.absfactory.fruit1;
/**
* @author Dongguo
* @date 2021/8/22 0022 11:10
* @description:
*/
public class SouthBanana extends Banana {
@Override
public void eat() {
System.out.println("吃北方的香蕉");
}
}
水果分为苹果和香蕉 苹果分为南方苹果、北方苹果 香蕉分为南方香蕉、北方香蕉
那我们使用抽象工厂模式是如何实现的呢:
创建FruitFactory 有两个方法分别是获取Apple实例和Banana实例
package com.dongguo.absfactory.fruit1;
/**
* @author Dongguo
* @date 2021/8/22 0022-11:12
* @description:
*/
public interface FruitFactory {
//实例化Apple
public Fruit getApple();
//实例化Banana
public Fruit getBanna();
}
南方水果工厂获得南方苹果和南方香蕉的实例
package com.dongguo.absfactory.fruit1;
/**
* @author Dongguo
* @date 2021/8/22 0022-11:14
* @description:
*/
public class NorthFruitFactory implements FruitFactory {
public Fruit getApple() {
return new NorthApple();
}
public Fruit getBanna() {
return new NorthBanana();
}
}
北方水果工厂获得北方苹果和北方香蕉的实例
package com.dongguo.absfactory.fruit1;
/**
* @author Dongguo
* @date 2021/8/22 0022-11:15
* @description:
*/
public class SouthFruitFactory implements FruitFactory {
public Fruit getApple() {
return new SouthApple();
}
public Fruit getBanna() {
return new SouthBanana();
}
}
package com.dongguo.absfactory.fruit1;
/**
* @author Dongguo
* @date 2021/8/22 0022-11:15
* @description:
*/
public class AbsFactory {
public static void main(String[] args) {
FruitFactory ff = new NorthFruitFactory();
Fruit northApple = ff.getApple();
northApple.eat();
Fruit northBanna = ff.getBanna();
northBanna.eat();
FruitFactory ff1 = new SouthFruitFactory();
Fruit southApple = ff1.getApple();
southApple.eat();
Fruit southBanana = ff1.getBanna();
southBanana.eat();
}
}
运行结果:
吃南方的苹果
吃南方的香蕉
吃北方的苹果
吃北方的香蕉
此时又新加了一个产品族 温室水果
package com.dongguo.absfactory.fruit1;
/**
* @author Dongguo
* @date 2021/8/22 0022-11:16
* @description:
*/
public class GreenhouseApple extends Apple{
@Override
public void eat() {
System.out.println("吃温室苹果");
}
}
package com.dongguo.absfactory.fruit1;
/**
* @author Dongguo
* @date 2021/8/22 0022-11:17
* @description:
*/
public class GreenhouseBanana extends Banana{
@Override
public void eat() {
System.out.println("吃温室香蕉");
}
}
package com.dongguo.absfactory.fruit1;
/**
* @author Dongguo
* @date 2021/8/22 0022-11:17
* @description:
*/
public class GreenhouseFruitFactory implements FruitFactory {
@Override
public Fruit getApple() {
return new GreenhouseApple();
}
@Override
public Fruit getBanna() {
return new GreenhouseBanana();
}
}
package com.dongguo.absfactory.fruit1;
/**
* @author Dongguo
* @date 2021/8/22 0022-11:15
* @description:
*/
public class AbsFactory {
public static void main(String[] args) {
FruitFactory ff = new NorthFruitFactory();
Fruit northApple = ff.getApple();
northApple.eat();
Fruit northBanna = ff.getBanna();
northBanna.eat();
FruitFactory ff1 = new SouthFruitFactory();
Fruit southApple = ff1.getApple();
southApple.eat();
Fruit southBanana = ff1.getBanna();
southBanana.eat();
FruitFactory ff2 = new GreenhouseFruitFactory();
Fruit greenhouseApple = ff2.getApple();
greenhouseApple.eat();
Fruit greenhouseBanna = ff2.getBanna();
greenhouseBanna.eat();
}
}
运行结果:
吃南方的苹果
吃南方的香蕉
吃北方的苹果
吃北方的香蕉
吃温室苹果
吃温室香蕉
优点
1、 抽象工厂隔离了具体类的生成,是的客户端不需要知道什么被创建。所有的具体工厂都实现了抽象工厂中定义的公共接口,因此只需要改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。
2、 当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象。
缺点添加新的行为时比较麻烦。如果需要添加一个新产品族对象时,需要更改接口及其下所有子类,这必然会带来很大的麻烦。
模式使用场景一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有类型的工厂模式都是重要的。
系统中有多于一个的产品族,而每次只使用其中某一产品族。
- 属于同一个产品族的产品将在一起使用,这一约束必须在系统的设计中体现出来。
- 系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现。
抽象工厂模式中主要的优点在于具体类的隔离,是的客户端不需要知道什么被创建了。其缺点在于增加新的等级产品结构比较复杂,需要修改接口及其所有子类。
抽象工厂中方法对应产品结构,具体工厂对应产品族