设计模式(四)—— 抽象工厂

161 阅读6分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第24天,点击查看活动详情

概念

日常工作中,为了满足不同用户对产品的多样化需求,工厂不会只局限于生产一类产品,但是系统如果按工厂方法那样为每种产品都增加一个新工厂又会造成工厂泛滥。

为了解决这个问题,一种新的设计模式出现了,即 抽象工厂。。抽象工厂是建立在制造复杂产品体系需求基础之上的一种设计模式,在某种意义上,我们可以将抽象工厂模式理解为工厂方法模式的高度集群化升级版。

抽象工厂模式(Abstract Factory)是对工厂的抽象化,而不只是制造方法。

实例演示

注:本文内容参考 《秒懂设计模式》一书,本文对其做了概括凝练,主要是为了自身学习使用,如果无法理解,建议查看原书。

在介绍抽象工厂之前,我们先设计一个业务背景,比如说星际大战,星际大战假定有两个兵种,人类和外星怪兽,他们之间有个共同点,升级的概念,都为 1 级、2 级、3 级。

如果我们按照之前的抽象方法来实现的话,我们肯定需要先来创建一个父级兵种,然后创建 6(2x3)个工厂方法,如果后面我们每增加一种兵种,那么就需要多实现 3 个方法,我们的代码中就会有多个工厂方法,这样显然是不够优雅的。

下面我们就来展示抽象工厂是如何实现的。

1. 抽象父类兵种

无论是工厂方法还是抽象工厂,我们要做的第一步都是一样的,抽象出父类兵种。其基本属性我们假定有横纵坐标、攻击力、防御力。对于其中的展示 show()(绘制到图上)与攻击 attack() 这两个抽象方法交由子类实现。

public abstract class Unit {

    protected int attack;// 攻击力
    protected int defence;// 防御力
    protected int health;// 生命力
    protected int x;// 横坐标
    protected int y;// 纵坐标

    public Unit(int attack, int defence, int health, int x, int y) {
        this.attack = attack;
        this.defence = defence;
        this.health = health;
        this.x = x;
        this.y = y;
    }

    public abstract void show();

    public abstract void attack();

}

2. 兵种类定义

接下来我们将兵种按等级分类,假设同一等级的攻击力、防御力等属性值是相同的,所以初级、中级、高级兵种会分别对应 3 个等级的兵种类。各等级兵种类都继承自兵种抽象类 Unit,它们对应的攻击力、防御力及生命力也各不相同,等级越高,其属性值也越高。

初级兵种实现如下:

public abstract class LowClassUnit extends Unit {

    public LowClassUnit(int x, int y) {
        super(5, 2, 35, x, y);
    }

}

中级兵种实现如下:

public abstract class MidClassUnit extends Unit {

    public MidClassUnit(int x, int y) {
        super(10, 8, 80, x, y);
    }

}

高级兵种实现如下:

public abstract class HighClassUnit extends Unit {

    public HighClassUnit(int x, int y) {
        super(25, 30, 300, x, y);
    }

}

对于具体的兵种,比如人类初级兵种,我们只需要实现 LowClassUnit 类即可,例如:

public class Marine extends LowClassUnit {

    public Marine(int x, int y) {
        super(x, y);
    }

    @Override
    public void show() {
        System.out.println("士兵出现在坐标:[" + x + "," + y + "]");
    }

    @Override
    public void attack() {
        System.out.println("士兵用机关枪射击,攻击力:" + attack);
    }

}

其他兵种实现也是如此,这里不一一赘述。

3. 抽象工厂实现

上面我们看了兵种类的定义,我们总共定义了 6 个兵种产品,那么每个产品都需要对应一个工厂类吗?答案当然是否定的。

对于上面的产品,我们可以抽象成两个族,并且每个族工厂都应该拥有 3 个等级兵种的制造方法。如此规划不但合理,而且 避免了工厂类泛滥 的问题。

那么,首先我们来制定这 3 个工业制造标准,也就是定义抽象工厂接口。

public interface AbstractFactory {

    LowClassUnit createLowClass();// 初级兵种制造标准

    MidClassUnit createMidClass();// 中级兵种制造标准

    HighClassUnit createHighClass();// 高级兵种制造标准
}

既然抽象工厂我们已经定义好了,现在我们就可以实现人类工厂和外星工厂了。

人类工厂代码如下:

public class HumanFactory implements AbstractFactory {

    private int x;//工厂横坐标
    private int y;//工厂纵坐标

    public HumanFactory(int x, int y) {
        this.x = x;
        this.y = y;
    }

    @Override
    public LowClassUnit createLowClass() {
        LowClassUnit unit = new Marine(x, y);
        System.out.println("制造海军陆战队员成功。");
        return unit;
    }

    @Override
    public MidClassUnit createMidClass() {
        MidClassUnit unit = new Tank(x, y);
        System.out.println("制造变形坦克成功。");
        return unit;
    }

    @Override
    public HighClassUnit createHighClass() {
        HighClassUnit unit = new Battleship(x, y);
        System.out.println("制造巨型战舰成功。");
        return unit;
    }

}

外星工厂如下:


    private int x;//工厂横坐标
    private int y;//工厂纵坐标

    public AlienFactory(int x, int y) {
        this.x = x;
        this.y = y;
    }

    @Override
    public LowClassUnit createLowClass() {
        LowClassUnit unit = new Roach(x, y);
        System.out.println("制造蟑螂兵成功。");
        return unit;
    }

    @Override
    public MidClassUnit createMidClass() {
        MidClassUnit unit = new Poison(x, y);
        System.out.println("制造毒液兵成功。");
        return unit;
    }

    @Override
    public HighClassUnit createHighClass() {
        HighClassUnit unit = new Mammoth(x, y);
        System.out.println("制造猛犸巨兽成功。");
        return unit;
    }

}
public class AlienFactory implements AbstractFactory {

    private int x;//工厂横坐标
    private int y;//工厂纵坐标

    public AlienFactory(int x, int y) {
        this.x = x;
        this.y = y;
    }

    @Override
    public LowClassUnit createLowClass() {
        LowClassUnit unit = new Roach(x, y);
        System.out.println("制造蟑螂兵成功。");
        return unit;
    }

    @Override
    public MidClassUnit createMidClass() {
        MidClassUnit unit = new Poison(x, y);
        System.out.println("制造毒液兵成功。");
        return unit;
    }

    @Override
    public HighClassUnit createHighClass() {
        HighClassUnit unit = new Mammoth(x, y);
        System.out.println("制造猛犸巨兽成功。");
        return unit;
    }

}

客户端代码实现:

public class Client {

    public static void main(String[] args) {
        System.out.println("游戏开始……");
        System.out.println("双方挖矿攒钱……");

        //第一位玩家选择了人类族
        System.out.println("工人建造人类族工厂……");
        AbstractFactory factory = new HumanFactory(10, 10);

        Unit marine = factory.createLowClass();
        marine.show();

        Unit tank = factory.createMidClass();
        tank.show();

        Unit ship = factory.createHighClass();
        ship.show();

        //第二位玩家选择了外星怪兽族
        System.out.println("工蜂建造外星怪兽族工厂……");
        factory = new AlienFactory(200, 200);

        Unit roach = factory.createLowClass();
        roach.show();

        Unit poison = factory.createMidClass();
        poison.show();

        Unit mammoth = factory.createHighClass();
        mammoth.show();

        System.out.println("两族开始大混战……");
        marine.attack();
        roach.attack();
        poison.attack();
        tank.attack();
        mammoth.attack();
        ship.attack();


     }

}

这样一来,如果玩家需要一个新族加入,我们可以在此模式之上去实现一个新的族工厂并实现 3 个等级的制造方法,工厂一经替换即可产出各系列产品兵种,且无须改动现有代码,良好的可扩展性一览无遗,这就是一套拥有完备生产模式的标准化工业系统所带来的好处。

总结

抽象工厂可以看作是“工厂的工厂”

抽象工厂模式的各角色定义如下。

  • AbstractProduct(抽象产品):产品系列的抽象类,对应本文例子中的初级、中级、高级兵种抽象类。
  • Product(产品):继承自抽象产品的产品实体类,类似于本文例子中人类族与外星怪兽族的初级兵种。
  • AbstractFactory(抽象工厂接口):各族工厂的高层抽象,可以是接口或者抽象类。抽象工厂对各产品系列的制造标准进行规范化定义,但具体返回哪个族的产品由具体族工厂决定,它并不关心。
  • ConcreteFactoryA、ConcreteFactoryB(工厂 A 实现、工厂 B 实现):继承自抽象工厂的各族工厂,需实现抽象工厂所定义的产品系列制造方法,可以扩展多个工厂实现。对应本章例程中的人类兵工厂与外星母巢。
  • Client(客户端):产品的使用者,只关心制造出的产品系列,具体是哪个产品族由工厂决定。

参考文档

  • 《秒懂设计模式》—— 刘韬