Day 07: Creational patterns - Abstract Factory

目的

假如产品之间有可以负责联系的元素,那依赖该元素找出共同点後建立关联,进而减少工厂的数量,却可以维持更多的生产。

说明

昨天提到 Factory Method,建立许多工厂来负责生产产品。让我们试想一个情境,现在行销部门决定要建立多种口味的饮料,因此会有樱桃口味、柠檬口味与加盐,套用在现有的三个产品(可乐、矿泉水、柳橙汁),会有九种结果,此时,必须建立多达九个的 Factory 来处理这件事。

可乐 矿泉水 柳橙汁
樱桃口味 樱桃口味可乐 人工樱桃风味矿泉水 樱桃柳橙汁
柠檬口味 柠檬口味可乐 柠檬矿泉水 柠檬柳橙汁
加盐 加盐可乐 加盐矿泉水 加盐柳橙汁

此时,可以使用 Abstract Factory 减少工厂的数量,作法是:

  1. 将原有产品转变成虚拟层,视为亲代,可以用 Abstract ClassInterface
  2. 原有产品每一个都建立三个口味的子代。
  3. 建立工厂的亲代,可以用 Abstract ClassInterface,负责要求每间口味工厂需要生产三种原有产品。
  4. 建立一系列的口味工厂,每间工厂都可以产出以原有口味为基底的新饮料。
  5. 决策者依据口味使用对应的工厂,好取得对应的新饮料。

UML 图

Abstract Factory Pattern UML Diagram

使用 Java 实作

产品祖代:PETBottle

public abstract class PETBottle {
    protected String smell = "";
    protected String color = "";

    protected PETBottle(String smell, String color) {
        this.smell = smell;
        this.color = color;
    }

    public abstract String getTaste();
}

产品亲代-可乐:Coke

public abstract class Coke extends PETBottle {
    protected Coke(String smell) {
        super(smell, "黑色");
    }

    @Override
    public String getTaste() {
        return "这瓶可乐的颜色:" + color + ",香味是:" + smell;
    }
}

产品子代-可乐相关口味系列:CherryCokeLemonCokeSaltCoke

public class CherryCoke extends Coke {
    public CherryCoke() {
        super("樱桃味道");
    }

    @Override
    public String getTaste() {
        return "这瓶樱桃可乐的颜色:" + color + ",香味是:" + smell;
    }
}

public class LemonCoke extends Coke {
    public LemonCoke() {
        super("清新的酸味");
    }

    @Override
    public String getTaste() {
        return "这瓶柠檬可乐的颜色:" + color + ",香味是:" + smell;
    }
}

public class SaltCoke extends Coke {
    public SaltCoke() {
        super("咸咸的");
    }

    @Override
    public String getTaste() {
        return "这瓶加盐可乐的颜色:" + color + ",香味是:" + smell;
    }
}

产品亲代-矿泉水:Water

public abstract class Water extends PETBottle {
    protected Water(String smell) {
        super(smell, "透明");
    }

    @Override
    public String getTaste() {
        return "这瓶水的颜色:" + color + ",香味是:" + smell;
    }
}

产品子代-矿泉水相关口味系列:CherryWaterLemonWaterSaltWater

public class CherryWater extends Water {
    public CherryWater() {
        super("樱桃味道");
    }

    @Override
    public String getTaste() {
        return "这瓶人工樱桃风味矿泉水的颜色:" + color + ",香味是:" + smell;
    }
}

public class LemonWater extends Water {
    public LemonWater() {
        super("清新的酸味");
    }

    @Override
    public String getTaste() {
        return "这瓶柠檬矿泉水的颜色:" + color + ",香味是:" + smell;
    }
}

public class SaltWater extends Water {
    public SaltWater() {
        super("咸咸的");
    }

    @Override
    public String getTaste() {
        return "这瓶加盐矿泉水的颜色:" + color + ",香味是:" + smell;
    }
}

产品亲代-柳橙汁:OrangeJuice

public abstract class OrangeJuice extends PETBottle {
    protected OrangeJuice(String smell) {
        super(smell, "橘色");
    }

    @Override
    public String getTaste() {
        return "这瓶柳橙汁的颜色:" + color + ",香味是:" + smell;
    }
}

产品子代-柳橙汁相关口味系列:CherryOrangeJuiceLemonOrangeJuiceSaltOrangeJuice

public class CherryOrangeJuice extends OrangeJuice {
    public CherryOrangeJuice() {
        super("樱桃味道");
    }

    @Override
    public String getTaste() {
        return "这瓶樱桃柳橙汁的颜色:" + color + ",香味是:" + smell;
    }
}

public class LemonOrangeJuice extends OrangeJuice {
    public LemonOrangeJuice() {
        super("柑橘类特有的酸味");
    }

    @Override
    public String getTaste() {
        return "这瓶柠檬柳橙汁的颜色:" + color + ",香味是:" + smell;
    }
}

public class SaltOrangeJuice extends OrangeJuice {
    public SaltOrangeJuice() {
        super("咸咸的");
    }

    @Override
    public String getTaste() {
        return "这瓶加盐柳橙汁的颜色:" + color + ",香味是:" + smell;
    }
}

工厂亲代:BeverageFactory

public interface BeverageFactory {
    Coke produceCoke();

    Water produceWater();

    OrangeJuice produceOrangeJuice();
}

工厂子代-相关口味系列:CherryFlavorFactoryLemonFlavorFactorySaltFlavorFactory

public class CherryFlavorFactory implements BeverageFactory {
    @Override
    public Coke produceCoke() {
        return new CherryCoke();
    }

    @Override
    public Water produceWater() {
        return new CherryWater();
    }

    @Override
    public OrangeJuice produceOrangeJuice() {
        return new CherryOrangeJuice();
    }
}

public class LemonFlavorFactory implements BeverageFactory {
    @Override
    public Coke produceCoke() {
        return new LemonCoke();
    }

    @Override
    public Water produceWater() {
        return new LemonWater();
    }

    @Override
    public OrangeJuice produceOrangeJuice() {
        return new LemonOrangeJuice();
    }
}

public class SaltFlavorFactory implements BeverageFactory {
    @Override
    public Coke produceCoke() {
        return new SaltCoke();
    }

    @Override
    public Water produceWater() {
        return new SaltWater();
    }

    @Override
    public OrangeJuice produceOrangeJuice() {
        return new SaltOrangeJuice();
    }
}

决策者:

public class BeverageAbstractFactorySample {
    public static void main(String[] args) {
        System.out.println("准备开喝!");
        BeverageFactory saltFlavorFactory = new SaltFlavorFactory();
        Coke saltCoke = saltFlavorFactory.produceCoke();
        Water saltWater = saltFlavorFactory.produceWater();
        OrangeJuice saltOrangeJuice = saltFlavorFactory.produceOrangeJuice();

        System.out.println("盐味派对!");
        System.out.println(saltCoke.getTaste());
        System.out.println(saltWater.getTaste());
        System.out.println(saltOrangeJuice.getTaste());

        System.out.println("\n口味更换");
        BeverageFactory lemonFlavorFactory = new LemonFlavorFactory();
        Coke lemonCoke = lemonFlavorFactory.produceCoke();
        Water lemonWater = lemonFlavorFactory.produceWater();
        OrangeJuice lemonOrangeJuice = lemonFlavorFactory.produceOrangeJuice();

        System.out.println("柠檬派对!");
        System.out.println(lemonCoke.getTaste());
        System.out.println(lemonWater.getTaste());
        System.out.println(lemonOrangeJuice.getTaste());

        System.out.println("\n最後一轮");
        BeverageFactory cherryFlavorFactory = new CherryFlavorFactory();
        Coke cherryCoke = cherryFlavorFactory.produceCoke();
        Water cherryWater = cherryFlavorFactory.produceWater();
        OrangeJuice cherryOrangeJuice = cherryFlavorFactory.produceOrangeJuice();

        System.out.println("樱桃派对!");
        System.out.println(cherryCoke.getTaste());
        System.out.println(cherryWater.getTaste());
        System.out.println(cherryOrangeJuice.getTaste());
    }
}

使用 JavaScript 实作

受限於 JavaScript 没有虚拟型别、无法限制型别。

产品祖代:PETBottle

/** @abstract */
class PETBottle {
  constructor(smell, color) {
    this.smell = smell;
    this.color = color;
  }

  getTaste() { return; }
}

产品亲代-可乐:Coke

/** @abstract */
class Coke extends PETBottle {
  constructor(smell, color) {
    super(smell, "黑色");
  }

  /** @override */
  getTaste() {
    return "这瓶可乐的颜色:" + this.color + ",香味是:" + this.smell;
  }
}

产品子代-可乐相关口味系列:CherryCokeLemonCokeSaltCoke

class CherryCoke extends Coke {
  constructor() {
    super("樱桃风味");
  }

  /** @override */
  getTaste() {
    return "这瓶樱桃可乐的颜色:" + this.color + ",香味是:" + this.smell;
  }
}

class LemonCoke extends Coke {
  constructor() {
    super("清新的酸味");
  }

  /** @override */
  getTaste() {
    return "这瓶柠檬可乐的颜色:" + this.color + ",香味是:" + this.smell;
  }
}

class SaltCoke extends Coke {
  constructor() {
    super("咸咸的");
  }

  /** @override */
  getTaste() {
    return "这瓶加盐可乐的颜色:" + this.color + ",香味是:" + this.smell;
  }
}

产品亲代-矿泉水:Water

/** @abstract */
class Water extends PETBottle {
  constructor(smell) {
    super(smell, "透明");
  }

  /** @override */
  getTaste() {
    return "这瓶水的颜色:" + this.color + ",香味是:" + this.smell;
  }
}

产品子代-矿泉水相关口味系列:CherryWaterLemonWaterSaltWater

class CherryWater extends Water {
  constructor() {
    super("樱桃味道");
  }

  /** @override */
  getTaste() {
    return "这瓶人工樱桃风味矿泉水的颜色:" + this.color + ",香味是:" + this.smell;
  }
}

class LemonWater extends Water {
  constructor() {
    super("清新的酸味");
  }

  /** @override */
  getTaste() {
    return "这瓶柠檬矿泉水的颜色:" + this.color + ",香味是:" + this.smell;
  }
}

class SaltWater extends Water {
  constructor() {
    super("咸咸的");
  }

  /** @override */
  getTaste() {
    return "这瓶加盐矿泉水的颜色:" + this.color + ",香味是:" + this.smell;
  }
}

产品亲代-柳橙汁:OrangeJuice

/** @abstract */
class OrangeJuice extends PETBottle {
  constructor(smell) {
    super(smell, "橘色");
  }

  /** @override */
  getTaste() {
    return "这瓶柳橙汁的颜色:" + this.color + ",香味是:" + this.smell;
  }
}

产品子代-柳橙汁相关口味系列:CherryOrangeJuiceLemonOrangeJuiceSaltOrangeJuice

class CherryOrangeJuice extends OrangeJuice {
  constructor() {
    super("樱桃味道");
  }

  /** @override */
  getTaste() {
    return "这瓶樱桃柳橙汁的颜色:" + this.color + ",香味是:" + this.smell;
  }
}

class LemonOrangeJuice extends OrangeJuice {
  constructor() {
    super("柑橘类特有的酸味");
  }

  /** @override */
  getTaste() {
    return "这瓶柠檬柳橙汁的颜色:" + this.color + ",香味是:" + this.smell;
  }
}

class SaltOrangeJuice extends OrangeJuice {
  constructor() {
    super("咸咸的");
  }

  /** @override */
  getTaste() {
    return "这瓶加盐柳橙汁的颜色:" + this.color + ",香味是:" + this.smell;
  }
}

工厂亲代:BeverageFactory

/** @interface */
class BeverageFactory {
  produceCoke() { return; }

  produceWater() { return; }

  produceOrangeJuice() { return; }
}

工厂子代-相关口味系列:CherryFlavorFactoryLemonFlavorFactorySaltFlavorFactory

class CherryFlavorFactory extends BeverageFactory {
  /** @override */
  produceCoke() {
    return new CherryCoke();
  }

  /** @override */
  produceWater() {
    return new CherryWater();
  }

  /** @override */
  produceOrangeJuice() {
    return new CherryOrangeJuice();
  }
}

class LemonFlavorFactory extends BeverageFactory {
  /** @override */
  produceCoke() {
    return new LemonCoke();
  }

  /** @override */
  produceWater() {
    return new LemonWater();
  }

  /** @override */
  produceOrangeJuice() {
    return new LemonOrangeJuice();
  }
}

class SaltFlavorFactory extends BeverageFactory {
  /** @override */
  produceCoke() {
    return new SaltCoke();
  }

  /** @override */
  produceWater() {
    return new SaltWater();
  }

  /** @override */
  produceOrangeJuice() {
    return new SaltOrangeJuice();
  }
}

实作:

const beverageAbstractFactorySample = () => {
  console.log("准备开喝!");
  const saltFlavorFactory = new SaltFlavorFactory();
  const saltCoke = saltFlavorFactory.produceCoke();
  const saltWater = saltFlavorFactory.produceWater();
  const saltOrangeJuice = saltFlavorFactory.produceOrangeJuice();

  console.log("盐味派对!");
  console.log(saltCoke.getTaste());
  console.log(saltWater.getTaste());
  console.log(saltOrangeJuice.getTaste());

  console.log("\n口味更换");
  const lemonFlavorFactory = new LemonFlavorFactory();
  const lemonCoke = lemonFlavorFactory.produceCoke();
  const lemonWater = lemonFlavorFactory.produceWater();
  const lemonOrangeJuice = lemonFlavorFactory.produceOrangeJuice();

  console.log("柠檬派对!");
  console.log(lemonCoke.getTaste());
  console.log(lemonWater.getTaste());
  console.log(lemonOrangeJuice.getTaste());

  console.log("\n最後一轮");
  const cherryFlavorFactory = new CherryFlavorFactory();
  const cherryCoke = cherryFlavorFactory.produceCoke();
  const cherryWater = cherryFlavorFactory.produceWater();
  const cherryOrangeJuice = cherryFlavorFactory.produceOrangeJuice();

  console.log("樱桃派对!");
  console.log(cherryCoke.getTaste());
  console.log(cherryWater.getTaste());
  console.log(cherryOrangeJuice.getTaste());
};

beverageAbstractFactorySample();

总结

Abstract Factory 改善了 Factory Method 面对更多种产品时,必须建立各自的工厂导致工厂数量过多的问题。当然,缺点也很明显,「必须找到多种产品之间有没有关联」,如果有才能抽离、转换成关系、建立工厂、减少数量;如果没有则无计可施,只能维持 Factory Method 多工厂的局面。

三个 Factory 模式介绍完毕,接着依照字母顺序,介绍 Builder 模式。


<<:  捉鳖神技 - 如何逆推使用者意图 (观念篇)

>>:  [Day 07] 使用 fastAPI 部署 YOLOv4 (1/2) — 以内建 Client 进行互动

Day 11-Atlantis 做 Terraform Remote Plan & Remote Apply

使用 atlantis 做 terraform automation,Terraform Remot...

Kotlin Android 第22天,从 0 到 ML - Canvas

前言: 今天来介绍使用Canvas 的绘图方法来创建 2D 绘图,并画出触控手势的轨迹。 大纲 : ...

【Day 27】Cmd 指令很乱,主办单位要不要管一下 (上) - Cmd 指令混淆

环境 Windows 10 21H1 System Monitor v13.01 前情提要 在【Da...

[Day26] - Django-REST-Framework API 期末专案实作 (一)

不知不觉,铁人赛慢慢要进入尾声了,感谢过程中队友们彼此提携,互相提醒。 在前几天中,和大家介绍了 D...

[RouterOS] NAT port 映射问题

大家好 我刚接触RouterOS没多久 因为内网有电脑在架SERVER 必须开 PORT 映射 所以...