使用口袋妖怪做案例演示Javascript的抽象工厂

19-04-17 banq
         

抽象工厂模式提供了一种封装一组具有共同主题但没有指定其具体类的单个工厂的方法。

假设您希望能够创建任何类型的口袋妖怪:让我们定义Pokemon抽象。

口袋妖怪抽象类

所有宠物小精灵都必须扩展这个抽象类

interface IPokemonProps {
  name: string;
  color: string;
}

abstract class Pokemon implements IPokemonProps {
  public name: string;
  public color: string;

  constructor (props: IPokemonProps) {
    this.name = props.name;
    this.color = props.color;
  }

  abstract attack (): IAttack;
}

每个口袋妖怪必须有一个name和color与具体口袋妖怪类必须实现抽象attack方法。不同的口袋妖怪有不同的攻击,对吧?这就是为什么它是抽象的。子类将定义它。

好吧,让我们创建我们的第一个神奇宝贝。让我们从皮卡丘开始吧。

我们如何创建皮卡丘?

我们的第一家工厂,皮卡丘工厂

假设,假设我们想创建一个皮卡丘,我们需要做以下事情:

  • 得到一堆电池
  • 得到一些磁带
  • 得到一些油漆
  • 得到一只猫
  • 将电池贴在猫背上

在这里运用你的想象力,好吗?

因为创建这个特定的口袋妖怪显然有一个过程,让我们把它放到一个抽象的工厂里。

class Pikachu extends Pokemon {
  private cat: TapedItem<Battery[], Cat>;

  constructor (cat: TapedItem<Battery[], Cat>) {
    super({ name: 'Pikachu', color: 'yellow' });
    this.cat = cat;
  }

  attack () : ZapAttack {
    return this.cat.zapAttack();
  }
}

class PikachuFactory {
  public static create (): Pikachu {
    const batteries: Battery[] = [
      new Battery(),
      new Battery()
    ];
    const paint: Paint = new Paint('yellow');
    const tape: Tape = new Tape();
    const cat: Cat = new Cat();

    const paintedCat: PaintedItem<Cat> = Paint.paintItem(cat, paint);

    const catTapedByBatteries: TapedItem<Battery[], Cat> = Tape
      .combineItems(batteries, paintedCat);

    return new Pikachu(catTapedByBatteries);
  }
}

好的很棒,我们有办法创建Pikachus。

我们可以这样做。

const pikachu: Pikachu = PikachuFactory.create();

我们在工厂内部封装了所有复杂的皮卡丘创造逻辑。

更多口袋妖怪工厂

现在让我们说我们想建立一个Charmander工厂,一个Bulbasaur工厂和一个Porygon工厂。而且他们每个人也会有同样创造性和复杂的方式来创造它们,封装在某种类型的口袋妖怪工厂内。

我们希望能够像这样创建所有这些:

const charmander: Charmander = CharmanderFactory.create();
const bulbasaur: Bulbasaur = BulbasaurFactory.create();
const porygon: Porygon = PorygonFactory.create();

理想情况下,我们希望以某种方式封装口袋妖怪的生产。根据定义:

抽象工厂模式提供了一种封装一组具有共同主题但没有指定其具体类的单个工厂的方法。

我们不需要通过导入我们想要的Pokemon类型来创建硬的源代码依赖:

import { Charmander } from 'pokemon/charmander'
import { CharmanderFactory } from 'pokemon/charmander/factory'
import { Bulbasaur } from 'pokemon/bulbasaur'
import { BulbasaurFactory } from 'pokemon/bulbasaur/factory'
import { Porygon } from 'pokemon/porygon'
import { PorygonFactory } from 'pokemon/porygon/factory'

const charmander: Charmander = CharmanderFactory.create();
const bulbasaur: Bulbasaur   = BulbasaurFactory.create();
const porygon: Porygon       = PorygonFactory.create();

我们可以使用abstract PokemonFactory

import { PokemonFactory, PokemonType } from 'pokemon/factory'
import { Charmander } from 'pokemon/charmander'
import { Bulbasaur } from 'pokemon/bulbasaur'
import { Porygon } from 'pokemon/porygon'

const charmander: Charmander = PokemonFactory.create(PokemonType.CHARMANDER);
const bulbasaur: Bulbasaur   = PokemonFactory.create(PokemonType.BULBASAUR);
const porygon: Porygon       = PokemonFactory.create(PokemonType.PORYGON);

PokemonFactory的代码:

import { CharmanderFactory } from 'pokemon/charmander/factory'
import { BulbasaurFactory } from 'pokemon/bulbasaur/factory'
import { PorygonFactory } from 'pokemon/porygon/factory'

enum PokemonType {
  CHARMANDER = 'charmander',
  BULBASAUR = 'bulbasaur',
  PORYGON = 'porygon'
}

export class PokemonFactory {
  public static create(pokemonType: PokemonType): Pokemon {
    switch (pokemonType) {
      case PokemonType.CHARMANDER:
        return CharmanderFactory.create();
      case PokemonType.BULBASAUR:
        return BulbasaurFactory.create();
      case PokemonType.PORYGON:
        return PorygonFactory.create();
      default:
        return null;
    }
  }
}

我们在这里所做的是抽象我们如何创建口袋妖怪。我们还将口袋妖怪创作的单一责任委托给了一个地方。当我们必须添加新的Pokemon时,我们添加一个新的PokemonType,创建新工厂并将其添加到此switch语句的末尾。