💡gntk.dev

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

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

post-cover

概要

Adapterパターンは、既に提供されているクラス等を使いたいがそのままでは使えないとき、必要な形に変換してから利用するための文字通り「アダプター」を提供するデザインパターン。

サンプルプログラム

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

以下のようなBannerクラスが用意されているとする。

export class Banner {
  private string: string;

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

  public showWithParen(): string {
    return `(${this.string})`;
  }

  public showWithAster(): string {
    return `*${this.string}*`;
  }
}

Bannerは、文字列をカッコでくくって表示するshowWithParenメソッド、文字列をアスタリスクでくくって表示するshowWithAsterを持つ。

一方で、Bannarを利用する側でPrintインターフェースが以下のように宣言されているとする。

interface Print {
  printWeak(): string;
  printStrong(): string;
}

Printインターフェースは、文字列を弱く(カッコでくくって)表示するprintWeakメソッド、文字列を強く(アスタリスクでくくって)表示するprintStrongメソッドを持つ。

以下のようにアダプターPrintBannerを使って、(Bannerによって実装された)Printを利用できるようにしたい。

import { PrintBanner } from 'adapter/inheritance/printBanner';
import { Print } from 'adapter/inheritance/print';

describe('PrintBanner', () => {
  const p: Print = new PrintBanner('Hello');

  test('printWeak', () => {
    expect(p.printWeak()).toEqual('(Hello)');
  });

  test('printStrong', () => {
    expect(p.printStrong()).toEqual('*Hello*');
  });
});

PrintBannerは、Bannerを使ってPrintインターフェースを実装するためのアダプターである。

継承を使ったAdapterパターンと委譲を使ったAdapterパターンの2パターンを紹介する。

継承を使ったパターン

継承を使ったパターンでは、以下のようにアダプターPrintBannerクラスを用意する。

import { Banner } from 'adapter/bannar';
import { Print } from 'adapter/inheritance/print';

export class PrintBanner extends Banner implements Print {
  public constructor(string: string) {
    super(string);
  }

  public printWeak(): string {
    return this.showWithParen();
  }

  public printStrong(): string {
    return this.showWithAster();
  }
}

extendsを使いPrintBannershowWithParenshowWithAsterを持たせ、それらをprintWeakprintStrongとして使えるようにしている。

委譲を使ったパターン

Printがインターフェースではなくクラスだった場合、委譲を使ったパターンを適用できる。

export abstract class Print {
  public abstract printWeak(): string;
  public abstract printStrong(): string;
}

委譲を使ったパターンでは、以下のようにアダプターPrintBannerクラスを用意する。

import { Banner } from 'adapter/bannar';
import { Print } from 'adapter/delegation/print';

export class PrintBanner extends Print {
  private banner: Banner;

  public constructor(string: string) {
    super();
    this.banner = new Banner(string);
  }

  public printWeak(): string {
    return this.banner.showWithParen();
  }

  public printStrong(): string {
    return this.banner.showWithAster();
  }
}

PrintBannerクラスのプロパティにBannerのインスタンスbannerを用意し、それらを使ってprintWeakprintStrongを実装している。

考察・感想

テストの内容は、本の中でmainクラスとして書かれている内容をテストの形に落とし込んだもの。本の中のmainクラスでPrintBannerのインスタンスpPrintBanner型ではなくPrint型にしているのがなるほどな〜と思った。「pはPrintのプロパティ・メソッドだけを持っていますよ」という意図が示せている。

© 2021 gntk.dev