💡gntk.dev

【Template Method】GoFのデザインパターンを学ぶ

GoFのデザインパターンをTypeScriptで写経しました。今回はTemplate Methodパターンです。

post-cover

概要

Template Methodは、スーパークラスにいくつかの抽象メソッドを用意して処理の枠組みを決め、サブクラスでそれらを実装して具体的な処理を決定するデザインパターン。

サンプルプログラム

結城浩先生の増補改訂版Java言語で学ぶデザインパターン入門のJavaプログラムをTypeScriptで写経した。

以下のようなスーパークラスAbstractDisplayを用意する。

export abstract class AbstractDisplay {
  public abstract open(): string;
  public abstract print(): string;
  public abstract close(): string;
  public display(): string {
    const open = this.open();
    let print = '';
    for (let i = 0; i < 5; i++) {
      this.print();
      print += this.print();
    }
    const close = this.close();

    return open + print + close;
  }
}

文字列を5回繰り返してカッコなどの装飾をつけて表示するためのスーパークラスである。どのような装飾をつけるかは、サブクラスの実装による。

サブクラスCharDisplayを以下のように実装する。

import { AbstractDisplay } from 'templateMethod/abstractDisplay';

export class CharDisplay extends AbstractDisplay {
  private ch: string;

  public constructor(ch: string) {
    super();
    this.ch = ch;
  }

  public open(): string {
    return '<<';
  }

  public print(): string {
    return this.ch;
  }

  public close(): string {
    return '>>';
  }
}

以下のテストが通る。

  test('charDisplay', () => {
    const charDisplay: AbstractDisplay = new CharDisplay('H');
    const actual = charDisplay.display();
    const expected = '<<HHHHH>>';

    expect(actual).toEqual(expected);
  });

サブクラスStringDisplayを以下のように実装する。

import { AbstractDisplay } from 'templateMethod/abstractDisplay';

export class StringDisplay extends AbstractDisplay {
  private string: string;
  private width: number;

  constructor(string: string) {
    super();
    this.string = string;
    this.width = string.length;
  }

  public open(): string {
    return this.printLine();
  }

  public print(): string {
    return '|' + this.string + '|\n';
  }

  public close(): string {
    return this.printLine();
  }

  private printLine(): string {
    let line = '';
    for (let i = 0; i < this.width; i++) {
      line += '-';
    }
    return '+' + line + '+\n';
  }
}

以下のテストが通る。

  test('stringDisplay', () => {
    const stringDisplay: AbstractDisplay = new StringDisplay('Hello, World');
    const actual = stringDisplay.display();
    const expected = `+------------+
|Hello, World|
|Hello, World|
|Hello, World|
|Hello, World|
|Hello, World|
+------------+
`;

    expect(actual).toEqual(expected);
  });

考察・感想

本のJavaプログラムだと、AbstractDisplaydisplayメソッドはfinalがついていてオーバーライド禁止になっていたが、typescriptでそれを表現する方法がわからなかった。この記事ではReadOnly<T>型のインスタンスを返すファクトリメソッドを用意する方法でメソッドをオーバーライド禁止にしている。ちょうど次回はファクトリメソッド。

© 2021 gntk.dev