抽象工厂模式定义

工厂方法模式中工厂只负责同类产品的生产。比如电视机工厂不应该生产汽车。

然而现实生活中有很多综合型的工厂,比如有些电视工厂不仅生产电视机,还会生产与之配套的机顶盒。

那么抽象工厂模式随之诞生,这种模式将考虑多种类型产品的生产。

我们总结下:

  • 工厂方法模式只考虑成产同一等级级的产品
  • 抽象方法模式考虑生产多等级的产品,可以说是工厂方法模式的升级版

设计模式3之抽象工厂模式_java


如上图,小米音响和苹果音响为同一个产品。而小米手机和小米音响为同一产品族。

使用场景

那么什么情况下可以使用抽象工厂模式?

使用抽象工厂模式一般要满足以下条件。

  • 系统中有多个产品族,每个具体工厂创建同一族但属于不同等级结构的产品。
  • 系统一次只可能消费其中某一族产品,即同族的产品一起使用。

抽象工厂模式同工厂方法模式结构一样,需要抽象产品,抽象工厂,具体产品,具体工厂4部分组成。

结构图如下:

设计模式3之抽象工厂模式_java_02

实现代码

这个图你可能看着头晕,我们用代码来表示:

首先创建抽象工厂AbstractFactory

public interface AbstractFactory {
    Product1 newProduct1();
    Product2 newProduct2();
}

然后在创建两个抽象产品

/**
 * 手机
 */

public interface Product1 {
    void show();
}

/**
 * 音响
 */

public interface Product2 {
    void show();
}

然后创建具体产品

public class ConcreteProduct11 implements Product1 {
    @Override
    public void show() {
        System.out.println("具体产品11显示【小米-手机】...");
    }
}

public class ConcreteProduct12 implements Product1 {
    @Override
    public void show() {
        System.out.println("具体产品12显示【苹果-手机】...");
    }
}

public class ConcreteProduct21 implements Product2 {
    @Override
    public void show() {
        System.out.println("具体产品21显示【小米-音响】...");
    }
}

public class ConcreteProduct22 implements Product2 {
    @Override
    public void show() {
        System.out.println("具体产品22显示【苹果-音响】...");
    }
}

再创建具体工厂ConcreteFactory1用来生产小米产品【小米-手机,小米-音响】

/**
 * Description: 小米工厂
 *
 * @author Lvshen
 * @since JDK 1.8
 */

public class ConcreteFactory1 implements AbstractFactory {
    public Product1 newProduct1() {
        System.out.println("具体工厂 1 【小米工厂】 生成-->具体产品 11...");
        return new ConcreteProduct11();
    }
    public Product2 newProduct2() {
        System.out.println("具体工厂 1 【小米工厂】 生成-->具体产品 21...");
        return new ConcreteProduct21();
    }
}

ConcreteFactory1用来生产苹果产品【苹果-手机,苹果-音响】

/**
 * Description: 苹果工厂
 *
 * @author Lvshen
 * @since JDK 1.8
 */

public class ConcreteFactory2 implements AbstractFactory {
    public Product1 newProduct1()
    
{
        System.out.println("具体工厂 2 【苹果工厂】生成-->具体产品 12...");
        return new ConcreteProduct12();
    }
    public Product2 newProduct2()
    
{
        System.out.println("具体工厂 2 【苹果工厂】生成-->具体产品 22...");
        return new ConcreteProduct22();
    }
}

测试

我们使用小米工厂,生产小米手机

@Slf4j
public class FactoryTest {
    @Test
    public void test() {
        ConcreteFactory1 concreteFactory1 = new ConcreteFactory1();
        Product1 product1 = concreteFactory1.newProduct1();
        product1.show();
    }
}

测试结果

设计模式3之抽象工厂模式_java_03

我们再来用苹果工厂生产苹果音响

@Slf4j
public class FactoryTest {
    @Test
    public void test() {
        ConcreteFactory2 concreteFactory = new ConcreteFactory2();
        Product2 product = concreteFactory.newProduct2();
        product.show();
    }
}

测试结果

设计模式3之抽象工厂模式_java_04

关于抽象工厂模式的思考

当新增一条产品族时,只需要新增一个工厂即可。比如新增华为手机,华为音响,我们就需要需新增华为工厂。

如果新增新产品等级的产品,那么就需要修改工厂。假如我们新增了手环产品,比如小米手环,苹果手环,华为手环。那么,每个工厂都需要修改。这并不满足闭开原则。

我们再总结下什么情况下会使用抽象工厂模式:

  • 系统中有多个产品族,但每次只使用其中的某一族产品。比如我就喜欢专门使用小米的手机和手环
  • 系统中提供了产品的类库,且所有产品的接口相同,客户端不依赖产品实例的创建细节和内部结构

同工厂模式一样,抽象工厂模式的优点在于,我们不需要知道产品是如何创建的。要获取产品对象,通过工厂就可以获取。做到了很好的封装。

如果产品族类的产品与产品之间存在约束,比如小米手环和小米手机有一定的约束【小米手环需要与小米手机配对才能激活小米手环(这里我瞎说,只是举个栗子)】。那么可以在小米工厂内做出约束,用户并不需要知道内部如如何约束。

我们再来列举一个使用场景

如一个文本编辑器和一个图片处理器,都是软件实体,但是Linux下的文本编辑器和Windows下的文本编辑器虽然功能和界面都相同,但是代码实现是不同的,图片处理器也有类似情况。也就是具有了共同的约束条件:操作系统类型。于是我们可以使用抽象工厂模式,产生不同操作系统下的编辑器和图片处理器。