掘金 后端 ( ) • 2024-04-24 10:40

吴宣仪-28.jpeg

前言

  前面介绍的工厂方法模式中考虑的是一类产品的生产,如奥迪厂生产奥迪车、宝马厂只宝马车、计算机软件学院只培养计算机软件专业的学生等。同种类称为同等级,也就是说:工厂方法模式只考虑生产同等级的产品,但是在现实生活中许多工厂是综合型的工厂,能生产多等级(种类) 的产品,如农场里既养动物又种植物,电器厂既生产电视机又生产洗衣机或空调,大学既有软件专业又有生物专业等。

一、概述

1.1 基本介绍

  本节要介绍的抽象工厂模式将考虑多等级产品的生产,将同一个具体工厂所生产的位于不同等级的一组产品称为一个产品族,简单地说是抽象工厂可以创建具体工厂,由具体工厂来产生具体产品,抽象工厂中方法个数不同,产品的个数也不同。其中,抽象工厂创建其他工厂,提供了创建一系列相关或者相互依赖产品的接口,它包含多个创建产品的方法,可以创建多个不同等级的产品,而无需知道他们的具体类。而具体工厂主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。而实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间是多对一的关系。如下图所示。

graph TD
	A{{抽象工厂}}
	A-->B(具体工厂)
	A-->C(具体工厂)
	A-->D(具体工厂)
	B-->B1[产品]
	C-->C1[产品]
	C-->C2[产品]
	D-->D1[产品]
	D-->D2[产品]
	D-->D3[产品]

  抽象工厂(Abstract Factory Pattern)模式的定义:是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。在抽象工厂模式中,每个具体工厂都提供了多个工厂方法用于创建多种不同类型的具体对象。抽象工厂模式是工厂方法模式的升级版本,工厂方法模式只生产一个等级的产品,而抽象工厂模式可生产多个等级的产品。

1.2 定义与特点

  抽象工厂的出现是为了解决工厂模式的问题,尽可能缩减工厂实现子类的数量,不必给每一个产品分配一个工程类,能够将产品进行分组,然后在每组中由同一个工厂类的不同方法来创建。

  例如真正的工厂可能又造手机,又搞电视,最近还要搞车!那么面对这种综合型能搞的公司,能够生产出多种类的产品,就可以采用抽象工厂模式考虑多个组。抽象工厂模式将考虑多等级产品的生产和多品牌的产品。

graph TB
    subgraph 汽车
    a1[小米汽车]
    a2[红米汽车]
    end
    subgraph 电视
    b1[小米电视]
    b2[红米电视]
    end
    subgraph 手机
    c1[小米手机]
    c2[红米手机]
    end

二、模式的实现

2.1 代码实现

  抽象工厂模式同工厂方法模式一样,也是由抽象工厂、具体工厂、抽象产品和具体产品等 4 个要素构成,但抽象工厂中方法个数不同,抽象产品的个数也不同。现在我们来分析其基本结构和实现方法。抽象工厂模式的主要角色如下:

  1. 抽象工厂(Abstract Factory):提供了创建产品的接口,它包含多个创建产品的方法,可以创建多个不同等级的产品。

    public interface AbstractFactory {
        public Product1 newProduct1();
        public Product2 newProduct2();
    }
    
  2. 具体工厂(Concrete Factory):主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。

    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();
        }
    }
    
  3. 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。

    public interface Product {
        public void show();
    }
    
    public interface Product2 {
        public void show();
    }
    
  4. 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间是多对一的关系。

    public class ConcreteProduct11 implements Product1 {
        @Override
        public void show() {
    
        }
    }
    
    public class ConcreteProduct21 implements Product2 {
        @Override
        public void show() {
    
        }
    }
    

2.2 适用场景

  农场中除了像畜牧场一样可以养动物,还可以培养植物,如养马、养牛、种菜、种水果等,所以本实例要稍微复杂些,须用抽象工厂模式来实现。本例用抽象工厂模式来设计两个农场,一个是北方农场用于养牛和种菜,一个是南方农场用于养马和种水果,可以在以上两个农场中定义一个生成动物的方法 newAnimal() 和一个培养植物的方法 newPlant()。程序代码如下:

public class FactoryMethod {
    public static void main(String[] args) {
        new SouthFarm().newAnimal().show();
        new NorthFarm().newPlant().show();
    }
}

/**
 * 抽象产品:动物类
 */
interface Animal {
    void show();
}

/**
 * 具体产品:马类
 */
class Horse implements Animal {
    @Override
    public void show() {
        System.out.println("新马出生!");
    }
}

/**
 * 具体产品:牛类
 */
class Cattle implements Animal {
    @Override
    public void show() {
        System.out.println("新牛出生!");
    }
}

/**
 * 抽象产品:植物类
 */
interface Plant {
    void show();
}

/**
 * 具体产品:水果类
 */
class Fruitage implements Plant {
    @Override
    public void show() {
        System.out.println("水果长成!");
    }
}

/**
 * 具体产品:蔬菜类
 */
class Vegetables implements Plant {
    @Override
    public void show() {
        System.out.println("蔬菜长成!");
    }
}

/**
 * 抽象工厂:农场类
 */
interface Farm {
    Animal newAnimal();
    Plant newPlant();
}

/**
 * 具体工厂:北方农场类
 */
class NorthFarm implements Farm {
    @Override
    public Animal newAnimal() {
        return new Cattle();
    }

    @Override
    public Plant newPlant() {
        return new Vegetables();
    }
}

/**
 * 具体工厂:南方农场类
 */
class SouthFarm implements Farm {
    @Override
    public Animal newAnimal() {
        return new Horse();
    }

    @Override
    public Plant newPlant() {
        return new Fruitage();
    }
}

三、小结

  抽象工厂可以做到一组产品的使用和生产相分离。通过抽象工厂模式,我们切换一组产品族的,只需要更换抽象工厂实现即可。由于产品生产被分离出去,所以添加新的产品族完全通过扩展来实现的。很好的实现了开闭原则。如果你要生产的产品很多,而且是一个产品族,并且面临不同产品族切换的情况,那么可以考虑通过抽象工厂来实现。

  请注意,抽象工厂模式允许客户端使用不同的产品族,但如果需要向产品族中添加新的产品类型,那么所有相关的工厂类都需要进行修改,这违背了开闭原则中的“对修改封闭”部分。因此,在设计时需要权衡抽象工厂模式的优点和缺点。

划重点.gif