抽象工厂模式

106 阅读10分钟

抽象工厂

抽象工厂模式(Abstract Factory)是对工厂的抽象化,而不只是制造方法。我们知道,为了满足不同用户对产品的多样化需求,工厂不会只局限于生产一类产品,但是系统如果按工厂方法那样为每种产品都增加一个新工厂又会造成工厂泛滥。所以,为了调和这种矛盾,抽象工厂模式提供了另一种思路,将各种产品分门别类,基于此来规划各种工厂的制造接口,最终确立产品制造的顶级规范,使其与具体产品彻底脱钩。抽象工厂是建立在制造复杂产品体系需求基础之上的一种设计模式,在某种意义上,我们可以将抽象工厂模式理解为工厂方法模式的高度集群化升级版

品牌与系列

我们都知道,在工厂方法模式中每个实际的工厂只定义了一个工厂方法。而随着经济发展,人们对产品的需求不断升级,并逐渐走向个性化、多元化,制造业也随之发展壮大起来,各类工厂遍地开花,能够制造的产品种类也丰富了起来,随之而来的弊端就是工厂泛滥

针对这种情况,我们就需要进行产业规划与整合,对现有工厂进行重构。例如,我们可以基于产品品牌与系列进行生产线规划,按品牌划分A工厂与B工厂。具体以汽车工厂举例,A品牌汽车有轿车、越野车、跑车3个系列的产品,同样地,B品牌汽车也包括以上3个系列的产品,如此便形成了两个产品族,分别由A工厂和B工厂负责生产,每个工厂都有3条生产线,分别生产这3个系列的汽车

基于这2个品牌汽车工厂的系列生产线,如果今后产生新的C品牌汽车、D品牌汽车等,都可以沿用此种规划好的生产模式,这便是抽象工厂模式的基础数据模型。

产品规划

无论哪种工厂模式,都一定是基于特定的产品特性发展而来的,所以我们首先得从产品建模切入。假设某公司要开发一款星际战争游戏,战争设定在太阳系文明与异星系文明之间展开,游戏兵种就可以分为人类与外星怪兽2个族

人类拥有各种军工高科技装备,而外星怪兽则靠血肉之躯与人类战斗,所以这两族的兵种必然有着巨大的差异,这就意味着各兵种首先应该按族划分。此外,从另一个角度来看,它们又有相同之处,2个族的兵种都可以被简单归纳为初级(1级)、中级(2级)、高级(3级)3个等级,如同之前对汽车品牌系列的规划一样,各族兵种也应当按等级划分,最终我们可以得到一个对所有兵种分类归纳的表格

兵种规划表格以列划分等级,以行划分族,一目了然,我们可以据此建立数据模型。首先,我们来定义一个所有兵种的顶层父类兵种,这里我们使用抽象类,以达到属性继承给子类的目的

Java版本

package abstractFactory;
//兵种抽象类Unit
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();
}
//初级兵种类LowClassUnit
package abstractFactory;

public abstract class LowClassUnit extends Unit {
    public LowClassUnit( int x, int y) {
        super(5,2,35, x, y);
    }
}
//中级兵种类MidClassUnit
package abstractFactory;

public abstract class MidClassUnit extends Unit  {
    public MidClassUnit(int x, int y) {
        super(10,8,80, x, y);
    }
}
package abstractFactory;
//高级兵种类HighClassUnit
public abstract class HighClassUnit extends Unit  {
    public HighClassUnit(int x, int y) {
        super(25,30,300, x, y);
    }
}
//海军陆战队员类Marine
package abstractFactory;

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);
    }
}
//变形坦克类Tank
package abstractFactory;

public class Tank extends MidClassUnit {
    public Tank(int x, int y) {
        super(x, y);
    }

    @Override
    public void show() {
        System.out.println("坦克出现坐标:"+x+","+y);
    }

    @Override
    public void attack() {
        System.out.println("坦克用炮轰击,攻击力: "+ attack);
    }
}
//巨型战舰类Battleship
package abstractFactory;

public class Battleship extends HighClassUnit {
    public Battleship(int x, int y) {
        super(x, y);
    }

    @Override
    public void show() {
        System.out.println("战舰出现坐标:"+x+","+y);
    }

    @Override
    public void attack() {
        System.out.println("战舰用机关炮射击,攻击力: "+ attack);
    }
}
//蟑螂类Roach
package abstractFactory;

public class Roach extends LowClassUnit {
    public Roach(int x, int y) {
        super(x, y);
    }

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

    @Override
    public void attack() {
        System.out.println("蟑螂兵攻击,攻击力: "+ attack);
    }
}
//毒液类Poison
package abstractFactory;

public class Poison extends MidClassUnit {
    public Poison(int x, int y) {
        super(x, y);
    }

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

    @Override
    public void attack() {
        System.out.println("毒液兵攻击,攻击力: "+ attack);
    }
}
//猛犸类Mammoth
package abstractFactory;

public class Mammoth extends HighClassUnit {
    public Mammoth(int x, int y) {
        super(x, y);
    }

    @Override
    public void show() {
        System.out.println("猛犸巨兽出现坐标:"+x+","+y);
    }

    @Override
    public void attack() {
        System.out.println("猛犸巨兽攻击,攻击力: "+ attack);
    }
}

Go版本

由于go不能使用无抽象类,但是我们通过如下组合,可以实现类似抽象类的继承关系

package abstractfactory

type unit struct {
    attackPower int
    defence     int
    health      int
    x           int
    y           int
    show        func()
    attack      func()
}

type LowClassUnit unit

func (e LowClassUnit) Show() {
    e.show()
}
func (e LowClassUnit) Attack() {
    e.attack()
}
func newLowClassUnit(x, y int) LowClassUnit {
    return LowClassUnit{
        attackPower: 5,
        defence:     2,
        health:      35,
        x:           x,
        y:           y,
    }
}

type MidClassUnit unit

func (e MidClassUnit) Show() {
    e.show()
}
func (e MidClassUnit) Attack() {
    e.attack()
}

func newMidClassUnit(x, y int) MidClassUnit {
    return MidClassUnit{
        attackPower: 10,
        defence:     8,
        health:      80,
        x:           x,
        y:           y,
    }
}

type HighClassUnit unit

func (e HighClassUnit) Show() {
    e.show()
}
func (e HighClassUnit) Attack() {
    e.attack()
}
func newHighClassUnit(x, y int) HighClassUnit {

    return HighClassUnit{
        attackPower: 25,
        defence:     30,
        health:      300,
        x:           x,
        y:           y,
    }
}

生产线规划

既然产品类的数据模型构建完成,相应的产品生产线也应该建立起来,接下来我们就可以定义这些产品的制造工厂了。我们一共定义了6个兵种产品,那么每个产品都需要对应一个工厂类吗?答案是否定的。本着人类靠科技、怪兽靠繁育的游戏理念,人类兵工厂自然是高度工业化的,而怪兽的生产一定靠的是母巢繁殖,所以应该将工厂分为2个族,并且每个族工厂都应该拥有3个等级兵种的制造方法。如此规划不但合理,而且避免了工厂类泛滥的问题。那么,首先我们来制定这3个工业制造标准,也就是定义抽象工厂接口

Java版

package abstractFactory;
//抽象兵工厂接口AbstractFactory
public interface AbstractFactory {
        LowClassUnit createLowClass();//初级兵种制造标准
        MidClassUnit createMidClass();//中级
        HighClassUnit createHighClass();//高级
}
//人类兵工厂HumanFactory
package abstractFactory;


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;
    }
}
//外星母巢AlienFactory
package abstractFactory;

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;
    }
}
//客户端类Client
package abstractFactory;

import java.sql.SQLOutput;

public class client {
    public static void main(String[] args) {
        System.out.println("game start.....");
        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();
        tank.attack();
        poison.attack();
        ship.attack();
        mammoth.attack();
    }
}

Go版本

package abstractfactory

import "fmt"

type Abstractfactory interface {
    CreateLowClass() LowClassUnit
    CreateMidClass() MidClassUnit
    CreateHighClass() HighClassUnit
}

//HunmanFactory
type HunmanFactory struct {
    X int
    Y int
}

func (h *HunmanFactory) CreateLowClass() LowClassUnit {
    unit := newLowClassUnit(h.X, h.Y)
    unit.show = func() {
        fmt.Printf("士兵出现在坐标 %d,%d", unit.x, unit.y)
        fmt.Println()
    }
    unit.attack = func() {
        fmt.Printf("士兵用机关枪设计,攻击力为:%d", unit.attackPower)
        fmt.Println()
    }
    return unit
}

func (h *HunmanFactory) CreateMidClass() MidClassUnit {
    unit := newMidClassUnit(h.X, h.Y)
    unit.show = func() {
        fmt.Printf("坦克出现在坐标 %d,%d", unit.x, unit.y)
        fmt.Println()
    }
    unit.attack = func() {
        fmt.Printf("坦克用机关枪设计,攻击力为:%d", unit.attackPower)
        fmt.Println()
    }
    return unit
}

func (h *HunmanFactory) CreateHighClass() HighClassUnit {
    unit := newHighClassUnit(h.X, h.Y)
    unit.show = func() {
        fmt.Printf("战舰出现在坐标 %d,%d", unit.x, unit.y)
        fmt.Println()
    }
    unit.attack = func() {
        fmt.Printf("战舰攻击,攻击力为:%d", unit.attackPower)
        fmt.Println()
    }
    return unit
}

// AlienFactory
type AlienFactory struct {
    X int
    Y int
}

func (a *AlienFactory) CreateLowClass() LowClassUnit {
    unit := newLowClassUnit(a.X, a.Y)
    unit.show = func() {
        fmt.Printf("蟑螂兵出现在坐标 %d,%d", unit.x, unit.y)
        fmt.Println()
    }
    unit.attack = func() {
        fmt.Printf("蟑螂兵攻击,攻击力为:%d", unit.attackPower)
        fmt.Println()
    }
    return unit
}

func (a *AlienFactory) CreateMidClass() MidClassUnit {
    unit := newMidClassUnit(a.X, a.Y)
    unit.show = func() {
        fmt.Printf("毒液出现在坐标 %d,%d", unit.x, unit.y)
        fmt.Println()
    }
    unit.attack = func() {
        fmt.Printf("毒液攻击,攻击力为:%d", unit.attackPower)
        fmt.Println()
    }
    return unit
}
func (a *AlienFactory) CreateHighClass() HighClassUnit {
    unit := newHighClassUnit(a.X, a.Y)
    unit.show = func() {
        fmt.Printf("猛犸出现在坐标 %d,%d", unit.x, unit.y)
        fmt.Println()
    }
    unit.attack = func() {
        fmt.Printf("猛犸攻击,攻击力为:%d", unit.attackPower)
        fmt.Println()
    }
    return unit
}

main.go

package main

import abstractfactory "desginPatterns/AbstractFactory"

func main() {
    var Abstractfactory abstractfactory.Abstractfactory = &abstractfactory.HunmanFactory{
        X: 1,
        Y: 2,
    }
    marine := Abstractfactory.CreateLowClass()
    tank := Abstractfactory.CreateMidClass()
    ship := Abstractfactory.CreateHighClass()

    Abstractfactory = &abstractfactory.AlienFactory{
        X: 2,
        Y: 3,
    }
    roach := Abstractfactory.CreateLowClass()
    poison := Abstractfactory.CreateMidClass()
    mammoth := Abstractfactory.CreateHighClass()
    marine.Show()
    marine.Attack()
    tank.Show()
    tank.Attack()
    ship.Show()
    ship.Attack()
    roach.Show()
    roach.Attack()
    poison.Show()
    poison.Attack()
    mammoth.Show()
    mammoth.Attack()
}

分而治之

至此,抽象工厂制造模式已经布局完成,各工厂可以随时大规模投入生产活动了。当然,我们还可以进一步,再加一个“制造工厂的工厂”来决定具体让哪个工厂投入生产活动。此时客户端就无须关心工厂的实例化过程了,直接使用产品就可以了,至于产品属于哪个族也已经无关紧要,这也是抽象工厂可以被视为“工厂的工厂”的原因

与工厂方法模式不同,抽象工厂模式能够应对更加复杂的产品族系,它更类似于一种对“工业制造标准”的制定与推行,各工厂实现都遵循此标准来进行生产活动,以工厂类划分产品族,以制造方法划分产品系列,达到无限扩展产品的目的。最后我们来看抽象工厂模式的类结构

AbstractProduct1、AbstractProduct2(抽象产品1、抽象产品2):产品系列的抽象类,图中一系产品与二系产品分别代表同一产品族的多个产品系列,对应本章例程中的初级、中级、高级兵种抽象类。

ProductA1、ProductB1、ProductA2、ProductB2(产品A1、产品B1、产品A2、产品B2):继承自抽象产品的产品实体类,其中ProductA1与ProductB1代表A族产品与B族产品的同一产品系列,类似于本章例程中人类族与外星怪兽族的初级兵种,之后的产品实体类以此类推。

AbstractFactory(抽象工厂接口):各族工厂的高层抽象,可以是接口或者抽象类。抽象工厂对各产品系列的制造标准进行规范化定义,但具体返回哪个族的产品由具体族工厂决定,它并不关心。

ConcreteFactoryA、ConcreteFactoryB(工厂A实现、工厂B实现):继承自抽象工厂的各族工厂,需实现抽象工厂所定义的产品系列制造方法,可以扩展多个工厂实现。对应本章例程中的人类兵工厂与外星母巢。

Client(客户端):产品的使用者,只关心制造出的产品系列,具体是哪个产品族由工厂决定。

产品虽然繁多,但总有品牌、系列之分。基于此抽象工厂模式以品牌与系列进行全局规划,将看似杂乱无章的产品规划至不同的族系,再通过抽象工厂管理起来,分而治之,合纵连横。需要注意的是,抽象工厂模式一定是基于产品的族系划分来布局的,其产品系列一定是相对固定的,故以抽象工厂来确立工业制造标准(各产品系列生产接口)。而产品族则可以相对灵活多变,如此一来,我们就可以方便地扩展与替换族工厂,以达到灵活产出各类产品族系的目的。